[gnuoy,r=james-page] Add support for Neutron DVR

When Neutron DVR is enabled, nova-compute nodes must run the metadata-api
service for instances on the local hypervisor.
This commit is contained in:
James Page 2015-03-31 08:32:43 +01:00
commit 6155d08997
8 changed files with 112 additions and 4 deletions

View File

@ -393,6 +393,19 @@ class InstanceConsoleContext(context.OSContextGenerator):
return ctxt 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): class NeutronComputeContext(context.NeutronContext):
interfaces = [] interfaces = []

View File

@ -21,6 +21,7 @@ from charmhelpers.core.host import (
from charmhelpers.fetch import ( from charmhelpers.fetch import (
apt_install, apt_install,
apt_purge,
apt_update, apt_update,
filter_installed_packages, filter_installed_packages,
) )
@ -324,6 +325,18 @@ def update_nrpe_config():
nrpe_setup.write() 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(): def main():
try: try:
hooks.execute(sys.argv) hooks.execute(sys.argv)

View File

@ -39,6 +39,7 @@ from charmhelpers.contrib.openstack.utils import (
from nova_compute_context import ( from nova_compute_context import (
CloudComputeContext, CloudComputeContext,
MetadataServiceContext,
NovaComputeLibvirtContext, NovaComputeLibvirtContext,
NovaComputeCephContext, NovaComputeCephContext,
NeutronComputeContext, NeutronComputeContext,
@ -94,6 +95,7 @@ BASE_RESOURCE_MAP = {
service='nova', service='nova',
config_file=NOVA_CONF), config_file=NOVA_CONF),
InstanceConsoleContext(), InstanceConsoleContext(),
MetadataServiceContext(),
HostIPContext()], HostIPContext()],
}, },
} }
@ -203,6 +205,8 @@ def resource_map():
} }
resource_map.update(CEPH_RESOURCES) resource_map.update(CEPH_RESOURCES)
if enable_nova_metadata():
resource_map[NOVA_CONF]['services'].append('nova-api-metadata')
return resource_map return resource_map
@ -271,6 +275,8 @@ def determine_packages():
except KeyError: except KeyError:
log('Unsupported virt-type configured: %s' % virt_type) log('Unsupported virt-type configured: %s' % virt_type)
raise raise
if enable_nova_metadata():
packages.append('nova-api-metadata')
return packages return packages
@ -488,3 +494,8 @@ def assert_charm_supports_ipv6():
if lsb_release()['DISTRIB_CODENAME'].lower() < "trusty": if lsb_release()['DISTRIB_CODENAME'].lower() < "trusty":
raise Exception("IPv6 is not supported in the charms for Ubuntu " raise Exception("IPv6 is not supported in the charms for Ubuntu "
"versions less than Trusty 14.04") "versions less than Trusty 14.04")
def enable_nova_metadata():
ctxt = MetadataServiceContext()()
return 'metadata_shared_secret' in ctxt

View File

@ -38,6 +38,11 @@ my_ip = {{ host_ip }}
glance_api_servers = {{ glance_api_servers }} glance_api_servers = {{ glance_api_servers }}
{% endif -%} {% endif -%}
{% if metadata_shared_secret -%}
neutron_metadata_proxy_shared_secret = {{ metadata_shared_secret }}
service_neutron_metadata_proxy=True
{% endif -%}
{% if console_vnc_type -%} {% if console_vnc_type -%}
vnc_enabled = True vnc_enabled = True
novnc_enabled = True novnc_enabled = True

View File

@ -26,6 +26,11 @@ libvirt_use_virtio_for_bridges=False
libvirt_disk_prefix=vd libvirt_disk_prefix=vd
{% endif -%} {% endif -%}
{% if metadata_shared_secret -%}
neutron_metadata_proxy_shared_secret = {{ metadata_shared_secret }}
service_neutron_metadata_proxy=True
{% endif -%}
{% if console_vnc_type -%} {% if console_vnc_type -%}
vnc_enabled = True vnc_enabled = True
novnc_enabled = True novnc_enabled = True

View File

@ -233,3 +233,11 @@ class NovaComputeContextTests(CharmTestCase):
self.assertEquals( self.assertEquals(
{'host_ip': '172.24.0.79'}, host_ip()) {'host_ip': '172.24.0.79'}, host_ip())
self.unit_get.assert_called_with('private-address') 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'})

View File

@ -378,3 +378,12 @@ class NovaComputeRelationsTests(CharmTestCase):
call('/etc/nova/nova.conf'), call('/etc/nova/nova.conf'),
] ]
self.assertEquals(ex, configs.write.call_args_list) 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')

View File

@ -24,7 +24,8 @@ TO_PATCH = [
'relation_ids', 'relation_ids',
'relation_get', 'relation_get',
'mkdir', 'mkdir',
'install_alternative' 'install_alternative',
'MetadataServiceContext',
] ]
OVS_PKGS = [ OVS_PKGS = [
@ -41,8 +42,10 @@ class NovaComputeUtilsTests(CharmTestCase):
super(NovaComputeUtilsTests, self).setUp(utils, TO_PATCH) super(NovaComputeUtilsTests, self).setUp(utils, TO_PATCH)
self.config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get
@patch.object(utils, 'enable_nova_metadata')
@patch.object(utils, 'network_manager') @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' net_man.return_value = 'flatdhcpmanager'
self.relation_ids.return_value = [] self.relation_ids.return_value = []
result = utils.determine_packages() result = utils.determine_packages()
@ -53,9 +56,11 @@ class NovaComputeUtilsTests(CharmTestCase):
] ]
self.assertEquals(ex, result) self.assertEquals(ex, result)
@patch.object(utils, 'enable_nova_metadata')
@patch.object(utils, 'neutron_plugin') @patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager') @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 self.neutron_plugin_attribute.return_value = OVS_PKGS
net_man.return_value = 'quantum' net_man.return_value = 'quantum'
n_plugin.return_value = 'ovs' n_plugin.return_value = 'ovs'
@ -64,9 +69,11 @@ class NovaComputeUtilsTests(CharmTestCase):
ex = utils.BASE_PACKAGES + OVS_PKGS_FLAT + ['nova-compute-kvm'] ex = utils.BASE_PACKAGES + OVS_PKGS_FLAT + ['nova-compute-kvm']
self.assertEquals(ex, result) self.assertEquals(ex, result)
@patch.object(utils, 'enable_nova_metadata')
@patch.object(utils, 'neutron_plugin') @patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager') @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 self.neutron_plugin_attribute.return_value = OVS_PKGS
net_man.return_value = 'quantum' net_man.return_value = 'quantum'
n_plugin.return_value = 'ovs' n_plugin.return_value = 'ovs'
@ -76,6 +83,18 @@ class NovaComputeUtilsTests(CharmTestCase):
['ceph-common', 'nova-compute-kvm']) ['ceph-common', 'nova-compute-kvm'])
self.assertEquals(ex, result) 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') @patch.object(utils, 'network_manager')
def test_resource_map_nova_network_no_multihost(self, net_man): def test_resource_map_nova_network_no_multihost(self, net_man):
self.skipTest('skipped until contexts are properly mocked') self.skipTest('skipped until contexts are properly mocked')
@ -172,6 +191,17 @@ class NovaComputeUtilsTests(CharmTestCase):
result = utils.resource_map() result = utils.resource_map()
self.assertTrue('/etc/neutron/neutron.conf' not in result) 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'): def fake_user(self, username='foo'):
user = MagicMock() user = MagicMock()
user.pw_dir = '/home/' + username user.pw_dir = '/home/' + username
@ -375,3 +405,17 @@ class NovaComputeUtilsTests(CharmTestCase):
'secret-set-value', '--secret', 'secret-set-value', '--secret',
compute_context.CEPH_SECRET_UUID, compute_context.CEPH_SECRET_UUID,
'--base64', key]) '--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)