diff --git a/lib/charms/ovn_charm.py b/lib/charms/ovn_charm.py index 46ba7b0..64ceacd 100644 --- a/lib/charms/ovn_charm.py +++ b/lib/charms/ovn_charm.py @@ -27,6 +27,7 @@ import charmhelpers.contrib.network.ovs as ch_ovs import charmhelpers.contrib.network.ovs.ovsdb as ch_ovsdb import charmhelpers.contrib.openstack.deferred_events as deferred_events +import charmhelpers.core.host as ch_host import charmhelpers.fetch as ch_fetch import charms_openstack.adapters @@ -1297,6 +1298,12 @@ def configure_bridges(self): 'other-config': {'disable-in-band': 'true'}, }, }) + + # We need to build a mac mapping to enable distributed ports on tenant + # VLAN networks. + # see: https://www.ovn.org/support/dist-docs/ovn-architecture.7.html + ovn_chassis_mac_mapping = '' + for br in bim: if br not in ovnbridges: continue @@ -1328,6 +1335,24 @@ def configure_bridges(self): portdata={ 'external-ids': { 'charm-ovn-chassis': br}}) + # We can use the MAC of the bridge as the unique chassis MAC. If + # a physical port has been added to the bridge, the MAC will most + # likely be inherited. If not, a unique mac will be generated + # anyway. + hw_addr = None + try: + hw_addr = ch_host.get_nic_hwaddr(br) + except Exception as err: + ch_core.hookenv.log('failed to get MAC for bridge "{}": {}' + .format(br, err), + level=ch_core.hookenv.ERROR) + if hw_addr: + networks = ovnbridges.get(br, []) + for net in networks: + if ovn_chassis_mac_mapping: + ovn_chassis_mac_mapping += ',' + ovn_chassis_mac_mapping += '{}:{}'.format( + net, hw_addr) opvs = ch_ovsdb.SimpleOVSDB('ovs-vsctl').open_vswitch if ovn_br_map_str: @@ -1335,6 +1360,12 @@ def configure_bridges(self): else: opvs.remove('.', 'external_ids', 'ovn-bridge-mappings') + if ovn_chassis_mac_mapping: + opvs.set('.', 'external_ids:ovn-chassis-mac-mappings', + ovn_chassis_mac_mapping) + else: + opvs.remove('.', 'external_ids', 'ovn-chassis-mac-mappings') + cms_opts = self._get_ovn_cms_options() if cms_opts: opvs.set('.', 'external_ids:ovn-cms-options', ','.join(cms_opts)) diff --git a/unit_tests/test_lib_charms_ovn_charm.py b/unit_tests/test_lib_charms_ovn_charm.py index d18ff06..a2366e9 100644 --- a/unit_tests/test_lib_charms_ovn_charm.py +++ b/unit_tests/test_lib_charms_ovn_charm.py @@ -781,6 +781,18 @@ def test__init__(self): }) def test_configure_bridges(self): + def mock_get_nic_hwaddr_fn(iface): + mac_mapping = { + "br-ex": "a0:36:9f:dd:37:dd", + "br-data": "a0:36:9f:dd:37:db", + } + return mac_mapping.get(iface, "") + mock_get_nic_hwaddr = mock.MagicMock() + mock_get_nic_hwaddr.side_effect = mock_get_nic_hwaddr_fn + + self.patch_object(ovn_charm.ch_host, "get_nic_hwaddr") + self.get_nic_hwaddr.side_effect = mock_get_nic_hwaddr + self.patch_object(ovn_charm.os_context, 'BridgePortInterfaceMap') dict_bpi = { 'br-ex': { # bridge @@ -891,10 +903,17 @@ def test_configure_bridges(self): 'data': 'fake', 'external-ids': {'charm-ovn-chassis': 'br-ex'}}, linkup=False, promisc=None, portdata={ - 'external-ids': {'charm-ovn-chassis': 'br-ex'}}), - ovsdb.open_vswitch.set.assert_called_once_with( - '.', 'external_ids:ovn-bridge-mappings', - 'other:br-data,provider:br-ex') + 'external-ids': {'charm-ovn-chassis': 'br-ex'}}) + ovsdb.open_vswitch.set.assert_has_calls([ + mock.call( + '.', 'external_ids:ovn-bridge-mappings', + 'other:br-data,provider:br-ex' + ), + mock.call( + '.', 'external_ids:ovn-chassis-mac-mappings', + 'provider:a0:36:9f:dd:37:dd,other:a0:36:9f:dd:37:db' + ), + ], any_order=False) ovsdb.open_vswitch.remove.assert_called_once_with( '.', 'external_ids', 'ovn-cms-options')