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

This commit is contained in:
James Page 2015-03-31 08:58:21 +01:00
commit c85c3170bc
8 changed files with 165 additions and 22 deletions

View File

@ -99,6 +99,7 @@ def core_plugin():
class L3AgentContext(OSContextGenerator): class L3AgentContext(OSContextGenerator):
def __call__(self): def __call__(self):
api_settings = NeutronAPIContext()()
ctxt = {} ctxt = {}
if config('run-internal-router') == 'leader': if config('run-internal-router') == 'leader':
ctxt['handle_internal_only_router'] = eligible_leader(None) ctxt['handle_internal_only_router'] = eligible_leader(None)
@ -111,16 +112,19 @@ class L3AgentContext(OSContextGenerator):
if config('external-network-id'): if config('external-network-id'):
ctxt['ext_net_id'] = config('external-network-id') ctxt['ext_net_id'] = config('external-network-id')
if config('plugin'): if config('plugin'):
ctxt['plugin'] = config('plugin') ctxt['plugin'] = config('plugin')
if api_settings['enable_dvr']:
ctxt['agent_mode'] = 'dvr_snat'
else:
ctxt['agent_mode'] = 'legacy'
return ctxt return ctxt
class QuantumGatewayContext(OSContextGenerator): class QuantumGatewayContext(OSContextGenerator):
def __call__(self): def __call__(self):
neutron_api_settings = NeutronAPIContext()() api_settings = NeutronAPIContext()()
ctxt = { ctxt = {
'shared_secret': get_shared_secret(), 'shared_secret': get_shared_secret(),
'local_ip': 'local_ip':
@ -131,9 +135,11 @@ class QuantumGatewayContext(OSContextGenerator):
'debug': config('debug'), 'debug': config('debug'),
'verbose': config('verbose'), 'verbose': config('verbose'),
'instance_mtu': config('instance-mtu'), 'instance_mtu': config('instance-mtu'),
'l2_population': neutron_api_settings['l2_population'], 'l2_population': api_settings['l2_population'],
'enable_dvr': api_settings['enable_dvr'],
'enable_l3ha': api_settings['enable_l3ha'],
'overlay_network_type': 'overlay_network_type':
neutron_api_settings['overlay_network_type'], api_settings['overlay_network_type'],
} }
mappings = config('bridge-mappings') mappings = config('bridge-mappings')
@ -147,7 +153,7 @@ class QuantumGatewayContext(OSContextGenerator):
ctxt['network_providers'] = ' '.join(providers) ctxt['network_providers'] = ' '.join(providers)
ctxt['vlan_ranges'] = vlan_ranges ctxt['vlan_ranges'] = vlan_ranges
net_dev_mtu = neutron_api_settings.get('network_device_mtu') net_dev_mtu = api_settings['network_device_mtu']
if net_dev_mtu: if net_dev_mtu:
ctxt['network_device_mtu'] = net_dev_mtu ctxt['network_device_mtu'] = net_dev_mtu
ctxt['veth_mtu'] = net_dev_mtu ctxt['veth_mtu'] = net_dev_mtu

View File

@ -40,6 +40,7 @@ from charmhelpers.contrib.charmsupport import nrpe
import sys import sys
from quantum_utils import ( from quantum_utils import (
L3HA_PACKAGES,
register_configs, register_configs,
restart_map, restart_map,
services, services,
@ -56,7 +57,8 @@ from quantum_utils import (
install_legacy_ha_files, install_legacy_ha_files,
cleanup_ovs_netns, cleanup_ovs_netns,
reassign_agent_resources, reassign_agent_resources,
stop_neutron_ha_monitor_daemon stop_neutron_ha_monitor_daemon,
use_l3ha,
) )
hooks = Hooks() hooks = Hooks()
@ -193,13 +195,23 @@ def amqp_departed():
'pgsql-db-relation-changed', 'pgsql-db-relation-changed',
'amqp-relation-changed', 'amqp-relation-changed',
'cluster-relation-changed', 'cluster-relation-changed',
'cluster-relation-joined', 'cluster-relation-joined')
'neutron-plugin-api-relation-changed')
@restart_on_change(restart_map()) @restart_on_change(restart_map())
def db_amqp_changed(): def db_amqp_changed():
CONFIGS.write_all() CONFIGS.write_all()
@hooks.hook('neutron-plugin-api-relation-changed')
@restart_on_change(restart_map())
def neutron_plugin_api_changed():
if use_l3ha():
apt_update()
apt_install(L3HA_PACKAGES, fatal=True)
else:
apt_purge(L3HA_PACKAGES, fatal=True)
CONFIGS.write_all()
@hooks.hook('quantum-network-service-relation-changed') @hooks.hook('quantum-network-service-relation-changed')
@restart_on_change(restart_map()) @restart_on_change(restart_map())
def nm_changed(): def nm_changed():

View File

@ -42,6 +42,7 @@ from charmhelpers.contrib.openstack.neutron import (
import charmhelpers.contrib.openstack.context as context import charmhelpers.contrib.openstack.context as context
from charmhelpers.contrib.openstack.context import ( from charmhelpers.contrib.openstack.context import (
SyslogContext, SyslogContext,
NeutronAPIContext,
NetworkServiceContext, NetworkServiceContext,
ExternalPortContext, ExternalPortContext,
PhyNICMTUContext, PhyNICMTUContext,
@ -55,7 +56,7 @@ from quantum_contexts import (
networking_name, networking_name,
QuantumGatewayContext, QuantumGatewayContext,
L3AgentContext, L3AgentContext,
remap_plugin remap_plugin,
) )
from charmhelpers.contrib.openstack.neutron import ( from charmhelpers.contrib.openstack.neutron import (
parse_bridge_mappings, parse_bridge_mappings,
@ -171,6 +172,7 @@ LEGACY_FILES_MAP = {
}, },
} }
LEGACY_RES_MAP = ['res_monitor'] LEGACY_RES_MAP = ['res_monitor']
L3HA_PACKAGES = ['keepalived']
def get_early_packages(): def get_early_packages():
@ -201,15 +203,26 @@ def get_packages():
packages.append('openswan') packages.append('openswan')
if source >= 'kilo': if source >= 'kilo':
packages.append('python-neutron-fwaas') packages.append('python-neutron-fwaas')
packages.extend(determine_l3ha_packages())
return packages return packages
def determine_l3ha_packages():
if use_l3ha():
return L3HA_PACKAGES
return []
def get_common_package(): def get_common_package():
if get_os_codename_package('quantum-common', fatal=False) is not None: if get_os_codename_package('quantum-common', fatal=False) is not None:
return 'quantum-common' return 'quantum-common'
else: else:
return 'neutron-common' return 'neutron-common'
def use_l3ha():
return NeutronAPIContext()()['enable_l3ha']
EXT_PORT_CONF = '/etc/init/ext-port.conf' EXT_PORT_CONF = '/etc/init/ext-port.conf'
PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf' PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf'
TEMPLATES = 'templates' TEMPLATES = 'templates'
@ -322,7 +335,7 @@ NEUTRON_OVS_CONFIG_FILES = {
'hook_contexts': [NetworkServiceContext(), 'hook_contexts': [NetworkServiceContext(),
L3AgentContext(), L3AgentContext(),
QuantumGatewayContext()], QuantumGatewayContext()],
'services': ['neutron-l3-agent'] 'services': ['neutron-l3-agent', 'neutron-vpn-agent']
}, },
NEUTRON_METERING_AGENT_CONF: { NEUTRON_METERING_AGENT_CONF: {
'hook_contexts': [QuantumGatewayContext()], 'hook_contexts': [QuantumGatewayContext()],
@ -340,7 +353,7 @@ NEUTRON_OVS_CONFIG_FILES = {
}, },
NEUTRON_FWAAS_CONF: { NEUTRON_FWAAS_CONF: {
'hook_contexts': [QuantumGatewayContext()], 'hook_contexts': [QuantumGatewayContext()],
'services': ['neutron-l3-agent'] 'services': ['neutron-l3-agent', 'neutron-vpn-agent']
}, },
NEUTRON_OVS_PLUGIN_CONF: { NEUTRON_OVS_PLUGIN_CONF: {
'hook_contexts': [QuantumGatewayContext()], 'hook_contexts': [QuantumGatewayContext()],

View File

@ -0,0 +1,25 @@
###############################################################################
# [ WARNING ]
# Configuration file maintained by Juju. Local changes may be overwritten.
###############################################################################
[DEFAULT]
interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver
auth_url = {{ service_protocol }}://{{ keystone_host }}:{{ service_port }}/v2.0
auth_region = {{ region }}
admin_tenant_name = {{ service_tenant }}
admin_user = {{ service_username }}
admin_password = {{ service_password }}
root_helper = sudo /usr/bin/neutron-rootwrap /etc/neutron/rootwrap.conf
handle_internal_only_routers = {{ handle_internal_only_router }}
{% if plugin == 'n1kv' %}
l3_agent_manager = neutron.agent.l3_agent.L3NATAgentWithStateReport
external_network_bridge = br-int
ovs_use_veth = False
use_namespaces = True
{% else %}
ovs_use_veth = True
{% endif %}
{% if ext_net_id -%}
gateway_external_network_id = {{ ext_net_id }}
{% endif -%}
agent_mode = {{ agent_mode }}

View File

@ -0,0 +1,36 @@
###############################################################################
# [ WARNING ]
# Configuration file maintained by Juju. Local changes may be overwritten.
###############################################################################
[ml2]
type_drivers = gre,vxlan,vlan,flat
tenant_network_types = gre,vxlan,vlan,flat
mechanism_drivers = openvswitch,l2population
[ml2_type_gre]
tunnel_id_ranges = 1:1000
[ml2_type_vxlan]
vni_ranges = 1001:2000
[ml2_type_vlan]
network_vlan_ranges = {{ vlan_ranges }}
[ml2_type_flat]
flat_networks = {{ network_providers }}
[ovs]
enable_tunneling = True
local_ip = {{ local_ip }}
bridge_mappings = {{ bridge_mappings }}
[agent]
tunnel_types = {{ overlay_network_type }}
l2_population = {{ l2_population }}
enable_distributed_routing = {{ enable_dvr }}
{% if veth_mtu -%}
veth_mtu = {{ veth_mtu }}
{% endif %}
[securitygroup]
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver

View File

@ -38,6 +38,15 @@ def patch_open():
yield mock_open, mock_file yield mock_open, mock_file
class DummyNeutronAPIContext():
def __init__(self, return_value):
self.return_value = return_value
def __call__(self):
return self.return_value
class TestL3AgentContext(CharmTestCase): class TestL3AgentContext(CharmTestCase):
def setUp(self): def setUp(self):
@ -45,32 +54,51 @@ class TestL3AgentContext(CharmTestCase):
TO_PATCH) TO_PATCH)
self.config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get
def test_no_ext_netid(self): @patch('quantum_contexts.NeutronAPIContext')
def test_no_ext_netid(self, _NeutronAPIContext):
_NeutronAPIContext.return_value = \
DummyNeutronAPIContext(return_value={'enable_dvr': False})
self.test_config.set('run-internal-router', 'none') self.test_config.set('run-internal-router', 'none')
self.test_config.set('external-network-id', '') self.test_config.set('external-network-id', '')
self.eligible_leader.return_value = False self.eligible_leader.return_value = False
self.assertEquals(quantum_contexts.L3AgentContext()(), self.assertEquals(quantum_contexts.L3AgentContext()(),
{'handle_internal_only_router': False, {'agent_mode': 'legacy',
'handle_internal_only_router': False,
'plugin': 'ovs'}) 'plugin': 'ovs'})
def test_hior_leader(self): @patch('quantum_contexts.NeutronAPIContext')
def test_hior_leader(self, _NeutronAPIContext):
_NeutronAPIContext.return_value = \
DummyNeutronAPIContext(return_value={'enable_dvr': False})
self.test_config.set('run-internal-router', 'leader') self.test_config.set('run-internal-router', 'leader')
self.test_config.set('external-network-id', 'netid') self.test_config.set('external-network-id', 'netid')
self.eligible_leader.return_value = True self.eligible_leader.return_value = True
self.assertEquals(quantum_contexts.L3AgentContext()(), self.assertEquals(quantum_contexts.L3AgentContext()(),
{'handle_internal_only_router': True, {'agent_mode': 'legacy',
'handle_internal_only_router': True,
'ext_net_id': 'netid', 'ext_net_id': 'netid',
'plugin': 'ovs'}) 'plugin': 'ovs'})
def test_hior_all(self): @patch('quantum_contexts.NeutronAPIContext')
def test_hior_all(self, _NeutronAPIContext):
_NeutronAPIContext.return_value = \
DummyNeutronAPIContext(return_value={'enable_dvr': False})
self.test_config.set('run-internal-router', 'all') self.test_config.set('run-internal-router', 'all')
self.test_config.set('external-network-id', 'netid') self.test_config.set('external-network-id', 'netid')
self.eligible_leader.return_value = True self.eligible_leader.return_value = True
self.assertEquals(quantum_contexts.L3AgentContext()(), self.assertEquals(quantum_contexts.L3AgentContext()(),
{'handle_internal_only_router': True, {'agent_mode': 'legacy',
'handle_internal_only_router': True,
'ext_net_id': 'netid', 'ext_net_id': 'netid',
'plugin': 'ovs'}) 'plugin': 'ovs'})
@patch('quantum_contexts.NeutronAPIContext')
def test_dvr(self, _NeutronAPIContext):
_NeutronAPIContext.return_value = \
DummyNeutronAPIContext(return_value={'enable_dvr': True})
self.assertEquals(quantum_contexts.L3AgentContext()()['agent_mode'],
'dvr_snat')
class TestQuantumGatewayContext(CharmTestCase): class TestQuantumGatewayContext(CharmTestCase):
@ -78,6 +106,7 @@ class TestQuantumGatewayContext(CharmTestCase):
super(TestQuantumGatewayContext, self).setUp(quantum_contexts, super(TestQuantumGatewayContext, self).setUp(quantum_contexts,
TO_PATCH) TO_PATCH)
self.config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get
self.maxDiff = None
@patch('charmhelpers.contrib.openstack.context.relation_get') @patch('charmhelpers.contrib.openstack.context.relation_get')
@patch('charmhelpers.contrib.openstack.context.related_units') @patch('charmhelpers.contrib.openstack.context.related_units')
@ -85,6 +114,11 @@ class TestQuantumGatewayContext(CharmTestCase):
@patch.object(quantum_contexts, 'get_shared_secret') @patch.object(quantum_contexts, 'get_shared_secret')
@patch.object(quantum_contexts, 'get_host_ip') @patch.object(quantum_contexts, 'get_host_ip')
def test_all(self, _host_ip, _secret, _rids, _runits, _rget): def test_all(self, _host_ip, _secret, _rids, _runits, _rget):
rdata = {'l2-population': 'True',
'enable-dvr': 'True',
'overlay-network-type': 'gre',
'enable-l3ha': 'True',
'network-device-mtu': 9000}
self.test_config.set('plugin', 'ovs') self.test_config.set('plugin', 'ovs')
self.test_config.set('debug', False) self.test_config.set('debug', False)
self.test_config.set('verbose', True) self.test_config.set('verbose', True)
@ -94,7 +128,6 @@ class TestQuantumGatewayContext(CharmTestCase):
# Provided by neutron-api relation # Provided by neutron-api relation
_rids.return_value = ['neutron-plugin-api:0'] _rids.return_value = ['neutron-plugin-api:0']
_runits.return_value = ['neutron-api/0'] _runits.return_value = ['neutron-api/0']
rdata = {'network-device-mtu': 9000, 'l2-population': 'False'}
_rget.side_effect = lambda *args, **kwargs: rdata _rget.side_effect = lambda *args, **kwargs: rdata
self.get_os_codename_install_source.return_value = 'folsom' self.get_os_codename_install_source.return_value = 'folsom'
_host_ip.return_value = '10.5.0.1' _host_ip.return_value = '10.5.0.1'
@ -102,6 +135,8 @@ class TestQuantumGatewayContext(CharmTestCase):
ctxt = quantum_contexts.QuantumGatewayContext()() ctxt = quantum_contexts.QuantumGatewayContext()()
self.assertEquals(ctxt, { self.assertEquals(ctxt, {
'shared_secret': 'testsecret', 'shared_secret': 'testsecret',
'enable_dvr': True,
'enable_l3ha': True,
'local_ip': '10.5.0.1', 'local_ip': '10.5.0.1',
'instance_mtu': 1420, 'instance_mtu': 1420,
'core_plugin': "quantum.plugins.openvswitch.ovs_quantum_plugin." 'core_plugin': "quantum.plugins.openvswitch.ovs_quantum_plugin."
@ -109,7 +144,7 @@ class TestQuantumGatewayContext(CharmTestCase):
'plugin': 'ovs', 'plugin': 'ovs',
'debug': False, 'debug': False,
'verbose': True, 'verbose': True,
'l2_population': False, 'l2_population': True,
'overlay_network_type': 'gre', 'overlay_network_type': 'gre',
'bridge_mappings': 'physnet1:br-data', 'bridge_mappings': 'physnet1:br-data',
'network_providers': 'physnet1 physnet2', 'network_providers': 'physnet1 physnet2',

View File

@ -48,6 +48,7 @@ TO_PATCH = [
'remove_legacy_ha_files', 'remove_legacy_ha_files',
'cleanup_ovs_netns', 'cleanup_ovs_netns',
'stop_neutron_ha_monitor_daemon', 'stop_neutron_ha_monitor_daemon',
'use_l3ha',
] ]
@ -246,6 +247,12 @@ class TestQuantumHooks(CharmTestCase):
self.assertTrue(self.CONFIGS.write_all.called) self.assertTrue(self.CONFIGS.write_all.called)
self.install_ca_cert.assert_called_with('cert') self.install_ca_cert.assert_called_with('cert')
def test_neutron_plugin_changed(self):
self.use_l3ha.return_value = True
self._call_hook('neutron-plugin-api-relation-changed')
self.apt_install.assert_called_with(['keepalived'], fatal=True)
self.assertTrue(self.CONFIGS.write_all.called)
def test_cluster_departed_nvp(self): def test_cluster_departed_nvp(self):
self.test_config.set('plugin', 'nvp') self.test_config.set('plugin', 'nvp')
self._call_hook('cluster-relation-departed') self._call_hook('cluster-relation-departed')

View File

@ -45,7 +45,8 @@ TO_PATCH = [
'is_relation_made', 'is_relation_made',
'lsb_release', 'lsb_release',
'mkdir', 'mkdir',
'copy2' 'copy2',
'NeutronAPIContext',
] ]
@ -138,6 +139,11 @@ class TestQuantumUtils(CharmTestCase):
self.get_os_codename_install_source.return_value = 'kilo' self.get_os_codename_install_source.return_value = 'kilo'
self.assertTrue('python-neutron-fwaas' in quantum_utils.get_packages()) self.assertTrue('python-neutron-fwaas' in quantum_utils.get_packages())
def test_get_packages_l3ha(self):
self.config.return_value = 'ovs'
self.get_os_codename_install_source.return_value = 'juno'
self.assertTrue('keepalived' in quantum_utils.get_packages())
@patch('charmhelpers.contrib.openstack.context.config') @patch('charmhelpers.contrib.openstack.context.config')
def test_configure_ovs_starts_service_if_required(self, mock_config): def test_configure_ovs_starts_service_if_required(self, mock_config):
mock_config.side_effect = self.test_config.get mock_config.side_effect = self.test_config.get
@ -256,6 +262,7 @@ class TestQuantumUtils(CharmTestCase):
def test_restart_map_ovs(self): def test_restart_map_ovs(self):
self.config.return_value = 'ovs' self.config.return_value = 'ovs'
self.get_os_codename_install_source.return_value = 'havana'
ex_map = { ex_map = {
quantum_utils.NEUTRON_CONF: ['neutron-l3-agent', quantum_utils.NEUTRON_CONF: ['neutron-l3-agent',
'neutron-dhcp-agent', 'neutron-dhcp-agent',
@ -276,9 +283,11 @@ class TestQuantumUtils(CharmTestCase):
quantum_utils.NEUTRON_VPNAAS_AGENT_CONF: [ quantum_utils.NEUTRON_VPNAAS_AGENT_CONF: [
'neutron-plugin-vpn-agent', 'neutron-plugin-vpn-agent',
'neutron-vpn-agent'], 'neutron-vpn-agent'],
quantum_utils.NEUTRON_L3_AGENT_CONF: ['neutron-l3-agent'], quantum_utils.NEUTRON_L3_AGENT_CONF: ['neutron-l3-agent',
'neutron-vpn-agent'],
quantum_utils.NEUTRON_DHCP_AGENT_CONF: ['neutron-dhcp-agent'], quantum_utils.NEUTRON_DHCP_AGENT_CONF: ['neutron-dhcp-agent'],
quantum_utils.NEUTRON_FWAAS_CONF: ['neutron-l3-agent'], quantum_utils.NEUTRON_FWAAS_CONF: ['neutron-l3-agent',
'neutron-vpn-agent'],
quantum_utils.NEUTRON_METERING_AGENT_CONF: quantum_utils.NEUTRON_METERING_AGENT_CONF:
['neutron-metering-agent', 'neutron-plugin-metering-agent'], ['neutron-metering-agent', 'neutron-plugin-metering-agent'],
quantum_utils.NOVA_CONF: ['nova-api-metadata'], quantum_utils.NOVA_CONF: ['nova-api-metadata'],