diff --git a/neutron/agent/ovn/metadata/agent.py b/neutron/agent/ovn/metadata/agent.py index 807600a09aa..698947aaf16 100644 --- a/neutron/agent/ovn/metadata/agent.py +++ b/neutron/agent/ovn/metadata/agent.py @@ -109,7 +109,7 @@ class PortBindingEvent(row_event.RowEvent): with _SYNC_STATE_LOCK.read_lock(): self.log_row(row) try: - self.agent.provision_datapath(row.datapath) + self.agent.provision_datapath(row) except ConfigException: # We're now in the reader lock mode, we need to exit the # context and then use writer lock @@ -463,13 +463,13 @@ class MetadataAgent(object): "br-int instead.") return 'br-int' - def get_networks_datapaths(self): - """Return a set of datapath objects of the VIF ports on the current + def get_networks_port_bindings(self): + """Return a set of Port_Binding objects of the VIF ports on the current chassis. """ ports = self.sb_idl.get_ports_on_chassis( self.chassis, include_additional_chassis=True) - return set(p.datapath for p in self._vif_ports(ports)) + return list(self._vif_ports(ports)) @_sync_lock def sync(self, provision=True): @@ -484,12 +484,12 @@ class MetadataAgent(object): system_namespaces = tuple( ns.decode('utf-8') if isinstance(ns, bytes) else ns for ns in ip_lib.list_network_namespaces()) - net_datapaths = self.get_networks_datapaths() - metadata_namespaces = [ + net_port_bindings = self.get_networks_port_bindings() + metadata_namespaces = set( self._get_namespace_name( ovn_utils.get_network_name_from_datapath(datapath)) - for datapath in net_datapaths - ] + for datapath in (pb.datapath for pb in net_port_bindings) + ) unused_namespaces = [ns for ns in system_namespaces if ns.startswith(NS_PREFIX) and ns not in metadata_namespaces] @@ -503,8 +503,8 @@ class MetadataAgent(object): # even those that are already running. This is to make sure # everything within each namespace is up to date. if provision: - for datapath in net_datapaths: - self.provision_datapath(datapath) + for port_binding in net_port_bindings: + self.provision_datapath(port_binding) @staticmethod def _get_veth_name(datapath): @@ -675,7 +675,7 @@ class MetadataAgent(object): return net_name, datapath_ports_ips, metadata_port_info - def provision_datapath(self, datapath): + def provision_datapath(self, port_binding): """Provision the datapath so that it can serve metadata. This function will create the namespace and VETH pair if needed @@ -683,11 +683,13 @@ class MetadataAgent(object): metadata port of the network. It will also remove existing IP from the namespace if they are no longer needed. - :param datapath: datapath object. - :return: The metadata namespace name for the datapath or None - if namespace was not provisioned + :param port_binding: Port_Binding object. + :return: The metadata namespace name for the Port_Binding.datapath or + None if namespace was not provisioned """ - + datapath = port_binding.datapath + mtu = int(port_binding.external_ids.get( + ovn_const.OVN_NETWORK_MTU_EXT_ID_KEY) or '0') provision_params = self._get_provision_params(datapath) if not provision_params: return @@ -716,6 +718,11 @@ class MetadataAgent(object): # Configure the MAC address. ip2.link.set_address(metadata_port_info.mac) + # Set VETH ports MTU. + if mtu: + ip1.link.set_mtu(mtu) + ip2.link.set_mtu(mtu) + # Make sure both ends of the VETH are up ip1.link.set_up() ip2.link.set_up() diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py index 0fb4a44da09..f94fc799ef3 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py @@ -82,6 +82,7 @@ OvnPortInfo = collections.namedtuple( "address6_scope_id", "vnic_type", "capabilities", + "mtu", ], ) @@ -340,6 +341,7 @@ class OVNClient(object): address6_scope_id = "" dhcpv4_options = self._get_port_dhcp_options(port, const.IP_VERSION_4) dhcpv6_options = self._get_port_dhcp_options(port, const.IP_VERSION_6) + mtu = '' if vtep_physical_switch: vtep_logical_switch = bp_info.bp_param.get('vtep-logical-switch') port_type = 'vtep' @@ -433,10 +435,10 @@ class OVNClient(object): ovn_const.VIF_DETAILS_PF_MAC_ADDRESS in bp_info.bp_param): port_net = self._plugin.get_network( context, port['network_id']) + mtu = str(port_net['mtu']) options.update({ ovn_const.LSP_OPTIONS_VIF_PLUG_TYPE_KEY: 'representor', - ovn_const.LSP_OPTIONS_VIF_PLUG_MTU_REQUEST_KEY: str( - port_net['mtu']), + ovn_const.LSP_OPTIONS_VIF_PLUG_MTU_REQUEST_KEY: mtu, ovn_const.LSP_OPTIONS_VIF_PLUG_REPRESENTOR_PF_MAC_KEY: ( bp_info.bp_param.get( ovn_const.VIF_DETAILS_PF_MAC_ADDRESS)), @@ -477,7 +479,7 @@ class OVNClient(object): parent_name, tag, dhcpv4_options, dhcpv6_options, cidrs.strip(), device_owner, sg_ids, address4_scope_id, address6_scope_id, - bp_info.vnic_type, bp_info.capabilities + bp_info.vnic_type, bp_info.capabilities, mtu ) def update_port_dhcp_options(self, port_info, txn): @@ -518,6 +520,7 @@ class OVNClient(object): ovn_const.OVN_PORT_VNIC_TYPE_KEY: port_info.vnic_type, ovn_const.OVN_PORT_BP_CAPABILITIES_KEY: ';'.join(port_info.capabilities), + ovn_const.OVN_NETWORK_MTU_EXT_ID_KEY: port_info.mtu, } return port_info, external_ids diff --git a/neutron/tests/unit/agent/ovn/metadata/test_agent.py b/neutron/tests/unit/agent/ovn/metadata/test_agent.py index 0ed5bbb0a1d..03a449b8221 100644 --- a/neutron/tests/unit/agent/ovn/metadata/test_agent.py +++ b/neutron/tests/unit/agent/ovn/metadata/test_agent.py @@ -103,14 +103,8 @@ class TestMetadataAgent(base.BaseTestCase): self.agent.sync() - pdp.assert_has_calls( - [ - mock.call(p.datapath) - for p in self.ports - ], - any_order=True - ) - + pdp.assert_has_calls([mock.call(p) for p in self.ports], + any_order=True) lnn.assert_called_once_with() tdp.assert_not_called() @@ -129,13 +123,8 @@ class TestMetadataAgent(base.BaseTestCase): self.agent.sync() - pdp.assert_has_calls( - [ - mock.call(p.datapath) - for p in self.ports - ], - any_order=True - ) + pdp.assert_has_calls([mock.call(p) for p in self.ports], + any_order=True) lnn.assert_called_once_with() tdp.assert_called_once_with('3') @@ -154,27 +143,23 @@ class TestMetadataAgent(base.BaseTestCase): side_effect=Exception()) as tdp: self.agent.sync() - pdp.assert_has_calls( - [ - mock.call(p.datapath) - for p in self.ports - ], - any_order=True - ) + pdp.assert_has_calls([mock.call(p) for p in self.ports], + any_order=True) lnn.assert_called_once_with() tdp.assert_called_once_with('3') - def test_get_networks_datapaths(self): - """Test get_networks_datapaths returns only datapath objects for the - networks containing vif ports of type ''(blank) and 'external'. + def test_get_networks_port_bindings(self): + """Test get_networks_port_bindings returns only the port binding + objects for ports with VIF type empty ('') or 'external'. This test simulates that this chassis has the following ports: - * datapath '1': 1 port type '' , 1 port 'external' and - 1 port 'unknown' - * datapath '2': 1 port type '' - * datapath '3': 1 port with type 'external' - * datapath '4': 1 port with type 'unknown' + * port0: datapath 1, type '' + * port1: datapath 1, type 'external' + * port2: datapath 1, type 'unknown' + * port3: datapath 2, type '' + * port4: datapath 3, type 'external' + * port5: datapath 4, type 'unknown' - It is expected that only datapaths '1', '2' and '3' are returned + Only port bindings from ports 0, 1, 3, and 4 are expected. """ datapath_1 = DatapathInfo(uuid='uuid1', @@ -197,11 +182,8 @@ class TestMetadataAgent(base.BaseTestCase): with mock.patch.object(self.agent.sb_idl, 'get_ports_on_chassis', return_value=ports): - expected_datapaths = set([datapath_1, datapath_2, datapath_3]) - self.assertSetEqual( - expected_datapaths, - self.agent.get_networks_datapaths() - ) + self.assertEqual([ports[0], ports[1], ports[3], ports[4]], + self.agent.get_networks_port_bindings()) def test_teardown_datapath(self): """Test teardown datapath. @@ -444,7 +426,8 @@ class TestMetadataAgent(base.BaseTestCase): mock.patch.object(agent.MetadataAgent, '_get_namespace_name', return_value=nemaspace_name),\ mock.patch.object(ip_link, 'set_up') as link_set_up,\ - mock.patch.object(ip_link, 'set_address') as link_set_addr,\ + mock.patch.object(ip_link, 'set_address') as link_set_addr, \ + mock.patch.object(ip_link, 'set_mtu') as link_set_mtu, \ mock.patch.object(ip_addr, 'list', return_value=[]),\ mock.patch.object( ip_addr, 'add_multiple') as ip_addr_add_multiple,\ @@ -464,7 +447,11 @@ class TestMetadataAgent(base.BaseTestCase): # We need to assert that it was deleted first. self.agent.ovs_idl.list_br.return_value.execute.return_value = ( ['br-int', 'br-fake']) - self.agent.provision_datapath('fake_datapath') + mtu = 1500 + port_binding = mock.Mock( + datapath='fake_datapath', + external_ids={ovn_const.OVN_NETWORK_MTU_EXT_ID_KEY: str(mtu)}) + self.agent.provision_datapath(port_binding) # Check that the port was deleted from br-fake self.agent.ovs_idl.del_port.assert_called_once_with( @@ -474,6 +461,7 @@ class TestMetadataAgent(base.BaseTestCase): nemaspace_name) # Make sure that the two ends of the VETH pair have been set as up. self.assertEqual(2, link_set_up.call_count) + link_set_mtu.assert_has_calls([mock.call(mtu), mock.call(mtu)]) link_set_addr.assert_called_once_with('aa:bb:cc:dd:ee:ff') # Make sure that the port has been added to OVS. self.agent.ovs_idl.add_port.assert_called_once_with(