diff --git a/hooks/nova_compute_context.py b/hooks/nova_compute_context.py index b9a27b75..83c7b33f 100644 --- a/hooks/nova_compute_context.py +++ b/hooks/nova_compute_context.py @@ -393,6 +393,19 @@ class InstanceConsoleContext(context.OSContextGenerator): return ctxt +class MetadataServiceContext(context.OSContextGenerator): + + def __call__(self): + ctxt = {} + for rid in relation_ids('neutron-plugin'): + for unit in related_units(rid): + rdata = relation_get(rid=rid, unit=unit) + if 'metadata-shared-secret' in rdata: + ctxt['metadata_shared_secret'] = \ + rdata['metadata-shared-secret'] + return ctxt + + class NeutronComputeContext(context.NeutronContext): interfaces = [] diff --git a/hooks/nova_compute_hooks.py b/hooks/nova_compute_hooks.py index 3243fe5c..952f4938 100755 --- a/hooks/nova_compute_hooks.py +++ b/hooks/nova_compute_hooks.py @@ -21,6 +21,7 @@ from charmhelpers.core.host import ( from charmhelpers.fetch import ( apt_install, + apt_purge, apt_update, filter_installed_packages, ) @@ -324,6 +325,18 @@ def update_nrpe_config(): nrpe_setup.write() +@hooks.hook('neutron-plugin-relation-changed') +@restart_on_change(restart_map()) +def neutron_plugin_changed(): + settings = relation_get() + if 'metadata-shared-secret' in settings: + apt_update() + apt_install('nova-api-metadata', fatal=True) + else: + apt_purge('nova-api-metadata', fatal=True) + CONFIGS.write(NOVA_CONF) + + def main(): try: hooks.execute(sys.argv) diff --git a/hooks/nova_compute_utils.py b/hooks/nova_compute_utils.py index 9d5ce78e..bb61bb29 100644 --- a/hooks/nova_compute_utils.py +++ b/hooks/nova_compute_utils.py @@ -39,6 +39,7 @@ from charmhelpers.contrib.openstack.utils import ( from nova_compute_context import ( CloudComputeContext, + MetadataServiceContext, NovaComputeLibvirtContext, NovaComputeCephContext, NeutronComputeContext, @@ -94,6 +95,7 @@ BASE_RESOURCE_MAP = { service='nova', config_file=NOVA_CONF), InstanceConsoleContext(), + MetadataServiceContext(), HostIPContext()], }, } @@ -203,6 +205,8 @@ def resource_map(): } resource_map.update(CEPH_RESOURCES) + if enable_nova_metadata(): + resource_map[NOVA_CONF]['services'].append('nova-api-metadata') return resource_map @@ -271,6 +275,8 @@ def determine_packages(): except KeyError: log('Unsupported virt-type configured: %s' % virt_type) raise + if enable_nova_metadata(): + packages.append('nova-api-metadata') return packages @@ -488,3 +494,8 @@ def assert_charm_supports_ipv6(): if lsb_release()['DISTRIB_CODENAME'].lower() < "trusty": raise Exception("IPv6 is not supported in the charms for Ubuntu " "versions less than Trusty 14.04") + + +def enable_nova_metadata(): + ctxt = MetadataServiceContext()() + return 'metadata_shared_secret' in ctxt diff --git a/templates/juno/nova.conf b/templates/juno/nova.conf index 86642256..ffe839a5 100644 --- a/templates/juno/nova.conf +++ b/templates/juno/nova.conf @@ -38,6 +38,11 @@ my_ip = {{ host_ip }} glance_api_servers = {{ glance_api_servers }} {% endif -%} +{% if metadata_shared_secret -%} +neutron_metadata_proxy_shared_secret = {{ metadata_shared_secret }} +service_neutron_metadata_proxy=True +{% endif -%} + {% if console_vnc_type -%} vnc_enabled = True novnc_enabled = True diff --git a/templates/kilo/nova.conf b/templates/kilo/nova.conf index e24cec4e..83aee854 100644 --- a/templates/kilo/nova.conf +++ b/templates/kilo/nova.conf @@ -26,6 +26,11 @@ libvirt_use_virtio_for_bridges=False libvirt_disk_prefix=vd {% endif -%} +{% if metadata_shared_secret -%} +neutron_metadata_proxy_shared_secret = {{ metadata_shared_secret }} +service_neutron_metadata_proxy=True +{% endif -%} + {% if console_vnc_type -%} vnc_enabled = True novnc_enabled = True diff --git a/unit_tests/test_nova_compute_contexts.py b/unit_tests/test_nova_compute_contexts.py index 99a6b19c..e5987de3 100644 --- a/unit_tests/test_nova_compute_contexts.py +++ b/unit_tests/test_nova_compute_contexts.py @@ -233,3 +233,11 @@ class NovaComputeContextTests(CharmTestCase): self.assertEquals( {'host_ip': '172.24.0.79'}, host_ip()) self.unit_get.assert_called_with('private-address') + + def test_metadata_service_ctxt(self): + self.relation_ids.return_value = 'neutron-plugin:0' + self.related_units.return_value = 'neutron-openvswitch/0' + self.test_relation.set({'metadata-shared-secret': 'shared_secret'}) + metadatactxt = context.MetadataServiceContext() + self.assertEqual(metadatactxt(), {'metadata_shared_secret': + 'shared_secret'}) diff --git a/unit_tests/test_nova_compute_hooks.py b/unit_tests/test_nova_compute_hooks.py index b01d8169..6236970b 100644 --- a/unit_tests/test_nova_compute_hooks.py +++ b/unit_tests/test_nova_compute_hooks.py @@ -378,3 +378,12 @@ class NovaComputeRelationsTests(CharmTestCase): call('/etc/nova/nova.conf'), ] self.assertEquals(ex, configs.write.call_args_list) + + @patch.object(hooks, 'CONFIGS') + def test_neutron_plugin_changed(self, configs): + self.relation_get.return_value = {'metadata-shared-secret': + 'sharedsecret'} + hooks.neutron_plugin_changed() + self.assertTrue(self.apt_update.called) + self.apt_install.assert_called_with('nova-api-metadata', fatal=True) + configs.write.assert_called_with('/etc/nova/nova.conf') diff --git a/unit_tests/test_nova_compute_utils.py b/unit_tests/test_nova_compute_utils.py index bdc26b76..c6217db4 100644 --- a/unit_tests/test_nova_compute_utils.py +++ b/unit_tests/test_nova_compute_utils.py @@ -24,7 +24,8 @@ TO_PATCH = [ 'relation_ids', 'relation_get', 'mkdir', - 'install_alternative' + 'install_alternative', + 'MetadataServiceContext', ] OVS_PKGS = [ @@ -41,8 +42,10 @@ class NovaComputeUtilsTests(CharmTestCase): super(NovaComputeUtilsTests, self).setUp(utils, TO_PATCH) self.config.side_effect = self.test_config.get + @patch.object(utils, 'enable_nova_metadata') @patch.object(utils, 'network_manager') - def test_determine_packages_nova_network(self, net_man): + def test_determine_packages_nova_network(self, net_man, en_meta): + en_meta.return_value = False net_man.return_value = 'flatdhcpmanager' self.relation_ids.return_value = [] result = utils.determine_packages() @@ -53,9 +56,11 @@ class NovaComputeUtilsTests(CharmTestCase): ] self.assertEquals(ex, result) + @patch.object(utils, 'enable_nova_metadata') @patch.object(utils, 'neutron_plugin') @patch.object(utils, 'network_manager') - def test_determine_packages_quantum(self, net_man, n_plugin): + def test_determine_packages_quantum(self, net_man, n_plugin, en_meta): + en_meta.return_value = False self.neutron_plugin_attribute.return_value = OVS_PKGS net_man.return_value = 'quantum' n_plugin.return_value = 'ovs' @@ -64,9 +69,11 @@ class NovaComputeUtilsTests(CharmTestCase): ex = utils.BASE_PACKAGES + OVS_PKGS_FLAT + ['nova-compute-kvm'] self.assertEquals(ex, result) + @patch.object(utils, 'enable_nova_metadata') @patch.object(utils, 'neutron_plugin') @patch.object(utils, 'network_manager') - def test_determine_packages_quantum_ceph(self, net_man, n_plugin): + def test_determine_packages_quantum_ceph(self, net_man, n_plugin, en_meta): + en_meta.return_value = False self.neutron_plugin_attribute.return_value = OVS_PKGS net_man.return_value = 'quantum' n_plugin.return_value = 'ovs' @@ -76,6 +83,18 @@ class NovaComputeUtilsTests(CharmTestCase): ['ceph-common', 'nova-compute-kvm']) self.assertEquals(ex, result) + @patch.object(utils, 'enable_nova_metadata') + @patch.object(utils, 'neutron_plugin') + @patch.object(utils, 'network_manager') + def test_determine_packages_metadata(self, net_man, n_plugin, en_meta): + en_meta.return_value = True + self.neutron_plugin_attribute.return_value = OVS_PKGS + net_man.return_value = 'bob' + n_plugin.return_value = 'ovs' + self.relation_ids.return_value = [] + result = utils.determine_packages() + self.assertTrue('nova-api-metadata' in result) + @patch.object(utils, 'network_manager') def test_resource_map_nova_network_no_multihost(self, net_man): self.skipTest('skipped until contexts are properly mocked') @@ -172,6 +191,17 @@ class NovaComputeUtilsTests(CharmTestCase): result = utils.resource_map() self.assertTrue('/etc/neutron/neutron.conf' not in result) + @patch.object(utils, 'enable_nova_metadata') + @patch.object(utils, 'neutron_plugin') + @patch.object(utils, 'network_manager') + def test_resource_map_metadata(self, net_man, _plugin, _metadata): + _metadata.return_value = True + net_man.return_value = 'bob' + _plugin.return_value = 'ovs' + self.relation_ids.return_value = [] + result = utils.resource_map()['/etc/nova/nova.conf']['services'] + self.assertTrue('nova-api-metadata' in result) + def fake_user(self, username='foo'): user = MagicMock() user.pw_dir = '/home/' + username @@ -375,3 +405,17 @@ class NovaComputeUtilsTests(CharmTestCase): 'secret-set-value', '--secret', compute_context.CEPH_SECRET_UUID, '--base64', key]) + + def test_enable_nova_metadata(self): + class DummyContext(): + + def __init__(self, return_value): + self.return_value = return_value + + def __call__(self): + return self.return_value + + self.MetadataServiceContext.return_value = \ + DummyContext(return_value={'metadata_shared_secret': + 'sharedsecret'}) + self.assertEqual(utils.enable_nova_metadata(), True)