#!/usr/bin/env python3 from base64 import b64decode from charmhelpers.core.hookenv import ( log, ERROR, WARNING, config, relation_get, relation_set, relation_ids, Hooks, UnregisteredHookError, status_set, ) from charmhelpers.core.host import service_restart from charmhelpers.core.unitdata import kv from charmhelpers.fetch import ( apt_update, apt_install, filter_installed_packages, apt_purge, ) from charmhelpers.core.host import ( is_container, lsb_release, ) from charmhelpers.contrib.hahelpers.cluster import ( get_hacluster_config, ) from charmhelpers.contrib.hahelpers.apache import ( install_ca_cert ) from charmhelpers.contrib.openstack.utils import ( configure_installation_source, openstack_upgrade_available, pausable_restart_on_change as restart_on_change, is_unit_paused_set, series_upgrade_prepare, series_upgrade_complete, ) from charmhelpers.payload.execd import execd_preinstall from charmhelpers.core.sysctl import create as create_sysctl from charmhelpers.core.kernel import modprobe from charmhelpers.contrib.charmsupport import nrpe from charmhelpers.contrib.hardening.harden import harden import sys from neutron_utils import ( L3HA_PACKAGES, register_configs, restart_map, services, do_openstack_upgrade, get_packages, get_early_packages, valid_plugin, configure_ovs, stop_services, cache_env_data, update_legacy_ha_files, remove_legacy_ha_files, install_legacy_ha_files, cleanup_ovs_netns, stop_neutron_ha_monitor_daemon, use_l3ha, NEUTRON_COMMON, assess_status, install_systemd_override, configure_apparmor, pause_unit_helper, resume_unit_helper, remove_legacy_nova_metadata, remove_legacy_neutron_lbaas, disable_nova_metadata, disable_neutron_lbaas, remove_old_packages, deprecated_services, ) hooks = Hooks() # Note that CONFIGS is now set up via resolve_CONFIGS so that it is not a # module load time constraint. CONFIGS = None def resolve_CONFIGS(): """lazy function to resolve the CONFIGS so that it doesn't have to evaluate at module load time. Note that it also returns the CONFIGS so that it can be used in other, module loadtime, functions. :returns: CONFIGS variable :rtype: `:class:templating.OSConfigRenderer` """ global CONFIGS if CONFIGS is None: CONFIGS = register_configs() return CONFIGS @hooks.hook('install') @harden() def install(): status_set('maintenance', 'Executing pre-install') execd_preinstall() src = config('openstack-origin') if (lsb_release()['DISTRIB_CODENAME'] == 'precise' and src == 'distro'): src = 'cloud:precise-icehouse' configure_installation_source(src) status_set('maintenance', 'Installing apt packages') apt_update(fatal=True) apt_install('python-six', fatal=True) # Force upgrade if valid_plugin(): apt_install(filter_installed_packages(get_early_packages()), fatal=True) apt_install(filter_installed_packages(get_packages()), fatal=True) else: message = 'Please provide a valid plugin config' log(message, level=ERROR) status_set('blocked', message) sys.exit(1) # Legacy HA for Icehouse update_legacy_ha_files() # Install systemd overrides to remove service startup race between # n-gateway and n-cloud-controller services. install_systemd_override() # LP #1825906: prefer to install the lbaas package and then mask it # instead of checking if we need to install that package on each # config-changed hook if disable_neutron_lbaas(): remove_legacy_neutron_lbaas() @hooks.hook('config-changed') @restart_on_change(restart_map) @harden() def config_changed(): if not config('action-managed-upgrade'): if openstack_upgrade_available(NEUTRON_COMMON): status_set('maintenance', 'Running openstack upgrade') do_openstack_upgrade(CONFIGS) update_nrpe_config() module_settings = config('kernel-modules') if module_settings: if is_container(): log("Cannot load modules inside of a container", level=WARNING) else: for module in module_settings.split(): try: modprobe(module) except Exception: message = "Failed to load kernel module '%s'" % module log(message, level=WARNING) sysctl_settings = config('sysctl') if sysctl_settings: if is_container(): log("Cannot create sysctls inside of a container", level=WARNING) else: create_sysctl(sysctl_settings, '/etc/sysctl.d/50-quantum-gateway.conf') # Re-run joined hooks as config might have changed for r_id in relation_ids('amqp'): amqp_joined(relation_id=r_id) for r_id in relation_ids('amqp-nova'): amqp_nova_joined(relation_id=r_id) if valid_plugin(): CONFIGS.write_all() configure_ovs() configure_apparmor() else: message = 'Please provide a valid plugin config' log(message, level=ERROR) status_set('blocked', message) sys.exit(1) if config('plugin') == 'n1kv': if config('enable-l3-agent'): status_set('maintenance', 'Installing apt packages') apt_install(filter_installed_packages('neutron-l3-agent')) else: apt_purge('neutron-l3-agent') # Setup legacy ha configurations update_legacy_ha_files() # Disable nova metadata if possible, if disable_nova_metadata(): remove_legacy_nova_metadata() if disable_neutron_lbaas(): remove_legacy_neutron_lbaas() @hooks.hook('upgrade-charm') @harden() def upgrade_charm(): install() packages_removed = remove_old_packages() if packages_removed and not is_unit_paused_set(): log("Package purge detected, restarting services", "INFO") for s in services(): service_restart(s) config_changed() update_legacy_ha_files(force=True) # Install systemd overrides to remove service startup race between # n-gateway and n-cloud-controller services. install_systemd_override() @hooks.hook('amqp-nova-relation-joined') def amqp_nova_joined(relation_id=None): relation_set(relation_id=relation_id, username=config('nova-rabbit-user'), vhost=config('nova-rabbit-vhost')) @hooks.hook('amqp-relation-joined') def amqp_joined(relation_id=None): relation_set(relation_id=relation_id, username=config('rabbit-user'), vhost=config('rabbit-vhost')) @hooks.hook('amqp-nova-relation-departed') @hooks.hook('amqp-nova-relation-changed') @restart_on_change(restart_map) def amqp_nova_changed(): if 'amqp-nova' not in CONFIGS.complete_contexts(): log('amqp relation incomplete. Peer not ready?') return CONFIGS.write_all() @hooks.hook('amqp-relation-departed') @restart_on_change(restart_map) def amqp_departed(): if 'amqp' not in CONFIGS.complete_contexts(): log('amqp relation incomplete. Peer not ready?') return CONFIGS.write_all() @hooks.hook('amqp-relation-changed', 'cluster-relation-changed', 'cluster-relation-joined') @restart_on_change(restart_map) def amqp_changed(): CONFIGS.write_all() @hooks.hook('neutron-plugin-api-relation-changed') @restart_on_change(restart_map) def neutron_plugin_api_changed(): if use_l3ha(): apt_update() apt_install(L3HA_PACKAGES, fatal=True) CONFIGS.write_all() @hooks.hook('quantum-network-service-relation-changed') @restart_on_change(restart_map) def nm_changed(): CONFIGS.write_all() if relation_get('ca_cert'): ca_crt = b64decode(relation_get('ca_cert')) install_ca_cert(ca_crt) if config('ha-legacy-mode'): cache_env_data() # Disable nova metadata if possible, if disable_nova_metadata(): remove_legacy_nova_metadata() else: # NOTE: nova-api-metadata needs to be restarted # once the nova-conductor is up and running # on the nova-cc units. restart_nonce = relation_get('restart_trigger') if restart_nonce is not None: db = kv() previous_nonce = db.get('restart_nonce') if previous_nonce != restart_nonce: if not is_unit_paused_set(): service_restart('nova-api-metadata') db.set('restart_nonce', restart_nonce) db.flush() # LP: #1812813 update_nrpe_config() @hooks.hook("cluster-relation-departed") @restart_on_change(restart_map) def cluster_departed(): if config('plugin') in ['nvp', 'nsx']: log('Unable to re-assign agent resources for' ' failed nodes with nvp|nsx', level=WARNING) return if config('plugin') == 'n1kv': log('Unable to re-assign agent resources for failed nodes with n1kv', level=WARNING) return @hooks.hook('cluster-relation-broken') @hooks.hook('stop') def stop(): stop_services() if config('ha-legacy-mode'): # Cleanup ovs and netns for destroyed units. cleanup_ovs_netns() @hooks.hook('nrpe-external-master-relation-joined', 'nrpe-external-master-relation-changed') def update_nrpe_config(): # python-dbus is used by check_upstart_job apt_install('python-dbus') hostname = nrpe.get_nagios_hostname() current_unit = nrpe.get_nagios_unit_name() nrpe_setup = nrpe.NRPE(hostname=hostname) nrpe.remove_deprecated_check(nrpe_setup, deprecated_services()) nrpe.add_init_service_checks(nrpe_setup, services(), current_unit) cronpath = '/etc/cron.d/nagios-netns-check' cron_template = ('*/5 * * * * root ' '/usr/local/lib/nagios/plugins/check_netns.sh ' '> /var/lib/nagios/netns-check.txt\n' ) f = open(cronpath, 'w') f.write(cron_template) f.close() nrpe_setup.add_check( shortname="netns", description='Network Namespace check {%s}' % current_unit, check_cmd='check_status_file.py -f /var/lib/nagios/netns-check.txt' ) nrpe_setup.write() @hooks.hook('ha-relation-joined') @hooks.hook('ha-relation-changed') def ha_relation_joined(): if config('ha-legacy-mode'): log('ha-relation-changed update_legacy_ha_files') install_legacy_ha_files() cache_env_data() cluster_config = get_hacluster_config(exclude_keys=['vip']) resources = { 'res_monitor': 'ocf:canonical:NeutronAgentMon', } resource_params = { 'res_monitor': 'op monitor interval="60s"', } clones = { 'cl_monitor': 'res_monitor meta interleave="true"', } relation_set(corosync_bindiface=cluster_config['ha-bindiface'], corosync_mcastport=cluster_config['ha-mcastport'], resources=resources, resource_params=resource_params, clones=clones) @hooks.hook('ha-relation-departed') def ha_relation_destroyed(): # If e.g. we want to upgrade to Juno and use native Neutron HA support then # we need to un-corosync-cluster to enable the transition. if config('ha-legacy-mode'): stop_neutron_ha_monitor_daemon() remove_legacy_ha_files() @hooks.hook('update-status') @harden() def update_status(): log('Updating status.') @hooks.hook('pre-series-upgrade') def pre_series_upgrade(): log("Running prepare series upgrade hook", "INFO") series_upgrade_prepare( pause_unit_helper, CONFIGS) @hooks.hook('post-series-upgrade') def post_series_upgrade(): log("Running complete series upgrade hook", "INFO") series_upgrade_complete( resume_unit_helper, CONFIGS) def main(): try: hooks.execute(sys.argv) except UnregisteredHookError as e: log('Unknown hook {} - skipping.'.format(e)) assess_status(CONFIGS) if __name__ == '__main__': resolve_CONFIGS() main()