From 9c12812735cc0b0a6d1bc74ca595d931d43f3245 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Mon, 15 Oct 2018 16:39:39 +0100 Subject: [PATCH] Switch the charm to support py3 Some major changes: * the charm has been rebased (from a Python perspective) to be rooted in the charm directory. This is a single root. * Imports have been changed so that the don't add lots of imports to the namespace of the module doing the import. * The code that used to run at module import time has been made lazy such that it only has to run if the relevant functions are called. This includes restart_on_change parameters, the harden function and the parameters to the guard_map. Appropriate changes will be submitted to charm-helpers. * Several tests had to be re-written as (incorrect) mocking meant that text fixtures didn't actually match what the code was doing. Thus, the tests were meaningless. * This has had a net positive impact on the unit tests wrt to importing modules and mocking. Change-Id: Id07d9d1caaa9b29453a63c2e49ba831071e9457f --- actions/actions.py | 37 +- actions/openstack_upgrade.py | 46 +- charm-helpers-hooks.yaml | 2 +- .../charmhelpers => charmhelpers}/__init__.py | 0 .../cli/__init__.py | 0 .../cli/benchmark.py | 0 .../cli/commands.py | 0 .../cli/hookenv.py | 0 .../charmhelpers => charmhelpers}/cli/host.py | 0 .../cli/unitdata.py | 0 .../contrib/__init__.py | 0 .../contrib/charmsupport/__init__.py | 0 .../contrib/charmsupport/nrpe.py | 0 .../contrib/charmsupport/volumes.py | 0 .../contrib/hahelpers/__init__.py | 0 .../contrib/hahelpers/apache.py | 0 .../contrib/hahelpers/cluster.py | 0 .../contrib/hardening/README.hardening.md | 0 .../contrib/hardening/__init__.py | 0 .../contrib/hardening/apache/__init__.py | 0 .../hardening/apache/checks/__init__.py | 0 .../contrib/hardening/apache/checks/config.py | 0 .../apache/templates/99-hardening.conf | 0 .../hardening/apache/templates/__init__.py | 0 .../hardening/apache/templates/alias.conf | 0 .../contrib/hardening/audits/__init__.py | 0 .../contrib/hardening/audits/apache.py | 0 .../contrib/hardening/audits/apt.py | 0 .../contrib/hardening/audits/file.py | 0 .../contrib/hardening/defaults/__init__.py | 0 .../contrib/hardening/defaults/apache.yaml | 0 .../hardening/defaults/apache.yaml.schema | 0 .../contrib/hardening/defaults/mysql.yaml | 0 .../hardening/defaults/mysql.yaml.schema | 0 .../contrib/hardening/defaults/os.yaml | 0 .../contrib/hardening/defaults/os.yaml.schema | 0 .../contrib/hardening/defaults/ssh.yaml | 0 .../hardening/defaults/ssh.yaml.schema | 0 .../contrib/hardening/harden.py | 13 +- .../contrib/hardening/host/__init__.py | 0 .../contrib/hardening/host/checks/__init__.py | 0 .../contrib/hardening/host/checks/apt.py | 0 .../contrib/hardening/host/checks/limits.py | 0 .../contrib/hardening/host/checks/login.py | 0 .../hardening/host/checks/minimize_access.py | 0 .../contrib/hardening/host/checks/pam.py | 0 .../contrib/hardening/host/checks/profile.py | 0 .../hardening/host/checks/securetty.py | 0 .../hardening/host/checks/suid_sgid.py | 0 .../contrib/hardening/host/checks/sysctl.py | 0 .../hardening/host/templates/10.hardcore.conf | 0 .../hardening/host/templates/99-hardening.sh | 0 .../host/templates/99-juju-hardening.conf | 0 .../hardening/host/templates/__init__.py | 0 .../hardening/host/templates/login.defs | 0 .../contrib/hardening/host/templates/modules | 0 .../hardening/host/templates/passwdqc.conf | 0 .../host/templates/pinerolo_profile.sh | 0 .../hardening/host/templates/securetty | 0 .../contrib/hardening/host/templates/tally2 | 0 .../contrib/hardening/mysql/__init__.py | 0 .../hardening/mysql/checks/__init__.py | 0 .../contrib/hardening/mysql/checks/config.py | 0 .../hardening/mysql/templates/__init__.py | 0 .../hardening/mysql/templates/hardening.cnf | 0 .../contrib/hardening/ssh/__init__.py | 0 .../contrib/hardening/ssh/checks/__init__.py | 0 .../contrib/hardening/ssh/checks/config.py | 0 .../hardening/ssh/templates/__init__.py | 0 .../hardening/ssh/templates/ssh_config | 0 .../hardening/ssh/templates/sshd_config | 0 .../contrib/hardening/templating.py | 0 .../contrib/hardening/utils.py | 0 .../contrib/network/__init__.py | 0 .../contrib/network/ip.py | 0 .../contrib/openstack/__init__.py | 0 .../contrib/openstack/alternatives.py | 0 .../contrib/openstack/amulet/__init__.py | 0 .../contrib/openstack/amulet/deployment.py | 0 .../contrib/openstack/amulet/utils.py | 0 .../contrib/openstack/cert_utils.py | 0 .../contrib/openstack/context.py | 0 .../contrib/openstack/exceptions.py | 0 .../contrib/openstack/files/__init__.py | 0 .../contrib/openstack/files/check_haproxy.sh | 0 .../files/check_haproxy_queue_depth.sh | 0 .../contrib/openstack/ha/__init__.py | 0 .../contrib/openstack/ha/utils.py | 0 .../contrib/openstack/ip.py | 0 .../contrib/openstack/keystone.py | 0 .../contrib/openstack/neutron.py | 0 .../contrib/openstack/ssh_migrations.py | 0 .../contrib/openstack/templates/__init__.py | 0 .../contrib/openstack/templates/ceph.conf | 0 .../contrib/openstack/templates/git.upstart | 0 .../contrib/openstack/templates/haproxy.cfg | 0 .../openstack/templates/memcached.conf | 0 .../templates/openstack_https_frontend | 0 .../templates/openstack_https_frontend.conf | 0 .../templates/section-keystone-authtoken | 0 .../section-keystone-authtoken-legacy | 0 .../section-keystone-authtoken-mitaka | 0 .../openstack/templates/section-oslo-cache | 0 .../templates/section-oslo-middleware | 0 .../templates/section-oslo-notifications | 0 .../openstack/templates/section-rabbitmq-oslo | 0 .../openstack/templates/section-zeromq | 0 .../templates/wsgi-openstack-api.conf | 0 .../templates/wsgi-openstack-metadata.conf | 0 .../contrib/openstack/templating.py | 0 .../contrib/openstack/utils.py | 18 +- .../contrib/openstack/vaultlocker.py | 0 .../contrib/peerstorage/__init__.py | 0 .../contrib/python/__init__.py | 0 .../contrib/python/packages.py | 0 .../contrib/storage/__init__.py | 0 .../contrib/storage/linux/__init__.py | 0 .../contrib/storage/linux/bcache.py | 0 .../contrib/storage/linux/ceph.py | 0 .../contrib/storage/linux/loopback.py | 0 .../contrib/storage/linux/lvm.py | 0 .../contrib/storage/linux/utils.py | 0 .../core/__init__.py | 0 .../core/decorators.py | 0 .../core/files.py | 0 .../core/fstab.py | 0 .../core/hookenv.py | 0 .../core/host.py | 0 .../core/host_factory/__init__.py | 0 .../core/host_factory/centos.py | 0 .../core/host_factory/ubuntu.py | 0 .../core/hugepage.py | 0 .../core/kernel.py | 0 .../core/kernel_factory/__init__.py | 0 .../core/kernel_factory/centos.py | 0 .../core/kernel_factory/ubuntu.py | 0 .../core/services/__init__.py | 0 .../core/services/base.py | 0 .../core/services/helpers.py | 0 .../core/strutils.py | 0 .../core/sysctl.py | 0 .../core/templating.py | 0 .../core/unitdata.py | 0 .../fetch/__init__.py | 0 .../fetch/archiveurl.py | 0 .../fetch/bzrurl.py | 0 .../fetch/centos.py | 0 .../fetch/giturl.py | 0 .../fetch/snap.py | 0 .../fetch/ubuntu.py | 0 .../osplatform.py | 0 .../payload/__init__.py | 0 .../payload/archive.py | 0 .../payload/execd.py | 0 files/README.txt | 3 + hooks/install | 2 +- hooks/nova_cc_common.py | 77 ++ hooks/nova_cc_context.py | 272 +++-- hooks/nova_cc_hooks.py | 998 +++++++++--------- hooks/nova_cc_utils.py | 899 ++++++++-------- templates/icehouse/nova.conf | 6 +- templates/juno/nova.conf | 6 +- templates/kilo/nova.conf | 6 +- templates/liberty/nova.conf | 6 +- templates/mitaka/nova.conf | 6 +- templates/ocata/nova.conf | 6 +- templates/parts/database-api | 2 +- templates/parts/database-v2 | 2 +- templates/parts/section-database | 2 +- templates/pike/nova.conf | 6 +- tox.ini | 6 +- unit_tests/__init__.py | 11 +- unit_tests/test_actions.py | 45 +- unit_tests/test_actions_openstack_upgrade.py | 26 +- unit_tests/test_nova_cc_contexts.py | 185 ++-- unit_tests/test_nova_cc_hooks.py | 554 +++++----- unit_tests/test_nova_cc_utils.py | 336 +++--- unit_tests/test_utils.py | 29 +- 178 files changed, 1838 insertions(+), 1769 deletions(-) rename {hooks/charmhelpers => charmhelpers}/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/cli/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/cli/benchmark.py (100%) rename {hooks/charmhelpers => charmhelpers}/cli/commands.py (100%) rename {hooks/charmhelpers => charmhelpers}/cli/hookenv.py (100%) rename {hooks/charmhelpers => charmhelpers}/cli/host.py (100%) rename {hooks/charmhelpers => charmhelpers}/cli/unitdata.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/charmsupport/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/charmsupport/nrpe.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/charmsupport/volumes.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hahelpers/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hahelpers/apache.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hahelpers/cluster.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/README.hardening.md (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/apache/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/apache/checks/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/apache/checks/config.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/apache/templates/99-hardening.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/apache/templates/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/apache/templates/alias.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/audits/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/audits/apache.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/audits/apt.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/audits/file.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/apache.yaml (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/apache.yaml.schema (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/mysql.yaml (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/mysql.yaml.schema (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/os.yaml (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/os.yaml.schema (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/ssh.yaml (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/defaults/ssh.yaml.schema (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/harden.py (85%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/apt.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/limits.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/login.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/minimize_access.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/pam.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/profile.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/securetty.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/suid_sgid.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/checks/sysctl.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/10.hardcore.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/99-hardening.sh (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/99-juju-hardening.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/login.defs (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/modules (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/passwdqc.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/pinerolo_profile.sh (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/securetty (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/host/templates/tally2 (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/mysql/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/mysql/checks/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/mysql/checks/config.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/mysql/templates/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/mysql/templates/hardening.cnf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/ssh/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/ssh/checks/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/ssh/checks/config.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/ssh/templates/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/ssh/templates/ssh_config (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/ssh/templates/sshd_config (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/templating.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/hardening/utils.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/network/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/network/ip.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/alternatives.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/amulet/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/amulet/deployment.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/amulet/utils.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/cert_utils.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/context.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/exceptions.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/files/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/files/check_haproxy.sh (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/files/check_haproxy_queue_depth.sh (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/ha/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/ha/utils.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/ip.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/keystone.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/neutron.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/ssh_migrations.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/ceph.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/git.upstart (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/haproxy.cfg (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/memcached.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/openstack_https_frontend (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/openstack_https_frontend.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-keystone-authtoken (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-keystone-authtoken-legacy (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-keystone-authtoken-mitaka (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-oslo-cache (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-oslo-middleware (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-oslo-notifications (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-rabbitmq-oslo (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/section-zeromq (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/wsgi-openstack-api.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templates/wsgi-openstack-metadata.conf (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/templating.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/utils.py (98%) rename {hooks/charmhelpers => charmhelpers}/contrib/openstack/vaultlocker.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/peerstorage/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/python/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/python/packages.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/storage/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/storage/linux/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/storage/linux/bcache.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/storage/linux/ceph.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/storage/linux/loopback.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/storage/linux/lvm.py (100%) rename {hooks/charmhelpers => charmhelpers}/contrib/storage/linux/utils.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/decorators.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/files.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/fstab.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/hookenv.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/host.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/host_factory/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/host_factory/centos.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/host_factory/ubuntu.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/hugepage.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/kernel.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/kernel_factory/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/kernel_factory/centos.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/kernel_factory/ubuntu.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/services/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/services/base.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/services/helpers.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/strutils.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/sysctl.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/templating.py (100%) rename {hooks/charmhelpers => charmhelpers}/core/unitdata.py (100%) rename {hooks/charmhelpers => charmhelpers}/fetch/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/fetch/archiveurl.py (100%) rename {hooks/charmhelpers => charmhelpers}/fetch/bzrurl.py (100%) rename {hooks/charmhelpers => charmhelpers}/fetch/centos.py (100%) rename {hooks/charmhelpers => charmhelpers}/fetch/giturl.py (100%) rename {hooks/charmhelpers => charmhelpers}/fetch/snap.py (100%) rename {hooks/charmhelpers => charmhelpers}/fetch/ubuntu.py (100%) rename {hooks/charmhelpers => charmhelpers}/osplatform.py (100%) rename {hooks/charmhelpers => charmhelpers}/payload/__init__.py (100%) rename {hooks/charmhelpers => charmhelpers}/payload/archive.py (100%) rename {hooks/charmhelpers => charmhelpers}/payload/execd.py (100%) create mode 100644 files/README.txt create mode 100644 hooks/nova_cc_common.py diff --git a/actions/actions.py b/actions/actions.py index c6e43c81..df80b4dc 100755 --- a/actions/actions.py +++ b/actions/actions.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Copyright 2016 Canonical Ltd # @@ -17,40 +17,39 @@ import os import sys -sys.path.append('hooks/') +_path = os.path.dirname(os.path.realpath(__file__)) +_root = os.path.abspath(os.path.join(_path, '..')) -from charmhelpers.core.hookenv import ( - action_fail, - action_get, - action_set, -) -from nova_cc_utils import ( - pause_unit_helper, - resume_unit_helper, - register_configs, - archive_deleted_rows, -) + +def _add_path(path): + if path not in sys.path: + sys.path.insert(1, path) + +_add_path(_root) + +import charmhelpers.core.hookenv as hookenv +import hooks.nova_cc_utils as utils def pause(args): """Pause the Ceilometer services. @raises Exception should the service fail to stop. """ - pause_unit_helper(register_configs()) + utils.pause_unit_helper(utils.register_configs()) def resume(args): """Resume the Ceilometer services. @raises Exception should the service fail to start.""" - resume_unit_helper(register_configs()) + utils.resume_unit_helper(utils.register_configs()) def archive_data(args): """Run data archival process @raises Exception should the archival fail""" - action_set({ - 'archive-deleted-rows': archive_deleted_rows( - max_rows=action_get('batch-size'))}) + hookenv.action_set({ + 'archive-deleted-rows': utils.archive_deleted_rows( + max_rows=hookenv.action_get('batch-size'))}) # A dictionary of all the defined actions to callables (which take @@ -71,7 +70,7 @@ def main(args): try: action(args) except Exception as e: - action_fail(str(e)) + hookenv.action_fail(str(e)) if __name__ == "__main__": diff --git a/actions/openstack_upgrade.py b/actions/openstack_upgrade.py index 694155e9..ee0c702e 100755 --- a/actions/openstack_upgrade.py +++ b/actions/openstack_upgrade.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Copyright 2016 Canonical Ltd # @@ -14,28 +14,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import sys -sys.path.append('hooks/') +_path = os.path.dirname(os.path.realpath(__file__)) +_root = os.path.abspath(os.path.join(_path, '..')) -from charmhelpers.contrib.openstack.utils import ( - do_action_openstack_upgrade, -) -from charmhelpers.core.hookenv import ( - relation_ids, -) +def _add_path(path): + if path not in sys.path: + sys.path.insert(1, path) -from nova_cc_utils import ( - do_openstack_upgrade, -) +_add_path(_root) -from nova_cc_hooks import ( - config_changed, - CONFIGS, - neutron_api_relation_joined, - db_joined, -) +import charmhelpers.contrib.openstack.utils as ch_utils +import charmhelpers.core.hookenv as hookenv +import hooks.nova_cc_utils as utils +import hooks.nova_cc_hooks as hooks def openstack_upgrade(): @@ -46,16 +41,17 @@ def openstack_upgrade(): code to run, otherwise a full service level upgrade will fire on config-changed.""" - if (do_action_openstack_upgrade('nova-common', - do_openstack_upgrade, - CONFIGS)): - [neutron_api_relation_joined(rid=rid, remote_restart=True) - for rid in relation_ids('neutron-api')] + if (ch_utils.do_action_openstack_upgrade('nova-common', + utils.do_openstack_upgrade, + hooks.CONFIGS)): + for rid in hookenv.relation_ids('neutron-api'): + hooks.neutron_api_relation_joined(rid=rid, remote_restart=True) # NOTE(thedac): Force re-fire of shared-db joined hook # to ensure that nova_api database is setup if required. - [db_joined(relation_id=r_id) - for r_id in relation_ids('shared-db')] - config_changed() + for r_id in hookenv.relation_ids('shared-db'): + hooks.db_joined(relation_id=r_id) + hooks.config_changed() if __name__ == '__main__': + hooks.resolve_CONFIGS() openstack_upgrade() diff --git a/charm-helpers-hooks.yaml b/charm-helpers-hooks.yaml index 85ed4407..3fdc3313 100644 --- a/charm-helpers-hooks.yaml +++ b/charm-helpers-hooks.yaml @@ -1,5 +1,5 @@ repo: https://github.com/juju/charm-helpers -destination: hooks/charmhelpers +destination: charmhelpers include: - core - osplatform diff --git a/hooks/charmhelpers/__init__.py b/charmhelpers/__init__.py similarity index 100% rename from hooks/charmhelpers/__init__.py rename to charmhelpers/__init__.py diff --git a/hooks/charmhelpers/cli/__init__.py b/charmhelpers/cli/__init__.py similarity index 100% rename from hooks/charmhelpers/cli/__init__.py rename to charmhelpers/cli/__init__.py diff --git a/hooks/charmhelpers/cli/benchmark.py b/charmhelpers/cli/benchmark.py similarity index 100% rename from hooks/charmhelpers/cli/benchmark.py rename to charmhelpers/cli/benchmark.py diff --git a/hooks/charmhelpers/cli/commands.py b/charmhelpers/cli/commands.py similarity index 100% rename from hooks/charmhelpers/cli/commands.py rename to charmhelpers/cli/commands.py diff --git a/hooks/charmhelpers/cli/hookenv.py b/charmhelpers/cli/hookenv.py similarity index 100% rename from hooks/charmhelpers/cli/hookenv.py rename to charmhelpers/cli/hookenv.py diff --git a/hooks/charmhelpers/cli/host.py b/charmhelpers/cli/host.py similarity index 100% rename from hooks/charmhelpers/cli/host.py rename to charmhelpers/cli/host.py diff --git a/hooks/charmhelpers/cli/unitdata.py b/charmhelpers/cli/unitdata.py similarity index 100% rename from hooks/charmhelpers/cli/unitdata.py rename to charmhelpers/cli/unitdata.py diff --git a/hooks/charmhelpers/contrib/__init__.py b/charmhelpers/contrib/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/__init__.py rename to charmhelpers/contrib/__init__.py diff --git a/hooks/charmhelpers/contrib/charmsupport/__init__.py b/charmhelpers/contrib/charmsupport/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/charmsupport/__init__.py rename to charmhelpers/contrib/charmsupport/__init__.py diff --git a/hooks/charmhelpers/contrib/charmsupport/nrpe.py b/charmhelpers/contrib/charmsupport/nrpe.py similarity index 100% rename from hooks/charmhelpers/contrib/charmsupport/nrpe.py rename to charmhelpers/contrib/charmsupport/nrpe.py diff --git a/hooks/charmhelpers/contrib/charmsupport/volumes.py b/charmhelpers/contrib/charmsupport/volumes.py similarity index 100% rename from hooks/charmhelpers/contrib/charmsupport/volumes.py rename to charmhelpers/contrib/charmsupport/volumes.py diff --git a/hooks/charmhelpers/contrib/hahelpers/__init__.py b/charmhelpers/contrib/hahelpers/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hahelpers/__init__.py rename to charmhelpers/contrib/hahelpers/__init__.py diff --git a/hooks/charmhelpers/contrib/hahelpers/apache.py b/charmhelpers/contrib/hahelpers/apache.py similarity index 100% rename from hooks/charmhelpers/contrib/hahelpers/apache.py rename to charmhelpers/contrib/hahelpers/apache.py diff --git a/hooks/charmhelpers/contrib/hahelpers/cluster.py b/charmhelpers/contrib/hahelpers/cluster.py similarity index 100% rename from hooks/charmhelpers/contrib/hahelpers/cluster.py rename to charmhelpers/contrib/hahelpers/cluster.py diff --git a/hooks/charmhelpers/contrib/hardening/README.hardening.md b/charmhelpers/contrib/hardening/README.hardening.md similarity index 100% rename from hooks/charmhelpers/contrib/hardening/README.hardening.md rename to charmhelpers/contrib/hardening/README.hardening.md diff --git a/hooks/charmhelpers/contrib/hardening/__init__.py b/charmhelpers/contrib/hardening/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/__init__.py rename to charmhelpers/contrib/hardening/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/apache/__init__.py b/charmhelpers/contrib/hardening/apache/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/apache/__init__.py rename to charmhelpers/contrib/hardening/apache/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/apache/checks/__init__.py b/charmhelpers/contrib/hardening/apache/checks/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/apache/checks/__init__.py rename to charmhelpers/contrib/hardening/apache/checks/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/apache/checks/config.py b/charmhelpers/contrib/hardening/apache/checks/config.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/apache/checks/config.py rename to charmhelpers/contrib/hardening/apache/checks/config.py diff --git a/hooks/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf b/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf similarity index 100% rename from hooks/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf rename to charmhelpers/contrib/hardening/apache/templates/99-hardening.conf diff --git a/hooks/charmhelpers/contrib/hardening/apache/templates/__init__.py b/charmhelpers/contrib/hardening/apache/templates/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/apache/templates/__init__.py rename to charmhelpers/contrib/hardening/apache/templates/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/apache/templates/alias.conf b/charmhelpers/contrib/hardening/apache/templates/alias.conf similarity index 100% rename from hooks/charmhelpers/contrib/hardening/apache/templates/alias.conf rename to charmhelpers/contrib/hardening/apache/templates/alias.conf diff --git a/hooks/charmhelpers/contrib/hardening/audits/__init__.py b/charmhelpers/contrib/hardening/audits/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/audits/__init__.py rename to charmhelpers/contrib/hardening/audits/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/audits/apache.py b/charmhelpers/contrib/hardening/audits/apache.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/audits/apache.py rename to charmhelpers/contrib/hardening/audits/apache.py diff --git a/hooks/charmhelpers/contrib/hardening/audits/apt.py b/charmhelpers/contrib/hardening/audits/apt.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/audits/apt.py rename to charmhelpers/contrib/hardening/audits/apt.py diff --git a/hooks/charmhelpers/contrib/hardening/audits/file.py b/charmhelpers/contrib/hardening/audits/file.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/audits/file.py rename to charmhelpers/contrib/hardening/audits/file.py diff --git a/hooks/charmhelpers/contrib/hardening/defaults/__init__.py b/charmhelpers/contrib/hardening/defaults/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/__init__.py rename to charmhelpers/contrib/hardening/defaults/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/defaults/apache.yaml b/charmhelpers/contrib/hardening/defaults/apache.yaml similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/apache.yaml rename to charmhelpers/contrib/hardening/defaults/apache.yaml diff --git a/hooks/charmhelpers/contrib/hardening/defaults/apache.yaml.schema b/charmhelpers/contrib/hardening/defaults/apache.yaml.schema similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/apache.yaml.schema rename to charmhelpers/contrib/hardening/defaults/apache.yaml.schema diff --git a/hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml b/charmhelpers/contrib/hardening/defaults/mysql.yaml similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml rename to charmhelpers/contrib/hardening/defaults/mysql.yaml diff --git a/hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema b/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema rename to charmhelpers/contrib/hardening/defaults/mysql.yaml.schema diff --git a/hooks/charmhelpers/contrib/hardening/defaults/os.yaml b/charmhelpers/contrib/hardening/defaults/os.yaml similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/os.yaml rename to charmhelpers/contrib/hardening/defaults/os.yaml diff --git a/hooks/charmhelpers/contrib/hardening/defaults/os.yaml.schema b/charmhelpers/contrib/hardening/defaults/os.yaml.schema similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/os.yaml.schema rename to charmhelpers/contrib/hardening/defaults/os.yaml.schema diff --git a/hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml b/charmhelpers/contrib/hardening/defaults/ssh.yaml similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml rename to charmhelpers/contrib/hardening/defaults/ssh.yaml diff --git a/hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema b/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema similarity index 100% rename from hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema rename to charmhelpers/contrib/hardening/defaults/ssh.yaml.schema diff --git a/hooks/charmhelpers/contrib/hardening/harden.py b/charmhelpers/contrib/hardening/harden.py similarity index 85% rename from hooks/charmhelpers/contrib/hardening/harden.py rename to charmhelpers/contrib/hardening/harden.py index b55764cd..2559daf6 100644 --- a/hooks/charmhelpers/contrib/hardening/harden.py +++ b/charmhelpers/contrib/hardening/harden.py @@ -27,6 +27,8 @@ from charmhelpers.contrib.hardening.ssh.checks import run_ssh_checks from charmhelpers.contrib.hardening.mysql.checks import run_mysql_checks from charmhelpers.contrib.hardening.apache.checks import run_apache_checks +_DISABLE_HARDENING_FOR_UNIT_TEST = False + def harden(overrides=None): """Hardening decorator. @@ -48,9 +50,18 @@ def harden(overrides=None): :returns: Returns value returned by decorated function once executed. """ def _harden_inner1(f): - log("Hardening function '%s'" % (f.__name__), level=DEBUG) + # As this has to be py2.7 compat, we can't use nonlocal. Use a trick + # to capture the dictionary that can then be updated. + _logged = {'done': False} def _harden_inner2(*args, **kwargs): + # knock out hardening via a config var; normally it won't get + # disabled. + if _DISABLE_HARDENING_FOR_UNIT_TEST: + return f(*args, **kwargs) + if not _logged['done']: + log("Hardening function '%s'" % (f.__name__), level=DEBUG) + _logged['done'] = True RUN_CATALOG = OrderedDict([('os', run_os_checks), ('ssh', run_ssh_checks), ('mysql', run_mysql_checks), diff --git a/hooks/charmhelpers/contrib/hardening/host/__init__.py b/charmhelpers/contrib/hardening/host/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/__init__.py rename to charmhelpers/contrib/hardening/host/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/__init__.py b/charmhelpers/contrib/hardening/host/checks/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/__init__.py rename to charmhelpers/contrib/hardening/host/checks/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/apt.py b/charmhelpers/contrib/hardening/host/checks/apt.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/apt.py rename to charmhelpers/contrib/hardening/host/checks/apt.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/limits.py b/charmhelpers/contrib/hardening/host/checks/limits.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/limits.py rename to charmhelpers/contrib/hardening/host/checks/limits.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/login.py b/charmhelpers/contrib/hardening/host/checks/login.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/login.py rename to charmhelpers/contrib/hardening/host/checks/login.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/minimize_access.py b/charmhelpers/contrib/hardening/host/checks/minimize_access.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/minimize_access.py rename to charmhelpers/contrib/hardening/host/checks/minimize_access.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/pam.py b/charmhelpers/contrib/hardening/host/checks/pam.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/pam.py rename to charmhelpers/contrib/hardening/host/checks/pam.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/profile.py b/charmhelpers/contrib/hardening/host/checks/profile.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/profile.py rename to charmhelpers/contrib/hardening/host/checks/profile.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/securetty.py b/charmhelpers/contrib/hardening/host/checks/securetty.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/securetty.py rename to charmhelpers/contrib/hardening/host/checks/securetty.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/suid_sgid.py b/charmhelpers/contrib/hardening/host/checks/suid_sgid.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/suid_sgid.py rename to charmhelpers/contrib/hardening/host/checks/suid_sgid.py diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/sysctl.py b/charmhelpers/contrib/hardening/host/checks/sysctl.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/checks/sysctl.py rename to charmhelpers/contrib/hardening/host/checks/sysctl.py diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf b/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf rename to charmhelpers/contrib/hardening/host/templates/10.hardcore.conf diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/99-hardening.sh b/charmhelpers/contrib/hardening/host/templates/99-hardening.sh similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/99-hardening.sh rename to charmhelpers/contrib/hardening/host/templates/99-hardening.sh diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf b/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf rename to charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/__init__.py b/charmhelpers/contrib/hardening/host/templates/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/__init__.py rename to charmhelpers/contrib/hardening/host/templates/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/login.defs b/charmhelpers/contrib/hardening/host/templates/login.defs similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/login.defs rename to charmhelpers/contrib/hardening/host/templates/login.defs diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/modules b/charmhelpers/contrib/hardening/host/templates/modules similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/modules rename to charmhelpers/contrib/hardening/host/templates/modules diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/passwdqc.conf b/charmhelpers/contrib/hardening/host/templates/passwdqc.conf similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/passwdqc.conf rename to charmhelpers/contrib/hardening/host/templates/passwdqc.conf diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh b/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh rename to charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/securetty b/charmhelpers/contrib/hardening/host/templates/securetty similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/securetty rename to charmhelpers/contrib/hardening/host/templates/securetty diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/tally2 b/charmhelpers/contrib/hardening/host/templates/tally2 similarity index 100% rename from hooks/charmhelpers/contrib/hardening/host/templates/tally2 rename to charmhelpers/contrib/hardening/host/templates/tally2 diff --git a/hooks/charmhelpers/contrib/hardening/mysql/__init__.py b/charmhelpers/contrib/hardening/mysql/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/mysql/__init__.py rename to charmhelpers/contrib/hardening/mysql/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/mysql/checks/__init__.py b/charmhelpers/contrib/hardening/mysql/checks/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/mysql/checks/__init__.py rename to charmhelpers/contrib/hardening/mysql/checks/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/mysql/checks/config.py b/charmhelpers/contrib/hardening/mysql/checks/config.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/mysql/checks/config.py rename to charmhelpers/contrib/hardening/mysql/checks/config.py diff --git a/hooks/charmhelpers/contrib/hardening/mysql/templates/__init__.py b/charmhelpers/contrib/hardening/mysql/templates/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/mysql/templates/__init__.py rename to charmhelpers/contrib/hardening/mysql/templates/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf b/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf similarity index 100% rename from hooks/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf rename to charmhelpers/contrib/hardening/mysql/templates/hardening.cnf diff --git a/hooks/charmhelpers/contrib/hardening/ssh/__init__.py b/charmhelpers/contrib/hardening/ssh/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/ssh/__init__.py rename to charmhelpers/contrib/hardening/ssh/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/ssh/checks/__init__.py b/charmhelpers/contrib/hardening/ssh/checks/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/ssh/checks/__init__.py rename to charmhelpers/contrib/hardening/ssh/checks/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/ssh/checks/config.py b/charmhelpers/contrib/hardening/ssh/checks/config.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/ssh/checks/config.py rename to charmhelpers/contrib/hardening/ssh/checks/config.py diff --git a/hooks/charmhelpers/contrib/hardening/ssh/templates/__init__.py b/charmhelpers/contrib/hardening/ssh/templates/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/ssh/templates/__init__.py rename to charmhelpers/contrib/hardening/ssh/templates/__init__.py diff --git a/hooks/charmhelpers/contrib/hardening/ssh/templates/ssh_config b/charmhelpers/contrib/hardening/ssh/templates/ssh_config similarity index 100% rename from hooks/charmhelpers/contrib/hardening/ssh/templates/ssh_config rename to charmhelpers/contrib/hardening/ssh/templates/ssh_config diff --git a/hooks/charmhelpers/contrib/hardening/ssh/templates/sshd_config b/charmhelpers/contrib/hardening/ssh/templates/sshd_config similarity index 100% rename from hooks/charmhelpers/contrib/hardening/ssh/templates/sshd_config rename to charmhelpers/contrib/hardening/ssh/templates/sshd_config diff --git a/hooks/charmhelpers/contrib/hardening/templating.py b/charmhelpers/contrib/hardening/templating.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/templating.py rename to charmhelpers/contrib/hardening/templating.py diff --git a/hooks/charmhelpers/contrib/hardening/utils.py b/charmhelpers/contrib/hardening/utils.py similarity index 100% rename from hooks/charmhelpers/contrib/hardening/utils.py rename to charmhelpers/contrib/hardening/utils.py diff --git a/hooks/charmhelpers/contrib/network/__init__.py b/charmhelpers/contrib/network/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/network/__init__.py rename to charmhelpers/contrib/network/__init__.py diff --git a/hooks/charmhelpers/contrib/network/ip.py b/charmhelpers/contrib/network/ip.py similarity index 100% rename from hooks/charmhelpers/contrib/network/ip.py rename to charmhelpers/contrib/network/ip.py diff --git a/hooks/charmhelpers/contrib/openstack/__init__.py b/charmhelpers/contrib/openstack/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/__init__.py rename to charmhelpers/contrib/openstack/__init__.py diff --git a/hooks/charmhelpers/contrib/openstack/alternatives.py b/charmhelpers/contrib/openstack/alternatives.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/alternatives.py rename to charmhelpers/contrib/openstack/alternatives.py diff --git a/hooks/charmhelpers/contrib/openstack/amulet/__init__.py b/charmhelpers/contrib/openstack/amulet/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/amulet/__init__.py rename to charmhelpers/contrib/openstack/amulet/__init__.py diff --git a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py b/charmhelpers/contrib/openstack/amulet/deployment.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/amulet/deployment.py rename to charmhelpers/contrib/openstack/amulet/deployment.py diff --git a/hooks/charmhelpers/contrib/openstack/amulet/utils.py b/charmhelpers/contrib/openstack/amulet/utils.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/amulet/utils.py rename to charmhelpers/contrib/openstack/amulet/utils.py diff --git a/hooks/charmhelpers/contrib/openstack/cert_utils.py b/charmhelpers/contrib/openstack/cert_utils.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/cert_utils.py rename to charmhelpers/contrib/openstack/cert_utils.py diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/charmhelpers/contrib/openstack/context.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/context.py rename to charmhelpers/contrib/openstack/context.py diff --git a/hooks/charmhelpers/contrib/openstack/exceptions.py b/charmhelpers/contrib/openstack/exceptions.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/exceptions.py rename to charmhelpers/contrib/openstack/exceptions.py diff --git a/hooks/charmhelpers/contrib/openstack/files/__init__.py b/charmhelpers/contrib/openstack/files/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/files/__init__.py rename to charmhelpers/contrib/openstack/files/__init__.py diff --git a/hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh b/charmhelpers/contrib/openstack/files/check_haproxy.sh similarity index 100% rename from hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh rename to charmhelpers/contrib/openstack/files/check_haproxy.sh diff --git a/hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh b/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh similarity index 100% rename from hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh rename to charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh diff --git a/hooks/charmhelpers/contrib/openstack/ha/__init__.py b/charmhelpers/contrib/openstack/ha/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/ha/__init__.py rename to charmhelpers/contrib/openstack/ha/__init__.py diff --git a/hooks/charmhelpers/contrib/openstack/ha/utils.py b/charmhelpers/contrib/openstack/ha/utils.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/ha/utils.py rename to charmhelpers/contrib/openstack/ha/utils.py diff --git a/hooks/charmhelpers/contrib/openstack/ip.py b/charmhelpers/contrib/openstack/ip.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/ip.py rename to charmhelpers/contrib/openstack/ip.py diff --git a/hooks/charmhelpers/contrib/openstack/keystone.py b/charmhelpers/contrib/openstack/keystone.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/keystone.py rename to charmhelpers/contrib/openstack/keystone.py diff --git a/hooks/charmhelpers/contrib/openstack/neutron.py b/charmhelpers/contrib/openstack/neutron.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/neutron.py rename to charmhelpers/contrib/openstack/neutron.py diff --git a/hooks/charmhelpers/contrib/openstack/ssh_migrations.py b/charmhelpers/contrib/openstack/ssh_migrations.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/ssh_migrations.py rename to charmhelpers/contrib/openstack/ssh_migrations.py diff --git a/hooks/charmhelpers/contrib/openstack/templates/__init__.py b/charmhelpers/contrib/openstack/templates/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/__init__.py rename to charmhelpers/contrib/openstack/templates/__init__.py diff --git a/hooks/charmhelpers/contrib/openstack/templates/ceph.conf b/charmhelpers/contrib/openstack/templates/ceph.conf similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/ceph.conf rename to charmhelpers/contrib/openstack/templates/ceph.conf diff --git a/hooks/charmhelpers/contrib/openstack/templates/git.upstart b/charmhelpers/contrib/openstack/templates/git.upstart similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/git.upstart rename to charmhelpers/contrib/openstack/templates/git.upstart diff --git a/hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg b/charmhelpers/contrib/openstack/templates/haproxy.cfg similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg rename to charmhelpers/contrib/openstack/templates/haproxy.cfg diff --git a/hooks/charmhelpers/contrib/openstack/templates/memcached.conf b/charmhelpers/contrib/openstack/templates/memcached.conf similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/memcached.conf rename to charmhelpers/contrib/openstack/templates/memcached.conf diff --git a/hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend b/charmhelpers/contrib/openstack/templates/openstack_https_frontend similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend rename to charmhelpers/contrib/openstack/templates/openstack_https_frontend diff --git a/hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf b/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf rename to charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken rename to charmhelpers/contrib/openstack/templates/section-keystone-authtoken diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-legacy b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-legacy similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-legacy rename to charmhelpers/contrib/openstack/templates/section-keystone-authtoken-legacy diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka rename to charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-oslo-cache b/charmhelpers/contrib/openstack/templates/section-oslo-cache similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-oslo-cache rename to charmhelpers/contrib/openstack/templates/section-oslo-cache diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-oslo-middleware b/charmhelpers/contrib/openstack/templates/section-oslo-middleware similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-oslo-middleware rename to charmhelpers/contrib/openstack/templates/section-oslo-middleware diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-oslo-notifications b/charmhelpers/contrib/openstack/templates/section-oslo-notifications similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-oslo-notifications rename to charmhelpers/contrib/openstack/templates/section-oslo-notifications diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo b/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo rename to charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo diff --git a/hooks/charmhelpers/contrib/openstack/templates/section-zeromq b/charmhelpers/contrib/openstack/templates/section-zeromq similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/section-zeromq rename to charmhelpers/contrib/openstack/templates/section-zeromq diff --git a/hooks/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf b/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf rename to charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf diff --git a/hooks/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf b/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf rename to charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf diff --git a/hooks/charmhelpers/contrib/openstack/templating.py b/charmhelpers/contrib/openstack/templating.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/templating.py rename to charmhelpers/contrib/openstack/templating.py diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/charmhelpers/contrib/openstack/utils.py similarity index 98% rename from hooks/charmhelpers/contrib/openstack/utils.py rename to charmhelpers/contrib/openstack/utils.py index ae48d6b4..2a681d81 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/charmhelpers/contrib/openstack/utils.py @@ -1450,20 +1450,32 @@ def pausable_restart_on_change(restart_map, stopstart=False, see core.utils.restart_on_change() for more details. + Note restart_map can be a callable, in which case, restart_map is only + evaluated at runtime. This means that it is lazy and the underlying + function won't be called if the decorated function is never called. Note, + retains backwards compatibility for passing a non-callable dictionary. + @param f: the function to decorate - @param restart_map: the restart map {conf_file: [services]} + @param restart_map: (optionally callable, which then returns the + restart_map) the restart map {conf_file: [services]} @param stopstart: DEFAULT false; whether to stop, start or just restart @returns decorator to use a restart_on_change with pausability """ def wrap(f): + # py27 compatible nonlocal variable. When py3 only, replace with + # nonlocal keyword + __restart_map_cache = {'cache': None} @functools.wraps(f) def wrapped_f(*args, **kwargs): if is_unit_paused_set(): return f(*args, **kwargs) + if __restart_map_cache['cache'] is None: + __restart_map_cache['cache'] = restart_map() \ + if callable(restart_map) else restart_map # otherwise, normal restart_on_change functionality return restart_on_change_helper( - (lambda: f(*args, **kwargs)), restart_map, stopstart, - restart_functions) + (lambda: f(*args, **kwargs)), __restart_map_cache['cache'], + stopstart, restart_functions) return wrapped_f return wrap diff --git a/hooks/charmhelpers/contrib/openstack/vaultlocker.py b/charmhelpers/contrib/openstack/vaultlocker.py similarity index 100% rename from hooks/charmhelpers/contrib/openstack/vaultlocker.py rename to charmhelpers/contrib/openstack/vaultlocker.py diff --git a/hooks/charmhelpers/contrib/peerstorage/__init__.py b/charmhelpers/contrib/peerstorage/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/peerstorage/__init__.py rename to charmhelpers/contrib/peerstorage/__init__.py diff --git a/hooks/charmhelpers/contrib/python/__init__.py b/charmhelpers/contrib/python/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/python/__init__.py rename to charmhelpers/contrib/python/__init__.py diff --git a/hooks/charmhelpers/contrib/python/packages.py b/charmhelpers/contrib/python/packages.py similarity index 100% rename from hooks/charmhelpers/contrib/python/packages.py rename to charmhelpers/contrib/python/packages.py diff --git a/hooks/charmhelpers/contrib/storage/__init__.py b/charmhelpers/contrib/storage/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/storage/__init__.py rename to charmhelpers/contrib/storage/__init__.py diff --git a/hooks/charmhelpers/contrib/storage/linux/__init__.py b/charmhelpers/contrib/storage/linux/__init__.py similarity index 100% rename from hooks/charmhelpers/contrib/storage/linux/__init__.py rename to charmhelpers/contrib/storage/linux/__init__.py diff --git a/hooks/charmhelpers/contrib/storage/linux/bcache.py b/charmhelpers/contrib/storage/linux/bcache.py similarity index 100% rename from hooks/charmhelpers/contrib/storage/linux/bcache.py rename to charmhelpers/contrib/storage/linux/bcache.py diff --git a/hooks/charmhelpers/contrib/storage/linux/ceph.py b/charmhelpers/contrib/storage/linux/ceph.py similarity index 100% rename from hooks/charmhelpers/contrib/storage/linux/ceph.py rename to charmhelpers/contrib/storage/linux/ceph.py diff --git a/hooks/charmhelpers/contrib/storage/linux/loopback.py b/charmhelpers/contrib/storage/linux/loopback.py similarity index 100% rename from hooks/charmhelpers/contrib/storage/linux/loopback.py rename to charmhelpers/contrib/storage/linux/loopback.py diff --git a/hooks/charmhelpers/contrib/storage/linux/lvm.py b/charmhelpers/contrib/storage/linux/lvm.py similarity index 100% rename from hooks/charmhelpers/contrib/storage/linux/lvm.py rename to charmhelpers/contrib/storage/linux/lvm.py diff --git a/hooks/charmhelpers/contrib/storage/linux/utils.py b/charmhelpers/contrib/storage/linux/utils.py similarity index 100% rename from hooks/charmhelpers/contrib/storage/linux/utils.py rename to charmhelpers/contrib/storage/linux/utils.py diff --git a/hooks/charmhelpers/core/__init__.py b/charmhelpers/core/__init__.py similarity index 100% rename from hooks/charmhelpers/core/__init__.py rename to charmhelpers/core/__init__.py diff --git a/hooks/charmhelpers/core/decorators.py b/charmhelpers/core/decorators.py similarity index 100% rename from hooks/charmhelpers/core/decorators.py rename to charmhelpers/core/decorators.py diff --git a/hooks/charmhelpers/core/files.py b/charmhelpers/core/files.py similarity index 100% rename from hooks/charmhelpers/core/files.py rename to charmhelpers/core/files.py diff --git a/hooks/charmhelpers/core/fstab.py b/charmhelpers/core/fstab.py similarity index 100% rename from hooks/charmhelpers/core/fstab.py rename to charmhelpers/core/fstab.py diff --git a/hooks/charmhelpers/core/hookenv.py b/charmhelpers/core/hookenv.py similarity index 100% rename from hooks/charmhelpers/core/hookenv.py rename to charmhelpers/core/hookenv.py diff --git a/hooks/charmhelpers/core/host.py b/charmhelpers/core/host.py similarity index 100% rename from hooks/charmhelpers/core/host.py rename to charmhelpers/core/host.py diff --git a/hooks/charmhelpers/core/host_factory/__init__.py b/charmhelpers/core/host_factory/__init__.py similarity index 100% rename from hooks/charmhelpers/core/host_factory/__init__.py rename to charmhelpers/core/host_factory/__init__.py diff --git a/hooks/charmhelpers/core/host_factory/centos.py b/charmhelpers/core/host_factory/centos.py similarity index 100% rename from hooks/charmhelpers/core/host_factory/centos.py rename to charmhelpers/core/host_factory/centos.py diff --git a/hooks/charmhelpers/core/host_factory/ubuntu.py b/charmhelpers/core/host_factory/ubuntu.py similarity index 100% rename from hooks/charmhelpers/core/host_factory/ubuntu.py rename to charmhelpers/core/host_factory/ubuntu.py diff --git a/hooks/charmhelpers/core/hugepage.py b/charmhelpers/core/hugepage.py similarity index 100% rename from hooks/charmhelpers/core/hugepage.py rename to charmhelpers/core/hugepage.py diff --git a/hooks/charmhelpers/core/kernel.py b/charmhelpers/core/kernel.py similarity index 100% rename from hooks/charmhelpers/core/kernel.py rename to charmhelpers/core/kernel.py diff --git a/hooks/charmhelpers/core/kernel_factory/__init__.py b/charmhelpers/core/kernel_factory/__init__.py similarity index 100% rename from hooks/charmhelpers/core/kernel_factory/__init__.py rename to charmhelpers/core/kernel_factory/__init__.py diff --git a/hooks/charmhelpers/core/kernel_factory/centos.py b/charmhelpers/core/kernel_factory/centos.py similarity index 100% rename from hooks/charmhelpers/core/kernel_factory/centos.py rename to charmhelpers/core/kernel_factory/centos.py diff --git a/hooks/charmhelpers/core/kernel_factory/ubuntu.py b/charmhelpers/core/kernel_factory/ubuntu.py similarity index 100% rename from hooks/charmhelpers/core/kernel_factory/ubuntu.py rename to charmhelpers/core/kernel_factory/ubuntu.py diff --git a/hooks/charmhelpers/core/services/__init__.py b/charmhelpers/core/services/__init__.py similarity index 100% rename from hooks/charmhelpers/core/services/__init__.py rename to charmhelpers/core/services/__init__.py diff --git a/hooks/charmhelpers/core/services/base.py b/charmhelpers/core/services/base.py similarity index 100% rename from hooks/charmhelpers/core/services/base.py rename to charmhelpers/core/services/base.py diff --git a/hooks/charmhelpers/core/services/helpers.py b/charmhelpers/core/services/helpers.py similarity index 100% rename from hooks/charmhelpers/core/services/helpers.py rename to charmhelpers/core/services/helpers.py diff --git a/hooks/charmhelpers/core/strutils.py b/charmhelpers/core/strutils.py similarity index 100% rename from hooks/charmhelpers/core/strutils.py rename to charmhelpers/core/strutils.py diff --git a/hooks/charmhelpers/core/sysctl.py b/charmhelpers/core/sysctl.py similarity index 100% rename from hooks/charmhelpers/core/sysctl.py rename to charmhelpers/core/sysctl.py diff --git a/hooks/charmhelpers/core/templating.py b/charmhelpers/core/templating.py similarity index 100% rename from hooks/charmhelpers/core/templating.py rename to charmhelpers/core/templating.py diff --git a/hooks/charmhelpers/core/unitdata.py b/charmhelpers/core/unitdata.py similarity index 100% rename from hooks/charmhelpers/core/unitdata.py rename to charmhelpers/core/unitdata.py diff --git a/hooks/charmhelpers/fetch/__init__.py b/charmhelpers/fetch/__init__.py similarity index 100% rename from hooks/charmhelpers/fetch/__init__.py rename to charmhelpers/fetch/__init__.py diff --git a/hooks/charmhelpers/fetch/archiveurl.py b/charmhelpers/fetch/archiveurl.py similarity index 100% rename from hooks/charmhelpers/fetch/archiveurl.py rename to charmhelpers/fetch/archiveurl.py diff --git a/hooks/charmhelpers/fetch/bzrurl.py b/charmhelpers/fetch/bzrurl.py similarity index 100% rename from hooks/charmhelpers/fetch/bzrurl.py rename to charmhelpers/fetch/bzrurl.py diff --git a/hooks/charmhelpers/fetch/centos.py b/charmhelpers/fetch/centos.py similarity index 100% rename from hooks/charmhelpers/fetch/centos.py rename to charmhelpers/fetch/centos.py diff --git a/hooks/charmhelpers/fetch/giturl.py b/charmhelpers/fetch/giturl.py similarity index 100% rename from hooks/charmhelpers/fetch/giturl.py rename to charmhelpers/fetch/giturl.py diff --git a/hooks/charmhelpers/fetch/snap.py b/charmhelpers/fetch/snap.py similarity index 100% rename from hooks/charmhelpers/fetch/snap.py rename to charmhelpers/fetch/snap.py diff --git a/hooks/charmhelpers/fetch/ubuntu.py b/charmhelpers/fetch/ubuntu.py similarity index 100% rename from hooks/charmhelpers/fetch/ubuntu.py rename to charmhelpers/fetch/ubuntu.py diff --git a/hooks/charmhelpers/osplatform.py b/charmhelpers/osplatform.py similarity index 100% rename from hooks/charmhelpers/osplatform.py rename to charmhelpers/osplatform.py diff --git a/hooks/charmhelpers/payload/__init__.py b/charmhelpers/payload/__init__.py similarity index 100% rename from hooks/charmhelpers/payload/__init__.py rename to charmhelpers/payload/__init__.py diff --git a/hooks/charmhelpers/payload/archive.py b/charmhelpers/payload/archive.py similarity index 100% rename from hooks/charmhelpers/payload/archive.py rename to charmhelpers/payload/archive.py diff --git a/hooks/charmhelpers/payload/execd.py b/charmhelpers/payload/execd.py similarity index 100% rename from hooks/charmhelpers/payload/execd.py rename to charmhelpers/payload/execd.py diff --git a/files/README.txt b/files/README.txt new file mode 100644 index 00000000..61891b1d --- /dev/null +++ b/files/README.txt @@ -0,0 +1,3 @@ +Note that the python files in THIS directory depend on the payload python version +and not the charm python version (which is PY3). These files will need to be modified +when the payload changes to PY3. diff --git a/hooks/install b/hooks/install index 29ff6894..50b8cad9 100755 --- a/hooks/install +++ b/hooks/install @@ -11,7 +11,7 @@ check_and_install() { fi } -PYTHON="python" +PYTHON="python3" for dep in ${DEPS[@]}; do check_and_install ${PYTHON} ${dep} diff --git a/hooks/nova_cc_common.py b/hooks/nova_cc_common.py new file mode 100644 index 00000000..1a89a610 --- /dev/null +++ b/hooks/nova_cc_common.py @@ -0,0 +1,77 @@ +# Copyright 2018 Canonical Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Note that *this* file exists to break a circular import dependency issue +# between nova_cc_utils and nova_cc_context. It may be beneficial to migrate +# other constants to this file. + +import charmhelpers.core.hookenv as hookenv + +API_PORTS = { + 'nova-api-ec2': 8773, + 'nova-api-os-compute': 8774, + 'nova-api-metadata': 8775, + 'nova-api-os-volume': 8776, + 'nova-placement-api': 8778, + 'nova-objectstore': 3333, +} + + +CONSOLE_CONFIG = { + 'spice': { + 'packages': ['nova-spiceproxy', 'nova-consoleauth'], + 'services': ['nova-spiceproxy', 'nova-consoleauth'], + 'proxy-page': '/spice_auto.html', + 'proxy-port': 6082, + }, + 'novnc': { + 'packages': ['nova-novncproxy', 'nova-consoleauth'], + 'services': ['nova-novncproxy', 'nova-consoleauth'], + 'proxy-page': '/vnc_auto.html', + 'proxy-port': 6080, + }, + 'xvpvnc': { + 'packages': ['nova-xvpvncproxy', 'nova-consoleauth'], + 'services': ['nova-xvpvncproxy', 'nova-consoleauth'], + 'proxy-page': '/console', + 'proxy-port': 6081, + }, +} + + +def api_port(service): + return API_PORTS[service] + + +def console_attributes(attr, proto=None): + '''Leave proto unset to query attributes of the protocal specified at + runtime''' + if proto: + console_proto = proto + else: + console_proto = hookenv.config('console-access-protocol') + if console_proto is not None and console_proto.lower() in ('none', ''): + console_proto = None + if attr == 'protocol': + return console_proto + # 'vnc' is a virtual type made up of novnc and xvpvnc + if console_proto == 'vnc': + if attr in ['packages', 'services']: + return list(set(CONSOLE_CONFIG['novnc'][attr] + + CONSOLE_CONFIG['xvpvnc'][attr])) + else: + return None + if console_proto in CONSOLE_CONFIG: + return CONSOLE_CONFIG[console_proto][attr] + return None diff --git a/hooks/nova_cc_context.py b/hooks/nova_cc_context.py index bb3a3c42..ce61292f 100644 --- a/hooks/nova_cc_context.py +++ b/hooks/nova_cc_context.py @@ -15,77 +15,61 @@ import json import os -from base64 import b64decode -from charmhelpers.core.hookenv import ( - config, - relation_ids, - relation_set, - leader_get, - log, - DEBUG, - related_units, - relations_for_id, - relation_get, - unit_get, -) -from charmhelpers.contrib.openstack import ( - context, - neutron, -) -from charmhelpers.contrib.hahelpers.cluster import ( - determine_apache_port, - determine_api_port, - https, - is_clustered, -) -from charmhelpers.contrib.network.ip import ( - format_ipv6_addr, -) -from charmhelpers.contrib.openstack.ip import ( - resolve_address, - INTERNAL, - PUBLIC, -) -from charmhelpers.contrib.openstack.utils import ( - os_release, - CompareOpenStackReleases, -) +import base64 + +import charmhelpers.contrib.hahelpers.cluster as ch_cluster +import charmhelpers.contrib.network.ip as ch_network_ip +import charmhelpers.contrib.openstack.context as ch_context +import charmhelpers.contrib.openstack.ip as ch_ip +import charmhelpers.contrib.openstack.neutron as ch_neutron +import charmhelpers.contrib.openstack.utils as ch_utils +import charmhelpers.core.hookenv as hookenv + +import hooks.nova_cc_common as common def context_complete(ctxt): _missing = [] - for k, v in ctxt.iteritems(): + for k, v in ctxt.items(): if v is None or v == '': _missing.append(k) if _missing: - log('Missing required data: %s' % ' '.join(_missing), level='INFO') + hookenv.log('Missing required data: %s' % ' '.join(_missing), + level='INFO') return False return True -class ApacheSSLContext(context.ApacheSSLContext): +class ApacheSSLContext(ch_context.ApacheSSLContext): interfaces = ['https'] external_ports = [] service_namespace = 'nova' + def __init__(self, _external_ports_maybe_callable): + self._external_ports_maybe_callable = _external_ports_maybe_callable + self.external_ports = None + super(ApacheSSLContext, self).__init__() + def __call__(self): - # late import to work around circular dependency - from nova_cc_utils import determine_ports - self.external_ports = determine_ports() + if self.external_ports is None: + if callable(self._external_ports_maybe_callable): + self.external_ports = self._external_ports_maybe_callable() + else: + self.external_ports = self._external_ports_maybe_callable return super(ApacheSSLContext, self).__call__() -class NovaCellV2Context(context.OSContextGenerator): +class NovaCellV2Context(ch_context.OSContextGenerator): interfaces = ['nova-cell-api'] def __call__(self): ctxt = {} required_keys = ['cell-name', 'amqp-service', 'db-service'] - for rid in relation_ids('nova-cell-api'): - for unit in related_units(rid): - data = relation_get(rid=rid, unit=unit) + for rid in hookenv.relation_ids('nova-cell-api'): + for unit in hookenv.related_units(rid): + data = hookenv.relation_get(rid=rid, unit=unit) if set(required_keys).issubset(data.keys()): ctxt[data['cell-name']] = { 'amqp_service': data['amqp-service'], @@ -93,46 +77,46 @@ class NovaCellV2Context(context.OSContextGenerator): return ctxt -class NovaCellV2SharedDBContext(context.OSContextGenerator): +class NovaCellV2SharedDBContext(ch_context.OSContextGenerator): interfaces = ['shared-db'] def __call__(self): - log('Generating template context for cell v2 share-db') + hookenv.log('Generating template context for cell v2 share-db') ctxt = {} - for rid in relation_ids('shared-db'): - for unit in related_units(rid): - rdata = relation_get(rid=rid, unit=unit) + for rid in hookenv.relation_ids('shared-db'): + for unit in hookenv.related_units(rid): + rdata = hookenv.relation_get(rid=rid, unit=unit) ctxt = { 'novaapi_password': rdata.get('novaapi_password'), 'novacell0_password': rdata.get('novacell0_password'), 'nova_password': rdata.get('nova_password'), } - if context.context_complete(ctxt): + if ch_context.context_complete(ctxt): return ctxt return {} -class CloudComputeContext(context.OSContextGenerator): +class CloudComputeContext(ch_context.OSContextGenerator): "Dummy context used by service status to check relation exists" interfaces = ['nova-compute'] def __call__(self): ctxt = {} - rids = [rid for rid in relation_ids('cloud-compute')] + rids = [rid for rid in hookenv.relation_ids('cloud-compute')] if rids: ctxt['rids'] = rids return ctxt -class NeutronAPIContext(context.OSContextGenerator): +class NeutronAPIContext(ch_context.OSContextGenerator): interfaces = ['neutron-api'] def __call__(self): - log('Generating template context from neutron api relation') + hookenv.log('Generating template context from neutron api relation') ctxt = {} - for rid in relation_ids('neutron-api'): - for unit in related_units(rid): - rdata = relation_get(rid=rid, unit=unit) + for rid in hookenv.relation_ids('neutron-api'): + for unit in hookenv.related_units(rid): + rdata = hookenv.relation_get(rid=rid, unit=unit) ctxt = { 'neutron_url': rdata.get('neutron-url'), 'neutron_plugin': rdata.get('neutron-plugin'), @@ -147,20 +131,20 @@ class NeutronAPIContext(context.OSContextGenerator): return {} -class VolumeServiceContext(context.OSContextGenerator): +class VolumeServiceContext(ch_context.OSContextGenerator): interfaces = ['cinder-volume-service'] def __call__(self): ctxt = {} - if relation_ids('cinder-volume-service'): + if hookenv.relation_ids('cinder-volume-service'): ctxt['volume_service'] = 'cinder' # kick all compute nodes to know they should use cinder now. - [relation_set(relation_id=rid, volume_service='cinder') - for rid in relation_ids('cloud-compute')] + for rid in hookenv.relation_ids('cloud-compute'): + hookenv.relation_set(relation_id=rid, volume_service='cinder') return ctxt -class HAProxyContext(context.HAProxyContext): +class HAProxyContext(ch_context.HAProxyContext): interfaces = ['ceph'] def __call__(self): @@ -169,32 +153,31 @@ class HAProxyContext(context.HAProxyContext): specific to this charm. Also used to extend nova.conf context with correct api_listening_ports ''' - from nova_cc_utils import api_port ctxt = super(HAProxyContext, self).__call__() # determine which port api processes should bind to, depending # on existence of haproxy + apache frontends - compute_api = determine_api_port(api_port('nova-api-os-compute'), - singlenode_mode=True) - ec2_api = determine_api_port(api_port('nova-api-ec2'), - singlenode_mode=True) - s3_api = determine_api_port(api_port('nova-objectstore'), - singlenode_mode=True) - placement_api = determine_api_port(api_port('nova-placement-api'), - singlenode_mode=True) - metadata_api = determine_api_port(api_port('nova-api-metadata'), - singlenode_mode=True) + compute_api = ch_cluster.determine_api_port( + common.api_port('nova-api-os-compute'), singlenode_mode=True) + ec2_api = ch_cluster.determine_api_port( + common.api_port('nova-api-ec2'), singlenode_mode=True) + s3_api = ch_cluster.determine_api_port( + common.api_port('nova-objectstore'), singlenode_mode=True) + placement_api = ch_cluster.determine_api_port( + common.api_port('nova-placement-api'), singlenode_mode=True) + metadata_api = ch_cluster.determine_api_port( + common.api_port('nova-api-metadata'), singlenode_mode=True) # Apache ports - a_compute_api = determine_apache_port(api_port('nova-api-os-compute'), - singlenode_mode=True) - a_ec2_api = determine_apache_port(api_port('nova-api-ec2'), - singlenode_mode=True) - a_s3_api = determine_apache_port(api_port('nova-objectstore'), - singlenode_mode=True) - a_placement_api = determine_apache_port(api_port('nova-placement-api'), - singlenode_mode=True) - a_metadata_api = determine_apache_port(api_port('nova-api-metadata'), - singlenode_mode=True) + a_compute_api = ch_cluster.determine_apache_port( + common.api_port('nova-api-os-compute'), singlenode_mode=True) + a_ec2_api = ch_cluster.determine_apache_port( + common.api_port('nova-api-ec2'), singlenode_mode=True) + a_s3_api = ch_cluster.determine_apache_port( + common.api_port('nova-objectstore'), singlenode_mode=True) + a_placement_api = ch_cluster.determine_apache_port( + common.api_port('nova-placement-api'), singlenode_mode=True) + a_metadata_api = ch_cluster.determine_apache_port( + common.api_port('nova-api-metadata'), singlenode_mode=True) # to be set in nova.conf accordingly. listen_ports = { 'osapi_compute_listen_port': compute_api, @@ -206,15 +189,15 @@ class HAProxyContext(context.HAProxyContext): port_mapping = { 'nova-api-os-compute': [ - api_port('nova-api-os-compute'), a_compute_api], + common.api_port('nova-api-os-compute'), a_compute_api], 'nova-api-ec2': [ - api_port('nova-api-ec2'), a_ec2_api], + common.api_port('nova-api-ec2'), a_ec2_api], 'nova-objectstore': [ - api_port('nova-objectstore'), a_s3_api], + common.api_port('nova-objectstore'), a_s3_api], 'nova-placement-api': [ - api_port('nova-placement-api'), a_placement_api], + common.api_port('nova-placement-api'), a_placement_api], 'nova-api-metadata': [ - api_port('nova-api-metadata'), a_metadata_api], + common.api_port('nova-api-metadata'), a_metadata_api], } # for haproxy.conf @@ -239,19 +222,19 @@ def canonical_url(): configuration and hacluster. """ scheme = 'http' - if https(): + if ch_cluster.https(): scheme = 'https' - addr = resolve_address(INTERNAL) - return '%s://%s' % (scheme, format_ipv6_addr(addr) or addr) + addr = ch_ip.resolve_address(ch_ip.INTERNAL) + return '%s://%s' % (scheme, ch_network_ip.format_ipv6_addr(addr) or addr) -class NeutronCCContext(context.NeutronContext): +class NeutronCCContext(ch_context.NeutronContext): interfaces = ['quantum-network-service', 'neutron-network-service'] @property def network_manager(self): - return neutron.network_manager() + return ch_neutron.network_manager() def _ensure_packages(self): # Only compute nodes need to ensure packages here, to install @@ -260,12 +243,12 @@ class NeutronCCContext(context.NeutronContext): def __call__(self): ctxt = super(NeutronCCContext, self).__call__() - ctxt['external_network'] = config('neutron-external-network') + ctxt['external_network'] = hookenv.config('neutron-external-network') ctxt['nova_url'] = "{}:8774/v2".format(canonical_url()) return ctxt -class IdentityServiceContext(context.IdentityServiceContext): +class IdentityServiceContext(ch_context.IdentityServiceContext): def __call__(self): ctxt = super(IdentityServiceContext, self).__call__() @@ -280,60 +263,63 @@ class IdentityServiceContext(context.IdentityServiceContext): ctxt['service_port'] ) ctxt['keystone_ec2_url'] = ec2_tokens - ctxt['region'] = config('region') + ctxt['region'] = hookenv.config('region') return ctxt -class NovaConfigContext(context.WorkerConfigContext): +class NovaConfigContext(ch_context.WorkerConfigContext): def __call__(self): ctxt = super(NovaConfigContext, self).__call__() - ctxt['scheduler_default_filters'] = config('scheduler-default-filters') - if config('pci-alias'): - aliases = json.loads(config('pci-alias')) + ctxt['scheduler_default_filters'] = ( + hookenv.config('scheduler-default-filters')) + if hookenv.config('pci-alias'): + aliases = json.loads(hookenv.config('pci-alias')) if isinstance(aliases, list): ctxt['pci_aliases'] = [json.dumps(x, sort_keys=True) for x in aliases] else: ctxt['pci_alias'] = json.dumps(aliases, sort_keys=True) - ctxt['disk_allocation_ratio'] = config('disk-allocation-ratio') - ctxt['cpu_allocation_ratio'] = config('cpu-allocation-ratio') - ctxt['ram_allocation_ratio'] = config('ram-allocation-ratio') - addr = resolve_address(INTERNAL) - ctxt['host_ip'] = format_ipv6_addr(addr) or addr + ctxt['disk_allocation_ratio'] = hookenv.config('disk-allocation-ratio') + ctxt['cpu_allocation_ratio'] = hookenv.config('cpu-allocation-ratio') + ctxt['ram_allocation_ratio'] = hookenv.config('ram-allocation-ratio') + addr = ch_ip.resolve_address(ch_ip.INTERNAL) + ctxt['host_ip'] = ch_network_ip.format_ipv6_addr(addr) or addr return ctxt -class NovaIPv6Context(context.BindHostContext): +class NovaIPv6Context(ch_context.BindHostContext): def __call__(self): ctxt = super(NovaIPv6Context, self).__call__() - ctxt['use_ipv6'] = config('prefer-ipv6') + ctxt['use_ipv6'] = hookenv.config('prefer-ipv6') return ctxt -class InstanceConsoleContext(context.OSContextGenerator): +class InstanceConsoleContext(ch_context.OSContextGenerator): interfaces = [] def __call__(self): ctxt = {} servers = [] try: - for rid in relation_ids('memcache'): - for rel in relations_for_id(rid): + for rid in hookenv.relation_ids('memcache'): + for rel in hookenv.relations_for_id(rid): priv_addr = rel['private-address'] # Format it as IPv6 address if needed - priv_addr = format_ipv6_addr(priv_addr) or priv_addr + priv_addr = (ch_network_ip.format_ipv6_addr(priv_addr) or + priv_addr) servers.append("%s:%s" % (priv_addr, rel['port'])) except Exception as ex: - log("Could not get memcache servers: %s" % (ex), level='WARNING') + hookenv.log("Could not get memcache servers: %s" % (ex), + level='WARNING') servers = [] ctxt['memcached_servers'] = ','.join(servers) # Configure nova-novncproxy https if nova-api is using https. - if https(): - cn = resolve_address(endpoint_type=INTERNAL) + if ch_cluster.https(): + cn = ch_ip.resolve_address(endpoint_type=ch_ip.INTERNAL) if cn: cert_filename = 'cert_{}'.format(cn) key_filename = 'key_{}'.format(cn) @@ -351,26 +337,27 @@ class InstanceConsoleContext(context.OSContextGenerator): return ctxt -class ConsoleSSLContext(context.OSContextGenerator): +class ConsoleSSLContext(ch_context.OSContextGenerator): interfaces = [] def __call__(self): ctxt = {} - from nova_cc_utils import console_attributes - if (config('console-ssl-cert') and - config('console-ssl-key') and - config('console-access-protocol')): + if (hookenv.config('console-ssl-cert') and + hookenv.config('console-ssl-key') and + hookenv.config('console-access-protocol')): ssl_dir = '/etc/nova/ssl/' if not os.path.exists(ssl_dir): - log('Creating %s.' % ssl_dir, level=DEBUG) + hookenv.log('Creating %s.' % ssl_dir, level=hookenv.DEBUG) os.mkdir(ssl_dir) cert_path = os.path.join(ssl_dir, 'nova_cert.pem') - decode_ssl_cert = b64decode(config('console-ssl-cert')) + decode_ssl_cert = base64.b64decode( + hookenv.config('console-ssl-cert')) key_path = os.path.join(ssl_dir, 'nova_key.pem') - decode_ssl_key = b64decode(config('console-ssl-key')) + decode_ssl_key = base64.b64decode( + hookenv.config('console-ssl-key')) with open(cert_path, 'w') as fh: fh.write(decode_ssl_cert) @@ -381,18 +368,18 @@ class ConsoleSSLContext(context.OSContextGenerator): ctxt['ssl_cert'] = cert_path ctxt['ssl_key'] = key_path - if is_clustered(): - ip_addr = resolve_address(endpoint_type=PUBLIC) + if ch_cluster.is_clustered(): + ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC) else: - ip_addr = unit_get('private-address') + ip_addr = hookenv.unit_get('private-address') - ip_addr = format_ipv6_addr(ip_addr) or ip_addr + ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr - _proto = config('console-access-protocol') + _proto = hookenv.config('console-access-protocol') url = "https://%s:%s%s" % ( ip_addr, - console_attributes('proxy-port', proto=_proto), - console_attributes('proxy-page', proto=_proto)) + common.console_attributes('proxy-port', proto=_proto), + common.console_attributes('proxy-page', proto=_proto)) if _proto == 'novnc': ctxt['novncproxy_base_url'] = url @@ -402,31 +389,31 @@ class ConsoleSSLContext(context.OSContextGenerator): return ctxt -class SerialConsoleContext(context.OSContextGenerator): +class SerialConsoleContext(ch_context.OSContextGenerator): interfaces = [] def __call__(self): - ip_addr = resolve_address(endpoint_type=PUBLIC) - ip_addr = format_ipv6_addr(ip_addr) or ip_addr + ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC) + ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr ctxt = { 'enable_serial_console': - str(config('enable-serial-console')).lower(), + str(hookenv.config('enable-serial-console')).lower(), 'serial_console_base_url': 'ws://{}:6083/'.format(ip_addr) } return ctxt -class APIRateLimitingContext(context.OSContextGenerator): +class APIRateLimitingContext(ch_context.OSContextGenerator): def __call__(self): ctxt = {} - rate_rules = config('api-rate-limit-rules') + rate_rules = hookenv.config('api-rate-limit-rules') if rate_rules: ctxt['api_rate_limit_rules'] = rate_rules return ctxt -class NovaAPISharedDBContext(context.SharedDBContext): +class NovaAPISharedDBContext(ch_context.SharedDBContext): ''' Wrapper context to support multiple database connections being represented to a single config file @@ -441,17 +428,18 @@ class NovaAPISharedDBContext(context.SharedDBContext): return ctxt -class NovaMetadataContext(context.OSContextGenerator): +class NovaMetadataContext(ch_context.OSContextGenerator): ''' Context used for configuring the nova metadata service. ''' def __call__(self): - cmp_os_release = CompareOpenStackReleases(os_release('nova-common')) + cmp_os_release = ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) ctxt = {} if cmp_os_release >= 'rocky': ctxt['vendordata_providers'] = [] - vdata = config('vendor-data') - vdata_url = config('vendor-data-url') + vdata = hookenv.config('vendor-data') + vdata_url = hookenv.config('vendor-data-url') if vdata: ctxt['vendor_data'] = True @@ -460,7 +448,7 @@ class NovaMetadataContext(context.OSContextGenerator): if vdata_url: ctxt['vendor_data_url'] = vdata_url ctxt['vendordata_providers'].append('DynamicJSON') - ctxt['metadata_proxy_shared_secret'] = leader_get( + ctxt['metadata_proxy_shared_secret'] = hookenv.leader_get( 'shared-metadata-secret') ctxt['enable_metadata'] = True else: diff --git a/hooks/nova_cc_hooks.py b/hooks/nova_cc_hooks.py index 2cd3c331..ec8f968a 100755 --- a/hooks/nova_cc_hooks.py +++ b/hooks/nova_cc_hooks.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Copyright 2016 Canonical Ltd # @@ -16,148 +16,42 @@ import os import shutil +import subprocess import sys +from urllib.parse import urlparse import uuid -from subprocess import ( - check_call, -) -from urlparse import urlparse +_path = os.path.dirname(os.path.realpath(__file__)) +_root = os.path.abspath(os.path.join(_path, '..')) -from charmhelpers.core.hookenv import ( - Hooks, - UnregisteredHookError, - config, - charm_dir, - is_leader, - is_relation_made, - log, - local_unit, - DEBUG, - WARNING, - relation_get, - relation_ids, - relation_set, - related_units, - open_port, - unit_get, - status_set, -) -from charmhelpers.core.host import ( - service_pause, - service_reload, - service_resume, -) +def _add_path(path): + if path not in sys.path: + sys.path.insert(1, path) -from charmhelpers.fetch import ( - apt_install, - apt_update, - filter_installed_packages -) +_add_path(_root) -from charmhelpers.contrib.openstack.utils import ( - config_value_changed, - configure_installation_source, - openstack_upgrade_available, - os_release, - sync_db_with_multi_ipv6_addresses, - pausable_restart_on_change as restart_on_change, - is_unit_paused_set, - CompareOpenStackReleases, - series_upgrade_prepare, - series_upgrade_complete, -) -from charmhelpers.contrib.openstack.neutron import ( - network_manager, -) +import charmhelpers.contrib.charmsupport.nrpe as nrpe +import charmhelpers.contrib.hahelpers.cluster as ch_cluster +import charmhelpers.contrib.hardening.harden as ch_harden +import charmhelpers.contrib.network.ip as ch_network_ip +import charmhelpers.contrib.openstack.cert_utils as cert_utils +import charmhelpers.contrib.openstack.context as ch_context +import charmhelpers.contrib.openstack.ha.utils as ch_ha_utils +import charmhelpers.contrib.openstack.ip as ch_ip +import charmhelpers.contrib.openstack.neutron as ch_neutron +import charmhelpers.contrib.openstack.utils as ch_utils +import charmhelpers.contrib.peerstorage as ch_peerstorage +import charmhelpers.core.hookenv as hookenv +import charmhelpers.core.host as ch_host +import charmhelpers.fetch as ch_fetch +import charmhelpers.payload.execd as execd -from nova_cc_context import ( - NeutronAPIContext, -) - -from charmhelpers.contrib.peerstorage import ( - peer_retrieve, - peer_echo, -) - -from nova_cc_utils import ( - add_hosts_to_cell, - auth_token_config, - determine_endpoints, - determine_packages, - determine_ports, - disable_package_apache_site, - do_openstack_upgrade, - get_metadata_settings, - get_shared_metadatasecret, - is_api_ready, - is_cellv2_init_ready, - keystone_ca_cert_b64, - migrate_nova_databases, - placement_api_enabled, - save_script_rc, - services, - set_shared_metadatasecret, - ssh_compute_add, - ssh_compute_remove, - ssh_known_hosts_lines, - ssh_authorized_keys_lines, - update_child_cell, - register_configs, - restart_map, - update_cell_database, - NOVA_CONF, - console_attributes, - service_guard, - guard_map, - setup_ipv6, - is_db_initialised, - assess_status, - update_aws_compat_services, - serial_console_settings, - pause_unit_helper, - resume_unit_helper, - write_vendordata, -) - -from charmhelpers.contrib.hahelpers.cluster import ( - get_hacluster_config, - https, - is_clustered, -) - -from charmhelpers.contrib.openstack.ha.utils import ( - update_dns_ha_resource_params, -) - -from charmhelpers.payload.execd import execd_preinstall - -from charmhelpers.contrib.openstack.ip import ( - canonical_url, - PUBLIC, INTERNAL, ADMIN, - resolve_address, -) - -from charmhelpers.contrib.network.ip import ( - format_ipv6_addr, - get_iface_for_address, - get_netmask_for_address, - is_ipv6, - get_relation_ip, -) - -from charmhelpers.contrib.openstack.cert_utils import ( - get_certificate_request, - process_certificates, -) - -from charmhelpers.contrib.openstack.context import ADDRESS_TYPES - -from charmhelpers.contrib.charmsupport import nrpe -from charmhelpers.contrib.hardening.harden import harden +import hooks.nova_cc_common as common +import hooks.nova_cc_context as nova_cc_context +import hooks.nova_cc_utils as ncc_utils try: FileNotFoundError @@ -165,39 +59,69 @@ except NameError: # python3 compatibility FileNotFoundError = OSError -hooks = Hooks() -CONFIGS = register_configs() +hooks = hookenv.Hooks() +# Note that CONFIGS is now set up via resolve_CONFIGS so that it is not a +# module load time constraint. +CONFIGS = None COLO_CONSOLEAUTH = 'inf: res_nova_consoleauth grp_nova_vips' AGENT_CONSOLEAUTH = 'ocf:openstack:nova-consoleauth' AGENT_CA_PARAMS = 'op monitor interval="5s"' +def deferred_config(k): + """Returns a callable that will return the config. To be used with + functions that need lazy evaluation because the run at loadtime, but the + evaluation should happen at runtime. + + :param k: the config key to lookup + :type k: Option[String, None] + :returns: the result of config(k) + """ + return lambda: hookenv.config(k) + + +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 = ncc_utils.register_configs() + return CONFIGS + + def leader_init_db_if_ready(skip_acl_check=False, db_rid=None, unit=None): """Initialise db if leader and db not yet intialised. NOTE: must be called from database context. """ - if not is_leader(): - log("Not leader - skipping db init", level=DEBUG) + if not hookenv.is_leader(): + hookenv.log("Not leader - skipping db init", level=hookenv.DEBUG) return - if is_db_initialised(): - log("Database already initialised - skipping db init", level=DEBUG) + if ncc_utils.is_db_initialised(): + hookenv.log("Database already initialised - skipping db init", + level=hookenv.DEBUG) return # Bugs 1353135 & 1187508. Dbs can appear to be ready before the units # acl entry has been added. So, if the db supports passing a list of # permitted units then check if we're in the list. - allowed_units = relation_get('nova_allowed_units', rid=db_rid, unit=unit) - if skip_acl_check or (allowed_units and local_unit() in + allowed_units = hookenv.relation_get('nova_allowed_units', + rid=db_rid, unit=unit) + if skip_acl_check or (allowed_units and hookenv.local_unit() in allowed_units.split()): - status_set('maintenance', 'Running nova db migration') - migrate_nova_databases() - log('Triggering remote restarts.') + hookenv.status_set('maintenance', 'Running nova db migration') + ncc_utils.migrate_nova_databases() + hookenv.log('Triggering remote restarts.') update_nova_relation(remote_restart=True) else: - log('allowed_units either not presented, or local unit ' - 'not in acl list: %s' % repr(allowed_units)) + hookenv.log('allowed_units either not presented, or local unit ' + 'not in acl list: %s' % repr(allowed_units)) def leader_init_db_if_ready_allowed_units(): @@ -209,30 +133,32 @@ def leader_init_db_if_ready_allowed_units(): """ rels = ['shared-db'] for rname in rels: - for rid in relation_ids(rname): - for unit in related_units(rid): + for rid in hookenv.relation_ids(rname): + for unit in hookenv.related_units(rid): leader_init_db_if_ready(db_rid=rid, unit=unit) def update_cell_db_if_ready(skip_acl_check=False, db_rid=None, unit=None): """Update the cells db if leader and db's are already intialised""" - if not is_leader(): + if not hookenv.is_leader(): return - if not is_db_initialised(): - log("Database not initialised - skipping cell db update", level=DEBUG) + if not ncc_utils.is_db_initialised(): + hookenv.log("Database not initialised - skipping cell db update", + level=hookenv.DEBUG) return - if not is_cellv2_init_ready(): + if not ncc_utils.is_cellv2_init_ready(): return - allowed_units = relation_get('nova_allowed_units', rid=db_rid, unit=unit) - if skip_acl_check or (allowed_units and local_unit() in + allowed_units = hookenv.relation_get('nova_allowed_units', + rid=db_rid, unit=unit) + if skip_acl_check or (allowed_units and hookenv.local_unit() in allowed_units.split()): - update_cell_database() + ncc_utils.update_cell_database() else: - log('allowed_units either not presented, or local unit ' - 'not in acl list: %s' % repr(allowed_units)) + hookenv.log('allowed_units either not presented, or local unit ' + 'not in acl list: %s' % repr(allowed_units)) def update_cell_db_if_ready_allowed_units(): @@ -244,145 +170,153 @@ def update_cell_db_if_ready_allowed_units(): """ rels = ['shared-db'] for rname in rels: - for rid in relation_ids(rname): - for unit in related_units(rid): + for rid in hookenv.relation_ids(rname): + for unit in hookenv.related_units(rid): update_cell_db_if_ready(db_rid=rid, unit=unit) def update_child_cell_records(): - for r_id in relation_ids('nova-cell-api'): - for unit in related_units(relid=r_id): + for r_id in hookenv.relation_ids('nova-cell-api'): + for unit in hookenv.related_units(relid=r_id): nova_cell_api_relation_changed(rid=r_id, unit=unit) @hooks.hook('install.real') -@harden() +@ch_harden.harden() def install(): - status_set('maintenance', 'Executing pre-install') - execd_preinstall() - configure_installation_source(config('openstack-origin')) + hookenv.status_set('maintenance', 'Executing pre-install') + execd.execd_preinstall() + ch_utils.configure_installation_source(hookenv.config('openstack-origin')) - status_set('maintenance', 'Installing apt packages') - apt_update() - apt_install(determine_packages(), fatal=True) + hookenv.status_set('maintenance', 'Installing apt packages') + ch_fetch.apt_update() + ch_fetch.apt_install(ncc_utils.determine_packages(), fatal=True) - if placement_api_enabled(): - disable_package_apache_site() + if ncc_utils.placement_api_enabled(): + ncc_utils.disable_package_apache_site() - _files = os.path.join(charm_dir(), 'files') + _files = os.path.join(hookenv.charm_dir(), 'files') if os.path.isdir(_files): for f in os.listdir(_files): f = os.path.join(_files, f) if os.path.isfile(f): - log('Installing %s to /usr/bin' % f) + hookenv.log('Installing %s to /usr/bin' % f) shutil.copy2(f, '/usr/bin') - [open_port(port) for port in determine_ports()] + for port in ncc_utils.determine_ports(): + hookenv.open_port(port) msg = 'Disabling services into db relation joined' - log(msg) - status_set('maintenance', msg) - if not is_unit_paused_set(): - for svc in services(): - service_pause(svc) + hookenv.log(msg) + hookenv.status_set('maintenance', msg) + if not ch_utils.is_unit_paused_set(): + for svc in ncc_utils.services(): + ch_host.service_pause(svc) else: - log('Unit is in paused state, not issuing stop/pause to all services') + hookenv.log('Unit is in paused state, not issuing stop/pause ' + 'to all services') @hooks.hook('config-changed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map(), stopstart=True) -@harden() +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map, stopstart=True) +@ch_harden.harden() def config_changed(): # if we are paused, delay doing any config changed hooks. # It is forced on the resume. - if is_unit_paused_set(): - log("Unit is pause or upgrading. Skipping config_changed", "WARN") + if ch_utils.is_unit_paused_set(): + hookenv.log("Unit is pause or upgrading. Skipping config_changed", + hookenv.WARNING) return # neutron-server runs if < juno. Neutron-server creates mysql tables # which will subsequently cause db migrations to fail if >= juno. # Disable neutron-server if >= juno - if CompareOpenStackReleases(os_release('nova-common')) >= 'juno': + if ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) >= 'juno': try: - service_pause('neutron-server') + ch_host.service_pause('neutron-server') except ValueError: # neutron-server service not installed, ignore. pass - if config('prefer-ipv6'): - status_set('maintenance', 'configuring ipv6') - setup_ipv6() - sync_db_with_multi_ipv6_addresses(config('database'), - config('database-user'), - relation_prefix='nova') + if hookenv.config('prefer-ipv6'): + hookenv.status_set('maintenance', 'configuring ipv6') + ncc_utils.setup_ipv6() + ch_utils.sync_db_with_multi_ipv6_addresses( + hookenv.config('database'), + hookenv.config('database-user'), + relation_prefix='nova') global CONFIGS - if not config('action-managed-upgrade'): - if openstack_upgrade_available('nova-common'): - status_set('maintenance', 'Running openstack upgrade') - do_openstack_upgrade(CONFIGS) - [neutron_api_relation_joined(rid=rid, remote_restart=True) - for rid in relation_ids('neutron-api')] + if not hookenv.config('action-managed-upgrade'): + if ch_utils.openstack_upgrade_available('nova-common'): + hookenv.status_set('maintenance', 'Running openstack upgrade') + ncc_utils.do_openstack_upgrade(CONFIGS) + for rid in hookenv.relation_ids('neutron-api'): + neutron_api_relation_joined(rid=rid, remote_restart=True) # NOTE(jamespage): Force re-fire of shared-db joined hook # to ensure that nova_api database is setup if required. - [db_joined(relation_id=r_id) - for r_id in relation_ids('shared-db')] + for r_id in hookenv.relation_ids('shared-db'): + db_joined(relation_id=r_id) - save_script_rc() + ncc_utils.save_script_rc() configure_https() CONFIGS.write_all() # NOTE(jamespage): deal with any changes to the console and serial # console configuration options - filtered = filter_installed_packages(determine_packages()) + filtered = ch_fetch.filter_installed_packages( + ncc_utils.determine_packages()) if filtered: - apt_install(filtered, fatal=True) + ch_fetch.apt_install(filtered, fatal=True) - for r_id in relation_ids('identity-service'): + for r_id in hookenv.relation_ids('identity-service'): identity_joined(rid=r_id) - [cluster_joined(rid) for rid in relation_ids('cluster')] + for rid in hookenv.relation_ids('cluster'): + cluster_joined(rid) update_nova_relation() update_nrpe_config() # If the region value has changed, notify the cloud-compute relations # to ensure the value is propagated to the compute nodes. - if config_value_changed('region'): - for rid in relation_ids('cloud-compute'): - for unit in related_units(rid): + if ch_utils.config_value_changed('region'): + for rid in hookenv.relation_ids('cloud-compute'): + for unit in hookenv.related_units(rid): compute_changed(rid, unit) update_nova_consoleauth_config() - update_aws_compat_services() + ncc_utils.update_aws_compat_services() - if config('vendor-data'): - write_vendordata(config('vendor-data')) - if is_leader() and not get_shared_metadatasecret(): - set_shared_metadatasecret() + if hookenv.config('vendor-data'): + ncc_utils.write_vendordata(hookenv.config('vendor-data')) + if hookenv.is_leader() and not ncc_utils.get_shared_metadatasecret(): + ncc_utils.set_shared_metadatasecret() @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')) + hookenv.relation_set(relation_id=relation_id, + username=hookenv.config('rabbit-user'), + vhost=hookenv.config('rabbit-vhost')) @hooks.hook('amqp-relation-changed') @hooks.hook('amqp-relation-departed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def amqp_changed(): if 'amqp' not in CONFIGS.complete_contexts(): - log('amqp relation incomplete. Peer not ready?') + hookenv.log('amqp relation incomplete. Peer not ready?') return - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) leader_init_db_if_ready_allowed_units() # db init for cells v2 requires amqp transport_url and db connections # to be set in nova.conf, so we attempt db init in here as well as the # db relation-changed hooks. update_cell_db_if_ready_allowed_units() - for r_id in relation_ids('nova-api'): + for r_id in hookenv.relation_ids('nova-api'): nova_api_relation_joined(rid=r_id) update_child_cell_records() @@ -390,66 +324,73 @@ def amqp_changed(): # NOTE: trigger restart on nova-api-metadata on # neutron-gateway units once nova-cc has working # amqp connection (avoiding service down on n-gateway) - for rid in relation_ids('quantum-network-service'): + for rid in hookenv.relation_ids('quantum-network-service'): quantum_joined(rid=rid, remote_restart=True) @hooks.hook('shared-db-relation-joined') def db_joined(relation_id=None): - cmp_os_release = CompareOpenStackReleases(os_release('nova-common')) - if config('prefer-ipv6'): - sync_db_with_multi_ipv6_addresses(config('database'), - config('database-user'), - relation_prefix='nova') + cmp_os_release = ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) + if hookenv.config('prefer-ipv6'): + ch_utils.sync_db_with_multi_ipv6_addresses( + hookenv.config('database'), + hookenv.config('database-user'), + relation_prefix='nova') if cmp_os_release >= 'mitaka': # NOTE: mitaka uses a second nova-api database as well - sync_db_with_multi_ipv6_addresses('nova_api', - config('database-user'), - relation_prefix='novaapi') + ch_utils.sync_db_with_multi_ipv6_addresses( + 'nova_api', + hookenv.config('database-user'), + relation_prefix='novaapi') if cmp_os_release >= 'ocata': # NOTE: ocata requires cells v2 - sync_db_with_multi_ipv6_addresses('nova_cell0', - config('database-user'), - relation_prefix='novacell0') + ch_utils.sync_db_with_multi_ipv6_addresses( + 'nova_cell0', + hookenv.config('database-user'), + relation_prefix='novacell0') else: # Avoid churn check for access-network early access_network = None - for unit in related_units(relid=relation_id): - access_network = relation_get(rid=relation_id, unit=unit, - attribute='access-network') + for unit in hookenv.related_units(relid=relation_id): + access_network = hookenv.relation_get(rid=relation_id, unit=unit, + attribute='access-network') if access_network: break - host = get_relation_ip('shared-db', cidr_network=access_network) + host = ch_network_ip.get_relation_ip('shared-db', + cidr_network=access_network) - relation_set(nova_database=config('database'), - nova_username=config('database-user'), - nova_hostname=host, - relation_id=relation_id) + hookenv.relation_set(nova_database=hookenv.config('database'), + nova_username=hookenv.config('database-user'), + nova_hostname=host, + relation_id=relation_id) if cmp_os_release >= 'mitaka': # NOTE: mitaka uses a second nova-api database as well - relation_set(novaapi_database='nova_api', - novaapi_username=config('database-user'), - novaapi_hostname=host, - relation_id=relation_id) + hookenv.relation_set( + novaapi_database='nova_api', + novaapi_username=hookenv.config('database-user'), + novaapi_hostname=host, + relation_id=relation_id) if cmp_os_release >= 'ocata': # NOTE: ocata requires cells v2 - relation_set(novacell0_database='nova_cell0', - novacell0_username=config('database-user'), - novacell0_hostname=host, - relation_id=relation_id) + hookenv.relation_set( + novacell0_database='nova_cell0', + novacell0_username=hookenv.config('database-user'), + novacell0_hostname=host, + relation_id=relation_id) @hooks.hook('shared-db-relation-changed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def db_changed(): if 'shared-db' not in CONFIGS.complete_contexts(): - log('shared-db relation incomplete. Peer not ready?') + hookenv.log('shared-db relation incomplete. Peer not ready?') return CONFIGS.write_all() @@ -462,88 +403,91 @@ def db_changed(): @hooks.hook('image-service-relation-changed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def image_service_changed(): if 'image-service' not in CONFIGS.complete_contexts(): - log('image-service relation incomplete. Peer not ready?') + hookenv.log('image-service relation incomplete. Peer not ready?') return - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) # TODO: special case config flag for essex (strip protocol) - for r_id in relation_ids('nova-api'): + for r_id in hookenv.relation_ids('nova-api'): nova_api_relation_joined(rid=r_id) @hooks.hook('identity-service-relation-joined') def identity_joined(rid=None): - if config('vip') and not is_clustered(): - log('Defering registration until clustered', level=DEBUG) + if hookenv.config('vip') and not ch_cluster.is_clustered(): + hookenv.log('Defering registration until clustered', + level=hookenv.DEBUG) return - - public_url = canonical_url(CONFIGS, PUBLIC) - internal_url = canonical_url(CONFIGS, INTERNAL) - admin_url = canonical_url(CONFIGS, ADMIN) - relation_set( - relation_id=rid, - **determine_endpoints(public_url, - internal_url, - admin_url)) + public_url = ch_ip.canonical_url(CONFIGS, ch_ip.PUBLIC) + internal_url = ch_ip.canonical_url(CONFIGS, ch_ip.INTERNAL) + admin_url = ch_ip.canonical_url(CONFIGS, ch_ip.ADMIN) + hookenv.relation_set(relation_id=rid, + **ncc_utils.determine_endpoints(public_url, + internal_url, + admin_url)) @hooks.hook('identity-service-relation-changed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def identity_changed(): if 'identity-service' not in CONFIGS.complete_contexts(): - log('identity-service relation incomplete. Peer not ready?') + hookenv.log('identity-service relation incomplete. Peer not ready?') return CONFIGS.write('/etc/nova/api-paste.ini') - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) update_nova_relation() - [nova_vmware_relation_joined(rid) for rid in relation_ids('nova-vmware')] - [neutron_api_relation_joined(rid) for rid in relation_ids('neutron-api')] + for rid in hookenv.relation_ids('nova-vmware'): + nova_vmware_relation_joined(rid) + for rid in hookenv.relation_ids('neutron-api'): + neutron_api_relation_joined(rid) configure_https() - for r_id in relation_ids('nova-api'): + for r_id in hookenv.relation_ids('nova-api'): nova_api_relation_joined(rid=r_id) @hooks.hook('nova-volume-service-relation-joined', 'cinder-volume-service-relation-joined') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def volume_joined(): - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) # kick identity_joined() to publish possibly new nova-volume endpoint. - [identity_joined(rid) for rid in relation_ids('identity-service')] + for rid in hookenv.relation_ids('identity-service'): + identity_joined(rid) def _auth_config(): '''Grab all KS auth token config from api-paste.ini, or return empty {}''' - ks_auth_host = auth_token_config('auth_host') + ks_auth_host = ncc_utils.auth_token_config('auth_host') if not ks_auth_host: # if there is no auth_host set, identity-service changed hooks # have not fired, yet. return {} cfg = { 'auth_host': ks_auth_host, - 'auth_port': auth_token_config('auth_port'), - 'auth_protocol': auth_token_config('auth_protocol'), - 'service_protocol': auth_token_config('service_protocol'), - 'service_port': auth_token_config('service_port'), - 'service_username': auth_token_config('admin_user'), - 'service_password': auth_token_config('admin_password'), - 'service_tenant_name': auth_token_config('admin_tenant_name'), - 'auth_uri': auth_token_config('auth_uri'), + 'auth_port': ncc_utils.auth_token_config('auth_port'), + 'auth_protocol': ncc_utils.auth_token_config('auth_protocol'), + 'service_protocol': ncc_utils.auth_token_config('service_protocol'), + 'service_port': ncc_utils.auth_token_config('service_port'), + 'service_username': ncc_utils.auth_token_config('admin_user'), + 'service_password': ncc_utils.auth_token_config('admin_password'), + 'service_tenant_name': ncc_utils.auth_token_config( + 'admin_tenant_name'), + 'auth_uri': ncc_utils.auth_token_config('auth_uri'), # quantum-gateway interface deviates a bit. 'keystone_host': ks_auth_host, - 'service_tenant': auth_token_config('admin_tenant_name'), + 'service_tenant': ncc_utils.auth_token_config('admin_tenant_name'), # add api version if found - 'api_version': auth_token_config('api_version') or '2.0', + 'api_version': ncc_utils.auth_token_config('api_version') or '2.0', } return cfg @@ -560,17 +504,17 @@ def save_novarc(): out.write('export OS_PASSWORD=%s\n' % auth['service_password']) out.write('export OS_TENANT_NAME=%s\n' % auth['service_tenant_name']) out.write('export OS_AUTH_URL=%s\n' % ks_url) - out.write('export OS_REGION_NAME=%s\n' % config('region')) + out.write('export OS_REGION_NAME=%s\n' % hookenv.config('region')) def neutron_settings(): neutron_settings = {} - if is_relation_made('neutron-api', 'neutron-plugin'): - neutron_api_info = NeutronAPIContext()() + if hookenv.is_relation_made('neutron-api', 'neutron-plugin'): + neutron_api_info = nova_cc_context.NeutronAPIContext()() neutron_settings.update({ # XXX: Rename these relations settings? 'quantum_plugin': neutron_api_info['neutron_plugin'], - 'region': config('region'), + 'region': hookenv.config('region'), 'quantum_security_groups': neutron_api_info['neutron_security_groups'], 'quantum_url': neutron_api_info['neutron_url'], @@ -585,11 +529,11 @@ def keystone_compute_settings(): ks_auth_config = _auth_config() rel_settings = {} - if network_manager() == 'neutron': + if ch_neutron.network_manager() == 'neutron': if ks_auth_config: rel_settings.update(ks_auth_config) rel_settings.update(neutron_settings()) - ks_ca = keystone_ca_cert_b64() + ks_ca = ncc_utils.keystone_ca_cert_b64() if ks_auth_config and ks_ca: rel_settings['ca_cert'] = ks_ca return rel_settings @@ -597,61 +541,64 @@ def keystone_compute_settings(): def console_settings(): rel_settings = {} - proto = console_attributes('protocol') + proto = common.console_attributes('protocol') if not proto: return {} - rel_settings['console_keymap'] = config('console-keymap') + rel_settings['console_keymap'] = hookenv.config('console-keymap') rel_settings['console_access_protocol'] = proto console_ssl = False - if config('console-ssl-cert') and config('console-ssl-key'): + if (hookenv.config('console-ssl-cert') and + hookenv.config('console-ssl-key')): console_ssl = True - if config('console-proxy-ip') == 'local': + if hookenv.config('console-proxy-ip') == 'local': if console_ssl: - address = resolve_address(endpoint_type=PUBLIC) - address = format_ipv6_addr(address) or address + address = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC) + address = ch_network_ip.format_ipv6_addr(address) or address proxy_base_addr = 'https://%s' % address else: # canonical_url will only return 'https:' if API SSL are enabled. - proxy_base_addr = canonical_url(CONFIGS, PUBLIC) + proxy_base_addr = ch_ip.canonical_url(CONFIGS, ch_ip.PUBLIC) else: - if console_ssl or https(): + if console_ssl or ch_cluster.https(): schema = "https" else: schema = "http" - proxy_base_addr = "%s://%s" % (schema, config('console-proxy-ip')) + proxy_base_addr = ("{}://{}" + .format(schema, hookenv.config('console-proxy-ip'))) if proto == 'vnc': protocols = ['novnc', 'xvpvnc'] else: protocols = [proto] for _proto in protocols: - rel_settings['console_proxy_%s_address' % (_proto)] = \ - "%s:%s%s" % (proxy_base_addr, - console_attributes('proxy-port', proto=_proto), - console_attributes('proxy-page', proto=_proto)) + rel_settings['console_proxy_{}_address'.format(_proto)] = \ + "{}:{}{}".format( + proxy_base_addr, + common.console_attributes('proxy-port', proto=_proto), + common.console_attributes('proxy-page', proto=_proto)) rel_settings['console_proxy_%s_host' % (_proto)] = \ urlparse(proxy_base_addr).hostname rel_settings['console_proxy_%s_port' % (_proto)] = \ - console_attributes('proxy-port', proto=_proto) + common.console_attributes('proxy-port', proto=_proto) return rel_settings def get_compute_config(rid=None, remote_restart=False): cons_settings = console_settings() - relation_set(relation_id=rid, **cons_settings) + hookenv.relation_set(relation_id=rid, **cons_settings) rel_settings = { - 'network_manager': network_manager(), + 'network_manager': ch_neutron.network_manager(), 'volume_service': 'cinder', # (comment from bash vers) XXX Should point to VIP if clustered, or # this may not even be needed. - 'ec2_host': unit_get('private-address'), - 'region': config('region'), + 'ec2_host': hookenv.unit_get('private-address'), + 'region': hookenv.config('region'), } - rel_settings.update(serial_console_settings()) + rel_settings.update(ncc_utils.serial_console_settings()) # update relation setting if we're attempting to restart remote # services if remote_restart: @@ -661,11 +608,11 @@ def get_compute_config(rid=None, remote_restart=False): def update_nova_relation(remote_restart=False): - for rid in relation_ids('cloud-compute'): + for rid in hookenv.relation_ids('cloud-compute'): compute_joined(rid=rid, remote_restart=remote_restart) - for rid in relation_ids('quantum-network-service'): + for rid in hookenv.relation_ids('quantum-network-service'): quantum_joined(rid=rid, remote_restart=remote_restart) - for rid in relation_ids('nova-cell-api'): + for rid in hookenv.relation_ids('nova-cell-api'): nova_cell_api_relation_joined(rid=rid, remote_restart=remote_restart) @@ -673,82 +620,87 @@ def update_nova_relation(remote_restart=False): def compute_joined(rid=None, remote_restart=False): rel_settings = get_compute_config(rid=rid, remote_restart=remote_restart) rel_settings.update(keystone_compute_settings()) - relation_set(relation_id=rid, **rel_settings) + hookenv.relation_set(relation_id=rid, **rel_settings) @hooks.hook('cloud-compute-relation-changed') def compute_changed(rid=None, unit=None): - for r_id in relation_ids('nova-api'): + for r_id in hookenv.relation_ids('nova-api'): nova_api_relation_joined(rid=r_id) - rel_settings = relation_get(rid=rid, unit=unit) - if not rel_settings.get('region', None) == config('region'): - relation_set(relation_id=rid, region=config('region')) + rel_settings = hookenv.relation_get(rid=rid, unit=unit) + if not rel_settings.get('region', None) == hookenv.config('region'): + hookenv.relation_set(relation_id=rid, region=hookenv.config('region')) - if is_leader() and is_cellv2_init_ready() and is_db_initialised(): - add_hosts_to_cell() + if (hookenv.is_leader() and + ncc_utils.is_cellv2_init_ready() and + ncc_utils.is_db_initialised()): + ncc_utils.add_hosts_to_cell() if 'migration_auth_type' not in rel_settings: return if rel_settings['migration_auth_type'] == 'ssh': - status_set('maintenance', 'configuring live migration') + hookenv.status_set('maintenance', 'configuring live migration') key = rel_settings.get('ssh_public_key') if not key: - log('SSH migration set but peer did not publish key.') + hookenv.log('SSH migration set but peer did not publish key.') return - ssh_compute_add(key, rid=rid, unit=unit) + ncc_utils.ssh_compute_add(key, rid=rid, unit=unit) index = 0 - for line in ssh_known_hosts_lines(unit=unit): - relation_set( - relation_id=rid, - relation_settings={ - 'known_hosts_{}'.format(index): line}) + for line in ncc_utils.ssh_known_hosts_lines(unit=unit): + hookenv.relation_set(relation_id=rid, + relation_settings={ + 'known_hosts_{}'.format(index): line + }) index += 1 - relation_set(relation_id=rid, known_hosts_max_index=index) + hookenv.relation_set(relation_id=rid, known_hosts_max_index=index) index = 0 - for line in ssh_authorized_keys_lines(unit=unit): - relation_set( - relation_id=rid, - relation_settings={ - 'authorized_keys_{}'.format(index): line}) + for line in ncc_utils.ssh_authorized_keys_lines(unit=unit): + hookenv.relation_set(relation_id=rid, + relation_settings={ + 'authorized_keys_{}'.format(index): line + }) index += 1 - relation_set(relation_id=rid, authorized_keys_max_index=index) + hookenv.relation_set(relation_id=rid, authorized_keys_max_index=index) if 'nova_ssh_public_key' not in rel_settings: return if rel_settings['nova_ssh_public_key']: - ssh_compute_add(rel_settings['nova_ssh_public_key'], - rid=rid, unit=unit, user='nova') + ncc_utils.ssh_compute_add(rel_settings['nova_ssh_public_key'], + rid=rid, unit=unit, user='nova') index = 0 - for line in ssh_known_hosts_lines(unit=unit, user='nova'): - relation_set( - relation_id=rid, - relation_settings={ - '{}_known_hosts_{}'.format( - 'nova', - index): line}) + for line in ncc_utils.ssh_known_hosts_lines(unit=unit, user='nova'): + hookenv.relation_set(relation_id=rid, + relation_settings={ + '{}_known_hosts_{}'.format('nova', + index): line + }) index += 1 - relation_set( - relation_id=rid, - relation_settings={ - '{}_known_hosts_max_index'.format('nova'): index}) + hookenv.relation_set(relation_id=rid, + relation_settings={ + ('{}_known_hosts_max_index' + .format('nova')): index + }) index = 0 - for line in ssh_authorized_keys_lines(unit=unit, user='nova'): - relation_set( - relation_id=rid, - relation_settings={ - '{}_authorized_keys_{}'.format( - 'nova', - index): line}) + for line in ncc_utils.ssh_authorized_keys_lines( + unit=unit, user='nova'): + hookenv.relation_set(relation_id=rid, + relation_settings={ + '{}_authorized_keys_{}'.format( + 'nova', + index): line + }) index += 1 - relation_set( - relation_id=rid, - relation_settings={ - '{}_authorized_keys_max_index'.format('nova'): index}) + hookenv.relation_set(relation_id=rid, + relation_settings={ + '{}_authorized_keys_max_index'.format( + 'nova'): index + }) @hooks.hook('cloud-compute-relation-departed') def compute_departed(): - ssh_compute_remove(public_key=relation_get('ssh_public_key')) + ncc_utils.ssh_compute_remove( + public_key=hookenv.relation_get('ssh_public_key')) @hooks.hook('neutron-network-service-relation-joined', @@ -761,7 +713,7 @@ def quantum_joined(rid=None, remote_restart=False): rel_settings.update(ks_auth_config) # must pass the keystone CA cert, if it exists. - ks_ca = keystone_ca_cert_b64() + ks_ca = ncc_utils.keystone_ca_cert_b64() if ks_auth_config and ks_ca: rel_settings['ca_cert'] = ks_ca @@ -770,62 +722,63 @@ def quantum_joined(rid=None, remote_restart=False): if remote_restart: rel_settings['restart_trigger'] = str(uuid.uuid4()) - rel_settings.update(get_metadata_settings(CONFIGS)) - relation_set(relation_id=rid, **rel_settings) + rel_settings.update(ncc_utils.get_metadata_settings(CONFIGS)) + hookenv.relation_set(relation_id=rid, **rel_settings) @hooks.hook('cluster-relation-joined') def cluster_joined(relation_id=None): settings = {} - for addr_type in ADDRESS_TYPES: - address = get_relation_ip( + for addr_type in ch_context.ADDRESS_TYPES: + address = ch_network_ip.get_relation_ip( addr_type, - cidr_network=config('os-{}-network'.format(addr_type))) + cidr_network=hookenv.config('os-{}-network'.format(addr_type))) if address: settings['{}-address'.format(addr_type)] = address - settings['private-address'] = get_relation_ip('cluster') + settings['private-address'] = ch_network_ip.get_relation_ip('cluster') - relation_set(relation_id=relation_id, relation_settings=settings) + hookenv.relation_set(relation_id=relation_id, relation_settings=settings) @hooks.hook('cluster-relation-changed', 'cluster-relation-departed', 'leader-settings-changed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map(), stopstart=True) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map, stopstart=True) def cluster_changed(): CONFIGS.write_all() - if relation_ids('cluster'): - peer_echo(includes=['dbsync_state']) - dbsync_state = peer_retrieve('dbsync_state') + if hookenv.relation_ids('cluster'): + ch_peerstorage.peer_echo(includes=['dbsync_state']) + dbsync_state = ch_peerstorage.peer_retrieve('dbsync_state') if dbsync_state == 'complete': - if not is_unit_paused_set(): - for svc in services(): - service_resume(svc) + if not ch_utils.is_unit_paused_set(): + for svc in ncc_utils.services(): + ch_host.service_resume(svc) else: - log('Unit is in paused state, not issuing start/resume to all ' - 'services') + hookenv.log('Unit is in paused state, not issuing ' + 'start/resume to all services') else: - if not is_unit_paused_set(): - log('Database sync not ready. Shutting down services') - for svc in services(): - service_pause(svc) + if not ch_utils.is_unit_paused_set(): + hookenv.log('Database sync not ready. Shutting down services') + for svc in ncc_utils.services(): + ch_host.service_pause(svc) else: - log('Database sync not ready. Would shut down services but ' + hookenv.log( + 'Database sync not ready. Would shut down services but ' 'unit is in paused state, not issuing stop/pause to all ' 'services') # The shared metadata secret is stored in the leader-db and if its changed # the gateway needs to know. - for rid in relation_ids('quantum-network-service'): + for rid in hookenv.relation_ids('quantum-network-service'): quantum_joined(rid=rid, remote_restart=False) @hooks.hook('ha-relation-joined') def ha_joined(relation_id=None): - cluster_config = get_hacluster_config() + cluster_config = ch_cluster.get_hacluster_config() resources = { 'res_nova_haproxy': 'lsb:haproxy', } @@ -840,24 +793,25 @@ def ha_joined(relation_id=None): } colocations = {} - if config('dns-ha'): - update_dns_ha_resource_params(relation_id=relation_id, - resources=resources, - resource_params=resource_params) + if hookenv.config('dns-ha'): + ch_ha_utils.update_dns_ha_resource_params( + relation_id=relation_id, + resources=resources, + resource_params=resource_params) else: vip_group = [] for vip in cluster_config['vip'].split(): - if is_ipv6(vip): + if ch_network_ip.is_ipv6(vip): res_nova_vip = 'ocf:heartbeat:IPv6addr' vip_params = 'ipv6addr' else: res_nova_vip = 'ocf:heartbeat:IPaddr2' vip_params = 'ip' - iface = (get_iface_for_address(vip) or - config('vip_iface')) - netmask = (get_netmask_for_address(vip) or - config('vip_cidr')) + iface = (ch_network_ip.get_iface_for_address(vip) or + hookenv.config('vip_iface')) + netmask = (ch_network_ip.get_netmask_for_address(vip) or + hookenv.config('vip_cidr')) if iface is not None: vip_key = 'res_nova_{}_vip'.format(iface) @@ -865,8 +819,10 @@ def ha_joined(relation_id=None): if vip not in resource_params[vip_key]: vip_key = '{}_{}'.format(vip_key, vip_params) else: - log("Resource '%s' (vip='%s') already exists in " - "vip group - skipping" % (vip_key, vip), WARNING) + hookenv.log( + "Resource '%s' (vip='%s') already exists in " + "vip group - skipping" % (vip_key, vip), + hookenv.WARNING) continue resources[vip_key] = res_nova_vip @@ -880,56 +836,57 @@ def ha_joined(relation_id=None): vip_group.append(vip_key) if len(vip_group) >= 1: - relation_set(groups={'grp_nova_vips': ' '.join(vip_group)}) + hookenv.relation_set( + groups={'grp_nova_vips': ' '.join(vip_group)}) - if (config('single-nova-consoleauth') and - console_attributes('protocol')): + if (hookenv.config('single-nova-consoleauth') and + common.console_attributes('protocol')): colocations['vip_consoleauth'] = COLO_CONSOLEAUTH init_services['res_nova_consoleauth'] = 'nova-consoleauth' resources['res_nova_consoleauth'] = AGENT_CONSOLEAUTH resource_params['res_nova_consoleauth'] = AGENT_CA_PARAMS - relation_set(relation_id=relation_id, - init_services=init_services, - corosync_bindiface=cluster_config['ha-bindiface'], - corosync_mcastport=cluster_config['ha-mcastport'], - resources=resources, - resource_params=resource_params, - clones=clones, - colocations=colocations) + hookenv.relation_set(relation_id=relation_id, + init_services=init_services, + corosync_bindiface=cluster_config['ha-bindiface'], + corosync_mcastport=cluster_config['ha-mcastport'], + resources=resources, + resource_params=resource_params, + clones=clones, + colocations=colocations) @hooks.hook('ha-relation-changed') def ha_changed(): - clustered = relation_get('clustered') + clustered = hookenv.relation_get('clustered') if not clustered or clustered in [None, 'None', '']: - log('ha_changed: hacluster subordinate not fully clustered.') + hookenv.log('ha_changed: hacluster subordinate not fully clustered.') return - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) - log('Cluster configured, notifying other services and updating ' - 'keystone endpoint configuration') - for rid in relation_ids('identity-service'): + hookenv.log('Cluster configured, notifying other services and updating ' + 'keystone endpoint configuration') + for rid in hookenv.relation_ids('identity-service'): identity_joined(rid=rid) update_nova_consoleauth_config() @hooks.hook('shared-db-relation-broken') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) def db_departed(): CONFIGS.write_all() update_cell_db_if_ready(skip_acl_check=True) - for r_id in relation_ids('cluster'): - relation_set(relation_id=r_id, dbsync_state='incomplete') - if not is_unit_paused_set(): - for svc in services(): - service_pause(svc) + for r_id in hookenv.relation_ids('cluster'): + hookenv.relation_set(relation_id=r_id, dbsync_state='incomplete') + if not ch_utils.is_unit_paused_set(): + for svc in ncc_utils.services(): + ch_host.service_pause(svc) else: - log('Unit is in paused state, not issuing stop/pause to all ' - 'services') + hookenv.log('Unit is in paused state, not issuing stop/pause to all ' + 'services') @hooks.hook('amqp-relation-broken', @@ -938,8 +895,8 @@ def db_departed(): 'image-service-relation-broken', 'nova-volume-service-relation-broken', 'quantum-network-service-relation-broken') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) def relation_broken(): CONFIGS.write_all() @@ -954,53 +911,54 @@ def configure_https(): CONFIGS.write_all() if 'https' in CONFIGS.complete_contexts(): cmd = ['a2ensite', 'openstack_https_frontend'] - check_call(cmd) + subprocess.check_call(cmd) else: cmd = ['a2dissite', 'openstack_https_frontend'] - check_call(cmd) + subprocess.check_call(cmd) # TODO: improve this by checking if local CN certs are available # first then checking reload status (see LP #1433114). - if not is_unit_paused_set(): - service_reload('apache2', restart_on_failure=True) + if not ch_utils.is_unit_paused_set(): + ch_host.service_reload('apache2', restart_on_failure=True) - for rid in relation_ids('identity-service'): + for rid in hookenv.relation_ids('identity-service'): identity_joined(rid=rid) @hooks.hook() def nova_vmware_relation_joined(rid=None): - rel_settings = {'network_manager': network_manager()} + rel_settings = {'network_manager': ch_neutron.network_manager()} ks_auth = _auth_config() if ks_auth: rel_settings.update(ks_auth) rel_settings.update(neutron_settings()) - relation_set(relation_id=rid, **rel_settings) + hookenv.relation_set(relation_id=rid, **rel_settings) @hooks.hook('nova-vmware-relation-changed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def nova_vmware_relation_changed(): CONFIGS.write('/etc/nova/nova.conf') @hooks.hook('upgrade-charm') -@harden() +@ch_harden.harden() def upgrade_charm(): - apt_install(filter_installed_packages(determine_packages()), - fatal=True) - for r_id in relation_ids('amqp'): + ch_fetch.apt_install( + ch_fetch.filter_installed_packages( + ncc_utils.determine_packages()), fatal=True) + for r_id in hookenv.relation_ids('amqp'): amqp_joined(relation_id=r_id) - for r_id in relation_ids('identity-service'): + for r_id in hookenv.relation_ids('identity-service'): identity_joined(rid=r_id) - for r_id in relation_ids('cloud-compute'): - for unit in related_units(r_id): + for r_id in hookenv.relation_ids('cloud-compute'): + for unit in hookenv.related_units(r_id): compute_changed(r_id, unit) - for r_id in relation_ids('shared-db'): + for r_id in hookenv.relation_ids('shared-db'): db_joined(relation_id=r_id) leader_init_db_if_ready_allowed_units() @@ -1011,29 +969,29 @@ def upgrade_charm(): @hooks.hook('neutron-api-relation-joined') def neutron_api_relation_joined(rid=None, remote_restart=False): - for id_rid in relation_ids('identity-service'): + for id_rid in hookenv.relation_ids('identity-service'): identity_joined(rid=id_rid) rel_settings = { - 'nova_url': canonical_url(CONFIGS, INTERNAL) + ":8774/v2" + 'nova_url': ch_ip.canonical_url(CONFIGS, ch_ip.INTERNAL) + ":8774/v2" } if remote_restart: rel_settings['restart_trigger'] = str(uuid.uuid4()) - relation_set(relation_id=rid, **rel_settings) + hookenv.relation_set(relation_id=rid, **rel_settings) @hooks.hook('neutron-api-relation-changed') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def neutron_api_relation_changed(): - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) update_nova_relation() @hooks.hook('neutron-api-relation-broken') -@service_guard(guard_map(), CONFIGS, - active=config('service-guard')) -@restart_on_change(restart_map()) +@ncc_utils.service_guard(ncc_utils.guard_map, resolve_CONFIGS, + active=deferred_config('service-guard')) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def neutron_api_relation_broken(): CONFIGS.write_all() update_nova_relation() @@ -1043,12 +1001,14 @@ def neutron_api_relation_broken(): 'nrpe-external-master-relation-changed') def update_nrpe_config(): # python-dbus is used by check_upstart_job - apt_install('python-dbus') + ch_fetch.apt_install('python-dbus') hostname = nrpe.get_nagios_hostname() current_unit = nrpe.get_nagios_unit_name() nrpe_setup = nrpe.NRPE(hostname=hostname) nrpe.copy_nrpe_checks() - nrpe.add_init_service_checks(nrpe_setup, services(), current_unit) + nrpe.add_init_service_checks(nrpe_setup, + ncc_utils.services(), + current_unit) nrpe.add_haproxy_checks(nrpe_setup, current_unit) nrpe_setup.write() @@ -1059,9 +1019,11 @@ def memcached_joined(): spaces address rather than leaving it as the unit address. This is to support network spaces in the memcached charm. """ - relation_set( + hookenv.relation_set( relation_id=None, - relation_settings={'private-address': get_relation_ip('memcache')}) + relation_settings={ + 'private-address': ch_network_ip.get_relation_ip('memcache') + }) memcached_common() @@ -1072,36 +1034,38 @@ def memcached_other_hooks(): memcached_common() -@restart_on_change(restart_map()) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map) def memcached_common(): - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) @hooks.hook('zeromq-configuration-relation-changed') -@restart_on_change(restart_map(), stopstart=True) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map, stopstart=True) def zeromq_configuration_relation_changed(): - CONFIGS.write(NOVA_CONF) + CONFIGS.write(ncc_utils.NOVA_CONF) def update_nova_consoleauth_config(): """ Configure nova-consoleauth pacemaker resources """ - relids = relation_ids('ha') + relids = hookenv.relation_ids('ha') if len(relids) == 0: - log('Related to {} ha services'.format(len(relids)), level='DEBUG') + hookenv.log('Related to {} ha services'.format(len(relids)), + level=hookenv.DEBUG) ha_relid = None data = {} else: ha_relid = relids[0] - data = relation_get(rid=ha_relid) or {} + data = hookenv.relation_get(rid=ha_relid) or {} # initialize keys in case this is a new dict data.setdefault('delete_resources', []) for k in ['colocations', 'init_services', 'resources', 'resource_params']: data.setdefault(k, {}) - if config('single-nova-consoleauth') and console_attributes('protocol'): + if (hookenv.config('single-nova-consoleauth') and + common.console_attributes('protocol')): for item in ['vip_consoleauth', 'res_nova_consoleauth']: try: data['delete_resources'].remove(item) @@ -1114,16 +1078,16 @@ def update_nova_consoleauth_config(): data['resources']['res_nova_consoleauth'] = AGENT_CONSOLEAUTH data['resource_params']['res_nova_consoleauth'] = AGENT_CA_PARAMS - for rid in relation_ids('ha'): - relation_set(rid, **data) + for rid in hookenv.relation_ids('ha'): + hookenv.relation_set(rid, **data) # nova-consoleauth will be managed by pacemaker, so stop it # and prevent it to be started again at boot. (LP: #1693629). - if relation_ids('ha'): - service_pause('nova-consoleauth') + if hookenv.relation_ids('ha'): + ch_host.service_pause('nova-consoleauth') - elif (not config('single-nova-consoleauth') and - console_attributes('protocol')): + elif (not hookenv.config('single-nova-consoleauth') and + common.console_attributes('protocol')): for item in ['vip_consoleauth', 'res_nova_consoleauth']: if item not in data['delete_resources']: data['delete_resources'].append(item) @@ -1135,61 +1099,62 @@ def update_nova_consoleauth_config(): data['resources'].pop('res_nova_consoleauth', None) data['resource_params'].pop('res_nova_consoleauth', None) - for rid in relation_ids('ha'): - relation_set(rid, **data) + for rid in hookenv.relation_ids('ha'): + hookenv.relation_set(rid, **data) - service_resume('nova-consoleauth') + ch_host.service_resume('nova-consoleauth') def nova_api_relation_joined(rid=None): rel_data = { - 'nova-api-ready': 'yes' if is_api_ready(CONFIGS) else 'no' + 'nova-api-ready': 'yes' if ncc_utils.is_api_ready(CONFIGS) else 'no' } - relation_set(rid, **rel_data) + hookenv.relation_set(rid, **rel_data) @hooks.hook('certificates-relation-joined') def certs_joined(relation_id=None): - relation_set( + hookenv.relation_set( relation_id=relation_id, - relation_settings=get_certificate_request()) + relation_settings=cert_utils.get_certificate_request()) @hooks.hook('certificates-relation-changed') -@restart_on_change(restart_map(), stopstart=True) +@ch_utils.pausable_restart_on_change(ncc_utils.restart_map, stopstart=True) def certs_changed(relation_id=None, unit=None): - process_certificates('nova', relation_id, unit) + cert_utils.process_certificates('nova', relation_id, unit) configure_https() @hooks.hook('amqp-cell-relation-joined') def amqp_cell_joined(relation_id=None): - relation_set(relation_id=relation_id, - username='nova', vhost='nova') + hookenv.relation_set(relation_id=relation_id, + username='nova', vhost='nova') @hooks.hook('shared-db-cell-relation-joined') def shared_db_cell_joined(relation_id=None): access_network = None - for unit in related_units(relid=relation_id): - access_network = relation_get(rid=relation_id, unit=unit, - attribute='access-network') + for unit in hookenv.related_units(relid=relation_id): + access_network = hookenv.relation_get(rid=relation_id, unit=unit, + attribute='access-network') if access_network: break - host = get_relation_ip('shared-db', cidr_network=access_network) + host = ch_network_ip.get_relation_ip('shared-db', + cidr_network=access_network) cell_db = { 'nova_database': 'nova', - 'nova_username': config('database-user'), + 'nova_username': hookenv.config('database-user'), 'nova_hostname': host} - relation_set(relation_id=relation_id, **cell_db) + hookenv.relation_set(relation_id=relation_id, **cell_db) @hooks.hook('nova-cell-api-relation-joined') def nova_cell_api_relation_joined(rid=None, remote_restart=False): rel_settings = get_compute_config(rid=rid, remote_restart=remote_restart) - if network_manager() == 'neutron': + if ch_neutron.network_manager() == 'neutron': rel_settings.update(neutron_settings()) - relation_set(relation_id=rid, **rel_settings) + hookenv.relation_set(relation_id=rid, **rel_settings) @hooks.hook('shared-db-cell-relation-changed') @@ -1204,50 +1169,49 @@ def amqp_cell_changed(relation_id=None): @hooks.hook('nova-cell-api-relation-changed') def nova_cell_api_relation_changed(rid=None, unit=None): - data = relation_get(rid=rid, unit=unit) - log("Data: {}".format(data, level=DEBUG)) + data = hookenv.relation_get(rid=rid, unit=unit) + ch_neutron.log("Data: {}".format(data, level=hookenv.DEBUG)) if not data.get('cell-name'): return - cell_updated = update_child_cell( + cell_updated = ncc_utils.update_child_cell( name=data['cell-name'], db_service=data['db-service'], amqp_service=data['amqp-service']) if cell_updated: - log( + hookenv.log( "Cell registration data changed, triggering a remote restart", - level=DEBUG) - relation_set( + level=hookenv.DEBUG) + hookenv.relation_set( relation_id=rid, restart_trigger=str(uuid.uuid4())) @hooks.hook('update-status') -@harden() +@ch_harden.harden() def update_status(): - log('Updating status.') + hookenv.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) + hookenv.log("Running prepare series upgrade hook", "INFO") + ch_utils.series_upgrade_prepare(ncc_utils.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) + hookenv.log("Running complete series upgrade hook", "INFO") + ch_utils.series_upgrade_complete(ncc_utils.resume_unit_helper, CONFIGS) def main(): try: hooks.execute(sys.argv) - except UnregisteredHookError as e: - log('Unknown hook {} - skipping.'.format(e)) - assess_status(CONFIGS) + except hookenv.UnregisteredHookError as e: + hookenv.log('Unknown hook {} - skipping.'.format(e)) + ncc_utils.assess_status(CONFIGS) if __name__ == '__main__': + resolve_CONFIGS() main() diff --git a/hooks/nova_cc_utils.py b/hooks/nova_cc_utils.py index deb9c397..847b6400 100644 --- a/hooks/nova_cc_utils.py +++ b/hooks/nova_cc_utils.py @@ -12,104 +12,30 @@ # See the License for the specific language governing permissions and # limitations under the License. +import base64 +import collections +import configparser +import copy +import json import os import subprocess -import ConfigParser +from urllib.parse import urlparse import uuid -from base64 import b64encode -from collections import OrderedDict -from copy import deepcopy -from urlparse import urlparse -from uuid import uuid1 -import json +import charmhelpers.contrib.hahelpers.cluster as ch_cluster +import charmhelpers.contrib.network.ip as ch_ip +import charmhelpers.contrib.openstack.context as ch_context +import charmhelpers.contrib.openstack.ip as ch_openstack_ip +import charmhelpers.contrib.openstack.templating as ch_templating +import charmhelpers.contrib.openstack.utils as ch_utils +import charmhelpers.contrib.peerstorage as ch_peerstorage +import charmhelpers.core.decorators as ch_decorators +import charmhelpers.core.hookenv as hookenv +import charmhelpers.core.host as ch_host +import charmhelpers.fetch as ch_fetch -from charmhelpers.contrib.openstack import context, templating - -from charmhelpers.contrib.hahelpers.cluster import ( - get_hacluster_config, -) - -from charmhelpers.contrib.peerstorage import ( - peer_retrieve, - peer_store, -) - -from charmhelpers.contrib.openstack.utils import ( - configure_installation_source, - get_host_ip, - get_hostname, - get_os_codename_install_source, - incomplete_relation_data, - is_ip, - os_release, - reset_os_release, - save_script_rc as _save_script_rc, - is_unit_paused_set, - make_assess_status_func, - pause_unit, - resume_unit, - os_application_version_set, - token_cache_pkgs, - enable_memcache, - CompareOpenStackReleases, -) - -from charmhelpers.fetch import ( - apt_upgrade, - apt_update, - apt_install, - apt_purge, - apt_autoremove, - add_source, - filter_installed_packages, - filter_missing_packages, -) - -from charmhelpers.core.hookenv import ( - config, - is_leader, - log, - leader_get, - leader_set, - relation_get, - relation_id, - relation_ids, - remote_unit, - DEBUG, - ERROR, - INFO, - status_set, - related_units, - local_unit, -) - -from charmhelpers.core.host import ( - service_pause, - service_resume, - service_running, - service_start, - service_stop, - service_restart, - lsb_release, - CompareHostReleases, -) - -from charmhelpers.contrib.network.ip import ( - is_ipv6, - ns_query, -) - -from charmhelpers.core.decorators import ( - retry_on_exception, -) - -from charmhelpers.contrib.openstack.ip import ( - canonical_url, - INTERNAL, -) - -import nova_cc_context +import hooks.nova_cc_common as common +import hooks.nova_cc_context as nova_cc_context TEMPLATES = 'templates/' @@ -168,14 +94,8 @@ SERVICE_BLACKLIST = { 'newton': ['nova-cert'], } -API_PORTS = { - 'nova-api-ec2': 8773, - 'nova-api-os-compute': 8774, - 'nova-api-metadata': 8775, - 'nova-api-os-volume': 8776, - 'nova-placement-api': 8778, - 'nova-objectstore': 3333, -} +# API_PORTS is now in nova_cc_common.py to break the circular dependency +# between nova_cc_utils.py and nova_cc_context.py NOVA_CONF_DIR = "/etc/nova" NEUTRON_CONF_DIR = "/etc/neutron" @@ -196,97 +116,98 @@ VENDORDATA_FILE = '/etc/nova/vendor_data.json' def resolve_services(): - _services = deepcopy(BASE_SERVICES) - os_rel = os_release('nova-common') + _services = copy.deepcopy(BASE_SERVICES) + os_rel = ch_utils.os_release('nova-common') + cmp_os_release = ch_utils.CompareOpenStackReleases(os_rel) for release in SERVICE_BLACKLIST: - if os_rel >= release or config('disable-aws-compat'): - [_services.remove(service) - for service in SERVICE_BLACKLIST[release]] + if cmp_os_release >= release or hookenv.config('disable-aws-compat'): + for service in SERVICE_BLACKLIST[release]: + _services.remove(service) return _services -BASE_RESOURCE_MAP = OrderedDict([ - (NOVA_CONF, { - 'services': resolve_services(), - 'contexts': [context.AMQPContext(ssl_dir=NOVA_CONF_DIR), - context.SharedDBContext( - relation_prefix='nova', ssl_dir=NOVA_CONF_DIR), - context.OSConfigFlagContext( - charm_flag='nova-alchemy-flags', - template_flag='nova_alchemy_flags'), - context.ImageServiceContext(), - context.OSConfigFlagContext(), - context.SubordinateConfigContext( - interface='nova-vmware', - service='nova', - config_file=NOVA_CONF), - context.SyslogContext(), - context.LogLevelContext(), - nova_cc_context.HAProxyContext(), - nova_cc_context.IdentityServiceContext( - service='nova', - service_user='nova'), - nova_cc_context.VolumeServiceContext(), - context.ZeroMQContext(), - context.NotificationDriverContext(), - nova_cc_context.NovaIPv6Context(), - nova_cc_context.NeutronCCContext(), - nova_cc_context.NovaConfigContext(), - nova_cc_context.InstanceConsoleContext(), - nova_cc_context.ConsoleSSLContext(), - nova_cc_context.CloudComputeContext(), - context.InternalEndpointContext(), - context.VolumeAPIContext('nova-common'), - nova_cc_context.NeutronAPIContext(), - nova_cc_context.SerialConsoleContext(), - context.MemcacheContext(), - nova_cc_context.NovaMetadataContext()], - }), - (NOVA_API_PASTE, { - 'services': [s for s in resolve_services() if 'api' in s], - 'contexts': [nova_cc_context.IdentityServiceContext(), - nova_cc_context.APIRateLimitingContext()], - }), - (HAPROXY_CONF, { - 'contexts': [context.HAProxyContext(singlenode_mode=True), - nova_cc_context.HAProxyContext()], - 'services': ['haproxy'], - }), - (APACHE_CONF, { - 'contexts': [nova_cc_context.ApacheSSLContext()], - 'services': ['apache2'], - }), - (APACHE_24_CONF, { - 'contexts': [nova_cc_context.ApacheSSLContext()], - 'services': ['apache2'], - }), -]) +# _BASE_RESOURCE_MAP is a caching global that is set up by +# get_base_resource_map() +_BASE_RESOURCE_MAP = None + + +def get_base_resource_map(): + """Return the base resource map. Note that it is cached in the + _BASE_RESOURCE_MAP global. + + :returns: The base resource map + :rtype: collections.OrderedDict + """ + global _BASE_RESOURCE_MAP + if _BASE_RESOURCE_MAP is None: + _BASE_RESOURCE_MAP = collections.OrderedDict([ + (NOVA_CONF, { + 'services': resolve_services(), + 'contexts': [ + ch_context.AMQPContext(ssl_dir=NOVA_CONF_DIR), + ch_context.SharedDBContext( + relation_prefix='nova', + ssl_dir=NOVA_CONF_DIR), + ch_context.OSConfigFlagContext( + charm_flag='nova-alchemy-flags', + template_flag='nova_alchemy_flags'), + ch_context.ImageServiceContext(), + ch_context.OSConfigFlagContext(), + ch_context.SubordinateConfigContext( + interface='nova-vmware', + service='nova', + config_file=NOVA_CONF), + ch_context.SyslogContext(), + ch_context.LogLevelContext(), + nova_cc_context.HAProxyContext(), + nova_cc_context.IdentityServiceContext( + service='nova', + service_user='nova'), + nova_cc_context.VolumeServiceContext(), + ch_context.ZeroMQContext(), + ch_context.NotificationDriverContext(), + nova_cc_context.NovaIPv6Context(), + nova_cc_context.NeutronCCContext(), + nova_cc_context.NovaConfigContext(), + nova_cc_context.InstanceConsoleContext(), + nova_cc_context.ConsoleSSLContext(), + nova_cc_context.CloudComputeContext(), + ch_context.InternalEndpointContext(), + ch_context.VolumeAPIContext('nova-common'), + nova_cc_context.NeutronAPIContext(), + nova_cc_context.SerialConsoleContext(), + ch_context.MemcacheContext(), + nova_cc_context.NovaMetadataContext()], + }), + (NOVA_API_PASTE, { + 'services': [s for s in resolve_services() if 'api' in s], + 'contexts': [nova_cc_context.IdentityServiceContext(), + nova_cc_context.APIRateLimitingContext()], + }), + (HAPROXY_CONF, { + 'contexts': [ + ch_context.HAProxyContext(singlenode_mode=True), + nova_cc_context.HAProxyContext()], + 'services': ['haproxy'], + }), + (APACHE_CONF, { + 'contexts': [nova_cc_context.ApacheSSLContext( + determine_ports)], + 'services': ['apache2'], + }), + (APACHE_24_CONF, { + 'contexts': [nova_cc_context.ApacheSSLContext( + determine_ports)], + 'services': ['apache2'], + }), + ]) + return _BASE_RESOURCE_MAP + CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' NOVA_SSH_DIR = '/etc/nova/compute_ssh/' -CONSOLE_CONFIG = { - 'spice': { - 'packages': ['nova-spiceproxy', 'nova-consoleauth'], - 'services': ['nova-spiceproxy', 'nova-consoleauth'], - 'proxy-page': '/spice_auto.html', - 'proxy-port': 6082, - }, - 'novnc': { - 'packages': ['nova-novncproxy', 'nova-consoleauth'], - 'services': ['nova-novncproxy', 'nova-consoleauth'], - 'proxy-page': '/vnc_auto.html', - 'proxy-port': 6080, - }, - 'xvpvnc': { - 'packages': ['nova-xvpvncproxy', 'nova-consoleauth'], - 'services': ['nova-xvpvncproxy', 'nova-consoleauth'], - 'proxy-page': '/console', - 'proxy-port': 6081, - }, -} - SERIAL_CONSOLE = { 'packages': ['nova-serialproxy', 'nova-consoleauth', 'websockify'], @@ -303,70 +224,72 @@ def resource_map(actual_services=True): unit (ie. apache2) or the services defined in BASE_SERVICES (ie.nova-placement-api). ''' - resource_map = deepcopy(BASE_RESOURCE_MAP) + _resource_map = copy.deepcopy(get_base_resource_map()) if os.path.exists('/etc/apache2/conf-available'): - resource_map.pop(APACHE_CONF) + _resource_map.pop(APACHE_CONF) else: - resource_map.pop(APACHE_24_CONF) + _resource_map.pop(APACHE_24_CONF) - resource_map[NOVA_CONF]['contexts'].append( + _resource_map[NOVA_CONF]['contexts'].append( nova_cc_context.NeutronCCContext()) - release = os_release('nova-common') - cmp_os_release = CompareOpenStackReleases(release) + release = ch_utils.os_release('nova-common') + cmp_os_release = ch_utils.CompareOpenStackReleases(release) if cmp_os_release >= 'mitaka': - resource_map[NOVA_CONF]['contexts'].append( + _resource_map[NOVA_CONF]['contexts'].append( nova_cc_context.NovaAPISharedDBContext(relation_prefix='novaapi', database='nova_api', ssl_dir=NOVA_CONF_DIR) ) - if console_attributes('services'): - resource_map[NOVA_CONF]['services'] += console_attributes('services') + if common.console_attributes('services'): + _resource_map[NOVA_CONF]['services'] += ( + common.console_attributes('services')) # nova-consoleauth will be managed by pacemaker, if # single-nova-consoleauth is used, then don't monitor for the # nova-consoleauth service to be started (LP: #1660244). - if config('single-nova-consoleauth') and relation_ids('ha'): - services = resource_map[NOVA_CONF]['services'] + if (hookenv.config('single-nova-consoleauth') and + hookenv.relation_ids('ha')): + services = _resource_map[NOVA_CONF]['services'] if 'nova-consoleauth' in services: services.remove('nova-consoleauth') - if (config('enable-serial-console') and cmp_os_release >= 'juno'): - resource_map[NOVA_CONF]['services'] += SERIAL_CONSOLE['services'] + if (hookenv.config('enable-serial-console') and cmp_os_release >= 'juno'): + _resource_map[NOVA_CONF]['services'] += SERIAL_CONSOLE['services'] # also manage any configs that are being updated by subordinates. - vmware_ctxt = context.SubordinateConfigContext(interface='nova-vmware', - service='nova', - config_file=NOVA_CONF) + vmware_ctxt = ch_context.SubordinateConfigContext( + interface='nova-vmware', service='nova', config_file=NOVA_CONF) vmware_ctxt = vmware_ctxt() if vmware_ctxt and 'services' in vmware_ctxt: for s in vmware_ctxt['services']: - if s not in resource_map[NOVA_CONF]['services']: - resource_map[NOVA_CONF]['services'].append(s) + if s not in _resource_map[NOVA_CONF]['services']: + _resource_map[NOVA_CONF]['services'].append(s) - if enable_memcache(release=release): - resource_map[MEMCACHED_CONF] = { - 'contexts': [context.MemcacheContext()], + if ch_utils.enable_memcache(release=release): + _resource_map[MEMCACHED_CONF] = { + 'contexts': [ch_context.MemcacheContext()], 'services': ['memcached']} if actual_services and placement_api_enabled(): - for cfile in resource_map: - svcs = resource_map[cfile]['services'] + for cfile in _resource_map: + svcs = _resource_map[cfile]['services'] if 'nova-placement-api' in svcs: svcs.remove('nova-placement-api') if 'apache2' not in svcs: svcs.append('apache2') wsgi_script = "/usr/bin/nova-placement-api" - resource_map[WSGI_NOVA_PLACEMENT_API_CONF] = { - 'contexts': [context.WSGIWorkerConfigContext(name="nova", - script=wsgi_script), - nova_cc_context.HAProxyContext()], + _resource_map[WSGI_NOVA_PLACEMENT_API_CONF] = { + 'contexts': [ + ch_context.WSGIWorkerConfigContext( + name="nova", script=wsgi_script), + nova_cc_context.HAProxyContext()], 'services': ['apache2'] } elif not placement_api_enabled(): - for cfile in resource_map: - svcs = resource_map[cfile]['services'] + for cfile in _resource_map: + svcs = _resource_map[cfile]['services'] if 'nova-placement-api' in svcs: svcs.remove('nova-placement-api') if enable_metadata_api(): @@ -374,23 +297,23 @@ def resource_map(actual_services=True): svcs = ['apache2'] else: svcs = ['nova-api-metadata'] - resource_map[WSGI_NOVA_METADATA_API_CONF] = { + _resource_map[WSGI_NOVA_METADATA_API_CONF] = { 'contexts': [ - context.WSGIWorkerConfigContext( + ch_context.WSGIWorkerConfigContext( name="nova_meta", user='nova', group='nova', script='/usr/bin/nova-metadata-wsgi'), nova_cc_context.MetaDataHAProxyContext()], 'services': svcs} - return resource_map + return _resource_map def register_configs(release=None): - release = release or os_release('nova-common') - configs = templating.OSConfigRenderer(templates_dir=TEMPLATES, - openstack_release=release) - for cfg, rscs in resource_map().iteritems(): + release = release or ch_utils.os_release('nova-common') + configs = ch_templating.OSConfigRenderer( + templates_dir=TEMPLATES, openstack_release=release) + for cfg, rscs in resource_map().items(): configs.register(cfg, rscs['contexts']) return configs @@ -403,9 +326,9 @@ def restart_map(actual_services=True): unit (ie. apache2) or the services defined in BASE_SERVICES (ie.nova-placement-api). ''' - return OrderedDict( + return collections.OrderedDict( [(cfg, v['services']) - for cfg, v in resource_map(actual_services).iteritems() + for cfg, v in resource_map(actual_services).items() if v['services']]) @@ -423,43 +346,17 @@ def determine_ports(): for services in restart_map(actual_services=False).values(): for svc in services: try: - ports.append(API_PORTS[svc]) + ports.append(common.api_port(svc)) except KeyError: pass return list(set(ports)) -def api_port(service): - return API_PORTS[service] - - -def console_attributes(attr, proto=None): - '''Leave proto unset to query attributes of the protocal specified at - runtime''' - if proto: - console_proto = proto - else: - console_proto = config('console-access-protocol') - if console_proto is not None and console_proto.lower() in ('none', ''): - console_proto = None - if attr == 'protocol': - return console_proto - # 'vnc' is a virtual type made up of novnc and xvpvnc - if console_proto == 'vnc': - if attr in ['packages', 'services']: - return list(set(CONSOLE_CONFIG['novnc'][attr] + - CONSOLE_CONFIG['xvpvnc'][attr])) - else: - return None - if console_proto in CONSOLE_CONFIG: - return CONSOLE_CONFIG[console_proto][attr] - return None - - def determine_packages(): # currently all packages match service names - release = CompareOpenStackReleases(os_release('nova-common')) - packages = deepcopy(BASE_PACKAGES) + release = ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) + packages = copy.deepcopy(BASE_PACKAGES) for v in resource_map(actual_services=False).values(): packages.extend(v['services']) # The nova-api-metadata service is served via wsgi and the package is @@ -469,11 +366,12 @@ def determine_packages(): packages.remove("nova-api-metadata") except ValueError: pass - if console_attributes('packages'): - packages.extend(console_attributes('packages')) - if (config('enable-serial-console') and release >= 'juno'): + if common.console_attributes('packages'): + packages.extend(common.console_attributes('packages')) + if (hookenv.config('enable-serial-console') and release >= 'juno'): packages.extend(SERIAL_CONSOLE['packages']) - packages.extend(token_cache_pkgs(source=config('openstack-origin'))) + packages.extend( + ch_utils.token_cache_pkgs(source=hookenv.config('openstack-origin'))) if release >= 'rocky': packages = [p for p in packages if not p.startswith('python-')] packages.extend(PY3_PACKAGES) @@ -489,7 +387,8 @@ def determine_purge_packages(): :returns: list of package names ''' - release = CompareOpenStackReleases(os_release('keystone')) + release = ch_utils.CompareOpenStackReleases( + ch_utils.os_release('keystone')) if release >= 'rocky': pkgs = [p for p in BASE_PACKAGES if p.startswith('python-')] pkgs.extend(['python-nova', 'python-memcache', 'libapache2-mod-wsgi']) @@ -499,7 +398,7 @@ def determine_purge_packages(): def save_script_rc(): env_vars = { - 'OPENSTACK_PORT_MCASTPORT': config('ha-mcastport'), + 'OPENSTACK_PORT_MCASTPORT': hookenv.config('ha-mcastport'), 'OPENSTACK_SERVICE_API_EC2': 'nova-api-ec2', 'OPENSTACK_SERVICE_API_OS_COMPUTE': 'nova-api-os-compute', 'OPENSTACK_SERVICE_CERT': 'nova-cert', @@ -507,9 +406,9 @@ def save_script_rc(): 'OPENSTACK_SERVICE_OBJECTSTORE': 'nova-objectstore', 'OPENSTACK_SERVICE_SCHEDULER': 'nova-scheduler', } - if relation_ids('nova-volume-service'): + if hookenv.relation_ids('nova-volume-service'): env_vars['OPENSTACK_SERVICE_API_OS_VOL'] = 'nova-api-os-volume' - _save_script_rc(**env_vars) + ch_utils.save_script_rc(**env_vars) def get_step_upgrade_source(new_src): @@ -530,17 +429,17 @@ def get_step_upgrade_source(new_src): 'xenial-ocata': ('*', 'cloud:xenial-newton'), # LP: #1711209 } try: - os_codename = get_os_codename_install_source(new_src) - ubuntu_series = lsb_release()['DISTRIB_CODENAME'].lower() + os_codename = ch_utils.get_os_codename_install_source(new_src) + ubuntu_series = ch_host.lsb_release()['DISTRIB_CODENAME'].lower() cur_pocket, step_src = sources['%s-%s' % (ubuntu_series, os_codename)] - current_src = os_release('nova-common') - step_src_codename = get_os_codename_install_source(step_src) + current_src = ch_utils.os_release('nova-common') + step_src_codename = ch_utils.get_os_codename_install_source(step_src) if cur_pocket == '*' and step_src_codename > current_src: return step_src except KeyError: pass - configure_installation_source(new_src) + ch_utils.configure_installation_source(new_src) # charmhelpers.contrib.openstack.utils.configure_installation_source() # configures the repository in juju_deb.list, while @@ -549,10 +448,10 @@ def get_step_upgrade_source(new_src): for fname in ['cloud-archive.list', 'juju_deb.list']: fpath = os.path.join('/etc/apt/sources.list.d/', fname) if not os.path.isfile(fpath): - log('Missing %s skipping it' % fpath, level=DEBUG) + hookenv.log('Missing %s skipping it' % fpath, level=hookenv.DEBUG) continue - with open(fpath, 'r') as f: + with open(fpath, 'rt') as f: for line in f.readlines(): for target_src, (cur_pocket, step_src) in sources.items(): if target_src != new_src: @@ -590,13 +489,13 @@ def disable_policy_rcd(): def is_db_initialised(): - if relation_ids('cluster'): - dbsync_state = peer_retrieve('dbsync_state') + if hookenv.relation_ids('cluster'): + dbsync_state = ch_peerstorage.peer_retrieve('dbsync_state') if dbsync_state == 'complete': - log("Database is initialised", level=DEBUG) + hookenv.log("Database is initialised", level=hookenv.DEBUG) return True - log("Database is NOT initialised", level=DEBUG) + hookenv.log("Database is NOT initialised", level=hookenv.DEBUG) return False @@ -606,14 +505,16 @@ def is_cellv2_init_ready(): Cells v2 init requires transport_url and database connections to be set in nova.conf. """ - amqp = context.AMQPContext() + amqp = ch_context.AMQPContext() shared_db = nova_cc_context.NovaCellV2SharedDBContext() - if (CompareOpenStackReleases(os_release('nova-common')) >= 'ocata' and + if (ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) >= 'ocata' and amqp() and shared_db()): return True - log("OpenStack release, database, or rabbitmq not ready for Cells V2", - level=DEBUG) + hookenv.log( + "OpenStack release, database, or rabbitmq not ready for Cells V2", + level=hookenv.DEBUG) return False @@ -622,8 +523,9 @@ def _do_openstack_upgrade(new_src): # All upgrades to Liberty are forced to step through Kilo. Liberty does # not have the migrate_flavor_data option (Bug #1511466) available so it # must be done pre-upgrade - if (CompareOpenStackReleases(os_release('nova-common')) == 'kilo' and - is_leader()): + if (ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) == 'kilo' and + hookenv.is_leader()): migrate_nova_flavors() # 'nova-manage db online_data_migrations' needs to be run before moving to @@ -631,25 +533,26 @@ def _do_openstack_upgrade(new_src): # step was not being executed (LP: #1711209). online_data_migrations_if_needed() - new_os_rel = get_os_codename_install_source(new_src) - cmp_new_os_rel = CompareOpenStackReleases(new_os_rel) - log('Performing OpenStack upgrade to %s.' % (new_os_rel)) + new_os_rel = ch_utils.get_os_codename_install_source(new_src) + cmp_new_os_rel = ch_utils.CompareOpenStackReleases(new_os_rel) + hookenv.log('Performing OpenStack upgrade to %s.' % (new_os_rel)) - configure_installation_source(new_src) + ch_utils.configure_installation_source(new_src) dpkg_opts = [ '--option', 'Dpkg::Options::=--force-confnew', '--option', 'Dpkg::Options::=--force-confdef', ] - apt_update(fatal=True) - apt_upgrade(options=dpkg_opts, fatal=True, dist=True) - reset_os_release() - apt_install(determine_packages(), fatal=True) + ch_fetch.apt_update(fatal=True) + ch_fetch.apt_upgrade(options=dpkg_opts, fatal=True, dist=True) + ch_utils.reset_os_release() + ch_fetch.apt_install(determine_packages(), fatal=True) - installed_pkgs = filter_missing_packages(determine_purge_packages()) + installed_pkgs = ch_fetch.filter_missing_packages( + determine_purge_packages()) if installed_pkgs: - apt_purge(installed_pkgs, fatal=True) - apt_autoremove(purge=True, fatal=True) + ch_fetch.apt_purge(installed_pkgs, fatal=True) + ch_fetch.apt_autoremove(purge=True, fatal=True) disable_policy_rcd() @@ -662,27 +565,27 @@ def _do_openstack_upgrade(new_src): if cmp_new_os_rel >= 'mitaka' and not database_setup(prefix='novaapi'): # NOTE: Defer service restarts and database migrations for now # as nova_api database is not yet created - if (relation_ids('cluster') and is_leader()): + if (hookenv.relation_ids('cluster') and hookenv.is_leader()): # NOTE: reset dbsync state so that migration will complete # when the nova_api database is setup. - peer_store('dbsync_state', None) + ch_peerstorage.peer_store('dbsync_state', None) return configs if cmp_new_os_rel >= 'ocata' and not database_setup(prefix='novacell0'): # NOTE: Defer service restarts and database migrations for now # as nova_cell0 database is not yet created - if (relation_ids('cluster') and is_leader()): + if (hookenv.relation_ids('cluster') and hookenv.is_leader()): # NOTE: reset dbsync state so that migration will complete # when the novacell0 database is setup. - peer_store('dbsync_state', None) + ch_peerstorage.peer_store('dbsync_state', None) return configs - if is_leader(): - status_set('maintenance', 'Running nova db migration') + if hookenv.is_leader(): + hookenv.status_set('maintenance', 'Running nova db migration') migrate_nova_databases() - if not is_unit_paused_set(): - [service_start(s) for s in services()] + if not ch_utils.is_unit_paused_set(): + [ch_host.service_start(s) for s in services()] return configs @@ -696,16 +599,16 @@ def database_setup(prefix): relation name using the provided prefix. ''' key = '{}_allowed_units'.format(prefix) - for db_rid in relation_ids('shared-db'): - for unit in related_units(db_rid): - allowed_units = relation_get(key, rid=db_rid, unit=unit) - if allowed_units and local_unit() in allowed_units.split(): + for db_rid in hookenv.relation_ids('shared-db'): + for unit in hookenv.related_units(db_rid): + allowed_units = hookenv.relation_get(key, rid=db_rid, unit=unit) + if allowed_units and hookenv.local_unit() in allowed_units.split(): return True return False def do_openstack_upgrade(configs): - new_src = config('openstack-origin') + new_src = hookenv.config('openstack-origin') step_src = get_step_upgrade_source(new_src) if step_src is not None: @@ -713,55 +616,61 @@ def do_openstack_upgrade(configs): return _do_openstack_upgrade(new_src) -@retry_on_exception(5, base_delay=3, exc_type=subprocess.CalledProcessError) +@ch_decorators.retry_on_exception( + 5, base_delay=3, exc_type=subprocess.CalledProcessError) def migrate_nova_flavors(): '''Runs nova-manage to migrate flavor data if needed''' - log('Migrating nova flavour information in database.', level=INFO) + hookenv.log('Migrating nova flavour information in database.', + level=hookenv.INFO) cmd = ['nova-manage', 'db', 'migrate_flavor_data'] try: subprocess.check_output(cmd) except subprocess.CalledProcessError as e: - log('migrate_flavor_data failed\n{}'.format(e.output), level=ERROR) + hookenv.log('migrate_flavor_data failed\n{}'.format(e.output), + level=hookenv.ERROR) raise -@retry_on_exception(5, base_delay=3, exc_type=subprocess.CalledProcessError) +@ch_decorators.retry_on_exception( + 5, base_delay=3, exc_type=subprocess.CalledProcessError) def online_data_migrations_if_needed(): '''Runs nova-manage to run online data migrations available since Mitaka''' - if (is_leader() and - CompareOpenStackReleases(os_release('nova-common')) >= 'mitaka'): - log('Running online_data_migrations', level=INFO) + if (hookenv.is_leader() and + ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) >= 'mitaka'): + hookenv.log('Running online_data_migrations', level=hookenv.INFO) cmd = ['nova-manage', 'db', 'online_data_migrations'] try: subprocess.check_output(cmd) except subprocess.CalledProcessError as e: - log('online_data_migrations failed\n{}'.format(e.output), - level=ERROR) + hookenv.log('online_data_migrations failed\n{}'.format(e.output), + level=hookenv.ERROR) raise def migrate_nova_api_database(): '''Initialize or migrate the nova_api database''' - if CompareOpenStackReleases(os_release('nova-common')) >= 'mitaka': - log('Migrating the nova-api database.', level=INFO) + if ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) >= 'mitaka': + hookenv.log('Migrating the nova-api database.', level=hookenv.INFO) cmd = ['nova-manage', 'api_db', 'sync'] try: subprocess.check_output(cmd) except subprocess.CalledProcessError as e: # NOTE(coreycb): sync of api_db on upgrade from newton->ocata # fails but cell init is successful. - log('Ignoring CalledProcessError during nova-api database ' - 'migration\n{}'.format(e.output), level=INFO) + hookenv.log('Ignoring CalledProcessError during nova-api database ' + 'migration\n{}'.format(e.output), level=hookenv.INFO) def migrate_nova_database(): '''Initialize or migrate the nova database''' - log('Migrating the nova database.', level=INFO) + hookenv.log('Migrating the nova database.', level=hookenv.INFO) cmd = ['nova-manage', 'db', 'sync'] try: subprocess.check_output(cmd) except subprocess.CalledProcessError as e: - log('db sync failed\n{}'.format(e.output), level=ERROR) + hookenv.log('db sync failed\n{}'.format(e.output), level=hookenv.ERROR) raise @@ -771,27 +680,30 @@ def initialize_cell_databases(): cell0 is stored in the database named 'nova_cell0'. cell1 is stored in the database named 'nova'. ''' - log('Creating cell0 database records', level=INFO) + hookenv.log('Creating cell0 database records', level=hookenv.INFO) cmd = ['nova-manage', 'cell_v2', 'map_cell0'] try: subprocess.check_output(cmd) except subprocess.CalledProcessError as e: - log('map_cell0 failed\n{}'.format(e.output), level=ERROR) + hookenv.log('map_cell0 failed\n{}'.format(e.output), + level=hookenv.ERROR) raise - log('Creating cell1 database records', level=INFO) + hookenv.log('Creating cell1 database records', level=hookenv.INFO) cmd = ['nova-manage', 'cell_v2', 'create_cell', '--name', 'cell1', '--verbose'] try: subprocess.check_output(cmd) - log('cell1 was successfully created', level=INFO) + hookenv.log('cell1 was successfully created', level=hookenv.INFO) except subprocess.CalledProcessError as e: if e.returncode == 1: - log('Cell1 create_cell failed\n{}'.format(e.output), level=ERROR) + hookenv.log('Cell1 create_cell failed\n{}'.format(e.output), + level=hookenv.ERROR) raise elif e.returncode == 2: - log('Cell1 create_cell failure ignored - a cell is already using ' - 'the transport_url/database combination.', level=INFO) + hookenv.log( + 'Cell1 create_cell failure ignored - a cell is already using ' + 'the transport_url/database combination.', level=hookenv.INFO) def get_cell_uuid(cell, fatal=True): @@ -799,7 +711,7 @@ def get_cell_uuid(cell, fatal=True): :param cell: string cell name i.e. 'cell1' :returns: string cell uuid ''' - log("Listing cell, '{}'".format(cell), level=INFO) + hookenv.log("Listing cell, '{}'".format(cell), level=hookenv.INFO) cells = get_cell_details() cell_info = cells.get(cell) if not cell_info: @@ -814,13 +726,14 @@ def get_cell_details(): '''Get cell details :returns: string cell uuid ''' - log("Getting details of cells", level=INFO) + hookenv.log("Getting details of cells", level=hookenv.INFO) cells = {} cmd = ['sudo', 'nova-manage', 'cell_v2', 'list_cells', '--verbose'] try: - out = subprocess.check_output(cmd) + out = subprocess.check_output(cmd).decode('utf-8') except subprocess.CalledProcessError as e: - log('list_cells failed\n{}'.format(e.output), level=ERROR) + hookenv.log('list_cells failed\n{}'.format(e.output), + level=hookenv.ERROR) raise for line in out.split('\n'): columns = line.split('|') @@ -844,20 +757,22 @@ def update_cell_database(): This should be called whenever a database or rabbitmq-server relation is changed to update the transport_url in the nova_api cell_mappings table. ''' - log('Updating cell1 properties', level=INFO) + hookenv.log('Updating cell1 properties', level=hookenv.INFO) cell1_uuid = get_cell_uuid('cell1') cmd = ['nova-manage', 'cell_v2', 'update_cell', '--cell_uuid', cell1_uuid] try: subprocess.check_output(cmd) except subprocess.CalledProcessError as e: if e.returncode == 1: - log('Cell1 update_cell failed\n{}'.format(e.output), level=ERROR) + hookenv.log('Cell1 update_cell failed\n{}'.format(e.output), + level=hookenv.ERROR) raise elif e.returncode == 2: - log('Cell1 update_cell failure ignored - the properties cannot ' - 'be set.', level=INFO) + hookenv.log( + 'Cell1 update_cell failure ignored - the properties cannot ' + 'be set.', level=hookenv.INFO) else: - log('cell1 was successfully updated', level=INFO) + hookenv.log('cell1 was successfully updated', level=hookenv.INFO) def map_instances(): @@ -878,8 +793,8 @@ def map_instances(): # still need mapping. while exit_code == 1: msg = 'Mapping instances. Batch number: {}'.format(iteration) - status_set('maintenance', msg) - log(msg, level=INFO) + hookenv.status_set('maintenance', msg) + hookenv.log(msg, level=hookenv.INFO) process = subprocess.Popen(cmd, stdout=subprocess.PIPE) stdout, stderr = process.communicate() exit_code = process.wait() @@ -887,16 +802,16 @@ def map_instances(): msg = 'Cell1 map_instances failed\nstdout: {}\nstderr: {}'.format( stdout, stderr) - log(msg, level=ERROR) + hookenv.log(msg, level=hookenv.ERROR) raise Exception(msg) iteration += 1 msg = 'Mapping instances complete' - status_set('maintenance', msg) - log(msg, level=INFO) + hookenv.status_set('maintenance', msg) + hookenv.log(msg, level=hookenv.INFO) def archive_deleted_rows(max_rows=None): - log('Archiving deleted rows', level=INFO) + hookenv.log('Archiving deleted rows', level=hookenv.INFO) cmd = ['nova-manage', 'db', 'archive_deleted_rows', '--verbose'] if max_rows: cmd.extend(['--max_rows', str(max_rows)]) @@ -907,7 +822,7 @@ def archive_deleted_rows(max_rows=None): msg = 'Archiving deleted rows failed\nstdout: {}\nstderr: {}'.format( stdout, stderr) - log(msg, level=ERROR) + hookenv.log(msg, level=hookenv.ERROR) raise Exception(msg) else: return stdout @@ -915,36 +830,40 @@ def archive_deleted_rows(max_rows=None): def add_hosts_to_cell(): '''Map compute hosts to cell''' - log('Cell1 discover_hosts', level=INFO) + hookenv.log('Cell1 discover_hosts', level=hookenv.INFO) cell1_uuid = get_cell_uuid('cell1') cmd = ['nova-manage', 'cell_v2', 'discover_hosts', '--cell_uuid', cell1_uuid, '--verbose'] try: subprocess.check_output(cmd) except subprocess.CalledProcessError as e: - log('Cell1 discover_hosts failed\n{}'.format(e.output), level=ERROR) + hookenv.log('Cell1 discover_hosts failed\n{}'.format(e.output), + level=hookenv.ERROR) raise def finalize_migrate_nova_databases(): - if relation_ids('cluster'): - log('Informing peers that dbsync is complete', level=INFO) - peer_store('dbsync_state', 'complete') - log('Enabling services', level=INFO) - if not is_unit_paused_set(): + if hookenv.relation_ids('cluster'): + hookenv.log('Informing peers that dbsync is complete', + level=hookenv.INFO) + ch_peerstorage.peer_store('dbsync_state', 'complete') + hookenv.log('Enabling services', level=hookenv.INFO) + if not ch_utils.is_unit_paused_set(): for svc in services(): - service_resume(svc) + ch_host.service_resume(svc) else: - log('Unit is in paused state, not issuing start/resume to all ' - 'services') + hookenv.log('Unit is in paused state, not issuing start/resume to all ' + 'services') # NOTE(jamespage): Retry deals with sync issues during one-shot HA deploys. # mysql might be restarting or suchlike. -@retry_on_exception(5, base_delay=3, exc_type=subprocess.CalledProcessError) +@ch_decorators.retry_on_exception( + 5, base_delay=3, exc_type=subprocess.CalledProcessError) def migrate_nova_databases(): '''Runs nova-manage to initialize new databases or migrate existing''' - release = CompareOpenStackReleases(os_release('nova-common')) + release = ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) if release < 'ocata': migrate_nova_api_database() migrate_nova_database() @@ -970,10 +889,10 @@ def auth_token_config(setting): Returns currently configured value for setting in api-paste.ini's authtoken section, or None. """ - config = ConfigParser.RawConfigParser() - config.read('/etc/nova/api-paste.ini') + _config = configparser.RawConfigParser() + _config.read('/etc/nova/api-paste.ini') try: - value = config.get('filter:authtoken', setting) + value = _config.get('filter:authtoken', setting) except: return None if value.startswith('%'): @@ -986,14 +905,14 @@ def keystone_ca_cert_b64(): if not os.path.isfile(CA_CERT_PATH): return None with open(CA_CERT_PATH) as _in: - return b64encode(_in.read()) + return base64.b64encode(_in.read()) def ssh_directory_for_unit(unit=None, user=None): if unit: remote_service = unit.split('/')[0] else: - remote_service = remote_unit().split('/')[0] + remote_service = hookenv.remote_unit().split('/')[0] if user: remote_service = "{}_{}".format(remote_service, user) _dir = os.path.join(NOVA_SSH_DIR, remote_service) @@ -1020,7 +939,7 @@ def ssh_known_host_key(host, unit=None, user=None): try: # The first line of output is like '# Host xx found: line 1 type RSA', # which should be excluded. - output = subprocess.check_output(cmd).strip() + output = subprocess.check_output(cmd).decode('utf-8').strip() except subprocess.CalledProcessError: return None @@ -1034,7 +953,7 @@ def ssh_known_host_key(host, unit=None, user=None): def remove_known_host(host, unit=None, user=None): - log('Removing SSH known host entry for compute host at %s' % host) + hookenv.log('Removing SSH known host entry for compute host at %s' % host) cmd = ['ssh-keygen', '-f', known_hosts(unit, user), '-R', host] subprocess.check_call(cmd) @@ -1053,20 +972,23 @@ def add_known_host(host, unit=None, user=None): '''Add variations of host to a known hosts file.''' cmd = ['ssh-keyscan', '-H', '-t', 'rsa', host] try: - remote_key = subprocess.check_output(cmd).strip() + remote_key = subprocess.check_output(cmd).decode('utf-8').strip() except Exception as e: - log('Could not obtain SSH host key from %s' % host, level=ERROR) + hookenv.log('Could not obtain SSH host key from %s' % host, + level=hookenv.ERROR) raise e current_key = ssh_known_host_key(host, unit, user) if current_key and remote_key: if is_same_key(remote_key, current_key): - log('Known host key for compute host %s up to date.' % host) + hookenv.log( + 'Known host key for compute host %s up to date.' % host) return else: remove_known_host(host, unit, user) - log('Adding SSH host key to known hosts for compute node at %s.' % host) + hookenv.log('Adding SSH host key to known hosts for compute node at {}.' + .format(host)) with open(known_hosts(unit, user), 'a') as out: out.write(remote_key + '\n') @@ -1084,29 +1006,29 @@ def add_authorized_key(public_key, unit=None, user=None): def ssh_compute_add(public_key, rid=None, unit=None, user=None): # If remote compute node hands us a hostname, ensure we have a # known hosts entry for its IP, hostname and FQDN. - private_address = relation_get(rid=rid, unit=unit, - attribute='private-address') + private_address = hookenv.relation_get(rid=rid, unit=unit, + attribute='private-address') hosts = [] - if not is_ipv6(private_address): - hostname = relation_get(rid=rid, unit=unit, - attribute='hostname') + if not ch_ip.is_ipv6(private_address): + hostname = hookenv.relation_get(rid=rid, unit=unit, + attribute='hostname') if hostname: hosts.append(hostname.lower()) - if not is_ip(private_address): + if not ch_utils.is_ip(private_address): hosts.append(private_address.lower()) - hosts.append(get_host_ip(private_address)) + hosts.append(ch_utils.get_host_ip(private_address)) short = private_address.split('.')[0] - if ns_query(short): + if ch_ip.ns_query(short): hosts.append(short.lower()) else: hosts.append(private_address) - hn = get_hostname(private_address) + hn = ch_utils.get_hostname(private_address) if hn: hosts.append(hn.lower()) short = hn.split('.')[0] - if ns_query(short): + if ch_ip.ns_query(short): hosts.append(short.lower()) else: hosts.append(private_address) @@ -1115,8 +1037,8 @@ def ssh_compute_add(public_key, rid=None, unit=None, user=None): add_known_host(host, unit, user) if not ssh_authorized_key_exists(public_key, unit, user): - log('Saving SSH authorized key for compute host at %s.' % - private_address) + hookenv.log('Saving SSH authorized key for compute host at %s.' % + private_address) add_authorized_key(public_key, unit, user) @@ -1145,7 +1067,7 @@ def ssh_compute_remove(public_key, unit=None, user=None): os.path.isfile(known_hosts(unit, user))): return - with open(authorized_keys(unit, user)) as _keys: + with open(authorized_keys(unit, user), 'rt') as _keys: keys = [k.strip() for k in _keys.readlines()] if public_key not in keys: @@ -1153,7 +1075,7 @@ def ssh_compute_remove(public_key, unit=None, user=None): [keys.remove(key) for key in keys if key == public_key] - with open(authorized_keys(unit, user), 'w') as _keys: + with open(authorized_keys(unit, user), 'wt') as _keys: keys = '\n'.join(keys) if not keys.endswith('\n'): keys += '\n' @@ -1163,48 +1085,50 @@ def ssh_compute_remove(public_key, unit=None, user=None): def determine_endpoints(public_url, internal_url, admin_url): '''Generates a dictionary containing all relevant endpoints to be passed to keystone as relation settings.''' - region = config('region') - os_rel = os_release('nova-common') - cmp_os_rel = CompareOpenStackReleases(os_rel) + region = hookenv.config('region') + os_rel = ch_utils.os_release('nova-common') + cmp_os_rel = ch_utils.CompareOpenStackReleases(os_rel) nova_public_url = ('%s:%s/v2/$(tenant_id)s' % - (public_url, api_port('nova-api-os-compute'))) + (public_url, common.api_port('nova-api-os-compute'))) nova_internal_url = ('%s:%s/v2/$(tenant_id)s' % - (internal_url, api_port('nova-api-os-compute'))) + (internal_url, + common.api_port('nova-api-os-compute'))) nova_admin_url = ('%s:%s/v2/$(tenant_id)s' % - (admin_url, api_port('nova-api-os-compute'))) + (admin_url, common.api_port('nova-api-os-compute'))) if cmp_os_rel >= 'queens': nova_public_url = ( '%s:%s/v2.1' % - (public_url, api_port('nova-api-os-compute')) + (public_url, common.api_port('nova-api-os-compute')) ) nova_internal_url = ( '%s:%s/v2.1' % - (internal_url, api_port('nova-api-os-compute')) + (internal_url, common.api_port('nova-api-os-compute')) ) nova_admin_url = ( '%s:%s/v2.1' % - (admin_url, api_port('nova-api-os-compute')) + (admin_url, common.api_port('nova-api-os-compute')) ) ec2_public_url = '%s:%s/services/Cloud' % ( - public_url, api_port('nova-api-ec2')) + public_url, common.api_port('nova-api-ec2')) ec2_internal_url = '%s:%s/services/Cloud' % ( - internal_url, api_port('nova-api-ec2')) + internal_url, common.api_port('nova-api-ec2')) ec2_admin_url = '%s:%s/services/Cloud' % (admin_url, - api_port('nova-api-ec2')) + common.api_port('nova-api-ec2')) - s3_public_url = '%s:%s' % (public_url, api_port('nova-objectstore')) - s3_internal_url = '%s:%s' % (internal_url, api_port('nova-objectstore')) - s3_admin_url = '%s:%s' % (admin_url, api_port('nova-objectstore')) + s3_public_url = '%s:%s' % (public_url, common.api_port('nova-objectstore')) + s3_internal_url = '%s:%s' % (internal_url, + common.api_port('nova-objectstore')) + s3_admin_url = '%s:%s' % (admin_url, common.api_port('nova-objectstore')) if cmp_os_rel >= 'ocata': placement_public_url = '%s:%s' % ( - public_url, api_port('nova-placement-api')) + public_url, common.api_port('nova-placement-api')) placement_internal_url = '%s:%s' % ( - internal_url, api_port('nova-placement-api')) + internal_url, common.api_port('nova-placement-api')) placement_admin_url = '%s:%s' % ( - admin_url, api_port('nova-placement-api')) + admin_url, common.api_port('nova-placement-api')) # the base endpoints endpoints = { @@ -1255,11 +1179,15 @@ def determine_endpoints(public_url, internal_url, admin_url): def guard_map(): - '''Map of services and required interfaces that must be present before - the service should be allowed to start''' + """Map of services and required interfaces that must be present before + the service should be allowed to start + + :returns: A map of service names to interface names + :rtype: Dict[String, String] + """ gmap = {} nova_services = resolve_services() - if os_release('nova-common') not in ['essex', 'folsom']: + if ch_utils.os_release('nova-common') not in ['essex', 'folsom']: nova_services.append('nova-conductor') nova_interfaces = ['identity-service', 'amqp'] @@ -1272,42 +1200,86 @@ def guard_map(): def service_guard(guard_map, contexts, active=False): - '''Inhibit services in guard_map from running unless - required interfaces are found complete in contexts.''' + """Inhibit services in guard_map from running unless required interfaces + are found complete in contexts. + + `guard_map`, `contexts` and `active` are all optionally callable so that + they don't have to run when the module is loaded. This allows them to be + lazy and ensure that they only need to be evaluated if the decorated + function is actually called. + + If `active` is not "truthy" then this decorator just returns the decorated + function with no changes. + + :param guard_map: a callable that returns a dict or a dictionary of nova + service names <-> interface names + :type guard_map: Option[Callable, Dict[String, String]] + :param contexts: the map of file name -> {'services' -> [names]}, + {'contexts' -> context objects} + :type contexts: Option[Callable, `:class:templating.OSConfigRenderer`] + :param active: Whether this service guard is active or not, optionally + callable + :type active: Option[Callable, Boolean] + :returns: wrapped function + :rtype: Callable + """ def wrap(f): - def wrapped_f(*args): - if active is True: + _guard_map = None + _contexts = None + _active = None + + def wrapped_f(*args, **kwargs): + nonlocal _active, _contexts, _guard_map + if _active is None: + if callable(active): + _active = True if active() else False + else: + _active = True if active else False + if _active: + if _guard_map is None: + if callable(guard_map): + _guard_map = guard_map() + else: + _guard_map = guard_map + if _contexts is None: + if callable(contexts): + _contexts = contexts() + else: + _contexts = contexts incomplete_services = [] - for svc in guard_map: + for svc in _guard_map: for interface in guard_map[svc]: - if interface not in contexts.complete_contexts(): + if interface not in _contexts.complete_contexts(): incomplete_services.append(svc) - f(*args) + ret = f(*args, **kwargs) for svc in incomplete_services: - if service_running(svc): - log('Service {} has unfulfilled ' + if ch_host.service_running(svc): + hookenv.log( + 'Service {} has unfulfilled ' 'interface requirements, stopping.'.format(svc)) - service_stop(svc) + ch_host.service_stop(svc) + return ret else: - f(*args) + return f(*args, **kwargs) return wrapped_f return wrap def setup_ipv6(): - ubuntu_rel = lsb_release()['DISTRIB_CODENAME'].lower() - if CompareHostReleases(ubuntu_rel) < "trusty": + ubuntu_rel = ch_host.lsb_release()['DISTRIB_CODENAME'].lower() + if ch_host.CompareHostReleases(ubuntu_rel) < "trusty": raise Exception("IPv6 is not supported in the charms for Ubuntu " "versions less than Trusty 14.04") # Need haproxy >= 1.5.3 for ipv6 so for Trusty if we are <= Kilo we need to # use trusty-backports otherwise we can use the UCA. if (ubuntu_rel == 'trusty' and - CompareOpenStackReleases(os_release('nova-api')) < 'liberty'): - add_source('deb http://archive.ubuntu.com/ubuntu trusty-backports ' - 'main') - apt_update() - apt_install('haproxy/trusty-backports', fatal=True) + ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-api')) < 'liberty'): + ch_fetch.add_source( + 'deb http://archive.ubuntu.com/ubuntu trusty-backports main') + ch_fetch.apt_update() + ch_fetch.apt_install('haproxy/trusty-backports', fatal=True) def get_optional_interfaces(): @@ -1317,11 +1289,11 @@ def get_optional_interfaces(): :returns: {general_interface: [specific_int1, specific_int2, ...], ...} """ optional_interfaces = {} - if relation_ids('quantum-network-service'): + if hookenv.relation_ids('quantum-network-service'): optional_interfaces['quantum'] = ['quantum-network-service'] - if relation_ids('cinder-volume-service'): + if hookenv.relation_ids('cinder-volume-service'): optional_interfaces['cinder'] = ['cinder-volume-service'] - if relation_ids('neutron-api'): + if hookenv.relation_ids('neutron-api'): optional_interfaces['neutron-api'] = ['neutron-api'] return optional_interfaces @@ -1338,9 +1310,9 @@ def check_optional_relations(configs): :param configs: an OSConfigRender() instance. :return 2-tuple: (string, string) = (status, message) """ - if relation_ids('ha'): + if hookenv.relation_ids('ha'): try: - get_hacluster_config() + ch_cluster.get_hacluster_config() except: return ('blocked', 'hacluster missing configuration: ' @@ -1351,7 +1323,8 @@ def check_optional_relations(configs): def is_api_ready(configs): - return (not incomplete_relation_data(configs, REQUIRED_INTERFACES)) + return (not ch_utils.incomplete_relation_data(configs, + REQUIRED_INTERFACES)) def assess_status(configs): @@ -1368,7 +1341,7 @@ def assess_status(configs): # assessing status. configs.register('', [nova_cc_context.NovaCellV2Context()]) assess_status_func(configs)() - os_application_version_set(VERSION_PACKAGE) + ch_utils.os_application_version_set(VERSION_PACKAGE) def assess_status_func(configs): @@ -1391,7 +1364,7 @@ def assess_status_func(configs): """ required_interfaces = REQUIRED_INTERFACES.copy() required_interfaces.update(get_optional_interfaces()) - return make_assess_status_func( + return ch_utils.make_assess_status_func( configs, required_interfaces, charm_func=check_optional_relations, services=services(), ports=None) @@ -1404,7 +1377,7 @@ def pause_unit_helper(configs): @param configs: a templating.OSConfigRenderer() object @returns None - this function is executed for its side-effect """ - _pause_resume_helper(pause_unit, configs) + _pause_resume_helper(ch_utils.pause_unit, configs) def resume_unit_helper(configs): @@ -1414,7 +1387,7 @@ def resume_unit_helper(configs): @param configs: a templating.OSConfigRenderer() object @returns None - this function is executed for its side-effect """ - _pause_resume_helper(resume_unit, configs) + _pause_resume_helper(ch_utils.resume_unit, configs) def _pause_resume_helper(f, configs): @@ -1439,16 +1412,16 @@ def update_aws_compat_services(): `nova-objectstore` services. """ # if packages aren't installed, then there is nothing to do - if filter_installed_packages(AWS_COMPAT_SERVICES) != []: + if ch_fetch.filter_installed_packages(AWS_COMPAT_SERVICES) != []: return - if config('disable-aws-compat'): + if hookenv.config('disable-aws-compat'): # TODO: the endpoints have to removed from keystone for service_ in AWS_COMPAT_SERVICES: - service_pause(service_) + ch_host.service_pause(service_) else: for service_ in AWS_COMPAT_SERVICES: - service_resume(service_) + ch_host.service_resume(service_) def serial_console_settings(): @@ -1460,14 +1433,15 @@ def serial_console_settings(): def placement_api_enabled(): """Return true if nova-placement-api is enabled in this release""" - return CompareOpenStackReleases(os_release('nova-common')) >= 'ocata' + return ch_utils.CompareOpenStackReleases( + ch_utils.os_release('nova-common')) >= 'ocata' def enable_metadata_api(release=None): """Should nova-metadata-api be running on this unit for this release.""" if not release: - release = os_release('nova-common') - return CompareOpenStackReleases(os_release('nova-common')) >= 'rocky' + release = ch_utils.os_release('nova-common') + return ch_utils.CompareOpenStackReleases(release) >= 'rocky' def disable_package_apache_site(): @@ -1480,22 +1454,23 @@ def disable_package_apache_site(): def get_shared_metadatasecret(): """Return the shared metadata secret.""" - return leader_get(SHARED_METADATA_SECRET_KEY) + return hookenv.leader_get(SHARED_METADATA_SECRET_KEY) def set_shared_metadatasecret(): """Store the shared metadata secret.""" - leader_set({SHARED_METADATA_SECRET_KEY: uuid1()}) + hookenv.leader_set({SHARED_METADATA_SECRET_KEY: uuid.uuid1()}) def get_metadata_settings(configs): """Return the settings for accessing the metadata service.""" if enable_metadata_api(): - url = urlparse(canonical_url(configs, INTERNAL)) + url = urlparse( + ch_openstack_ip.canonical_url(configs, ch_openstack_ip.INTERNAL)) settings = { 'nova-metadata-host': url.netloc, 'nova-metadata-protocol': url.scheme, - 'nova-metadata-port': API_PORTS['nova-api-metadata'], + 'nova-metadata-port': common.api_port('nova-api-metadata'), 'shared-metadata-secret': get_shared_metadatasecret()} else: settings = {} @@ -1507,7 +1482,8 @@ def write_vendordata(vdata): try: json_vdata = json.loads(vdata) except (TypeError, json.decoder.JSONDecodeError) as e: - log('Error decoding vendor-data. {}'.format(e), level=ERROR) + hookenv.log('Error decoding vendor-data. {}'.format(e), + level=hookenv.ERROR) return False with open(VENDORDATA_FILE, 'w') as vdata_file: vdata_file.write(json.dumps(json_vdata, sort_keys=True, indent=2)) @@ -1515,12 +1491,12 @@ def write_vendordata(vdata): def get_cell_db_context(db_service): """Return the database context for the given service name""" - db_rid = relation_id( + db_rid = hookenv.relation_id( relation_name='shared-db-cell', service_or_unit=db_service) if not db_rid: return {} - return context.SharedDBContext( + return ch_context.SharedDBContext( relation_prefix='nova', ssl_dir=NOVA_CONF_DIR, relation_id=db_rid)() @@ -1528,12 +1504,12 @@ def get_cell_db_context(db_service): def get_cell_amqp_context(amqp_service): """Return the amqp context for the given service name""" - amq_rid = relation_id( + amq_rid = hookenv.relation_id( relation_name='amqp-cell', service_or_unit=amqp_service) if not amq_rid: return {} - return context.AMQPContext( + return ch_context.AMQPContext( ssl_dir=NOVA_CONF_DIR, relation_id=amq_rid)() @@ -1560,31 +1536,31 @@ def update_child_cell(name, db_service, amqp_service, skip_acl_check=True): 3) Complete relation with cells amqp service. """ if not is_db_initialised(): - log( + hookenv.log( 'Defering registering Cell {}, api db not ready.'.format(name), - level=DEBUG) + level=hookenv.DEBUG) return False existing_cells = get_cell_details() if not existing_cells.get('cell1'): - log('Defering registering cell {}, api cell setup is not complete.' - ''.format(name), - level=DEBUG) + hookenv.log( + 'Defering registering cell {}, api cell setup is not complete.' + .format(name), level=hookenv.DEBUG) return False db_ctxt = get_cell_db_context(db_service) if not db_ctxt: - log('Defering registering cell {}, cell db relation not ' - 'ready.'.format(name), - level=DEBUG) + hookenv.log( + 'Defering registering cell {}, cell db relation not ready.' + .format(name), level=hookenv.DEBUG) return False sql_connection = get_sql_uri(db_ctxt) amqp_ctxt = get_cell_amqp_context(amqp_service) if not amqp_ctxt: - log('Defering registering cell {}, cell amqp relation not ' - 'ready.'.format(name), - level=DEBUG) + hookenv.log( + 'Defering registering cell {}, cell amqp relation not ready.' + .format(name), level=hookenv.DEBUG) return False cmd = [ @@ -1593,21 +1569,23 @@ def update_child_cell(name, db_service, amqp_service, skip_acl_check=True): ] if existing_cells.get(name): - log('Cell {} already registered, checking if details are correct.' - ''.format(name), level=DEBUG) + hookenv.log( + 'Cell {} already registered, checking if details are correct.' + .format(name), level=hookenv.DEBUG) if (amqp_ctxt['transport_url'] == existing_cells[name]['amqp'] and sql_connection == existing_cells[name]['db']): - log('Cell details are correct no update needed', level=DEBUG) + hookenv.log('Cell details are correct no update needed', + level=hookenv.DEBUG) return False else: - log('Cell details have changed', level=DEBUG) + hookenv.log('Cell details have changed', level=hookenv.DEBUG) cmd.extend([ 'update_cell', '--cell_uuid', existing_cells[name]['uuid']]) else: - log( + hookenv.log( 'Cell {} is new and needs to be created.'.format(name), - level=DEBUG) + level=hookenv.DEBUG) cmd.extend(['create_cell', '--verbose']) cmd.extend([ @@ -1615,10 +1593,11 @@ def update_child_cell(name, db_service, amqp_service, skip_acl_check=True): '--transport-url', amqp_ctxt['transport_url'], '--database_connection', sql_connection]) try: - log('Updating cell {}'.format(name), level=DEBUG) + hookenv.log('Updating cell {}'.format(name), level=hookenv.DEBUG) subprocess.check_output(cmd) except subprocess.CalledProcessError as e: - log('Register cell failed\n{}'.format(e.output), level=ERROR) + hookenv.log('Register cell failed\n{}'.format(e.output), + level=hookenv.ERROR) raise - service_restart('nova-scheduler') + ch_host.service_restart('nova-scheduler') return True diff --git a/templates/icehouse/nova.conf b/templates/icehouse/nova.conf index c6b41f3b..ef8ab9e1 100644 --- a/templates/icehouse/nova.conf +++ b/templates/icehouse/nova.conf @@ -96,7 +96,7 @@ firewall_driver = nova.virt.firewall.NoopFirewallDriver {% endif -%} {% if network_manager_config -%} -{% for key, value in network_manager_config.iteritems() -%} +{% for key, value in network_manager_config.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -140,13 +140,13 @@ os_region_name = {{ region }} {% endif -%} {% if user_config_flags -%} -{% for key, value in user_config_flags.iteritems() -%} +{% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} {% if listen_ports -%} -{% for key, value in listen_ports.iteritems() -%} +{% for key, value in listen_ports.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/juno/nova.conf b/templates/juno/nova.conf index cae921e3..774181da 100644 --- a/templates/juno/nova.conf +++ b/templates/juno/nova.conf @@ -96,7 +96,7 @@ firewall_driver = nova.virt.firewall.NoopFirewallDriver {% endif -%} {% if network_manager_config -%} -{% for key, value in network_manager_config.iteritems() -%} +{% for key, value in network_manager_config.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -134,13 +134,13 @@ volume_api_class=nova.volume.cinder.API {% endif -%} {% if user_config_flags -%} -{% for key, value in user_config_flags.iteritems() -%} +{% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} {% if listen_ports -%} -{% for key, value in listen_ports.iteritems() -%} +{% for key, value in listen_ports.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/kilo/nova.conf b/templates/kilo/nova.conf index 195f6d64..2c60096e 100644 --- a/templates/kilo/nova.conf +++ b/templates/kilo/nova.conf @@ -99,7 +99,7 @@ firewall_driver = nova.virt.firewall.NoopFirewallDriver {% endif -%} {% if network_manager_config -%} -{% for key, value in network_manager_config.iteritems() -%} +{% for key, value in network_manager_config.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -119,13 +119,13 @@ volume_api_class=nova.volume.cinder.API {% endif -%} {% if user_config_flags -%} -{% for key, value in user_config_flags.iteritems() -%} +{% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} {% if listen_ports -%} -{% for key, value in listen_ports.iteritems() -%} +{% for key, value in listen_ports.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/liberty/nova.conf b/templates/liberty/nova.conf index 7c1efa87..d93bce35 100644 --- a/templates/liberty/nova.conf +++ b/templates/liberty/nova.conf @@ -99,7 +99,7 @@ firewall_driver = nova.virt.firewall.NoopFirewallDriver {% endif -%} {% if network_manager_config -%} -{% for key, value in network_manager_config.iteritems() -%} +{% for key, value in network_manager_config.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -119,13 +119,13 @@ volume_api_class=nova.volume.cinder.API {% endif -%} {% if user_config_flags -%} -{% for key, value in user_config_flags.iteritems() -%} +{% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} {% if listen_ports -%} -{% for key, value in listen_ports.iteritems() -%} +{% for key, value in listen_ports.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/mitaka/nova.conf b/templates/mitaka/nova.conf index a03ca65a..1fd5f9ac 100644 --- a/templates/mitaka/nova.conf +++ b/templates/mitaka/nova.conf @@ -88,7 +88,7 @@ firewall_driver = nova.virt.firewall.NoopFirewallDriver {% endif -%} {% if network_manager_config -%} -{% for key, value in network_manager_config.iteritems() -%} +{% for key, value in network_manager_config.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -109,7 +109,7 @@ volume_api_class=nova.volume.cinder.API {% endif -%} {% if user_config_flags -%} -{% for key, value in user_config_flags.iteritems() -%} +{% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -120,7 +120,7 @@ vendordata_jsonfile_path = /etc/nova/vendor_data.json {% endif -%} {% if listen_ports -%} -{% for key, value in listen_ports.iteritems() -%} +{% for key, value in listen_ports.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/ocata/nova.conf b/templates/ocata/nova.conf index 394cf373..97c757b2 100644 --- a/templates/ocata/nova.conf +++ b/templates/ocata/nova.conf @@ -78,7 +78,7 @@ firewall_driver = nova.virt.firewall.NoopFirewallDriver {% endif -%} {% if network_manager_config -%} -{% for key, value in network_manager_config.iteritems() -%} +{% for key, value in network_manager_config.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -98,13 +98,13 @@ volume_api_class=nova.volume.cinder.API {% endif -%} {% if user_config_flags -%} -{% for key, value in user_config_flags.iteritems() -%} +{% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} {% if listen_ports -%} -{% for key, value in listen_ports.iteritems() -%} +{% for key, value in listen_ports.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/parts/database-api b/templates/parts/database-api index 5cf6319c..bbd5cf57 100644 --- a/templates/parts/database-api +++ b/templates/parts/database-api @@ -6,7 +6,7 @@ connection = {{ nova_api_database_type }}://{{ nova_api_database_user }}:{{ nova max_pool_size = {{ workers }} {% endif -%} {% if nova_alchemy_flags -%} -{% for key, value in nova_alchemy_flags.iteritems() -%} +{% for key, value in nova_alchemy_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/parts/database-v2 b/templates/parts/database-v2 index 05e59368..87468670 100644 --- a/templates/parts/database-v2 +++ b/templates/parts/database-v2 @@ -6,7 +6,7 @@ connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{ max_pool_size = {{ workers }} {% endif -%} {% if nova_alchemy_flags -%} -{% for key, value in nova_alchemy_flags.iteritems() -%} +{% for key, value in nova_alchemy_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/parts/section-database b/templates/parts/section-database index a5f94f28..b06eb9f0 100644 --- a/templates/parts/section-database +++ b/templates/parts/section-database @@ -2,7 +2,7 @@ [database] connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} {% if neutron_alchemy_flags -%} -{% for key, value in neutron_alchemy_flags.iteritems() -%} +{% for key, value in neutron_alchemy_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/templates/pike/nova.conf b/templates/pike/nova.conf index a1bd7026..99f527ab 100644 --- a/templates/pike/nova.conf +++ b/templates/pike/nova.conf @@ -78,7 +78,7 @@ firewall_driver = nova.virt.firewall.NoopFirewallDriver {% endif -%} {% if network_manager_config -%} -{% for key, value in network_manager_config.iteritems() -%} +{% for key, value in network_manager_config.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} @@ -98,13 +98,13 @@ volume_api_class=nova.volume.cinder.API {% endif -%} {% if user_config_flags -%} -{% for key, value in user_config_flags.iteritems() -%} +{% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} {% if listen_ports -%} -{% for key, value in listen_ports.iteritems() -%} +{% for key, value in listen_ports.items() -%} {{ key }} = {{ value }} {% endfor -%} {% endif -%} diff --git a/tox.ini b/tox.ini index b6e41e7e..3fa366a8 100644 --- a/tox.ini +++ b/tox.ini @@ -17,9 +17,7 @@ whitelist_externals = juju passenv = HOME TERM AMULET_* CS_API_* [testenv:py27] -basepython = python2.7 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt +commands = /bin/true [testenv:py35] basepython = python3.5 @@ -32,7 +30,7 @@ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt [testenv:pep8] -basepython = python2.7 +basepython = python3 deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = flake8 {posargs} hooks unit_tests tests actions lib diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py index ad1102f1..0a8932d9 100644 --- a/unit_tests/__init__.py +++ b/unit_tests/__init__.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import sys import mock @@ -21,6 +22,12 @@ mock_apt = mock.MagicMock() sys.modules['apt'] = mock_apt mock_apt.apt_pkg = mock.MagicMock() +_path = os.path.dirname(os.path.realpath(__file__)) +_root = os.path.abspath(os.path.join(_path, '..')) -sys.path.append('actions/') -sys.path.append('hooks/') + +def _add_path(path): + if path not in sys.path: + sys.path.insert(1, path) + +_add_path(_root) diff --git a/unit_tests/test_actions.py b/unit_tests/test_actions.py index fb857f11..d76b56f5 100644 --- a/unit_tests/test_actions.py +++ b/unit_tests/test_actions.py @@ -14,7 +14,7 @@ import mock -from test_utils import ( +from unit_tests.test_utils import ( CharmTestCase, get_default_config, ) @@ -24,29 +24,9 @@ __default_config = get_default_config() # depending on where it's being executed __default_config['openstack-origin'] = '' -with mock.patch('charmhelpers.core.hookenv.config') as config: - with mock.patch('charmhelpers.contrib.openstack.utils.get_os_codename_package'): # noqa - # this makes the config behave more similar to the real config() - config.side_effect = lambda k: __default_config.get(k) +import hooks.nova_cc_utils as utils # noqa +import actions.actions as actions - import nova_cc_utils as utils # noqa - -# Need to do some early patching to get the module loaded. -_reg = utils.register_configs -_map = utils.restart_map - -utils.register_configs = mock.MagicMock() -utils.restart_map = mock.MagicMock() - -with mock.patch('nova_cc_utils.guard_map') as gmap: - with mock.patch('charmhelpers.core.hookenv.config') as config: - config.return_value = False - gmap.return_value = {} - import actions - -# Unpatch it now that its loaded. -utils.register_configs = _reg -utils.restart_map = _map TO_PATCH = [ ] @@ -56,7 +36,11 @@ class PauseTestCase(CharmTestCase): def setUp(self): super(PauseTestCase, self).setUp( - actions, ["register_configs", "pause_unit_helper"]) + actions, + [ + "hooks.nova_cc_utils.register_configs", + "hooks.nova_cc_utils.pause_unit_helper" + ]) self.register_configs.return_value = 'test-config' def test_pauses_services(self): @@ -68,7 +52,10 @@ class ResumeTestCase(CharmTestCase): def setUp(self): super(ResumeTestCase, self).setUp( - actions, ["register_configs", "resume_unit_helper"]) + actions, [ + "hooks.nova_cc_utils.register_configs", + "hooks.nova_cc_utils.resume_unit_helper" + ]) self.register_configs.return_value = 'test-config' def test_resumes_services(self): @@ -79,8 +66,12 @@ class ResumeTestCase(CharmTestCase): class MainTestCase(CharmTestCase): def setUp(self): - super(MainTestCase, self).setUp(actions, ["register_configs", - "action_fail"]) + super(MainTestCase, self).setUp( + actions, + [ + "charmhelpers.core.hookenv.action_fail", + "hooks.nova_cc_utils.register_configs", + ]) self.register_configs.return_value = 'test-config' def test_invokes_action(self): diff --git a/unit_tests/test_actions_openstack_upgrade.py b/unit_tests/test_actions_openstack_upgrade.py index 0830f794..d2520322 100644 --- a/unit_tests/test_actions_openstack_upgrade.py +++ b/unit_tests/test_actions_openstack_upgrade.py @@ -12,30 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os - from mock import patch +from unit_tests.test_utils import CharmTestCase -os.environ['JUJU_UNIT_NAME'] = 'nova-cloud-controller' -with patch('charmhelpers.core.hookenv.config') as config: - with patch('charmhelpers.contrib.openstack.utils.get_os_codename_package'): - config.return_value = 'nova' - import nova_cc_utils as utils # noqa +import actions.openstack_upgrade as openstack_upgrade -with patch('charmhelpers.core.hookenv.config') as config: - with patch('nova_cc_utils.restart_map'): - config.return_value = 'ovs' - with patch('nova_cc_utils.register_configs') as register_configs: - import openstack_upgrade - -from test_utils import CharmTestCase TO_PATCH = [ - 'do_openstack_upgrade', - 'relation_ids', - 'neutron_api_relation_joined', - 'db_joined', - 'config_changed', + 'charmhelpers.core.hookenv.relation_ids', + 'hooks.nova_cc_hooks.config_changed', + 'hooks.nova_cc_hooks.db_joined', + 'hooks.nova_cc_hooks.neutron_api_relation_joined', + 'hooks.nova_cc_utils.do_openstack_upgrade', ] diff --git a/unit_tests/test_nova_cc_contexts.py b/unit_tests/test_nova_cc_contexts.py index 2f4965d6..b2be4c16 100644 --- a/unit_tests/test_nova_cc_contexts.py +++ b/unit_tests/test_nova_cc_contexts.py @@ -12,30 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import print_function - import json import mock -import nova_cc_context as context -with mock.patch('charmhelpers.core.hookenv.config'): - with mock.patch('charmhelpers.contrib.openstack.utils.get_os_codename_package'): # noqa - import nova_cc_utils as _utils # noqa +import hooks.nova_cc_context as context from charmhelpers.contrib.openstack import neutron from charmhelpers.contrib.openstack import utils -from test_utils import CharmTestCase +from unit_tests.test_utils import CharmTestCase TO_PATCH = [ - 'config', - 'https', - 'leader_get', - 'log', - 'os_release', - 'related_units', - 'relation_get', - 'relation_ids', - 'relations_for_id', + 'charmhelpers.contrib.hahelpers.cluster.https', + 'charmhelpers.contrib.openstack.utils.os_release', + 'charmhelpers.core.hookenv.config', + 'charmhelpers.core.hookenv.leader_get', + 'charmhelpers.core.hookenv.log', + 'charmhelpers.core.hookenv.related_units', + 'charmhelpers.core.hookenv.relation_get', + 'charmhelpers.core.hookenv.relation_ids', + 'charmhelpers.core.hookenv.relations_for_id', ] @@ -51,8 +46,8 @@ class NovaComputeContextTests(CharmTestCase): self.config.side_effect = self.test_config.get self.log.side_effect = fake_log - @mock.patch.object(context, 'resolve_address', - lambda *args, **kwargs: None) + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address', + lambda *args, **kwargs: None) @mock.patch.object(utils, 'os_release') @mock.patch('charmhelpers.contrib.network.ip.log') def test_instance_console_context_without_memcache(self, os_release, log_): @@ -63,8 +58,8 @@ class NovaComputeContextTests(CharmTestCase): self.assertEqual({'memcached_servers': ''}, instance_console()) - @mock.patch.object(context, 'resolve_address', - lambda *args, **kwargs: None) + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address', + lambda *args, **kwargs: None) @mock.patch.object(utils, 'os_release') @mock.patch('charmhelpers.contrib.network.ip.log') def test_instance_console_context_with_memcache(self, os_release, log_): @@ -72,8 +67,8 @@ class NovaComputeContextTests(CharmTestCase): '127.0.1.1', '127.0.1.1') - @mock.patch.object(context, 'resolve_address', - lambda *args, **kwargs: None) + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address', + lambda *args, **kwargs: None) @mock.patch.object(utils, 'os_release') @mock.patch('charmhelpers.contrib.network.ip.log') def test_instance_console_context_with_memcache_ipv6(self, os_release, @@ -94,40 +89,49 @@ class NovaComputeContextTests(CharmTestCase): self.assertEqual({'memcached_servers': "%s:11211" % (formated_ip, )}, instance_console()) - @mock.patch('charmhelpers.contrib.openstack.neutron.os_release') @mock.patch('charmhelpers.contrib.openstack.ip.config') + @mock.patch('charmhelpers.contrib.openstack.neutron.config') + @mock.patch('charmhelpers.contrib.openstack.context.config') + @mock.patch('charmhelpers.contrib.openstack.neutron.os_release') @mock.patch('charmhelpers.contrib.openstack.ip.is_clustered') - def test_neutron_context_single_vip(self, mock_is_clustered, mock_config, - _os_release): + def test_neutron_context_single_vip( + self, mock_is_clustered, _os_release, mock_config, + mock_config_neutron, mock_config_ip): self.https.return_value = False mock_is_clustered.return_value = True - config = {'vip': '10.0.0.1', - 'os-internal-network': '10.0.0.1/24', - 'os-admin-network': '10.0.1.0/24', - 'os-public-network': '10.0.2.0/24'} - mock_config.side_effect = lambda key: config.get(key) + _config = {'vip': '10.0.0.1', + 'os-internal-network': '10.0.0.1/24', + 'os-admin-network': '10.0.1.0/24', + 'os-public-network': '10.0.2.0/24', + 'network-manager': 'FlatDHCPManager'} + mock_config.side_effect = lambda key: _config.get(key) + mock_config_neutron.side_effect = lambda key: _config.get(key) + mock_config_ip.side_effect = lambda key: _config.get(key) ctxt = context.NeutronCCContext()() self.assertEqual(ctxt['nova_url'], 'http://10.0.0.1:8774/v2') self.assertFalse('neutron_url' in ctxt) - @mock.patch('charmhelpers.contrib.openstack.neutron.os_release') @mock.patch('charmhelpers.contrib.openstack.ip.config') + @mock.patch('charmhelpers.contrib.openstack.neutron.config') + @mock.patch('charmhelpers.contrib.openstack.neutron.os_release') @mock.patch('charmhelpers.contrib.openstack.ip.is_clustered') - def test_neutron_context_multi_vip(self, mock_is_clustered, mock_config, - _os_release): + def test_neutron_context_multi_vip( + self, mock_is_clustered, _os_release, mock_config, mock_config_ip): self.https.return_value = False mock_is_clustered.return_value = True - config = {'vip': '10.0.0.1 10.0.1.1 10.0.2.1', - 'os-internal-network': '10.0.1.0/24', - 'os-admin-network': '10.0.0.0/24', - 'os-public-network': '10.0.2.0/24'} - mock_config.side_effect = lambda key: config.get(key) - + _config = {'vip': '10.0.0.1 10.0.1.1 10.0.2.1', + 'os-internal-network': '10.0.1.0/24', + 'os-admin-network': '10.0.0.0/24', + 'os-public-network': '10.0.2.0/24', + 'network-manager': 'FlatDHCPManager'} + mock_config.side_effect = lambda key: _config.get(key) + mock_config_ip.side_effect = lambda key: _config.get(key) ctxt = context.NeutronCCContext()() self.assertEqual(ctxt['nova_url'], 'http://10.0.1.1:8774/v2') self.assertFalse('neutron_url' in ctxt) + @mock.patch('charmhelpers.contrib.openstack.context.config') @mock.patch('charmhelpers.contrib.openstack.context.get_relation_ip') @mock.patch('charmhelpers.contrib.openstack.context.mkdir') @mock.patch.object(neutron, 'network_manager') @@ -145,7 +149,8 @@ class NovaComputeContextTests(CharmTestCase): mock_local_unit, mock_get_netmask_for_address, mock_get_address_in_network, mock_kv, mock_https, mock_unit_get, mock_network_manager, mock_mkdir, - mock_get_relation_ip): + mock_get_relation_ip, mock_config): + mock_config.side_effect = self.test_config.get mock_https.return_value = False mock_unit_get.return_value = '127.0.0.1' mock_network_manager.return_value = 'neutron' @@ -153,7 +158,7 @@ class NovaComputeContextTests(CharmTestCase): self.assertEqual(ctxt['service_ports']['nova-api-os-compute'], [8774, 8764]) - @mock.patch.object(context, 'config') + @mock.patch('charmhelpers.contrib.openstack.context.config') def test_console_ssl_disabled(self, mock_config): config = {'console-ssl-cert': 'LS0tLS1CRUdJTiBDRV', 'console-ssl-key': 'LS0tLS1CRUdJTiBQUk'} @@ -177,21 +182,20 @@ class NovaComputeContextTests(CharmTestCase): ctxt = context.ConsoleSSLContext()() self.assertEqual(ctxt, {}) - @mock.patch('__builtin__.open') + @mock.patch('builtins.open') @mock.patch('os.path.exists') - @mock.patch.object(context, 'config') - @mock.patch.object(context, 'unit_get') - @mock.patch.object(context, 'is_clustered') - @mock.patch.object(context, 'resolve_address') - @mock.patch.object(context, 'b64decode') + @mock.patch('charmhelpers.core.hookenv.unit_get') + @mock.patch('charmhelpers.contrib.hahelpers.cluster.is_clustered') + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') + @mock.patch('base64.b64decode') def test_noVNC_ssl_enabled(self, mock_b64decode, mock_resolve_address, mock_is_clustered, mock_unit_get, - mock_config, mock_exists, mock_open): + mock_exists, mock_open): config = {'console-ssl-cert': 'LS0tLS1CRUdJTiBDRV', 'console-ssl-key': 'LS0tLS1CRUdJTiBQUk', 'console-access-protocol': 'novnc'} - mock_config.side_effect = lambda key: config.get(key) + self.test_config.update(config) mock_exists.return_value = True mock_unit_get.return_value = '127.0.0.1' mock_is_clustered.return_value = True @@ -208,21 +212,20 @@ class NovaComputeContextTests(CharmTestCase): self.assertEqual(ctxt['novncproxy_base_url'], 'https://10.5.100.1:6080/vnc_auto.html') - @mock.patch('__builtin__.open') + @mock.patch('builtins.open') @mock.patch('os.path.exists') - @mock.patch.object(context, 'config') - @mock.patch.object(context, 'unit_get') - @mock.patch.object(context, 'is_clustered') - @mock.patch.object(context, 'resolve_address') - @mock.patch.object(context, 'b64decode') + @mock.patch('charmhelpers.core.hookenv.unit_get') + @mock.patch('charmhelpers.contrib.hahelpers.cluster.is_clustered') + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') + @mock.patch('base64.b64decode') def test_noVNC_ssl_enabled_no_cluster(self, mock_b64decode, mock_resolve_address, mock_is_clustered, mock_unit_get, - mock_config, mock_exists, mock_open): + mock_exists, mock_open): config = {'console-ssl-cert': 'LS0tLS1CRUdJTiBDRV', 'console-ssl-key': 'LS0tLS1CRUdJTiBQUk', 'console-access-protocol': 'novnc'} - mock_config.side_effect = lambda key: config.get(key) + self.test_config.update(config) mock_exists.return_value = True mock_unit_get.return_value = '10.5.0.1' mock_is_clustered.return_value = False @@ -238,21 +241,20 @@ class NovaComputeContextTests(CharmTestCase): self.assertEqual(ctxt['novncproxy_base_url'], 'https://10.5.0.1:6080/vnc_auto.html') - @mock.patch('__builtin__.open') + @mock.patch('builtins.open') @mock.patch('os.path.exists') - @mock.patch.object(context, 'config') - @mock.patch.object(context, 'unit_get') - @mock.patch.object(context, 'is_clustered') - @mock.patch.object(context, 'resolve_address') - @mock.patch.object(context, 'b64decode') + @mock.patch('charmhelpers.core.hookenv.unit_get') + @mock.patch('charmhelpers.contrib.hahelpers.cluster.is_clustered') + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') + @mock.patch('base64.b64decode') def test_spice_html5_ssl_enabled(self, mock_b64decode, mock_resolve_address, mock_is_clustered, mock_unit_get, - mock_config, mock_exists, mock_open): + mock_exists, mock_open): config = {'console-ssl-cert': 'LS0tLS1CRUdJTiBDRV', 'console-ssl-key': 'LS0tLS1CRUdJTiBQUk', 'console-access-protocol': 'spice'} - mock_config.side_effect = lambda key: config.get(key) + self.test_config.update(config) mock_exists.return_value = True mock_unit_get.return_value = '127.0.0.1' mock_is_clustered.return_value = True @@ -269,23 +271,22 @@ class NovaComputeContextTests(CharmTestCase): self.assertEqual(ctxt['html5proxy_base_url'], 'https://10.5.100.1:6082/spice_auto.html') - @mock.patch('__builtin__.open') + @mock.patch('builtins.open') @mock.patch('os.path.exists') - @mock.patch.object(context, 'config') - @mock.patch.object(context, 'unit_get') - @mock.patch.object(context, 'is_clustered') - @mock.patch.object(context, 'resolve_address') - @mock.patch.object(context, 'b64decode') + @mock.patch('charmhelpers.core.hookenv.unit_get') + @mock.patch('charmhelpers.contrib.hahelpers.cluster.is_clustered') + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') + @mock.patch('base64.b64decode') def test_spice_html5_ssl_enabled_no_cluster(self, mock_b64decode, mock_resolve_address, mock_is_clustered, mock_unit_get, - mock_config, mock_exists, + mock_exists, mock_open): config = {'console-ssl-cert': 'LS0tLS1CRUdJTiBDRV', 'console-ssl-key': 'LS0tLS1CRUdJTiBQUk', 'console-access-protocol': 'spice'} - mock_config.side_effect = lambda key: config.get(key) + self.test_config.update(config) mock_exists.return_value = True mock_unit_get.return_value = '10.5.0.1' mock_is_clustered.return_value = False @@ -301,14 +302,17 @@ class NovaComputeContextTests(CharmTestCase): self.assertEqual(ctxt['html5proxy_base_url'], 'https://10.5.0.1:6082/spice_auto.html') + @mock.patch('charmhelpers.contrib.openstack.ip.config') @mock.patch('charmhelpers.contrib.openstack.ip.unit_get') @mock.patch('charmhelpers.contrib.hahelpers.cluster.relation_ids') @mock.patch('charmhelpers.core.hookenv.local_unit') @mock.patch('charmhelpers.contrib.openstack.context.config') def test_nova_config_context(self, mock_config, local_unit, - mock_relation_ids, mock_unit_get): + mock_relation_ids, mock_unit_get, + mock_config_ip): local_unit.return_value = 'nova-cloud-controller/0' mock_config.side_effect = self.test_config.get + mock_config_ip.side_effect = self.test_config.get mock_unit_get.return_value = '127.0.0.1' ctxt = context.NovaConfigContext()() self.assertEqual(ctxt['scheduler_default_filters'], @@ -336,6 +340,7 @@ class NovaComputeContextTests(CharmTestCase): _pci_alias_list = [_pci_alias1, _pci_alias2] + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') @mock.patch('charmhelpers.contrib.openstack.ip.unit_get') @mock.patch('charmhelpers.contrib.hahelpers.cluster.relation_ids') @mock.patch('charmhelpers.core.hookenv.local_unit') @@ -343,7 +348,8 @@ class NovaComputeContextTests(CharmTestCase): def test_nova_config_context_multi_pci_alias(self, mock_config, local_unit, mock_relation_ids, - mock_unit_get): + mock_unit_get, + mock_resolve_address): local_unit.return_value = 'nova-cloud-controller/0' mock_config.side_effect = self.test_config.get mock_unit_get.return_value = '127.0.0.1' @@ -356,14 +362,17 @@ class NovaComputeContextTests(CharmTestCase): '"name": "IntelNIC", "product_id": "1111", ' '"vendor_id": "8086"}')) + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') @mock.patch('charmhelpers.contrib.openstack.ip.unit_get') @mock.patch('charmhelpers.contrib.hahelpers.cluster.relation_ids') @mock.patch('charmhelpers.core.hookenv.local_unit') @mock.patch('charmhelpers.contrib.openstack.context.config') - def test_nova_config_context_multi_pci_aliases(self, mock_config, + def test_nova_config_context_multi_pci_aliases(self, + mock_config, local_unit, mock_relation_ids, - mock_unit_get): + mock_unit_get, + mock_resolve_address): local_unit.return_value = 'nova-cloud-controller/0' mock_config.side_effect = self.test_config.get mock_unit_get.return_value = '127.0.0.1' @@ -381,13 +390,11 @@ class NovaComputeContextTests(CharmTestCase): '"name": " Cirrus Logic ", "product_id": "0ff2", ' '"vendor_id": "10de"}')) - @mock.patch.object(context, 'format_ipv6_addr') - @mock.patch.object(context, 'resolve_address') - @mock.patch.object(context, 'config') - def test_serial_console_context(self, mock_config, + @mock.patch('charmhelpers.contrib.network.ip.format_ipv6_addr') + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') + def test_serial_console_context(self, mock_resolve_address, mock_format_ipv6_address): - mock_config.side_effect = self.test_config.get mock_format_ipv6_address.return_value = None mock_resolve_address.return_value = '10.10.10.1' ctxt = context.SerialConsoleContext()() @@ -396,15 +403,14 @@ class NovaComputeContextTests(CharmTestCase): {'serial_console_base_url': 'ws://10.10.10.1:6083/', 'enable_serial_console': 'false'} ) - mock_resolve_address.assert_called_with(endpoint_type=context.PUBLIC) + mock_resolve_address.assert_called_with( + endpoint_type=context.ch_ip.PUBLIC) - @mock.patch.object(context, 'format_ipv6_addr') - @mock.patch.object(context, 'resolve_address') - @mock.patch.object(context, 'config') - def test_serial_console_context_enabled(self, mock_config, + @mock.patch('charmhelpers.contrib.network.ip.format_ipv6_addr') + @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') + def test_serial_console_context_enabled(self, mock_resolve_address, mock_format_ipv6_address): - mock_config.side_effect = self.test_config.get self.test_config.set('enable-serial-console', True) mock_format_ipv6_address.return_value = None mock_resolve_address.return_value = '10.10.10.1' @@ -414,7 +420,8 @@ class NovaComputeContextTests(CharmTestCase): {'serial_console_base_url': 'ws://10.10.10.1:6083/', 'enable_serial_console': 'true'} ) - mock_resolve_address.assert_called_with(endpoint_type=context.PUBLIC) + mock_resolve_address.assert_called_with( + endpoint_type=context.ch_ip.PUBLIC) def test_nova_cellv2_shared_db_context(self): self.relation_ids.return_value = ['shared-db:0'] diff --git a/unit_tests/test_nova_cc_hooks.py b/unit_tests/test_nova_cc_hooks.py index 9b7e5272..70eb77a8 100644 --- a/unit_tests/test_nova_cc_hooks.py +++ b/unit_tests/test_nova_cc_hooks.py @@ -12,83 +12,64 @@ # See the License for the specific language governing permissions and # limitations under the License. +from mock import MagicMock, patch, call import os import tempfile -from mock import MagicMock, patch, call -from test_utils import CharmTestCase +from unit_tests.test_utils import CharmTestCase -with patch('charmhelpers.core.hookenv.config') as config: - with patch('charmhelpers.contrib.openstack.utils.get_os_codename_package'): - config.return_value = 'neutron' - import nova_cc_utils as utils +import charmhelpers.contrib.hardening.harden as harden -_reg = utils.register_configs -_map = utils.restart_map +import hooks.nova_cc_utils as utils +import hooks.nova_cc_hooks as hooks -utils.register_configs = MagicMock() -utils.restart_map = MagicMock() - -with patch('charmhelpers.contrib.hardening.harden.harden') as mock_dec: - mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f: - lambda *args, **kwargs: f(*args, **kwargs)) - with patch('nova_cc_utils.guard_map') as gmap: - with patch('charmhelpers.core.hookenv.config') as config: - config.return_value = False - gmap.return_value = {} - import nova_cc_hooks as hooks - -utils.register_configs = _reg -utils.restart_map = _map TO_PATCH = [ - 'apt_update', - 'apt_install', - 'configure_installation_source', - 'charm_dir', - 'do_openstack_upgrade', - 'openstack_upgrade_available', - 'config', - 'config_value_changed', - 'determine_endpoints', - 'determine_packages', - 'determine_ports', - 'open_port', - 'is_relation_made', - 'is_unit_paused_set', - 'local_unit', - 'log', - 'os_release', - 'related_units', - 'relation_get', - 'relation_set', - 'relation_ids', - 'placement_api_enabled', - 'ssh_compute_add', - 'ssh_known_hosts_lines', - 'ssh_authorized_keys_lines', - 'save_script_rc', - 'service_pause', - 'service_reload', - 'service_resume', - 'services', - 'execd_preinstall', - 'network_manager', - 'unit_get', + 'charmhelpers.contrib.hahelpers.cluster.get_hacluster_config', + 'charmhelpers.contrib.hahelpers.cluster.is_clustered', + 'charmhelpers.contrib.network.ip.get_iface_for_address', + 'charmhelpers.contrib.network.ip.get_netmask_for_address', + 'charmhelpers.contrib.network.ip.get_relation_ip', + 'charmhelpers.contrib.openstack.ha.utils.update_dns_ha_resource_params', + 'charmhelpers.contrib.openstack.neutron.network_manager', + 'charmhelpers.contrib.openstack.utils.configure_installation_source', + 'charmhelpers.contrib.openstack.utils.config_value_changed', + 'charmhelpers.contrib.openstack.utils.is_unit_paused_set', + 'charmhelpers.contrib.openstack.utils.openstack_upgrade_available', + 'charmhelpers.contrib.openstack.utils.os_release', + 'charmhelpers.core.hookenv.charm_dir', + 'charmhelpers.core.hookenv.config', + 'charmhelpers.core.hookenv.is_leader', + 'charmhelpers.core.hookenv.is_relation_made', + 'charmhelpers.core.hookenv.local_unit', + 'charmhelpers.core.hookenv.log', + 'charmhelpers.core.hookenv.open_port', + 'charmhelpers.core.hookenv.related_units', + 'charmhelpers.core.hookenv.relation_get', + 'charmhelpers.core.hookenv.relation_ids', + 'charmhelpers.core.hookenv.relation_set', + 'charmhelpers.core.hookenv.status_set', + 'charmhelpers.core.hookenv.unit_get', + 'charmhelpers.core.host.service_pause', + 'charmhelpers.core.host.service_reload', + 'charmhelpers.core.host.service_resume', + 'charmhelpers.fetch.apt_install', + 'charmhelpers.fetch.apt_update', + 'charmhelpers.payload.execd.execd_preinstall', + 'hooks.nova_cc_utils.determine_endpoints', + 'hooks.nova_cc_utils.determine_packages', + 'hooks.nova_cc_utils.determine_ports', + 'hooks.nova_cc_utils.do_openstack_upgrade', + 'hooks.nova_cc_utils.keystone_ca_cert_b64', + 'hooks.nova_cc_utils.migrate_nova_databases', + 'hooks.nova_cc_utils.placement_api_enabled', + 'hooks.nova_cc_utils.save_script_rc', + 'hooks.nova_cc_utils.serial_console_settings', + 'hooks.nova_cc_utils.services', + 'hooks.nova_cc_utils.ssh_authorized_keys_lines', + 'hooks.nova_cc_utils.ssh_compute_add', + 'hooks.nova_cc_utils.ssh_known_hosts_lines', 'uuid', - 'is_leader', - 'keystone_ca_cert_b64', - 'migrate_nova_databases', - 'uuid', - 'get_hacluster_config', - 'get_iface_for_address', - 'get_netmask_for_address', - 'update_nrpe_config', - 'status_set', - 'update_dns_ha_resource_params', - 'serial_console_settings', - 'get_relation_ip', - 'is_clustered', ] @@ -112,10 +93,13 @@ class NovaCCHooksTests(CharmTestCase): super(NovaCCHooksTests, self).setUp(hooks, TO_PATCH) (tmpfd, hooks.NOVA_CONSOLEAUTH_OVERRIDE) = tempfile.mkstemp() + hooks.CONFIGS = None # reset for each test self.config.side_effect = self.test_config.get self.relation_get.side_effect = self.test_relation.get self.charm_dir.return_value = '/var/lib/juju/charms/nova/charm' self.is_unit_paused_set.return_value = False + # disable hardening for unit tests + harden._DISABLE_HARDENING_FOR_UNIT_TEST = True def tearDown(self): try: @@ -137,178 +121,192 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(self.execd_preinstall.called) self.assertTrue(self.service_pause.called) - @patch.object(hooks, 'set_shared_metadatasecret') - @patch.object(hooks, 'get_shared_metadatasecret') - @patch.object(hooks, 'is_leader') - @patch.object(hooks, 'update_aws_compat_services') + @patch.object(utils, 'set_shared_metadatasecret') + @patch.object(utils, 'get_shared_metadatasecret') + @patch.object(hooks, 'update_nrpe_config') + @patch.object(utils, 'resource_map') + @patch('hooks.nova_cc_utils.update_aws_compat_services') @patch.object(hooks, 'update_nova_consoleauth_config') - @patch.object(hooks, 'is_db_initialised') - @patch.object(hooks, 'determine_packages') - @patch.object(utils, 'service_resume') - @patch.object(utils, 'config') - @patch.object(hooks, 'filter_installed_packages') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('charmhelpers.fetch.filter_installed_packages') @patch.object(hooks, 'configure_https') def test_config_changed_no_upgrade(self, conf_https, mock_filter_packages, - utils_config, mock_service_resume, - mock_determine_packages, mock_is_db_initialised, mock_update_nova_consoleauth_config, mock_update_aws_compat_services, - mock_is_leader, + mock_resource_map, + mock_update_nrpe_config, mock_get_shared_metadatasecret, mock_set_shared_metadatasecret): + mock_resource_map.return_value = {} self.get_shared_metadatasecret = None - mock_determine_packages.return_value = [] - utils_config.side_effect = self.test_config.get + self.determine_packages.return_value = [] + self.is_leader.return_value = True self.test_config.set('console-access-protocol', 'dummy') self.openstack_upgrade_available.return_value = False mock_is_db_initialised.return_value = False self.os_release.return_value = 'diablo' + hooks.resolve_CONFIGS() hooks.config_changed() self.assertTrue(self.save_script_rc.called) mock_filter_packages.assert_called_with([]) self.assertTrue(mock_update_nova_consoleauth_config.called) self.assertTrue(mock_update_aws_compat_services.called) - @patch.object(hooks, 'set_shared_metadatasecret') - @patch.object(hooks, 'get_shared_metadatasecret') - @patch.object(hooks, 'update_aws_compat_services') + @patch.object(utils, 'set_shared_metadatasecret') + @patch.object(utils, 'get_shared_metadatasecret') + @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') + @patch.object(hooks, 'update_nrpe_config') + @patch('hooks.nova_cc_utils.update_aws_compat_services') @patch.object(hooks, 'update_nova_consoleauth_config') - @patch.object(hooks, 'is_db_initialised') - @patch.object(hooks, 'determine_packages') - @patch.object(utils, 'service_resume') - @patch.object(utils, 'config') - @patch.object(hooks, 'filter_installed_packages') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('charmhelpers.fetch.filter_installed_packages') @patch.object(hooks, 'configure_https') - def test_config_changed_ocata(self, conf_https, mock_filter_packages, - utils_config, mock_service_resume, - mock_determine_packages, + def test_config_changed_ocata(self, + conf_https, + mock_filter_packages, mock_is_db_initialised, mock_update_nova_consoleauth_config, mock_update_aws_compat_services, + mock_update_nrpe_config, + mock_sub_ctxt, mock_get_shared_metadatasecret, mock_set_shared_metadatasecret): mock_get_shared_metadatasecret.return_value = None self.is_leader.return_value = True - mock_determine_packages.return_value = [] - utils_config.side_effect = self.test_config.get + self.determine_packages.return_value = [] self.test_config.set('console-access-protocol', 'dummy') self.openstack_upgrade_available.return_value = False mock_is_db_initialised.return_value = False self.os_release.return_value = 'diablo' - hooks.config_changed() + hooks.resolve_CONFIGS() + # probably need the with patch.object from below + with patch.object(hooks.CONFIGS, 'write_all') as wa: + hooks.config_changed() + self.assertTrue(wa.called) self.assertTrue(self.save_script_rc.called) mock_filter_packages.assert_called_with([]) self.assertTrue(mock_update_nova_consoleauth_config.called) self.assertTrue(mock_update_aws_compat_services.called) mock_set_shared_metadatasecret.assert_called_once_with() - @patch.object(hooks, 'set_shared_metadatasecret') - @patch.object(hooks, 'get_shared_metadatasecret') - @patch.object(hooks, 'update_aws_compat_services') + @patch.object(utils, 'set_shared_metadatasecret') + @patch.object(utils, 'get_shared_metadatasecret') + @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') + @patch.object(hooks, 'update_nrpe_config') + @patch('hooks.nova_cc_utils.update_aws_compat_services') @patch.object(hooks, 'update_nova_consoleauth_config') - @patch.object(hooks, 'is_db_initialised') - @patch.object(hooks, 'determine_packages') - @patch.object(utils, 'service_resume') - @patch.object(utils, 'config') - @patch.object(hooks, 'filter_installed_packages') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('charmhelpers.fetch.filter_installed_packages') @patch.object(hooks, 'configure_https') def test_config_changed_no_upgrade_juno(self, conf_https, mock_filter_packages, - utils_config, mock_service_resume, - mock_determine_packages, mock_is_db_initialised, mock_update_nova_consoleauth_cfg, mock_update_aws_compat_services, + mock_update_nrpe_config, + mock_sub_ctxt, mock_get_shared_metadatasecret, mock_set_shared_metadatasecret): - mock_determine_packages.return_value = [] - utils_config.side_effect = self.test_config.get + self.determine_packages.return_value = [] self.test_config.set('console-access-protocol', 'dummy') self.openstack_upgrade_available.return_value = False mock_is_db_initialised.return_value = False self.os_release.return_value = 'juno' - hooks.config_changed() + hooks.resolve_CONFIGS() + with patch.object(hooks.CONFIGS, 'write_all') as wa: + hooks.config_changed() + self.assertTrue(wa.called) self.assertTrue(self.save_script_rc.called) mock_filter_packages.assert_called_with([]) self.assertTrue(mock_update_nova_consoleauth_cfg.called) self.assertTrue(mock_update_aws_compat_services.called) self.service_pause.assert_called_with('neutron-server') - @patch.object(hooks, 'set_shared_metadatasecret') - @patch.object(hooks, 'get_shared_metadatasecret') - @patch.object(hooks, 'update_aws_compat_services') + @patch.object(utils, 'set_shared_metadatasecret') + @patch.object(utils, 'get_shared_metadatasecret') + @patch.object(hooks, 'update_nrpe_config') + @patch.object(utils, 'resource_map') + @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') + @patch('hooks.nova_cc_utils.update_aws_compat_services') @patch.object(hooks, 'update_nova_consoleauth_config') - @patch.object(hooks, 'is_db_initialised') - @patch.object(hooks, 'determine_packages') - @patch.object(utils, 'service_resume') - @patch.object(utils, 'config') - @patch.object(hooks, 'filter_installed_packages') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('charmhelpers.fetch.filter_installed_packages') @patch.object(hooks, 'configure_https') def test_config_changed_no_upgrade_juno_no_neutron_server( - self, conf_https, mock_filter_packages, - utils_config, mock_service_resume, - mock_determine_packages, + self, + conf_https, + mock_filter_packages, mock_is_db_initialised, mock_update_nova_consoleauth_cfg, mock_update_aws_compat_services, + mock_sub_ctxt, + mock_resource_map, + mock_update_nrpe_config, mock_get_shared_metadatasecret, mock_set_shared_metadatasecret): - mock_determine_packages.return_value = [] - utils_config.side_effect = self.test_config.get + mock_resource_map.return_value = {} + self.determine_packages.return_value = [] self.test_config.set('console-access-protocol', 'dummy') self.openstack_upgrade_available.return_value = False mock_is_db_initialised.return_value = False self.os_release.return_value = 'juno' self.service_pause.side_effect = ValueError - hooks.config_changed() + hooks.resolve_CONFIGS() + with patch.object(hooks.CONFIGS, 'write_all'): + hooks.config_changed() self.assertTrue(self.save_script_rc.called) mock_filter_packages.assert_called_with([]) self.assertTrue(mock_update_nova_consoleauth_cfg.called) self.assertTrue(mock_update_aws_compat_services.called) self.service_pause.assert_called_with('neutron-server') - @patch.object(hooks, 'set_shared_metadatasecret') - @patch.object(hooks, 'get_shared_metadatasecret') - @patch.object(hooks, 'update_aws_compat_services') + @patch.object(utils, 'set_shared_metadatasecret') + @patch.object(utils, 'get_shared_metadatasecret') + @patch.object(hooks, 'update_nrpe_config') + @patch.object(utils, 'resource_map') + @patch('hooks.nova_cc_utils.update_aws_compat_services') @patch.object(hooks, 'update_nova_consoleauth_config') - @patch.object(hooks, 'is_db_initialised') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'quantum_joined') - @patch.object(hooks, 'determine_packages') - @patch.object(utils, 'service_resume') @patch('charmhelpers.contrib.openstack.ip.unit_get') @patch('charmhelpers.contrib.hahelpers.cluster.relation_ids') - @patch.object(utils, 'config') @patch.object(hooks, 'db_joined') - @patch.object(hooks, 'filter_installed_packages') + @patch('charmhelpers.fetch.filter_installed_packages') @patch('charmhelpers.contrib.openstack.ip.service_name', lambda *args: 'nova-cloud-controller') @patch.object(hooks, 'cluster_joined') @patch.object(hooks, 'identity_joined') @patch.object(hooks, 'neutron_api_relation_joined') @patch.object(hooks, 'configure_https') - def test_config_changed_with_upgrade(self, conf_https, neutron_api_joined, + @patch.object(hooks, 'compute_joined') + @patch.object(hooks, 'nova_cell_api_relation_joined') + def test_config_changed_with_upgrade(self, + mock_nova_cell_api_relation_joined, + mock_compute_joined, + conf_https, neutron_api_joined, identity_joined, cluster_joined, mock_filter_packages, db_joined, - utils_config, mock_relids, + mock_relids, mock_unit_get, - mock_service_resume, - mock_determine_packages, mock_quantum_joined, mock_is_db_initialised, mock_update_nova_consoleauth_config, mock_update_aws_compat_services, + mock_resource_map, + mock_update_nrpe_config, mock_get_shared_metadatasecret, mock_set_shared_metadatasecret): - mock_determine_packages.return_value = [] + mock_resource_map.return_value = {} + self.determine_packages.return_value = [] mock_is_db_initialised.return_value = False self.openstack_upgrade_available.return_value = True self.relation_ids.return_value = ['generic_rid'] - utils_config.side_effect = self.test_config.get self.test_config.set('console-access-protocol', 'dummy') mock_relids.return_value = [] mock_unit_get.return_value = '127.0.0.1' self.os_release.return_value = 'diablo' + hooks.resolve_CONFIGS() hooks.config_changed() self.assertTrue(self.do_openstack_upgrade.called) self.assertTrue(neutron_api_joined.called) @@ -321,24 +319,31 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(mock_update_nova_consoleauth_config.called) self.assertTrue(mock_update_aws_compat_services.called) - @patch.object(hooks, 'set_shared_metadatasecret') - @patch.object(hooks, 'get_shared_metadatasecret') - @patch.object(hooks, 'update_aws_compat_services') + @patch.object(utils, 'set_shared_metadatasecret') + @patch.object(utils, 'get_shared_metadatasecret') + @patch.object(hooks, 'update_nrpe_config') + @patch.object(utils, 'resource_map') + @patch('charmhelpers.contrib.hahelpers.cluster.relation_ids') + @patch('hooks.nova_cc_utils.update_aws_compat_services') @patch.object(hooks, 'update_nova_consoleauth_config') - @patch.object(hooks, 'is_db_initialised') - @patch.object(utils, 'service_resume') - @patch.object(hooks, 'filter_installed_packages') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('charmhelpers.fetch.filter_installed_packages') @patch.object(hooks, 'configure_https') + @patch.object(hooks, 'compute_joined') @patch.object(hooks, 'compute_changed') def test_config_changed_region_change(self, mock_compute_changed, + mock_compute_joined, mock_config_https, mock_filter_packages, - mock_service_resume, mock_is_db_initialised, mock_update_nova_consoleauth_config, mock_update_aws_compat_services, + mock_relation_ids, + mock_resource_map, + mock_update_nrpe_config, mock_get_shared_metadatasecret, mock_set_shared_metadatasecret): + mock_resource_map.return_value = {} self.openstack_upgrade_available.return_value = False self.config_value_changed.return_value = True self.related_units.return_value = ['unit/0'] @@ -346,13 +351,16 @@ class NovaCCHooksTests(CharmTestCase): lambda x: ['generic_rid'] if x == 'cloud-compute' else [] mock_is_db_initialised.return_value = False self.os_release.return_value = 'diablo' + hooks.resolve_CONFIGS() hooks.config_changed() mock_compute_changed.assert_has_calls([call('generic_rid', 'unit/0')]) + mock_compute_joined.assert_has_calls( + [call(rid='generic_rid', remote_restart=False)]) self.assertTrue(mock_update_nova_consoleauth_config.called) self.assertTrue(mock_update_aws_compat_services.called) - @patch.object(hooks, 'is_cellv2_init_ready') - @patch.object(hooks, 'is_db_initialised') + @patch('hooks.nova_cc_utils.is_cellv2_init_ready') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'nova_api_relation_joined') def test_compute_changed_nova_api_trigger(self, api_joined, mock_is_db_initialised, @@ -363,8 +371,8 @@ class NovaCCHooksTests(CharmTestCase): hooks.compute_changed() api_joined.assert_called_with(rid='nova-api/0') - @patch.object(hooks, 'is_cellv2_init_ready') - @patch.object(hooks, 'is_db_initialised') + @patch('hooks.nova_cc_utils.is_cellv2_init_ready') + @patch('hooks.nova_cc_utils.is_db_initialised') def test_compute_changed_ssh_migration(self, mock_is_db_initialised, mock_is_cellv2_init_ready): self.test_relation.set({ @@ -393,11 +401,10 @@ class NovaCCHooksTests(CharmTestCase): relation_id=None), call(authorized_keys_max_index=3, relation_id=None), call(known_hosts_max_index=3, relation_id=None)] - self.assertEqual(sorted(self.relation_set.call_args_list), - sorted(expected_relations)) + self.relation_set.assert_has_calls(expected_relations, any_order=True) - @patch.object(hooks, 'is_cellv2_init_ready') - @patch.object(hooks, 'is_db_initialised') + @patch('hooks.nova_cc_utils.is_cellv2_init_ready') + @patch('hooks.nova_cc_utils.is_db_initialised') def test_compute_changed_nova_public_key(self, mock_is_db_initialised, mock_is_cellv2_init_ready): self.test_relation.set({ @@ -429,12 +436,11 @@ class NovaCCHooksTests(CharmTestCase): relation_id=None), call(relation_settings={'nova_authorized_keys_max_index': 3}, relation_id=None)] - self.assertEqual(sorted(self.relation_set.call_args_list), - sorted(expected_relations)) + self.relation_set.assert_has_calls(expected_relations, any_order=True) - @patch.object(hooks, 'is_cellv2_init_ready') - @patch.object(hooks, 'is_db_initialised') - @patch.object(hooks, 'add_hosts_to_cell') + @patch('hooks.nova_cc_utils.is_cellv2_init_ready') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('hooks.nova_cc_utils.add_hosts_to_cell') def test_compute_changed_add_hosts_leader(self, mock_add_hosts_to_cell, mock_is_db_initialised, @@ -448,9 +454,9 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(mock_is_cellv2_init_ready.called) self.assertTrue(mock_add_hosts_to_cell.called) - @patch.object(hooks, 'is_cellv2_init_ready') - @patch.object(hooks, 'is_db_initialised') - @patch.object(hooks, 'add_hosts_to_cell') + @patch('hooks.nova_cc_utils.is_cellv2_init_ready') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('hooks.nova_cc_utils.add_hosts_to_cell') def test_compute_changed_add_hosts_nonleader(self, mock_add_hosts_to_cell, mock_is_db_initialised, @@ -464,12 +470,9 @@ class NovaCCHooksTests(CharmTestCase): self.assertFalse(mock_is_cellv2_init_ready.called) self.assertFalse(mock_add_hosts_to_cell.called) - @patch.object(hooks, 'canonical_url') - @patch.object(utils, 'config') + @patch('charmhelpers.contrib.openstack.ip.canonical_url') @patch.object(hooks, '_auth_config') - def test_compute_joined_neutron(self, auth_config, _util_config, - _canonical_url): - _util_config.return_value = None + def test_compute_joined_neutron(self, auth_config, _canonical_url): self.is_relation_made.return_value = False self.network_manager.return_value = 'neutron' self.is_leader = True @@ -494,19 +497,18 @@ class NovaCCHooksTests(CharmTestCase): serial_console_base_url='ws://controller:6803', **FAKE_KS_AUTH_CFG) - @patch.object(hooks, 'canonical_url') - @patch.object(utils, 'config') - @patch.object(hooks, 'NeutronAPIContext') + @patch('charmhelpers.contrib.openstack.ip.canonical_url') + @patch('hooks.nova_cc_context.NeutronAPIContext') @patch.object(hooks, '_auth_config') def test_compute_joined_neutron_api_rel(self, auth_config, napi, - _util_config, _canonical_url): + _canonical_url): def mock_NeutronAPIContext(): return { 'neutron_plugin': 'bob', 'neutron_security_groups': 'yes', 'neutron_url': 'http://nova-cc-host1:9696', } - _util_config.return_value = None + napi.return_value = mock_NeutronAPIContext self.is_relation_made.return_value = True self.network_manager.return_value = 'neutron' @@ -536,7 +538,7 @@ class NovaCCHooksTests(CharmTestCase): serial_console_base_url='ws://controller:6803', **FAKE_KS_AUTH_CFG) - @patch.object(hooks, 'canonical_url') + @patch('charmhelpers.contrib.openstack.ip.canonical_url') @patch.object(hooks, '_auth_config') def test_nova_vmware_joined(self, auth_config, _canonical_url): auth_config.return_value = FAKE_KS_AUTH_CFG @@ -591,23 +593,25 @@ class NovaCCHooksTests(CharmTestCase): self.get_relation_ip.assert_called_with('shared-db', cidr_network=None) - @patch('charmhelpers.contrib.openstack.ip.service_name', - lambda *args: 'nova-cloud-controller') - @patch('charmhelpers.contrib.openstack.ip.unit_get') + @patch('charmhelpers.contrib.openstack.ip.canonical_url') @patch('charmhelpers.contrib.openstack.ip.is_clustered') - @patch('charmhelpers.contrib.openstack.ip.config') - def test_identity_joined(self, _ip_config, _is_clustered, _unit_get): + def test_identity_joined(self, _is_clustered, mock_canonical_url): _is_clustered.return_value = False - _unit_get.return_value = '127.0.0.1' - _ip_config.side_effect = self.test_config.get - + mock_canonical_url.side_effect = [ + 'http://ncc.example.com', + 'http://127.0.0.1', + 'http://127.0.0.2', + ] self.test_config.set('os-public-hostname', 'ncc.example.com') hooks.identity_joined() - - self.determine_endpoints.asssert_called_with( - public_url='http://ncc.example.com', - internal_url='http://127.0.0.1', - admin_url='http://127.0.0.1' + mock_canonical_url.assert_has_calls([ + call(hooks.CONFIGS, hooks.ch_ip.PUBLIC), + call(hooks.CONFIGS, hooks.ch_ip.INTERNAL), + call(hooks.CONFIGS, hooks.ch_ip.ADMIN)]) + self.determine_endpoints.assert_called_with( + 'http://ncc.example.com', + 'http://127.0.0.1', + 'http://127.0.0.2' ) def test_identity_joined_partial_cluster(self): @@ -616,8 +620,11 @@ class NovaCCHooksTests(CharmTestCase): hooks.identity_joined() self.assertFalse(self.relation_set.called) + @patch.object(utils, 'resource_map') @patch.object(hooks, 'CONFIGS') - def test_db_changed_missing_relation_data(self, configs): + def test_db_changed_missing_relation_data( + self, configs, mock_resource_map): + mock_resource_map.return_value = {} configs.complete_contexts = MagicMock() configs.complete_contexts.return_value = [] hooks.db_changed() @@ -631,13 +638,15 @@ class NovaCCHooksTests(CharmTestCase): configs.write = MagicMock() hooks.db_changed() + @patch.object(utils, 'resource_map') @patch.object(hooks, 'nova_api_relation_joined') - @patch.object(hooks, 'is_db_initialised') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'CONFIGS') def test_db_changed(self, configs, - mock_is_db_initialised, api_joined): + mock_is_db_initialised, api_joined, mock_resource_map): self.relation_ids.return_value = ['nova-api/0'] mock_is_db_initialised.return_value = False + mock_resource_map.return_value = {} 'No database migration is attempted when ACL list is not present' self.os_release.return_value = 'diablo' self._shared_db_test(configs) @@ -645,29 +654,31 @@ class NovaCCHooksTests(CharmTestCase): self.assertFalse(self.migrate_nova_databases.called) api_joined.asert_called_with(rid='nova-api/0') - @patch.object(utils, 'is_leader') - @patch.object(utils, 'os_release') - @patch.object(hooks, 'is_db_initialised') + @patch.object(utils, 'resource_map') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'CONFIGS') def test_db_changed_allowed(self, configs, mock_is_db_initialised, - utils_os_release, utils_is_leader): + mock_resource_map): mock_is_db_initialised.return_value = False + mock_resource_map.return_value = {} allowed_units = 'nova-cloud-controller/0 nova-cloud-controller/3' self.test_relation.set({ 'nova_allowed_units': allowed_units, }) self.local_unit.return_value = 'nova-cloud-controller/3' self.os_release.return_value = 'diablo' - utils_os_release.return_value = 'diablo' - utils_is_leader.return_value = False + self.is_leader.return_value = True self._shared_db_test(configs) self.assertTrue(configs.write_all.called) self.migrate_nova_databases.assert_called_with() - @patch.object(hooks, 'is_db_initialised') + @patch.object(utils, 'resource_map') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'CONFIGS') - def test_db_changed_not_allowed(self, configs, mock_is_db_initialised): + def test_db_changed_not_allowed(self, configs, mock_is_db_initialised, + mock_resource_map): mock_is_db_initialised.return_value = False + mock_resource_map.return_value = {} allowed_units = 'nova-cloud-controller/0 nova-cloud-controller/3' self.test_relation.set({ 'nova_allowed_units': allowed_units, @@ -678,17 +689,17 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(configs.write_all.called) self.assertFalse(self.migrate_nova_databases.called) - @patch.object(utils, 'is_leader') - @patch.object(utils, 'os_release') + @patch.object(utils, 'resource_map') @patch.object(hooks, 'quantum_joined') - @patch.object(hooks, 'is_db_initialised') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'compute_joined') @patch.object(hooks, 'CONFIGS') def test_db_changed_remote_restarts(self, configs, comp_joined, mock_is_db_initialised, - quantum_joined, utils_os_release, - utils_is_leader): + quantum_joined, + mock_resource_map): mock_is_db_initialised.return_value = False + mock_resource_map.return_value = {} def _relation_ids(rel): relid = { @@ -705,8 +716,7 @@ class NovaCCHooksTests(CharmTestCase): }) self.local_unit.return_value = 'nova-cloud-controller/0' self.os_release.return_value = 'diablo' - utils_os_release.return_value = 'diablo' - utils_is_leader.return_value = False + self.is_leader.return_value = True self._shared_db_test(configs) comp_joined.assert_called_with(remote_restart=True, rid='nova-compute/0') @@ -722,16 +732,19 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(configs.write_all.called) @patch.object(hooks, 'update_child_cell_records') + @patch.object(utils, 'resource_map') @patch.object(hooks, 'leader_init_db_if_ready_allowed_units') @patch.object(hooks, 'update_cell_db_if_ready_allowed_units') - @patch.object(hooks, 'is_db_initialised') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'quantum_joined') @patch.object(hooks, 'nova_api_relation_joined') @patch.object(hooks, 'CONFIGS') def test_amqp_changed_api_rel(self, configs, api_joined, quantum_joined, mock_is_db_initialised, update_db_allowed, init_db_allowed, + mock_resource_map, mock_update_child_cell_records): + mock_resource_map.return_value = {} self.relation_ids.side_effect = [ ['nova-api/0'], ['quantum-service/0'], @@ -752,14 +765,21 @@ class NovaCCHooksTests(CharmTestCase): @patch.object(hooks, 'update_child_cell_records') @patch.object(hooks, 'leader_init_db_if_ready_allowed_units') @patch.object(hooks, 'update_cell_db_if_ready_allowed_units') - @patch.object(hooks, 'is_db_initialised') + @patch.object(utils, 'resource_map') + @patch('hooks.nova_cc_utils.is_db_initialised') @patch.object(hooks, 'quantum_joined') @patch.object(hooks, 'nova_api_relation_joined') @patch.object(hooks, 'CONFIGS') - def test_amqp_changed_noapi_rel(self, configs, api_joined, - quantum_joined, mock_is_db_initialised, - update_db_allowed, init_db_allowed, + def test_amqp_changed_noapi_rel(self, + configs, + api_joined, + quantum_joined, + mock_is_db_initialised, + mock_resource_map, + update_db_allowed, + init_db_allowed, mock_update_child_cell_records): + mock_resource_map.return_value = {} mock_is_db_initialised.return_value = False configs.complete_contexts = MagicMock() configs.complete_contexts.return_value = ['amqp'] @@ -778,7 +798,7 @@ class NovaCCHooksTests(CharmTestCase): quantum_joined.assert_called_with(rid='quantum-service/0', remote_restart=True) - @patch.object(hooks, 'canonical_url') + @patch('charmhelpers.contrib.openstack.ip.canonical_url') @patch.object(os, 'rename') @patch.object(os.path, 'isfile') @patch.object(hooks, 'CONFIGS') @@ -796,8 +816,10 @@ class NovaCCHooksTests(CharmTestCase): nova_url=nova_url, restart_trigger='bob') + @patch('hooks.nova_cc_utils.resource_map') @patch.object(hooks, 'CONFIGS') - def test_neutron_api_relation_changed(self, configs): + def test_neutron_api_relation_changed(self, configs, mock_resource_map): + mock_resource_map.return_value = {} self.relation_ids.return_value = ['relid'] _compute_joined = self.patch('compute_joined') _quantum_joined = self.patch('quantum_joined') @@ -806,10 +828,13 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(_compute_joined.called) self.assertTrue(_quantum_joined.called) + @patch.object(utils, 'resource_map') @patch.object(os, 'remove') @patch.object(os.path, 'isfile') @patch.object(hooks, 'CONFIGS') - def test_neutron_api_relation_broken(self, configs, isfile, remove): + def test_neutron_api_relation_broken(self, configs, isfile, remove, + mock_resource_map): + mock_resource_map.return_value = {} isfile.return_value = True self.relation_ids.return_value = ['relid'] _compute_joined = self.patch('compute_joined') @@ -819,12 +844,15 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(_compute_joined.called) self.assertTrue(_quantum_joined.called) - @patch.object(hooks, 'canonical_url') - @patch.object(utils, 'config') - def test_console_settings_vnc(self, _utils_config, _canonical_url): - _utils_config.return_value = 'vnc' + @patch.object(utils, 'resource_map') + @patch('charmhelpers.contrib.openstack.ip.canonical_url') + def test_console_settings_vnc(self, _canonical_url, mock_resource_map): + self.test_config.set('console-access-protocol', 'vnc') + self.os_release.return_value = 'kilo' + mock_resource_map.return_value = {} _cc_host = "nova-cc-host1" _canonical_url.return_value = 'http://' + _cc_host + hooks.resolve_CONFIGS() _con_sets = hooks.console_settings() console_settings = { 'console_proxy_novnc_address': 'http://%s:6080/vnc_auto.html' % @@ -840,10 +868,9 @@ class NovaCCHooksTests(CharmTestCase): } self.assertEqual(_con_sets, console_settings) - @patch.object(hooks, 'canonical_url') - @patch.object(utils, 'config') - def test_console_settings_xvpvnc(self, _utils_config, _canonical_url): - _utils_config.return_value = 'xvpvnc' + @patch('charmhelpers.contrib.openstack.ip.canonical_url') + def test_console_settings_xvpvnc(self, _canonical_url): + self.test_config.set('console-access-protocol', 'xvpvnc') _cc_host = "nova-cc-host1" _canonical_url.return_value = 'http://' + _cc_host _con_sets = hooks.console_settings() @@ -857,10 +884,9 @@ class NovaCCHooksTests(CharmTestCase): } self.assertEqual(_con_sets, console_settings) - @patch.object(hooks, 'canonical_url') - @patch.object(utils, 'config') - def test_console_settings_novnc(self, _utils_config, _canonical_url): - _utils_config.return_value = 'novnc' + @patch('charmhelpers.contrib.openstack.ip.canonical_url') + def test_console_settings_novnc(self, _canonical_url): + self.test_config.set('console-access-protocol', 'novnc') _cc_host = "nova-cc-host1" _canonical_url.return_value = 'http://' + _cc_host _con_sets = hooks.console_settings() @@ -874,10 +900,9 @@ class NovaCCHooksTests(CharmTestCase): } self.assertEqual(_con_sets, console_settings) - @patch.object(hooks, 'canonical_url') - @patch.object(utils, 'config') - def test_console_settings_spice(self, _utils_config, _canonical_url): - _utils_config.return_value = 'spice' + @patch('charmhelpers.contrib.openstack.ip.canonical_url') + def test_console_settings_spice(self, _canonical_url): + self.test_config.set('console-access-protocol', 'spice') _cc_host = "nova-cc-host1" _canonical_url.return_value = 'http://' + _cc_host _con_sets = hooks.console_settings() @@ -891,10 +916,9 @@ class NovaCCHooksTests(CharmTestCase): } self.assertEqual(_con_sets, console_settings) - @patch.object(hooks, 'https') - @patch.object(utils, 'config') - def test_console_settings_explicit_ip(self, _utils_config, _https): - _utils_config.return_value = 'spice' + @patch('charmhelpers.contrib.hahelpers.cluster.https') + def test_console_settings_explicit_ip(self, _https): + self.test_config.set('console-access-protocol', 'spice') _https.return_value = False _cc_public_host = "public-host" self.test_config.set('console-proxy-ip', _cc_public_host) @@ -909,11 +933,9 @@ class NovaCCHooksTests(CharmTestCase): } self.assertEqual(_con_sets, console_settings) - @patch.object(hooks, 'https') - @patch.object(utils, 'config') - def test_console_settings_explicit_ip_with_https(self, _utils_config, - _https): - _utils_config.return_value = 'spice' + @patch('charmhelpers.contrib.hahelpers.cluster.https') + def test_console_settings_explicit_ip_with_https(self, _https): + self.test_config.set('console-access-protocol', 'spice') _https.return_value = True _cc_public_host = "public-host" self.test_config.set('console-proxy-ip', _cc_public_host) @@ -928,8 +950,7 @@ class NovaCCHooksTests(CharmTestCase): } self.assertEqual(_con_sets, console_settings) - @patch('nova_cc_utils.config') - def test_ha_relation_joined_no_bound_ip(self, config): + def test_ha_relation_joined_no_bound_ip(self): self.get_hacluster_config.return_value = { 'ha-bindiface': 'em0', 'ha-mcastport': '8080', @@ -937,7 +958,6 @@ class NovaCCHooksTests(CharmTestCase): } self.test_config.set('vip_iface', 'eth120') self.test_config.set('vip_cidr', '21') - config.return_value = None self.get_iface_for_address.return_value = None self.get_netmask_for_address.return_value = None hooks.ha_joined() @@ -996,8 +1016,7 @@ class NovaCCHooksTests(CharmTestCase): self.assertTrue(self.update_dns_ha_resource_params.called) self.relation_set.assert_called_with(**args) - @patch('nova_cc_utils.config') - def test_ha_relation_multi_consoleauth(self, config): + def test_ha_relation_multi_consoleauth(self): self.get_hacluster_config.return_value = { 'ha-bindiface': 'em0', 'ha-mcastport': '8080', @@ -1006,7 +1025,6 @@ class NovaCCHooksTests(CharmTestCase): self.test_config.set('vip_iface', 'eth120') self.test_config.set('vip_cidr', '21') self.test_config.set('single-nova-consoleauth', False) - config.return_value = 'novnc' self.get_iface_for_address.return_value = None self.get_netmask_for_address.return_value = None hooks.ha_joined() @@ -1029,8 +1047,7 @@ class NovaCCHooksTests(CharmTestCase): call(**args), ]) - @patch('nova_cc_utils.config') - def test_ha_relation_single_consoleauth(self, config): + def test_ha_relation_single_consoleauth(self): self.get_hacluster_config.return_value = { 'ha-bindiface': 'em0', 'ha-mcastport': '8080', @@ -1038,7 +1055,7 @@ class NovaCCHooksTests(CharmTestCase): } self.test_config.set('vip_iface', 'eth120') self.test_config.set('vip_cidr', '21') - config.return_value = 'novnc' + self.test_config.set('console-access-protocol', 'novnc') self.get_iface_for_address.return_value = None self.get_netmask_for_address.return_value = None hooks.ha_joined() @@ -1067,41 +1084,36 @@ class NovaCCHooksTests(CharmTestCase): call(**args), ]) - @patch.object(hooks, 'set_shared_metadatasecret') - @patch.object(hooks, 'get_shared_metadatasecret') - @patch.object(hooks, 'update_aws_compat_services') - @patch.object(hooks, 'is_db_initialised') - @patch.object(hooks, 'determine_packages') - @patch.object(hooks, 'service_pause') - @patch.object(hooks, 'filter_installed_packages') - @patch('nova_cc_hooks.configure_https') - @patch('nova_cc_utils.config') - def test_config_changed_single_consoleauth(self, mock_config, + @patch.object(utils, 'set_shared_metadatasecret') + @patch.object(utils, 'get_shared_metadatasecret') + @patch.object(hooks, 'update_nrpe_config') + @patch.object(utils, 'resource_map') + @patch('hooks.nova_cc_utils.update_aws_compat_services') + @patch('hooks.nova_cc_utils.is_db_initialised') + @patch('charmhelpers.fetch.filter_installed_packages') + @patch.object(hooks, 'configure_https') + def test_config_changed_single_consoleauth(self, mock_configure_https, mock_filter_packages, - mock_service_pause, - mock_determine_packages, mock_is_db_initialised, mock_update_aws_compat_svcs, + mock_resource_map, + mock_update_nrpe_config, mock_get_shared_metadatasecret, mock_set_shared_metadatasecret): - mock_determine_packages.return_value = [] + mock_resource_map.return_value = {} + self.determine_packages.return_value = [] mock_is_db_initialised.return_value = False self.config_value_changed.return_value = False self.os_release.return_value = 'diablo' - def cfg(k, v): - if k == "single-nova-authconsole": - return True - return 'novnc' + self.test_config.set('single-nova-consoleauth', True) + self.test_config.set('console-access-protocol', 'novnc') - config.side_effect = cfg rids = {'ha': ['ha:1']} + self.relation_ids.side_effect = lambda r: rids.get(r, []) - def f(r): - return rids.get(r, []) - - self.relation_ids.side_effect = f + hooks.resolve_CONFIGS() hooks.config_changed() args = { 'delete_resources': [], @@ -1118,15 +1130,15 @@ class NovaCCHooksTests(CharmTestCase): call(v, **args) for v in rids['ha'] ]) - mock_service_pause.assert_has_calls([ + self.service_pause.assert_has_calls([ call('nova-consoleauth')] ) mock_filter_packages.assert_called_with([]) self.assertTrue(mock_update_aws_compat_svcs.called) - @patch.object(hooks, 'is_api_ready') - def _test_nova_api_relation_joined(self, tgt, is_api_ready): + @patch('hooks.nova_cc_utils.is_api_ready') + def helper_test_nova_api_relation_joined(self, tgt, is_api_ready): is_api_ready.return_value = tgt exp = 'yes' if tgt else 'no' hooks.nova_api_relation_joined(rid='foo') @@ -1134,10 +1146,10 @@ class NovaCCHooksTests(CharmTestCase): 'foo', **{'nova-api-ready': exp}) def test_nova_api_relation_joined_ready(self): - self._test_nova_api_relation_joined(True) + self.helper_test_nova_api_relation_joined(True) def test_nova_api_relation_joined_not_ready(self): - self._test_nova_api_relation_joined(False) + self.helper_test_nova_api_relation_joined(False) @patch.object(hooks, 'memcached_common') def test_memcache_joined(self, _memcached_common): diff --git a/unit_tests/test_nova_cc_utils.py b/unit_tests/test_nova_cc_utils.py index 6d429022..9e2b7190 100644 --- a/unit_tests/test_nova_cc_utils.py +++ b/unit_tests/test_nova_cc_utils.py @@ -15,57 +15,51 @@ from collections import OrderedDict from mock import patch, MagicMock, call, mock_open -from test_utils import ( +from unit_tests.test_utils import ( CharmTestCase, - get_default_config, patch_open, ) -__default_config = get_default_config() +import hooks.nova_cc_utils as utils -with patch('charmhelpers.core.hookenv.config') as config: - with patch('charmhelpers.contrib.openstack.utils.get_os_codename_package'): # noqa - # this makes the config behave more similar to the real config() - config.side_effect = lambda k: __default_config[k] - - import nova_cc_utils as utils TO_PATCH = [ - 'apt_update', - 'apt_upgrade', - 'apt_install', - 'config', - 'configure_installation_source', - 'canonical_url', + 'charmhelpers.contrib.openstack.ip.canonical_url', + 'charmhelpers.contrib.openstack.utils.configure_installation_source', + 'charmhelpers.contrib.openstack.utils.enable_memcache', + 'charmhelpers.contrib.openstack.utils.get_os_codename_install_source', + 'charmhelpers.contrib.openstack.utils.is_unit_paused_set', + 'charmhelpers.contrib.openstack.utils.os_application_version_set', + 'charmhelpers.contrib.openstack.utils.os_release', + 'charmhelpers.contrib.openstack.utils.save_script_rc', + 'charmhelpers.contrib.openstack.utils.token_cache_pkgs', + 'charmhelpers.contrib.peerstorage.peer_store', + 'charmhelpers.core.hookenv.config', + 'charmhelpers.core.hookenv.is_leader', + 'charmhelpers.core.hookenv.leader_get', + 'charmhelpers.core.hookenv.leader_set', + 'charmhelpers.core.hookenv.local_unit', + 'charmhelpers.core.hookenv.log', + 'charmhelpers.core.hookenv.related_units', + 'charmhelpers.core.hookenv.relation_get', + 'charmhelpers.core.hookenv.relation_ids', + 'charmhelpers.core.hookenv.remote_unit', + 'charmhelpers.core.hookenv.status_set', + 'charmhelpers.core.host.lsb_release', + 'charmhelpers.core.host.service_pause', + 'charmhelpers.core.host.service_restart', + 'charmhelpers.core.host.service_resume', + 'charmhelpers.core.host.service_running', + 'charmhelpers.core.host.service_start', + 'charmhelpers.core.host.service_stop', + 'charmhelpers.fetch.apt_install', + 'charmhelpers.fetch.apt_update', + 'charmhelpers.fetch.apt_upgrade', 'disable_policy_rcd', - 'is_leader', - 'is_unit_paused_set', - 'lsb_release', - 'leader_get', - 'leader_set', 'enable_policy_rcd', - 'get_os_codename_install_source', - 'log', - 'os_release', - 'peer_store', - 'register_configs', - 'relation_ids', - 'remote_unit', - '_save_script_rc', - 'service_pause', - 'service_resume', - 'service_start', - 'services', - 'service_running', - 'service_stop', - 'related_units', - 'local_unit', - 'relation_get', - 'os_application_version_set', - 'token_cache_pkgs', - 'enable_memcache', - 'status_set', - 'uuid1', + 'hooks.nova_cc_utils.register_configs', + 'hooks.nova_cc_utils.services', + 'uuid.uuid1', ] SCRIPTRC_ENV_VARS = { @@ -141,23 +135,20 @@ RESTART_MAP_ICEHOUSE = OrderedDict([ ]) RESTART_MAP_OCATA_ACTUAL = OrderedDict([ ('/etc/nova/nova.conf', [ - 'nova-api-ec2', 'nova-api-os-compute', 'nova-objectstore', - 'nova-cert', 'nova-scheduler', 'nova-conductor', 'apache2', - ]), - ('/etc/nova/api-paste.ini', [ - 'nova-api-ec2', 'nova-api-os-compute', 'apache2' + 'nova-api-os-compute', 'nova-scheduler', 'nova-conductor', 'apache2', ]), + ('/etc/nova/api-paste.ini', ['nova-api-os-compute', 'apache2']), ('/etc/haproxy/haproxy.cfg', ['haproxy']), ('/etc/apache2/sites-available/openstack_https_frontend', ['apache2']), ('/etc/apache2/sites-enabled/wsgi-openstack-api.conf', ['apache2']), ]) RESTART_MAP_OCATA_BASE = OrderedDict([ ('/etc/nova/nova.conf', [ - 'nova-api-ec2', 'nova-api-os-compute', 'nova-placement-api', - 'nova-objectstore', 'nova-cert', 'nova-scheduler', 'nova-conductor' + 'nova-api-os-compute', 'nova-placement-api', + 'nova-scheduler', 'nova-conductor' ]), ('/etc/nova/api-paste.ini', [ - 'nova-api-ec2', 'nova-api-os-compute', 'nova-placement-api' + 'nova-api-os-compute', 'nova-placement-api' ]), ('/etc/haproxy/haproxy.cfg', ['haproxy']), ('/etc/apache2/sites-available/openstack_https_frontend', ['apache2']) @@ -198,7 +189,7 @@ ubuntu-cloud-archive/liberty-staging/ubuntu trusty main %s """ % GPG_PPA_CLOUD_ARCHIVE -NM_CELLS_LIST = """ +NM_CELLS_LIST = b""" +-------+--------------------------------------+--------------+-------------+ | Name | UUID | Transport | DB | +-------+--------------------------------------+--------------+-------------+ @@ -213,13 +204,36 @@ class NovaCCUtilsTests(CharmTestCase): def setUp(self): super(NovaCCUtilsTests, self).setUp(utils, TO_PATCH) self.config.side_effect = self.test_config.get + utils._BASE_RESOURCE_MAP = None # reset this for each test self.maxDiff = None - def _resource_map(self): - with patch('charmhelpers.contrib.openstack.context.' - 'SubordinateConfigContext'): - _map = utils.resource_map() - return _map + def test_resolve_services(self): + # Icehouse with disable-aws-compat = True + self.test_config.set('disable-aws-compat', True) + self.os_release.return_value = "icehouse" + _services = utils.resolve_services() + for _service in utils.AWS_COMPAT_SERVICES: + self.assertTrue(_service not in _services) + + # Icehouse with disable-aws-compat = False + self.test_config.set('disable-aws-compat', False) + self.os_release.return_value = "icehouse" + _services = utils.resolve_services() + for _service in utils.AWS_COMPAT_SERVICES: + self.assertTrue(_service in _services) + + # Liberty + self.os_release.return_value = "liberty" + _services = utils.resolve_services() + for _service in utils.AWS_COMPAT_SERVICES: + self.assertTrue(_service not in _services) + + # Newton + self.os_release.return_value = "newton" + _services = utils.resolve_services() + for _service in utils.AWS_COMPAT_SERVICES: + self.assertTrue(_service not in _services) + self.assertTrue('nova-cert' not in _services) @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') def test_resource_map_vmware(self, subcontext): @@ -238,7 +252,6 @@ class NovaCCUtilsTests(CharmTestCase): @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') def test_resource_map_neutron_no_agent_installed(self, subcontext): self.os_release.return_value = 'diablo' - self._resource_map() _map = utils.resource_map() services = [] [services.extend(_map[c]['services'])for c in _map] @@ -278,19 +291,19 @@ class NovaCCUtilsTests(CharmTestCase): def test_console_attributes_none(self): self.test_config.set('console-access-protocol', 'None') - _proto = utils.console_attributes('protocol') + _proto = utils.common.console_attributes('protocol') self.assertEqual(_proto, None) self.test_config.set('console-access-protocol', 'NONE') - _proto = utils.console_attributes('protocol') + _proto = utils.common.console_attributes('protocol') self.assertEqual(_proto, None) self.test_config.set('console-access-protocol', 'none') - _proto = utils.console_attributes('protocol') + _proto = utils.common.console_attributes('protocol') self.assertEqual(_proto, None) self.test_config.set('console-access-protocol', None) - _proto = utils.console_attributes('protocol') + _proto = utils.common.console_attributes('protocol') self.assertEqual(_proto, None) self.test_config.set('console-access-protocol', "") - _proto = utils.console_attributes('protocol') + _proto = utils.common.console_attributes('protocol') self.assertEqual(_proto, None) @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') @@ -316,13 +329,12 @@ class NovaCCUtilsTests(CharmTestCase): @patch('charmhelpers.contrib.openstack.neutron.os_release') @patch('os.path.exists') @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') - def test_restart_map_api_before_frontends_icehouse(self, subcontext, - _exists, _os_release): + def test_restart_map_api_before_frontends_icehouse( + self, subcontext, _exists, _os_release): _os_release.return_value = 'icehouse' self.os_release.return_value = 'icehouse' _exists.return_value = False self.enable_memcache.return_value = False - self._resource_map() _map = utils.restart_map() self.assertIsInstance(_map, OrderedDict) self.assertEqual(_map, RESTART_MAP_ICEHOUSE) @@ -330,13 +342,12 @@ class NovaCCUtilsTests(CharmTestCase): @patch('charmhelpers.contrib.openstack.neutron.os_release') @patch('os.path.exists') @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') - def test_restart_map_api_actual_ocata(self, subcontext, - _exists, _os_release): + def test_restart_map_api_actual_ocata( + self, subcontext, _exists, _os_release): _os_release.return_value = 'ocata' self.os_release.return_value = 'ocata' _exists.return_value = False self.enable_memcache.return_value = False - self._resource_map() _map = utils.restart_map() self.assertIsInstance(_map, OrderedDict) self.assertEqual(_map, RESTART_MAP_OCATA_ACTUAL) @@ -344,13 +355,12 @@ class NovaCCUtilsTests(CharmTestCase): @patch('charmhelpers.contrib.openstack.neutron.os_release') @patch('os.path.exists') @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') - def test_restart_map_api_ocata_base(self, subcontext, - _exists, _os_release): + def test_restart_map_api_ocata_base( + self, subcontext, _exists, _os_release): _os_release.return_value = 'ocata' self.os_release.return_value = 'ocata' _exists.return_value = False self.enable_memcache.return_value = False - self._resource_map() _map = utils.restart_map(actual_services=False) self.assertIsInstance(_map, OrderedDict) self.assertEqual(_map, RESTART_MAP_OCATA_BASE) @@ -360,7 +370,6 @@ class NovaCCUtilsTests(CharmTestCase): def test_restart_map_apache24(self, _exists, subcontext): _exists.return_Value = True self.os_release.return_value = 'diablo' - self._resource_map() _map = utils.restart_map() self.assertTrue('/etc/apache2/sites-available/' 'openstack_https_frontend.conf' in _map) @@ -368,20 +377,20 @@ class NovaCCUtilsTests(CharmTestCase): 'openstack_https_frontend' not in _map) def test_console_attributes_spice(self): - _proto = utils.console_attributes('protocol', proto='spice') + _proto = utils.common.console_attributes('protocol', proto='spice') self.assertEqual(_proto, 'spice') def test_console_attributes_vnc(self): self.test_config.set('console-access-protocol', 'vnc') - _proto = utils.console_attributes('protocol') - _servs = utils.console_attributes('services') - _pkgs = utils.console_attributes('packages') - _proxy_page = utils.console_attributes('proxy-page') + _proto = utils.common.console_attributes('protocol') + _servs = utils.common.console_attributes('services') + _pkgs = utils.common.console_attributes('packages') + _proxy_page = utils.common.console_attributes('proxy-page') vnc_pkgs = ['nova-novncproxy', 'nova-xvpvncproxy', 'nova-consoleauth'] vnc_servs = ['nova-novncproxy', 'nova-xvpvncproxy', 'nova-consoleauth'] self.assertEqual(_proto, 'vnc') - self.assertEqual(_servs, vnc_servs) - self.assertEqual(_pkgs, vnc_pkgs) + self.assertEqual(sorted(_servs), sorted(vnc_servs)) + self.assertEqual(sorted(_pkgs), sorted(vnc_pkgs)) self.assertEqual(_proxy_page, None) def test_database_setup(self): @@ -428,7 +437,7 @@ class NovaCCUtilsTests(CharmTestCase): self.enable_memcache.return_value = False pkgs = utils.determine_packages() ex = list(set(utils.BASE_PACKAGES + utils.BASE_SERVICES)) - # nova-placement-api is purposely dropped unless it's ocata + # nova-placement-api, et al, are purposely dropped unless it's ocata ex.remove('nova-placement-api') self.assertEqual(sorted(ex), sorted(pkgs)) @@ -440,6 +449,10 @@ class NovaCCUtilsTests(CharmTestCase): self.enable_memcache.return_value = False pkgs = utils.determine_packages() ex = list(set(utils.BASE_PACKAGES + utils.BASE_SERVICES)) + # some packages still need to be removed + ex.remove('nova-cert') + ex.remove('nova-objectstore') + ex.remove('nova-api-ec2') self.assertEqual(sorted(ex), sorted(pkgs)) @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') @@ -451,12 +464,16 @@ class NovaCCUtilsTests(CharmTestCase): pkgs = utils.determine_packages() ex = list(set([p for p in utils.BASE_PACKAGES + utils.BASE_SERVICES if not p.startswith('python-')] + utils.PY3_PACKAGES)) + # some packages still need to be removed ex.remove('libapache2-mod-wsgi') + ex.remove('nova-cert') + ex.remove('nova-objectstore') + ex.remove('nova-api-ec2') self.assertEqual(sorted(ex), sorted(pkgs)) + self.assertEqual(ex, pkgs) @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') - def test_determine_packages_serial_console(self, - subcontext): + def test_determine_packages_serial_console(self, subcontext): self.test_config.set('enable-serial-console', True) self.relation_ids.return_value = [] self.os_release.return_value = 'juno' @@ -466,8 +483,7 @@ class NovaCCUtilsTests(CharmTestCase): self.assertIn(console_pkg, pkgs) @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') - def test_determine_packages_serial_console_icehouse(self, - subcontext): + def test_determine_packages_serial_console_icehouse(self, subcontext): self.test_config.set('enable-serial-console', True) self.relation_ids.return_value = [] self.os_release.return_value = 'icehouse' @@ -503,13 +519,14 @@ class NovaCCUtilsTests(CharmTestCase): def test_save_script_rc_base(self): self.relation_ids.return_value = [] utils.save_script_rc() - self._save_script_rc.called_with(**SCRIPTRC_ENV_VARS) + self.save_script_rc.called_with(**SCRIPTRC_ENV_VARS) @patch('charmhelpers.contrib.openstack.utils.lsb_release') def test_get_step_upgrade_source_target_liberty(self, lsb_release): self.lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'} lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'} self.get_os_codename_install_source.side_effect = self.originals[ + 'charmhelpers.contrib.openstack.utils.' 'get_os_codename_install_source'] # icehouse -> liberty @@ -538,6 +555,7 @@ class NovaCCUtilsTests(CharmTestCase): lsb_release.return_value = {'DISTRIB_CODENAME': 'xenial'} self.os_release.return_value = 'mitaka' self.get_os_codename_install_source.side_effect = self.originals[ + 'charmhelpers.contrib.openstack.utils.' 'get_os_codename_install_source'] step_src = utils.get_step_upgrade_source(OS_ORIGIN_NEWTON_STAGING) @@ -550,6 +568,7 @@ class NovaCCUtilsTests(CharmTestCase): lsb_release.return_value = {'DISTRIB_CODENAME': 'xenial'} self.os_release.return_value = 'mitaka' self.get_os_codename_install_source.side_effect = self.originals[ + 'charmhelpers.contrib.openstack.utils.' 'get_os_codename_install_source'] step_src = utils.get_step_upgrade_source("cloud:xenial-ocata") @@ -562,6 +581,7 @@ class NovaCCUtilsTests(CharmTestCase): self.lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'} lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'} self.get_os_codename_install_source.side_effect = self.originals[ + 'charmhelpers.contrib.openstack.utils.' 'get_os_codename_install_source'] self.os_release.return_value = 'icehouse' step_src = utils.get_step_upgrade_source(OS_ORIGIN_LIBERTY_STAGING) @@ -571,7 +591,7 @@ class NovaCCUtilsTests(CharmTestCase): @patch.object(utils, 'ssh_known_host_key') @patch('subprocess.check_output') def test_add_known_host_exists(self, check_output, host_key, rm): - check_output.return_value = '|1|= fookey' + check_output.return_value = b'|1|= fookey' host_key.return_value = '|1|= fookey' with patch_open() as (_open, _file): utils.add_known_host('foohost') @@ -584,7 +604,7 @@ class NovaCCUtilsTests(CharmTestCase): @patch('subprocess.check_output') def test_add_known_host_exists_outdated( self, check_output, host_key, rm, known_hosts): - check_output.return_value = '|1|= fookey' + check_output.return_value = b'|1|= fookey' host_key.return_value = '|1|= fookey_old' with patch_open() as (_open, _file): utils.add_known_host('foohost', None, None) @@ -596,7 +616,7 @@ class NovaCCUtilsTests(CharmTestCase): @patch('subprocess.check_output') def test_add_known_host_exists_added( self, check_output, host_key, rm, known_hosts): - check_output.return_value = '|1|= fookey' + check_output.return_value = b'|1|= fookey' host_key.return_value = None with patch_open() as (_open, _file): _file.write = MagicMock() @@ -604,7 +624,7 @@ class NovaCCUtilsTests(CharmTestCase): self.assertFalse(rm.called) _file.write.assert_called_with('|1|= fookey\n') - @patch('__builtin__.open') + @patch('builtins.open') @patch('os.mkdir') @patch('os.path.isdir') def test_ssh_directory_for_unit(self, isdir, mkdir, _open): @@ -716,7 +736,7 @@ class NovaCCUtilsTests(CharmTestCase): """On precise ssh-keygen does not error if host not found in file. So check charm processes empty output properly""" _known_hosts.return_value = '/foo/known_hosts' - _check_output.return_value = '' + _check_output.return_value = b'' key = utils.ssh_known_host_key('test') self.assertEqual(key, None) @@ -991,47 +1011,60 @@ class NovaCCUtilsTests(CharmTestCase): def test_service_guard_active_guard(self): '''Ensure services with incomplete interfaces are stopped''' - contexts = MagicMock() - contexts.complete_contexts.return_value = ['interfacea'] + class MockContext(object): + called = False + + def complete_contexts(self): + self.called = True + return ['interfacea'] + + _mc = MockContext() self.service_running.return_value = True @utils.service_guard({'test': ['interfacea', 'interfaceb']}, - contexts, True) + _mc, True) def dummy_func(): pass dummy_func() self.service_running.assert_called_with('test') self.service_stop.assert_called_with('test') - self.assertTrue(contexts.complete_contexts.called) + self.assertTrue(_mc.called) def test_service_guard_active_release(self): '''Ensure services with complete interfaces are not stopped''' - contexts = MagicMock() - contexts.complete_contexts.return_value = ['interfacea', - 'interfaceb'] + class MockContext(object): + called = False + + def complete_contexts(self): + self.called = True + return ['interfacea', 'interfaceb'] + + _mc = MockContext() @utils.service_guard({'test': ['interfacea', 'interfaceb']}, - contexts, True) + _mc, True) def dummy_func(): pass + dummy_func() self.assertFalse(self.service_running.called) self.assertFalse(self.service_stop.called) - self.assertTrue(contexts.complete_contexts.called) + self.assertTrue(_mc.called) - def _test_is_api_ready(self, tgt): + def helper_test_is_api_ready(self, tgt): fake_config = MagicMock() - with patch.object(utils, 'incomplete_relation_data') as ird: + with patch('charmhelpers.contrib.openstack.utils.' + 'incomplete_relation_data') as ird: ird.return_value = (not tgt) self.assertEqual(utils.is_api_ready(fake_config), tgt) ird.assert_called_with( fake_config, utils.REQUIRED_INTERFACES) def test_is_api_ready_true(self): - self._test_is_api_ready(True) + self.helper_test_is_api_ready(True) def test_is_api_ready_false(self): - self._test_is_api_ready(False) + self.helper_test_is_api_ready(False) def test_assess_status(self): with patch.object(utils, 'assess_status_func') as asf: @@ -1050,7 +1083,7 @@ class NovaCCUtilsTests(CharmTestCase): @patch.object(utils, 'REQUIRED_INTERFACES') @patch.object(utils, 'services') @patch.object(utils, 'determine_ports') - @patch.object(utils, 'make_assess_status_func') + @patch.object(utils.ch_utils, 'make_assess_status_func') def test_assess_status_func(self, make_assess_status_func, determine_ports, @@ -1073,10 +1106,12 @@ class NovaCCUtilsTests(CharmTestCase): def test_pause_unit_helper(self): with patch.object(utils, '_pause_resume_helper') as prh: utils.pause_unit_helper('random-config') - prh.assert_called_once_with(utils.pause_unit, 'random-config') + prh.assert_called_once_with(utils.ch_utils.pause_unit, + 'random-config') with patch.object(utils, '_pause_resume_helper') as prh: utils.resume_unit_helper('random-config') - prh.assert_called_once_with(utils.resume_unit, 'random-config') + prh.assert_called_once_with(utils.ch_utils.resume_unit, + 'random-config') @patch.object(utils, 'services') @patch.object(utils, 'determine_ports') @@ -1091,45 +1126,35 @@ class NovaCCUtilsTests(CharmTestCase): # ports=None whilst port checks are disabled. f.assert_called_once_with('assessor', services='s1', ports=None) - @patch.object(utils, 'service_pause') - @patch.object(utils, 'service_resume') - @patch.object(utils, 'config') - @patch.object(utils, 'filter_installed_packages') - def test_disable_aws_compat_services_uinstalled(self, - filter_installed_packages, - config, service_resume, - service_pause): + @patch('charmhelpers.fetch.filter_installed_packages') + def test_disable_aws_compat_services_uninstalled( + self, filter_installed_packages,): filter_installed_packages.return_value = utils.AWS_COMPAT_SERVICES utils.update_aws_compat_services() - config.assert_not_called() - service_pause.assert_not_called() - service_resume.assert_not_called() + self.config.assert_not_called() + self.service_pause.assert_not_called() + self.service_resume.assert_not_called() - @patch.object(utils, 'service_pause') - @patch.object(utils, 'service_resume') - @patch.object(utils, 'config') - @patch.object(utils, 'filter_installed_packages') - def test_disable_aws_compat_services_true(self, filter_installed_packages, - config, s_resume, s_pause): + @patch('charmhelpers.fetch.filter_installed_packages') + def test_disable_aws_compat_services_true(self, filter_installed_packages): filter_installed_packages.return_value = [] - config.return_value = True + self.test_config.set('disable-aws-compat', True) utils.update_aws_compat_services() - s_resume.assert_not_called() - s_pause.assert_has_calls([call(s) for s in utils.AWS_COMPAT_SERVICES]) + self.service_resume.assert_not_called() + self.service_pause.assert_has_calls( + [call(s) for s in utils.AWS_COMPAT_SERVICES]) - @patch.object(utils, 'service_pause') - @patch.object(utils, 'service_resume') - @patch.object(utils, 'config') - @patch.object(utils, 'filter_installed_packages') - def test_disable_aws_compat_services_false(self, filter_installed_packages, - config, s_resume, s_pause): + @patch('charmhelpers.fetch.filter_installed_packages') + def test_disable_aws_compat_services_false( + self, filter_installed_packages): filter_installed_packages.return_value = [] - config.return_value = False + self.test_config.set('disable-aws-compat', False) utils.update_aws_compat_services() - s_resume.assert_has_calls([call(s) for s in utils.AWS_COMPAT_SERVICES]) - s_pause.assert_not_called() + self.service_resume.assert_has_calls( + [call(s) for s in utils.AWS_COMPAT_SERVICES]) + self.service_pause.assert_not_called() @patch('subprocess.check_output') def test_get_cell_uuid(self, mock_check_call): @@ -1249,7 +1274,7 @@ class NovaCCUtilsTests(CharmTestCase): ['nova-manage', 'cell_v2', 'discover_hosts', '--cell_uuid', 'c83121db-f1c7-464a-b657-38c28fac84c6', '--verbose']) - @patch('nova_cc_context.NovaCellV2SharedDBContext') + @patch('hooks.nova_cc_context.NovaCellV2SharedDBContext') @patch('charmhelpers.contrib.openstack.context.AMQPContext') def test_is_cellv2_init_ready_mitaka(self, amqp, shared_db): self.os_release.return_value = 'mitaka' @@ -1259,7 +1284,7 @@ class NovaCCUtilsTests(CharmTestCase): shared_db.assert_called_once() self.log.assert_called_once() - @patch('nova_cc_context.NovaCellV2SharedDBContext') + @patch('hooks.nova_cc_context.NovaCellV2SharedDBContext') @patch('charmhelpers.contrib.openstack.context.AMQPContext') def test_is_cellv2_init_ready_ocata(self, amqp, shared_db): self.os_release.return_value = 'ocata' @@ -1320,8 +1345,8 @@ class NovaCCUtilsTests(CharmTestCase): for c in expected_calls: self.assertTrue(c in m.mock_calls) - @patch.object(utils.context, 'SharedDBContext') - @patch.object(utils, 'relation_id') + @patch.object(utils.ch_context, 'SharedDBContext') + @patch('charmhelpers.core.hookenv.relation_id') def test_get_cell_db_context(self, mock_relation_id, mock_SharedDBContext): mock_relation_id.return_value = 'dbid' utils.get_cell_db_context('mysql-cell2') @@ -1333,8 +1358,8 @@ class NovaCCUtilsTests(CharmTestCase): relation_name='shared-db-cell', service_or_unit='mysql-cell2') - @patch.object(utils.context, 'AMQPContext') - @patch.object(utils, 'relation_id') + @patch.object(utils.ch_context, 'AMQPContext') + @patch('charmhelpers.core.hookenv.relation_id') def test_get_cell_amqp_context(self, mock_relation_id, mock_AMQPContext): mock_relation_id.return_value = 'amqpid' utils.get_cell_amqp_context('rabbitmq-server-cell2') @@ -1375,10 +1400,12 @@ class NovaCCUtilsTests(CharmTestCase): @patch.object(utils, 'get_cell_amqp_context') @patch.object(utils, 'get_sql_uri') @patch.object(utils.subprocess, 'check_output') - @patch.object(utils, 'service_restart') - def test_update_child_cell(self, mock_service_restart, mock_check_output, - mock_get_sql_uri, mock_get_cell_amqp_context, - mock_get_cell_db_context, mock_get_cell_details, + def test_update_child_cell(self, + mock_check_output, + mock_get_sql_uri, + mock_get_cell_amqp_context, + mock_get_cell_db_context, + mock_get_cell_details, mock_is_db_initialised): mock_is_db_initialised.return_value = True mock_get_cell_details.return_value = {'cell1': 'cell1uuid'} @@ -1396,25 +1423,22 @@ class NovaCCUtilsTests(CharmTestCase): '--name', 'cell2', '--transport-url', 'amqp-uri', '--database_connection', 'db-uri']) - mock_service_restart.assert_called_once_with('nova-scheduler') + self.service_restart.assert_called_once_with('nova-scheduler') @patch.object(utils, 'is_db_initialised') @patch.object(utils.subprocess, 'check_output') - @patch.object(utils, 'service_restart') - def test_update_child_cell_no_local_db(self, mock_service_restart, + def test_update_child_cell_no_local_db(self, mock_check_output, mock_is_db_initialised): mock_is_db_initialised.return_value = False utils.update_child_cell('cell2', 'mysql-cell2', 'amqp-cell2') self.assertFalse(mock_check_output.called) - self.assertFalse(mock_service_restart.called) + self.assertFalse(self.service_restart.called) @patch.object(utils, 'get_cell_details') @patch.object(utils, 'is_db_initialised') @patch.object(utils.subprocess, 'check_output') - @patch.object(utils, 'service_restart') def test_update_child_cell_api_cell_not_registered(self, - mock_service_restart, mock_check_output, mock_is_db_initialised, mock_get_cell_details): @@ -1423,36 +1447,32 @@ class NovaCCUtilsTests(CharmTestCase): utils.update_child_cell('cell2', 'mysql-cell2', 'amqp-cell2') mock_get_cell_details.assert_called_once_with() self.assertFalse(mock_check_output.called) - self.assertFalse(mock_service_restart.called) + self.assertFalse(self.service_restart.called) @patch.object(utils.subprocess, 'check_output') - @patch.object(utils, 'service_restart') @patch.object(utils, 'get_cell_details') @patch.object(utils, 'is_db_initialised') @patch.object(utils, 'get_cell_db_context') def test_update_child_cell_no_cell_db(self, mock_get_cell_db_context, mock_is_db_initialised, mock_get_cell_details, - mock_service_restart, mock_check_output): mock_is_db_initialised.return_value = True mock_get_cell_details.return_value = {'cell1': 'uuid4cell1'} mock_get_cell_db_context.return_value = {} utils.update_child_cell('cell2', 'mysql-cell2', 'amqp-cell2') self.assertFalse(mock_check_output.called) - self.assertFalse(mock_service_restart.called) + self.assertFalse(self.service_restart.called) @patch.object(utils, 'get_cell_amqp_context') @patch.object(utils, 'get_sql_uri') @patch.object(utils.subprocess, 'check_output') - @patch.object(utils, 'service_restart') @patch.object(utils, 'get_cell_details') @patch.object(utils, 'is_db_initialised') @patch.object(utils, 'get_cell_db_context') def test_update_child_cell_no_cell_amqp(self, mock_get_cell_db_context, mock_is_db_initialised, mock_get_cell_details, - mock_service_restart, mock_check_output, mock_get_sql_uri, mock_get_cell_amqp_context): @@ -1462,4 +1482,4 @@ class NovaCCUtilsTests(CharmTestCase): mock_get_cell_amqp_context.return_value = {} utils.update_child_cell('cell2', 'mysql-cell2', 'amqp-cell2') self.assertFalse(mock_check_output.called) - self.assertFalse(mock_service_restart.called) + self.assertFalse(self.service_restart.called) diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py index 799c9ce4..2649f841 100644 --- a/unit_tests/test_utils.py +++ b/unit_tests/test_utils.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import io +import importlib import os import logging import unittest @@ -51,7 +53,7 @@ def get_default_config(): ''' default_config = {} config = load_config() - for k, v in config.iteritems(): + for k, v in config.items(): if 'default' in v: default_config[k] = v['default'] else: @@ -71,15 +73,26 @@ class CharmTestCase(unittest.TestCase): self.patch_all() def patch(self, method): - self.originals[method] = getattr(self.obj, method) - _m = patch.object(self.obj, method) + if "." in method: + _mod = importlib.import_module('.'.join(method.split('.')[0:-1])) + _m = patch(method) + name = method.split('.')[-1] + self.originals[method] = getattr(_mod, name) + else: + self.originals[method] = getattr(self.obj, method) + _m = patch.object(self.obj, method) + name = method mock = _m.start() self.addCleanup(_m.stop) return mock def patch_all(self): for method in self.patches: - setattr(self, method, self.patch(method)) + mock = self.patch(method) + if "." in method: + setattr(self, method.split('.')[-1], mock) + else: + setattr(self, method, mock) class TestConfig(object): @@ -103,6 +116,10 @@ class TestConfig(object): raise KeyError self.config[attr] = value + def update(self, d): + for k, v in d.items(): + self.set(k, v) + class TestRelation(object): @@ -127,12 +144,12 @@ def patch_open(): Yields the mock for "open" and "file", respectively.''' mock_open = MagicMock(spec=open) - mock_file = MagicMock(spec=file) + mock_file = MagicMock(spec=io.FileIO) @contextmanager def stub_open(*args, **kwargs): mock_open(*args, **kwargs) yield mock_file - with patch('__builtin__.open', stub_open): + with patch('builtins.open', stub_open): yield mock_open, mock_file