blob: f695b6dc4f403ad25a283519dbb287ae06d35991 [file] [edit]
import os
import pwd
import re
import platform
import socket
import crm_script
PACKAGES = ['booth', 'cluster-glue', 'corosync', 'crmsh', 'csync2', 'drbd',
'fence-agents', 'gfs2', 'gfs2-utils', 'hawk', 'ocfs2',
'ocfs2-tools', 'pacemaker', 'pacemaker-mgmt',
'resource-agents', 'sbd']
SERVICES = ['sshd', 'ntp', 'corosync', 'pacemaker', 'hawk', 'SuSEfirewall2_init']
SSH_KEY = os.path.expanduser('~/.ssh/id_rsa')
CSYNC2_KEY = '/etc/csync2/key_hagroup'
CSYNC2_CFG = '/etc/csync2/csync2.cfg'
COROSYNC_CONF = '/etc/corosync/corosync.conf'
SYSCONFIG_SBD = '/etc/sysconfig/sbd'
SYSCONFIG_FW = '/etc/sysconfig/SuSEfirewall2'
SYSCONFIG_FW_CLUSTER = '/etc/sysconfig/SuSEfirewall2.d/services/cluster'
def rpm_info():
'check installed packages'
return crm_script.rpmcheck(PACKAGES)
def service_info(service):
"Returns information about a given service"
active, enabled = 'unknown', 'unknown'
rc, out, err = crm_script.call(["/usr/bin/systemctl", "is-enabled", "%s.service" % (service)])
if rc in (0, 1, 3) and out:
enabled = out.strip()
else:
return {'name': service, 'error': err.strip()}
rc, out, err = crm_script.call(["/usr/bin/systemctl", "is-active", "%s.service" % (service)])
if rc in (0, 1, 3) and out:
active = out.strip()
else:
return {'name': service, 'error': err.strip()}
return {'name': service, 'active': active, 'enabled': enabled}
def services_info():
'check enabled/active services'
return [service_info(service) for service in SERVICES]
def get_user():
return pwd.getpwuid(os.getuid()).pw_name
def sys_info():
'system information'
system, node, release, version, machine, processor = platform.uname()
distname, distver, distid = platform.linux_distribution()
hostname = platform.node().split('.')[0]
return {'system': system,
'node': node,
'release': release,
'version': version,
'machine': machine,
'processor': processor,
'distname': distname,
'distver': distver,
'distid': distid,
'user': get_user(),
'hostname': hostname,
'fqdn': socket.getfqdn()}
def net_info():
ret = {}
interfaces = []
rc, out, err = crm_script.call(['netstat', '-nr'])
if rc == 0:
data = [l.split() for l in out.split('\n')]
if len(data) < 3:
return {'error': "Failed to parse netstat output"}
keys = data[1]
for line in data[2:]:
if len(line) == len(keys):
interfaces.append(dict(zip(keys, line)))
else:
interfaces.append({'error': err.strip()})
ret['interfaces'] = interfaces
hostname = platform.node().split('.')[0]
try:
ip = socket.gethostbyname(hostname)
ret['hostname'] = {'name': hostname, 'ip': ip}
except Exception as e:
ret['hostname'] = {'error': str(e)}
return ret
def files_info():
def check(fn):
if os.path.isfile(os.path.expanduser(fn)):
return os.path.expanduser(fn)
return ''
return {'ssh_key': check(SSH_KEY),
'csync2_key': check(CSYNC2_KEY),
'csync2_cfg': check(CSYNC2_CFG),
'corosync_conf': check(COROSYNC_CONF),
'sysconfig_sbd': check(SYSCONFIG_SBD),
'sysconfig_fw': check(SYSCONFIG_FW),
'sysconfig_fw_cluster': check(SYSCONFIG_FW_CLUSTER),
}
def logrotate_info():
rc, _, _ = crm_script.call(
'grep -r corosync.conf /etc/logrotate.d',
shell=True)
return {'corosync.conf': rc == 0}
def disk_info():
rc, out, err = crm_script.call(['df'], shell=False)
if rc == 0:
disk_use = []
for line in out.split('\n')[1:]:
line = line.strip()
if line:
data = line.split()
if len(data) >= 6:
disk_use.append((data[5], int(data[4][:-1])))
return disk_use
return []
def info():
return {'rpm': rpm_info(),
'services': services_info(),
'system': sys_info(),
'net': net_info(),
'files': files_info(),
'logrotate': logrotate_info(),
'disk': disk_info()}
def verify(data):
"""
Given output from info(), verifies
as much as possible before init/add.
"""
def check_diskspace():
for host, info in data.iteritems():
for mount, percent in info['disk']:
interesting = (mount == '/' or
mount.startswith('/var/log') or
mount.startswith('/tmp'))
if interesting and percent > 90:
crm_script.exit_fail("Not enough space on %s:%s" % (host, mount))
def check_services():
for host, info in data.iteritems():
for svc in info['services']:
if svc['name'] == 'pacemaker' and svc['active'] == 'active':
crm_script.exit_fail("%s already running pacemaker" % (host))
if svc['name'] == 'corosync' and svc['active'] == 'active':
crm_script.exit_fail("%s already running corosync" % (host))
def verify_host(host, info):
if host != info['system']['hostname']:
crm_script.exit_fail("Hostname mismatch: %s is not %s" %
(host, info['system']['hostname']))
def compare_system(systems):
def check(value, msg):
vals = set([system[value] for host, system in systems])
if len(vals) > 1:
info = ', '.join('%s: %s' % (h, system[value]) for h, system in systems)
crm_script.exit_fail("%s: %s" % (msg, info))
check('machine', 'Architecture differs')
#check('release', 'Kernel release differs')
check('distname', 'Distribution differs')
check('distver', 'Distribution version differs')
#check('version', 'Kernel version differs')
for host, info in data.iteritems():
verify_host(host, info)
compare_system((h, info['system']) for h, info in data.iteritems())
check_diskspace()
check_services()
# common functions to initialize a cluster node
def is_service_enabled(name):
info = service_info(name)
if info.get('name') == name and info.get('enabled') == 'enabled':
return True
return False
def is_service_active(name):
info = service_info(name)
if info.get('name') == name and info.get('active') == 'active':
return True
return False
def install_packages(packages):
for pkg in packages:
try:
crm_script.package(pkg, 'latest')
except Exception as e:
crm_script.exit_fail("Failed to install %s: %s" % (pkg, e))
def configure_firewall():
_SUSE_FW_TEMPLATE = """## Name: HAE cluster ports
## Description: opens ports for HAE cluster services
TCP="%(tcp)s"
UDP="%(udp)s"
"""
corosync_mcastport = crm_script.param('mcastport')
if not corosync_mcastport:
rc, out, err = crm_script.call(['crm', 'corosync', 'get', 'totem.interface.mcastport'])
if rc == 0:
corosync_mcastport = out.strip()
FW = '/etc/sysconfig/SuSEfirewall2'
FW_CLUSTER = '/etc/sysconfig/SuSEfirewall2.d/services/cluster'
tcp_ports = '30865 5560 7630 21064'
udp_ports = '%s %s' % (corosync_mcastport, int(corosync_mcastport) - 1)
if is_service_enabled('SuSEfirewall2'):
if os.path.isfile(FW_CLUSTER):
tmpl = open(FW_CLUSTER).read()
tmpl = re.sub(r'^TCP="(.*)"', 'TCP="%s"' % (tcp_ports), tmpl, flags=re.M)
tmpl = re.sub(r'^UDP="(.*)"', 'UDP="%s"' % (udp_ports), tmpl, flags=re.M)
with open(FW_CLUSTER, 'w') as f:
f.write(tmpl)
elif os.path.isdir(os.path.dirname(FW_CLUSTER)):
with open(FW_CLUSTER, 'w') as fwc:
fwc.write(_SUSE_FW_TEMPLATE % {'tcp': tcp_ports,
'udp': udp_ports})
else:
# neither the cluster file nor the services
# directory exists
crm_script.exit_fail("SUSE firewall is configured but %s does not exist" %
os.path.dirname(FW_CLUSTER))
# add cluster to FW_CONFIGURATIONS_EXT
if os.path.isfile(FW):
txt = open(FW).read()
m = re.search(r'^FW_CONFIGURATIONS_EXT="(.*)"', txt, re.M)
if m:
services = m.group(1).split()
if 'cluster' not in services:
services.append('cluster')
txt = re.sub(r'^FW_CONFIGURATIONS_EXT="(.*)"',
r'FW_CONFIGURATIONS_EXT="%s"' % (' '.join(services)),
txt,
flags=re.M)
else:
txt += '\nFW_CONFIGURATIONS_EXT="cluster"'
with open(FW, 'w') as fw:
fw.write(txt)
if is_service_active('SuSEfirewall2'):
crm_script.service('SuSEfirewall2', 'restart')
# TODO: other platforms