diff --git a/config.yaml b/config.yaml index acc64c46..b467c519 100644 --- a/config.yaml +++ b/config.yaml @@ -16,6 +16,12 @@ options: traffic to the external public network. Valid values are either MAC addresses (in which case only MAC addresses for interfaces without an IP address already assigned will be used), or interfaces (eth0) + data-port: + type: string + default: + description: | + The data port will be added to br-data and will allow usage of flat or VLAN + network types with Neutron. openstack-origin: type: string default: distro diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index f62a58d9..6a8eb10f 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -169,10 +169,10 @@ class L3AgentContext(OSContextGenerator): return ctxt -class ExternalPortContext(OSContextGenerator): +class NeutronPortContext(OSContextGenerator): - def __call__(self): - if not config('ext-port'): + def _resolve_port(self, config_key): + if not config(config_key): return None hwaddr_to_nic = {} hwaddr_to_ip = {} @@ -183,7 +183,7 @@ class ExternalPortContext(OSContextGenerator): get_ipv6_addr(iface=nic, fatal=False) hwaddr_to_ip[hwaddr] = addresses mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I) - for entry in config('ext-port').split(): + for entry in config(config_key).split(): entry = entry.strip() if re.match(mac_regex, entry): if entry in hwaddr_to_nic and len(hwaddr_to_ip[entry]) == 0: @@ -192,15 +192,35 @@ class ExternalPortContext(OSContextGenerator): continue # Entry is a MAC address for a valid interface that doesn't # have an IP address assigned yet. - return {"ext_port": hwaddr_to_nic[entry]} + return hwaddr_to_nic[entry] else: # If the passed entry is not a MAC address, assume it's a valid # interface, and that the user put it there on purpose (we can # trust it to be the real external network). - return {"ext_port": entry} + return entry return None +class ExternalPortContext(NeutronPortContext): + + def __call__(self): + port = self._resolve_port('ext-port') + if port: + return {"ext_port": port} + else: + return None + + +class DataPortContext(NeutronPortContext): + + def __call__(self): + port = self._resolve_port('data-port') + if port: + return {"data_port": port} + else: + return None + + class QuantumGatewayContext(OSContextGenerator): def __call__(self): diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 7bc3a439..e25fbd5f 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -46,6 +46,7 @@ from quantum_contexts import ( NetworkServiceContext, L3AgentContext, ExternalPortContext, + DataPortContext, remap_plugin ) @@ -404,6 +405,7 @@ def restart_map(): INT_BRIDGE = "br-int" EXT_BRIDGE = "br-ex" +DATA_BRIDGE = 'br-data' DHCP_AGENT = "DHCP Agent" L3_AGENT = "L3 Agent" @@ -533,5 +535,11 @@ def configure_ovs(): add_bridge(INT_BRIDGE) add_bridge(EXT_BRIDGE) ext_port_ctx = ExternalPortContext()() - if ext_port_ctx is not None and ext_port_ctx['ext_port']: + if ext_port_ctx and ext_port_ctx['ext_port']: add_bridge_port(EXT_BRIDGE, ext_port_ctx['ext_port']) + + add_bridge(DATA_BRIDGE) + data_port_ctx = DataPortContext()() + if data_port_ctx and data_port_ctx['data_port']: + add_bridge_port(DATA_BRIDGE, data_port_ctx['data_port'], + promisc=True) diff --git a/templates/icehouse/ml2_conf.ini b/templates/icehouse/ml2_conf.ini index 0de6e19c..b7245222 100644 --- a/templates/icehouse/ml2_conf.ini +++ b/templates/icehouse/ml2_conf.ini @@ -3,18 +3,30 @@ # Configuration file maintained by Juju. Local changes may be overwritten. ############################################################################### [ml2] -type_drivers = gre,vxlan -tenant_network_types = gre,vxlan +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 = physnet1:1000:2000 + +[ml2_type_flat] +flat_networks = physnet1 + [ovs] enable_tunneling = True local_ip = {{ local_ip }} +bridge_mappings = physnet1:br-data + [agent] tunnel_types = {{ overlay_network_type }} l2_population = {{ l2_population }} + [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 298f6221..4d3eefd1 100644 --- a/unit_tests/test_quantum_contexts.py +++ b/unit_tests/test_quantum_contexts.py @@ -117,11 +117,11 @@ class TestNetworkServiceContext(_TestQuantumContext): } -class TestExternalPortContext(CharmTestCase): +class TestNeutronPortContext(CharmTestCase): def setUp(self): - super(TestExternalPortContext, self).setUp(quantum_contexts, - TO_PATCH) + super(TestNeutronPortContext, self).setUp(quantum_contexts, + TO_PATCH) self.machine_macs = { 'eth0': 'fe:c5:ce:8e:2b:00', 'eth1': 'fe:c5:ce:8e:2b:01', @@ -174,6 +174,11 @@ class TestExternalPortContext(CharmTestCase): self.assertEquals(quantum_contexts.ExternalPortContext()(), {'ext_port': 'eth2'}) + def test_data_port_eth(self): + self.config.return_value = 'eth1010' + self.assertEquals(quantum_contexts.DataPortContext()(), + {'data_port': 'eth1010'}) + class TestL3AgentContext(CharmTestCase): diff --git a/unit_tests/test_quantum_utils.py b/unit_tests/test_quantum_utils.py index 88697c44..64c0c25e 100644 --- a/unit_tests/test_quantum_utils.py +++ b/unit_tests/test_quantum_utils.py @@ -36,6 +36,7 @@ TO_PATCH = [ 'service_running', 'NetworkServiceContext', 'ExternalPortContext', + 'DataPortContext', 'unit_private_ip', 'relations_of_type', 'service_stop', @@ -148,13 +149,33 @@ class TestQuantumUtils(CharmTestCase): self.test_config.set('ext-port', 'eth0') self.ExternalPortContext.return_value = \ DummyExternalPortContext(return_value={'ext_port': 'eth0'}) + self.DataPortContext.return_value = \ + DummyExternalPortContext(return_value=None) quantum_utils.configure_ovs() self.add_bridge.assert_has_calls([ call('br-int'), - call('br-ex') + call('br-ex'), + call('br-data') ]) self.add_bridge_port.assert_called_with('br-ex', 'eth0') + def test_configure_ovs_ovs_data_port(self): + self.config.side_effect = self.test_config.get + self.test_config.set('plugin', 'ovs') + self.test_config.set('data-port', 'eth0') + self.ExternalPortContext.return_value = \ + DummyExternalPortContext(return_value=None) + self.DataPortContext.return_value = \ + DummyExternalPortContext(return_value={'data_port': 'eth0'}) + quantum_utils.configure_ovs() + self.add_bridge.assert_has_calls([ + call('br-int'), + call('br-ex'), + call('br-data') + ]) + self.add_bridge_port.assert_called_with('br-data', 'eth0', + promisc=True) + def test_do_openstack_upgrade(self): self.config.side_effect = self.test_config.get self.is_relation_made.return_value = False