diff --git a/actions/actions.py b/actions/actions.py index 88d5f52ece657287f65234fb4e7b22aed107ca1c..3234e67057c20dfb2ffd1d8e0e93008b832c32b0 100755 --- a/actions/actions.py +++ b/actions/actions.py @@ -22,6 +22,7 @@ from charmhelpers.core.hookenv import ( action_set, ) from ceilometer_utils import ( + assess_status, ceilometer_upgrade_helper, pause_unit_helper, register_configs, @@ -59,6 +60,7 @@ def ceilometer_upgrade(args): if e.trace: action_set({'traceback': e.trace}) raise Exception(str(e.message)) + assess_status(register_configs()) # A dictionary of all the defined actions to callables (which take diff --git a/charmhelpers/contrib/openstack/utils.py b/charmhelpers/contrib/openstack/utils.py index 4e432a25bd2e00ab4bf5fa9ba6d05457fce99c7b..1d1f8641e4dcf0ba85d55ca93534d5753bf0d8bd 100644 --- a/charmhelpers/contrib/openstack/utils.py +++ b/charmhelpers/contrib/openstack/utils.py @@ -83,7 +83,8 @@ from charmhelpers.fetch import ( add_source as fetch_add_source, SourceConfigError, GPGKeyError, - get_upstream_version + get_upstream_version, + filter_missing_packages ) from charmhelpers.fetch.snap import ( @@ -309,6 +310,15 @@ def error_out(msg): sys.exit(1) +def get_installed_semantic_versioned_packages(): + '''Get a list of installed packages which have OpenStack semantic versioning + + :returns List of installed packages + :rtype: [pkg1, pkg2, ...] + ''' + return filter_missing_packages(PACKAGE_CODENAMES.keys()) + + def get_os_codename_install_source(src): '''Derive OpenStack release codename from a given installation source.''' ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] @@ -972,7 +982,9 @@ def _ows_check_charm_func(state, message, charm_func_with_configs): """ if charm_func_with_configs: charm_state, charm_message = charm_func_with_configs() - if charm_state != 'active' and charm_state != 'unknown': + if (charm_state != 'active' and + charm_state != 'unknown' and + charm_state is not None): state = workload_state_compare(state, charm_state) if message: charm_message = charm_message.replace("Incomplete relations: ", diff --git a/lib/ceilometer_utils.py b/lib/ceilometer_utils.py index 838ffe87de7ebf825f5738afb81cb0ea29e38843..940cad2a3efe91fd4eb02953aa33a1af0392236b 100644 --- a/lib/ceilometer_utils.py +++ b/lib/ceilometer_utils.py @@ -19,6 +19,8 @@ import traceback from collections import OrderedDict +from charmhelpers.core.unitdata import kv + from charmhelpers.contrib.openstack import ( templating, context, @@ -75,6 +77,7 @@ POLLING_CONF = "%s/polling.yaml" % CEILOMETER_CONF_DIR CEILOMETER_API_SYSTEMD_CONF = ( '/etc/systemd/system/ceilometer-api.service.d/override.conf' ) +CEILOMETER_UPGRADED = "ceilometer-upgrade-run" HTTPS_APACHE_CONF = "/etc/apache2/sites-available/openstack_https_frontend" HTTPS_APACHE_24_CONF = "/etc/apache2/sites-available/" \ "openstack_https_frontend.conf" @@ -545,6 +548,7 @@ def assess_status_func(configs): """ return make_assess_status_func( configs, resolve_required_interfaces(), + charm_func=check_ceilometer_upgraded, services=services(), ports=determine_ports()) @@ -655,6 +659,10 @@ def ceilometer_upgrade_helper(CONFIGS): 'unexpected error: {}'.format(e.message), outcome='ceilometer-upgrade failed, see traceback.', trace=traceback.format_exc()) + kvstore = kv() + if not kvstore.get(CEILOMETER_UPGRADED, False): + kvstore.set(key=CEILOMETER_UPGRADED, value=True) + kvstore.flush() def ceilometer_upgrade(action=False): @@ -669,3 +677,25 @@ def ceilometer_upgrade(action=False): log("Running ceilomter-upgrade: {}".format(" ".join(cmd)), DEBUG) subprocess.check_call(cmd) log("ceilometer-upgrade succeeded", DEBUG) + + +def check_ceilometer_upgraded(configs): + """Assess status check if ceilometer-upgrade action has run + + When related to gnocchi check that the ceilometer-upgrade action has been + run. Set blocked when action run is still required. Set None None when no + action is required. + + :param configs: The charms main OSConfigRenderer object. + :return: str, str tuple or None, None + """ + + if relation_ids("metric-service"): + kvstore = kv() + if not kvstore.get(CEILOMETER_UPGRADED, False): + log("Action ceilometer-upgrade not yet run, setting status " + "blocked") + return "blocked", ("Run the ceilometer-upgrade action to " + "initialize ceilometer and gnocchi") + # Avoid changing status check + return None, None diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 164d5aa8abbd367a70608526e63fb51d41311aa7..a2da927dd709a36c5fc68f7c53bfd2b4c956be5a 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -45,8 +45,12 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment): self._deploy() u.log.info('Waiting on extended status checks...') - exclude_services = ['mongodb', 'memcached'] - self._auto_wait_for_status(exclude_services=exclude_services) + self.exclude_services = ['mongodb', 'memcached'] + if self._get_openstack_release() >= self.xenial_pike: + # Ceilometer will come up blocked until the ceilometer-upgrade + # action is run + self.exclude_services.append("ceilometer") + self._auto_wait_for_status(exclude_services=self.exclude_services) self.d.sentry.wait() self._initialize_tests() @@ -163,6 +167,7 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment): self.nova_sentry = self.d.sentry['nova-compute'][0] if self._get_openstack_release() >= self.xenial_pike: self.gnocchi_sentry = self.d.sentry['gnocchi'][0] + self.run_ceilometer_upgrade_action() else: self.mongodb_sentry = self.d.sentry['mongodb'][0] u.log.debug('openstack release val: {}'.format( @@ -681,8 +686,13 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment): assert u.status_get(unit)[0] == "active" u.log.debug('OK') - def test_920_ceilometer_upgrade(self): - """Run ceilometer-upgrade""" + def run_ceilometer_upgrade_action(self): + """Run ceilometer-upgrade + + This action will be run early to initialize ceilometer + when gnocchi is related. + Ceilometer will be in a blocked state until this runs. + """ if self._get_openstack_release() < self.xenial_pike: u.log.debug('Not checking ceilometer-upgrade') return @@ -691,4 +701,7 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment): action_id = unit.run_action("ceilometer-upgrade") assert u.wait_on_action(action_id), "ceilometer-upgrade action failed" + # Wait for acivte Unit is ready on ceilometer + self.exclude_services.remove('ceilometer') + self._auto_wait_for_status(exclude_services=self.exclude_services) u.log.debug('OK') diff --git a/unit_tests/test_ceilometer_utils.py b/unit_tests/test_ceilometer_utils.py index 26b6d49bf1bd31b31b8d51b06aa0ae9b1334f7a8..1c531a70246ec481fab4232f932c3fa5d1e0824e 100644 --- a/unit_tests/test_ceilometer_utils.py +++ b/unit_tests/test_ceilometer_utils.py @@ -270,6 +270,7 @@ class CeilometerUtilsTest(CharmTestCase): utils.VERSION_PACKAGE ) + @patch.object(utils, 'check_ceilometer_upgraded') @patch.object(utils, 'resolve_required_interfaces') @patch.object(utils, 'services') @patch.object(utils, 'determine_ports') @@ -278,13 +279,16 @@ class CeilometerUtilsTest(CharmTestCase): make_assess_status_func, determine_ports, services, - resolve_required_interfaces): + resolve_required_interfaces, + check_ceilometer_upgraded): + check_ceilometer_upgraded.return_value = None, None services.return_value = 's1' determine_ports.return_value = 'p1' resolve_required_interfaces.return_value = {'a': ['b']} utils.assess_status_func('test-config') make_assess_status_func.assert_called_once_with( - 'test-config', {'a': ['b']}, services='s1', ports='p1') + 'test-config', {'a': ['b']}, charm_func=check_ceilometer_upgraded, + services='s1', ports='p1') def test_pause_unit_helper(self): with patch.object(utils, '_pause_resume_helper') as prh: @@ -433,3 +437,29 @@ class CeilometerUtilsTest(CharmTestCase): with self.assertRaises(utils.FailedAction): utils.ceilometer_upgrade_helper(self.CONFIGS) mock_ceilometer_upgrade.assert_called_once_with(action=True) + + @patch.object(utils, 'kv') + def test_check_ceilometer_upgraded(self, mock_kv): + self.CONFIGS = MagicMock() + _kv = MagicMock() + mock_kv.return_value = _kv + + # Not related + self.relation_ids.return_value = [] + self.assertEqual( + (None, None), + utils.check_ceilometer_upgraded(self.CONFIGS)) + + # Related not ready + self.relation_ids.return_value = ['metric:1'] + _kv.get.return_value = False + self.assertEqual( + ("blocked", "Run the ceilometer-upgrade action to initialize " + "ceilometer and gnocchi"), + utils.check_ceilometer_upgraded(self.CONFIGS)) + + # Related ready + _kv.get.return_value = True + self.assertEqual( + (None, None), + utils.check_ceilometer_upgraded(self.CONFIGS))