From 0f9ada1713977218fa0a36ed855eafa4ea975b9c Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 8 Oct 2018 12:30:22 +0000 Subject: [PATCH] Fix assess status for super-conductor In a cells deployment its possible that the top-level nova-cloud-controller will have no compute nodes associated with it. This change fixes the work load status in that scenario. There is also a drive by fix to return empty contexts for the cell db and amqp contexts if the relation do not yet exists. Change-Id: Ia8eeccb6794dd016185eb0cfb05339b76cef9348 --- hooks/nova_cc_context.py | 17 +++++++++++++ hooks/nova_cc_utils.py | 9 ++++++- unit_tests/test_nova_cc_contexts.py | 37 +++++++++++++++++++++++++++++ unit_tests/test_nova_cc_utils.py | 5 ++-- 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/hooks/nova_cc_context.py b/hooks/nova_cc_context.py index 70e9b6ce..9f5fcfca 100644 --- a/hooks/nova_cc_context.py +++ b/hooks/nova_cc_context.py @@ -75,6 +75,23 @@ class ApacheSSLContext(context.ApacheSSLContext): return super(ApacheSSLContext, self).__call__() +class NovaCellV2Context(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) + if set(required_keys).issubset(data.keys()): + ctxt[data['cell-name']] = { + 'amqp_service': data['amqp-service'], + 'db_service': data['db-service']} + return ctxt + + class NovaCellV2SharedDBContext(context.OSContextGenerator): interfaces = ['shared-db'] diff --git a/hooks/nova_cc_utils.py b/hooks/nova_cc_utils.py index 35eda470..deb9c397 100644 --- a/hooks/nova_cc_utils.py +++ b/hooks/nova_cc_utils.py @@ -124,7 +124,7 @@ REQUIRED_INTERFACES = { 'messaging': ['amqp'], 'identity': ['identity-service'], 'image': ['image-service'], - 'compute': ['nova-compute'], + 'compute': ['nova-compute', 'nova-cell-api'], } # removed from original: charm-helper-sh @@ -1364,6 +1364,9 @@ def assess_status(configs): @param configs: a templating.OSConfigRenderer() object @returns None - this function is executed for its side-effect """ + # Add the cell context as its not used for rendering files, only for + # assessing status. + configs.register('', [nova_cc_context.NovaCellV2Context()]) assess_status_func(configs)() os_application_version_set(VERSION_PACKAGE) @@ -1515,6 +1518,8 @@ def get_cell_db_context(db_service): db_rid = relation_id( relation_name='shared-db-cell', service_or_unit=db_service) + if not db_rid: + return {} return context.SharedDBContext( relation_prefix='nova', ssl_dir=NOVA_CONF_DIR, @@ -1526,6 +1531,8 @@ def get_cell_amqp_context(amqp_service): amq_rid = relation_id( relation_name='amqp-cell', service_or_unit=amqp_service) + if not amq_rid: + return {} return context.AMQPContext( ssl_dir=NOVA_CONF_DIR, relation_id=amq_rid)() diff --git a/unit_tests/test_nova_cc_contexts.py b/unit_tests/test_nova_cc_contexts.py index cbf2ff4a..4167c3c3 100644 --- a/unit_tests/test_nova_cc_contexts.py +++ b/unit_tests/test_nova_cc_contexts.py @@ -438,3 +438,40 @@ class NovaComputeContextTests(CharmTestCase): ctxt = context.NovaMetadataContext()() self.assertEqual(ctxt, {'enable_metadata': False}) + + def test_NovaCellV2Context(self): + settings = {'cell-name': 'cell32', + 'amqp-service': 'rabbitmq-cell2', + 'db-service': 'percona-cell2'} + + def fake_rel_get(attribute=None, unit=None, rid=None): + if attribute: + return settings.get(attribute) + + return settings + + self.relation_get.side_effect = fake_rel_get + self.relation_ids.return_value = ['nova-cell:0'] + self.related_units.return_value = ['nova-cell-conductor/0'] + ctxt = context.NovaCellV2Context()() + self.assertEqual( + ctxt, + {'cell32': { + 'amqp_service': 'rabbitmq-cell2', + 'db_service': 'percona-cell2'}}) + + def test_NovaCellV2Context_missing_amqp(self): + settings = {'cell-name': 'cell32', + 'db-service': 'percona-cell2'} + + def fake_rel_get(attribute=None, unit=None, rid=None): + if attribute: + return settings.get(attribute) + + return settings + + self.relation_get.side_effect = fake_rel_get + self.relation_ids.return_value = ['nova-cell:0'] + self.related_units.return_value = ['nova-cell-conductor/0'] + ctxt = context.NovaCellV2Context()() + self.assertEqual(ctxt, {}) diff --git a/unit_tests/test_nova_cc_utils.py b/unit_tests/test_nova_cc_utils.py index 5e6ccad0..6d429022 100644 --- a/unit_tests/test_nova_cc_utils.py +++ b/unit_tests/test_nova_cc_utils.py @@ -1035,10 +1035,11 @@ class NovaCCUtilsTests(CharmTestCase): def test_assess_status(self): with patch.object(utils, 'assess_status_func') as asf: + configs = MagicMock() callee = MagicMock() asf.return_value = callee - utils.assess_status('test-config') - asf.assert_called_once_with('test-config') + utils.assess_status(configs) + asf.assert_called_once_with(configs) callee.assert_called_once_with() self.os_application_version_set.assert_called_with( utils.VERSION_PACKAGE