260 lines
9.3 KiB
Python
Executable File
260 lines
9.3 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
#
|
|
# Copyright 2012 Canonical Ltd.
|
|
#
|
|
# Authors:
|
|
# Andres Rodriguez <andres.rodriguez@canonical.com>
|
|
#
|
|
|
|
import glob
|
|
import os
|
|
import subprocess
|
|
import shutil
|
|
import sys
|
|
import time
|
|
|
|
import utils
|
|
import pcmk
|
|
|
|
|
|
def install():
|
|
utils.juju_log('INFO', 'Begin install hook.')
|
|
utils.configure_source()
|
|
utils.install('corosync', 'pacemaker', 'openstack-resource-agents', 'python-netaddr')
|
|
utils.juju_log('INFO', 'End install hook.')
|
|
|
|
|
|
def get_corosync_conf():
|
|
for relid in utils.relation_ids('ha'):
|
|
for unit in utils.relation_list(relid):
|
|
conf = {
|
|
'corosync_bindnetaddr': utils.get_network_address(
|
|
utils.relation_get('corosync_bindiface',
|
|
unit, relid)),
|
|
'corosync_mcastport': utils.relation_get('corosync_mcastport',
|
|
unit, relid),
|
|
'corosync_mcastaddr': utils.config_get('corosync_mcastaddr'),
|
|
'corosync_pcmk_ver': utils.config_get('corosync_pcmk_ver'),
|
|
}
|
|
if None not in conf.itervalues():
|
|
return conf
|
|
return None
|
|
|
|
|
|
def emit_corosync_conf():
|
|
# read config variables
|
|
corosync_conf_context = get_corosync_conf()
|
|
|
|
# write config file (/etc/corosync/corosync.conf
|
|
with open('/etc/corosync/corosync.conf', 'w') as corosync_conf:
|
|
corosync_conf.write(utils.render_template('corosync.conf', corosync_conf_context))
|
|
|
|
|
|
def emit_base_conf():
|
|
corosync_default_context = {'corosync_enabled': 'yes'}
|
|
# write /etc/default/corosync file
|
|
with open('/etc/default/corosync', 'w') as corosync_default:
|
|
corosync_default.write(utils.render_template('corosync', corosync_default_context))
|
|
|
|
# write the authkey
|
|
corosync_key=utils.config_get('corosync_key')
|
|
with open(corosync_key, 'w') as corosync_key_file:
|
|
corosync_key_file.write(corosync_key)
|
|
|
|
|
|
def config_changed():
|
|
utils.juju_log('INFO', 'Begin config-changed hook.')
|
|
|
|
corosync_key = utils.config_get('corosync_key')
|
|
if corosync_key == '':
|
|
utils.juju_log('CRITICAL',
|
|
'No Corosync key supplied, cannot proceed')
|
|
sys.exit(1)
|
|
|
|
# Create a new config file
|
|
emit_base_conf()
|
|
|
|
utils.juju_log('INFO', 'End config-changed hook.')
|
|
|
|
|
|
def upgrade_charm():
|
|
utils.juju_log('INFO', 'Begin upgrade-charm hook.')
|
|
emit_corosync_conf()
|
|
utils.juju_log('INFO', 'End upgrade-charm hook.')
|
|
|
|
|
|
def start():
|
|
if utils.running("corosync"):
|
|
utils.restart("corosync")
|
|
else:
|
|
utils.start("corosync")
|
|
|
|
# Only start pacemaker after making sure
|
|
# corosync has been started
|
|
# Wait a few seconds for corosync to start.
|
|
time.sleep(2)
|
|
if utils.running("corosync"):
|
|
if utils.running("pacemaker"):
|
|
utils.restart("pacemaker")
|
|
else:
|
|
utils.start("pacemaker")
|
|
|
|
|
|
def stop():
|
|
service("corosync", "stop")
|
|
time.sleep(2)
|
|
service("pacemaker", "stop")
|
|
|
|
|
|
def ha_relation():
|
|
utils.juju_log('INFO', 'Begin ha relation joined/changed hook')
|
|
|
|
if utils.relation_get("corosync_bindiface") is None:
|
|
return
|
|
elif utils.relation_get("corosync_mcastport") is None:
|
|
return
|
|
else:
|
|
emit_corosync_conf()
|
|
utils.restart("corosync")
|
|
time.sleep(2)
|
|
utils.restart("pacemaker")
|
|
|
|
# Check that there's enough nodes in order to perform the
|
|
# configuration of the HA cluster
|
|
if len(get_cluster_nodes()) < 2:
|
|
return
|
|
else:
|
|
utils.juju_log('INFO', 'hanode-relation: Waiting for PCMK to start')
|
|
pcmk.wait_for_pcmk()
|
|
|
|
# Obtain relation information
|
|
import ast
|
|
resources = {} if utils.relation_get("resources") is None else ast.literal_eval(utils.relation_get("resources"))
|
|
resource_params = {} if utils.relation_get("resource_params") is None else ast.literal_eval(utils.relation_get("resource_params"))
|
|
groups = {} if utils.relation_get("groups") is None else ast.literal_eval(utils.relation_get("groups"))
|
|
orders = {} if utils.relation_get("orders") is None else ast.literal_eval(utils.relation_get("orders"))
|
|
colocations = {} if utils.relation_get("colocations") is None else ast.literal_eval(utils.relation_get("colocations"))
|
|
clones = {} if utils.relation_get("clones") is None else ast.literal_eval(utils.relation_get("clones"))
|
|
init_services = {} if utils.relation_get("init_services") is None else ast.literal_eval(utils.relation_get("init_services"))
|
|
|
|
# Configuring the Resource
|
|
utils.juju_log('INFO', 'ha-relation: Configuring Resources')
|
|
for res_name,res_type in resources.iteritems():
|
|
# disable the service we are going to put in HA
|
|
if res_type.split(':')[0] == "lsb":
|
|
utils.disable_lsb_services(res_type.split(':')[1])
|
|
if utils.running(res_type.split(':')[1]):
|
|
utils.stop(res_type.split(':')[1])
|
|
elif len(init_services) != 0 and res_name in init_services and init_services[res_name]:
|
|
utils.disable_upstart_services(init_services[res_name])
|
|
if utils.running(init_services[res_name]):
|
|
utils.stop(init_services[res_name])
|
|
# Put the services in HA, if not already done so
|
|
#if not pcmk.is_resource_present(res_name):
|
|
if not pcmk.crm_opt_exists(res_name):
|
|
if resource_params[res_name] is None:
|
|
cmd = 'crm -F configure primitive %s %s' % (res_name, res_type)
|
|
else:
|
|
cmd = 'crm -F configure primitive %s %s %s' % (res_name, res_type, resource_params[res_name])
|
|
pcmk.commit(cmd)
|
|
utils.juju_log('INFO', '%s' % cmd)
|
|
|
|
# Configuring groups
|
|
utils.juju_log('INFO', 'ha-relation: Configuring Groups')
|
|
for grp_name, grp_params in groups.iteritems():
|
|
if not pcmk.crm_opt_exists(grp_name):
|
|
cmd = 'crm -F configure group %s %s' % (grp_name, grp_params)
|
|
pcmk.commit(cmd)
|
|
utils.juju_log('INFO', '%s' % cmd)
|
|
|
|
# Configuring ordering
|
|
utils.juju_log('INFO', 'ha-relation: Configuring Orders')
|
|
for ord_name, ord_params in orders.iteritems():
|
|
if not pcmk.crm_opt_exists(ord_name):
|
|
cmd = 'crm -F configure order %s %s' % (ord_name, ord_params)
|
|
pcmk.commit(cmd)
|
|
utils.juju_log('INFO', '%s' % cmd)
|
|
|
|
# Configuring colocations
|
|
utils.juju_log('INFO', 'ha-relation: Configuring Colocations')
|
|
for col_name, col_params in colocations.iteritems():
|
|
if not pcmk.crm_opt_exists(col_name):
|
|
cmd = 'crm -F configure colocation %s %s' % (col_name, col_params)
|
|
pcmk.commit(cmd)
|
|
utils.juju_log('INFO', '%s' % cmd)
|
|
|
|
# Configuring clones
|
|
utils.juju_log('INFO', 'ha-relation: Configuring Clones')
|
|
for cln_name, cln_params in clones.iteritems():
|
|
if not pcmk.crm_opt_exists(cln_name):
|
|
cmd = 'crm -F configure clone %s %s' % (cln_name, cln_params)
|
|
pcmk.commit(cmd)
|
|
utils.juju_log('INFO', '%s' % cmd)
|
|
|
|
for res_name,res_type in resources.iteritems():
|
|
# TODO: This should first check that the resources is running
|
|
if len(init_services) != 0 and res_name in init_services:
|
|
# If the resource is in HA already, and it is a service, restart
|
|
# the pcmk resource as the config file might have changed by the
|
|
# principal charm
|
|
cmd = 'crm resource restart %s' % res_name
|
|
pcmk.commit(cmd)
|
|
|
|
utils.juju_log('INFO', 'End ha relation joined/changed hook')
|
|
|
|
|
|
def ha_relation_departed():
|
|
# TODO: Fin out which node is departing and put it in standby mode.
|
|
# If this happens, and a new relation is created in the same machine
|
|
# (which already has node), then check whether it is standby and put it
|
|
# in online mode. This should be done in ha_relation_joined.
|
|
pcmk.standby(utils.get_unit_hostname())
|
|
|
|
|
|
def get_cluster_nodes():
|
|
hosts = []
|
|
hosts.append('{}:6789'.format(utils.get_host_ip()))
|
|
|
|
for relid in utils.relation_ids('hanode'):
|
|
for unit in utils.relation_list(relid):
|
|
hosts.append(
|
|
'{}:6789'.format(utils.get_host_ip(
|
|
utils.relation_get('private-address',
|
|
unit, relid)))
|
|
)
|
|
|
|
hosts.sort()
|
|
return hosts
|
|
|
|
|
|
def hanode_relation():
|
|
utils.juju_log('INFO', 'Begin hanode peer relation hook')
|
|
if len(get_cluster_nodes()) >= 2:
|
|
utils.juju_log('INFO', 'hanode-relation: Waiting for PCMK to start')
|
|
pcmk.wait_for_pcmk()
|
|
|
|
utils.juju_log('INFO', 'hanode-relation: Doing global configuration')
|
|
cmd = "crm configure property stonith-enabled=false"
|
|
pcmk.commit(cmd)
|
|
cmd = "crm configure property no-quorum-policy=ignore"
|
|
pcmk.commit(cmd)
|
|
cmd = 'crm configure rsc_defaults $id="rsc-options" resource-stickiness="100"'
|
|
pcmk.commit(cmd)
|
|
|
|
|
|
utils.do_hooks({
|
|
'install': install,
|
|
'config-changed': config_changed,
|
|
'start': start,
|
|
'stop': stop,
|
|
'upgrade-charm': upgrade_charm,
|
|
'ha-relation-joined': ha_relation,
|
|
'ha-relation-changed': ha_relation,
|
|
'ha-relation-departed': ha_relation_departed,
|
|
'hanode-relation-joined': hanode_relation,
|
|
#'hanode-relation-departed': hanode_relation_departed, # TODO: should probably remove nodes from the cluster
|
|
})
|
|
|
|
sys.exit(0)
|