diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index b262a019..82f112e7 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -99,6 +99,7 @@ def core_plugin(): class L3AgentContext(OSContextGenerator): def __call__(self): + api_settings = NeutronAPIContext()() ctxt = {} if config('run-internal-router') == 'leader': ctxt['handle_internal_only_router'] = eligible_leader(None) @@ -111,16 +112,19 @@ class L3AgentContext(OSContextGenerator): if config('external-network-id'): ctxt['ext_net_id'] = config('external-network-id') - if config('plugin'): ctxt['plugin'] = config('plugin') + if api_settings['enable_dvr']: + ctxt['agent_mode'] = 'dvr_snat' + else: + ctxt['agent_mode'] = 'legacy' return ctxt class QuantumGatewayContext(OSContextGenerator): def __call__(self): - neutron_api_settings = NeutronAPIContext()() + api_settings = NeutronAPIContext()() ctxt = { 'shared_secret': get_shared_secret(), 'local_ip': @@ -131,9 +135,11 @@ class QuantumGatewayContext(OSContextGenerator): 'debug': config('debug'), 'verbose': config('verbose'), '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': - neutron_api_settings['overlay_network_type'], + api_settings['overlay_network_type'], } mappings = config('bridge-mappings') @@ -147,7 +153,7 @@ class QuantumGatewayContext(OSContextGenerator): ctxt['network_providers'] = ' '.join(providers) 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: ctxt['network_device_mtu'] = net_dev_mtu ctxt['veth_mtu'] = net_dev_mtu diff --git a/hooks/quantum_hooks.py b/hooks/quantum_hooks.py index e8b74861..ca3fcd17 100755 --- a/hooks/quantum_hooks.py +++ b/hooks/quantum_hooks.py @@ -40,6 +40,7 @@ from charmhelpers.contrib.charmsupport import nrpe import sys from quantum_utils import ( + L3HA_PACKAGES, register_configs, restart_map, services, @@ -56,7 +57,8 @@ from quantum_utils import ( install_legacy_ha_files, cleanup_ovs_netns, reassign_agent_resources, - stop_neutron_ha_monitor_daemon + stop_neutron_ha_monitor_daemon, + use_l3ha, ) hooks = Hooks() @@ -193,13 +195,23 @@ def amqp_departed(): 'pgsql-db-relation-changed', 'amqp-relation-changed', 'cluster-relation-changed', - 'cluster-relation-joined', - 'neutron-plugin-api-relation-changed') + 'cluster-relation-joined') @restart_on_change(restart_map()) def db_amqp_changed(): 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') @restart_on_change(restart_map()) def nm_changed(): diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index eaec1a87..ad182075 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -42,6 +42,7 @@ from charmhelpers.contrib.openstack.neutron import ( import charmhelpers.contrib.openstack.context as context from charmhelpers.contrib.openstack.context import ( SyslogContext, + NeutronAPIContext, NetworkServiceContext, ExternalPortContext, PhyNICMTUContext, @@ -55,7 +56,7 @@ from quantum_contexts import ( networking_name, QuantumGatewayContext, L3AgentContext, - remap_plugin + remap_plugin, ) from charmhelpers.contrib.openstack.neutron import ( parse_bridge_mappings, @@ -171,6 +172,7 @@ LEGACY_FILES_MAP = { }, } LEGACY_RES_MAP = ['res_monitor'] +L3HA_PACKAGES = ['keepalived'] def get_early_packages(): @@ -201,15 +203,26 @@ def get_packages(): packages.append('openswan') if source >= 'kilo': packages.append('python-neutron-fwaas') + packages.extend(determine_l3ha_packages()) return packages +def determine_l3ha_packages(): + if use_l3ha(): + return L3HA_PACKAGES + return [] + + def get_common_package(): if get_os_codename_package('quantum-common', fatal=False) is not None: return 'quantum-common' else: return 'neutron-common' + +def use_l3ha(): + return NeutronAPIContext()()['enable_l3ha'] + EXT_PORT_CONF = '/etc/init/ext-port.conf' PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf' TEMPLATES = 'templates' @@ -322,7 +335,7 @@ NEUTRON_OVS_CONFIG_FILES = { 'hook_contexts': [NetworkServiceContext(), L3AgentContext(), QuantumGatewayContext()], - 'services': ['neutron-l3-agent'] + 'services': ['neutron-l3-agent', 'neutron-vpn-agent'] }, NEUTRON_METERING_AGENT_CONF: { 'hook_contexts': [QuantumGatewayContext()], @@ -340,7 +353,7 @@ NEUTRON_OVS_CONFIG_FILES = { }, NEUTRON_FWAAS_CONF: { 'hook_contexts': [QuantumGatewayContext()], - 'services': ['neutron-l3-agent'] + 'services': ['neutron-l3-agent', 'neutron-vpn-agent'] }, NEUTRON_OVS_PLUGIN_CONF: { 'hook_contexts': [QuantumGatewayContext()], diff --git a/templates/juno/l3_agent.ini b/templates/juno/l3_agent.ini new file mode 100644 index 00000000..6fd6cd4b --- /dev/null +++ b/templates/juno/l3_agent.ini @@ -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 }} diff --git a/templates/juno/ml2_conf.ini b/templates/juno/ml2_conf.ini new file mode 100644 index 00000000..4c1dfa9d --- /dev/null +++ b/templates/juno/ml2_conf.ini @@ -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 diff --git a/unit_tests/test_quantum_contexts.py b/unit_tests/test_quantum_contexts.py index e183c119..f5513c74 100644 --- a/unit_tests/test_quantum_contexts.py +++ b/unit_tests/test_quantum_contexts.py @@ -38,6 +38,15 @@ def patch_open(): 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): def setUp(self): @@ -45,32 +54,51 @@ class TestL3AgentContext(CharmTestCase): TO_PATCH) 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('external-network-id', '') self.eligible_leader.return_value = False self.assertEquals(quantum_contexts.L3AgentContext()(), - {'handle_internal_only_router': False, + {'agent_mode': 'legacy', + 'handle_internal_only_router': False, '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('external-network-id', 'netid') self.eligible_leader.return_value = True self.assertEquals(quantum_contexts.L3AgentContext()(), - {'handle_internal_only_router': True, + {'agent_mode': 'legacy', + 'handle_internal_only_router': True, 'ext_net_id': 'netid', '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('external-network-id', 'netid') self.eligible_leader.return_value = True self.assertEquals(quantum_contexts.L3AgentContext()(), - {'handle_internal_only_router': True, + {'agent_mode': 'legacy', + 'handle_internal_only_router': True, 'ext_net_id': 'netid', '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): @@ -78,6 +106,7 @@ class TestQuantumGatewayContext(CharmTestCase): super(TestQuantumGatewayContext, self).setUp(quantum_contexts, TO_PATCH) self.config.side_effect = self.test_config.get + self.maxDiff = None @patch('charmhelpers.contrib.openstack.context.relation_get') @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_host_ip') 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('debug', False) self.test_config.set('verbose', True) @@ -94,7 +128,6 @@ class TestQuantumGatewayContext(CharmTestCase): # Provided by neutron-api relation _rids.return_value = ['neutron-plugin-api:0'] _runits.return_value = ['neutron-api/0'] - rdata = {'network-device-mtu': 9000, 'l2-population': 'False'} _rget.side_effect = lambda *args, **kwargs: rdata self.get_os_codename_install_source.return_value = 'folsom' _host_ip.return_value = '10.5.0.1' @@ -102,6 +135,8 @@ class TestQuantumGatewayContext(CharmTestCase): ctxt = quantum_contexts.QuantumGatewayContext()() self.assertEquals(ctxt, { 'shared_secret': 'testsecret', + 'enable_dvr': True, + 'enable_l3ha': True, 'local_ip': '10.5.0.1', 'instance_mtu': 1420, 'core_plugin': "quantum.plugins.openvswitch.ovs_quantum_plugin." @@ -109,7 +144,7 @@ class TestQuantumGatewayContext(CharmTestCase): 'plugin': 'ovs', 'debug': False, 'verbose': True, - 'l2_population': False, + 'l2_population': True, 'overlay_network_type': 'gre', 'bridge_mappings': 'physnet1:br-data', 'network_providers': 'physnet1 physnet2', diff --git a/unit_tests/test_quantum_hooks.py b/unit_tests/test_quantum_hooks.py index fd555736..3146b771 100644 --- a/unit_tests/test_quantum_hooks.py +++ b/unit_tests/test_quantum_hooks.py @@ -48,6 +48,7 @@ TO_PATCH = [ 'remove_legacy_ha_files', 'cleanup_ovs_netns', 'stop_neutron_ha_monitor_daemon', + 'use_l3ha', ] @@ -246,6 +247,12 @@ class TestQuantumHooks(CharmTestCase): self.assertTrue(self.CONFIGS.write_all.called) 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): self.test_config.set('plugin', 'nvp') self._call_hook('cluster-relation-departed') diff --git a/unit_tests/test_quantum_utils.py b/unit_tests/test_quantum_utils.py index 1ceaa423..7fa472c6 100644 --- a/unit_tests/test_quantum_utils.py +++ b/unit_tests/test_quantum_utils.py @@ -45,7 +45,8 @@ TO_PATCH = [ 'is_relation_made', 'lsb_release', 'mkdir', - 'copy2' + 'copy2', + 'NeutronAPIContext', ] @@ -138,6 +139,11 @@ class TestQuantumUtils(CharmTestCase): self.get_os_codename_install_source.return_value = 'kilo' 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') def test_configure_ovs_starts_service_if_required(self, mock_config): mock_config.side_effect = self.test_config.get @@ -256,6 +262,7 @@ class TestQuantumUtils(CharmTestCase): def test_restart_map_ovs(self): self.config.return_value = 'ovs' + self.get_os_codename_install_source.return_value = 'havana' ex_map = { quantum_utils.NEUTRON_CONF: ['neutron-l3-agent', 'neutron-dhcp-agent', @@ -276,9 +283,11 @@ class TestQuantumUtils(CharmTestCase): quantum_utils.NEUTRON_VPNAAS_AGENT_CONF: [ 'neutron-plugin-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_FWAAS_CONF: ['neutron-l3-agent'], + quantum_utils.NEUTRON_FWAAS_CONF: ['neutron-l3-agent', + 'neutron-vpn-agent'], quantum_utils.NEUTRON_METERING_AGENT_CONF: ['neutron-metering-agent', 'neutron-plugin-metering-agent'], quantum_utils.NOVA_CONF: ['nova-api-metadata'],