diff --git a/neutron/common/ovn/constants.py b/neutron/common/ovn/constants.py index 6045832035a..3de372b2f7b 100644 --- a/neutron/common/ovn/constants.py +++ b/neutron/common/ovn/constants.py @@ -70,6 +70,10 @@ OVN_ROUTER_PORT_GW_MTU_OPTION = 'gateway_mtu' OVN_PROVNET_PORT_NAME_PREFIX = 'provnet-' OVN_NAME_PREFIX = 'neutron-' +# TODO(froyo): Move this to neutron-lib as soon as possible, and when a new +# release is created and pointed to in the requirements remove this code +OVN_LB_HM_PORT_DISTRIBUTED = 'ovn-lb-hm:distributed' + # Agent extension constants OVN_AGENT_DESC_KEY = 'neutron:description' OVN_AGENT_METADATA_SB_CFG_KEY = 'neutron:ovn-metadata-sb-cfg' diff --git a/neutron/common/ovn/utils.py b/neutron/common/ovn/utils.py index e006651b26e..5d564c6927f 100644 --- a/neutron/common/ovn/utils.py +++ b/neutron/common/ovn/utils.py @@ -608,6 +608,11 @@ def is_ovn_metadata_port(port): port['device_id'].startswith('ovnmeta')) +def is_ovn_lb_hm_port(port): + return (port['device_owner'] == constants.OVN_LB_HM_PORT_DISTRIBUTED and + port['device_id'].startswith('ovn-lb-hm')) + + def is_gateway_chassis_invalid(chassis_name, gw_chassis, physnet, chassis_physnets, az_hints, chassis_with_azs): diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py index d1f7144e63a..a0cc09d3c53 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py @@ -15,6 +15,7 @@ import functools import socket import uuid +from neutron_lib import constants from neutron_lib import exceptions as n_exc from neutron_lib.utils import helpers from oslo_log import log @@ -933,7 +934,10 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): except idlutils.RowNotFound: return None cmd = self.db_find_rows('Port_Binding', ('datapath', '=', dp), - ('type', '=', ovn_const.LSP_TYPE_LOCALPORT)) + ('type', '=', ovn_const.LSP_TYPE_LOCALPORT), + ('external_ids', '=', { + ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: + constants.DEVICE_OWNER_DISTRIBUTED})) return next(iter(cmd.execute(check_error=True)), None) def set_chassis_neutron_description(self, chassis, description, 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 1980faf0feb..9e2bde5497c 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 @@ -379,8 +379,9 @@ class OVNClient(object): cidrs += ' {}/{}'.format(ip['ip_address'], subnet['cidr'].split('/')[1]) - # Metadata port. - if utils.is_ovn_metadata_port(port): + # Metadata or OVN LB HM port. + if (utils.is_ovn_metadata_port(port) or + utils.is_ovn_lb_hm_port(port)): port_type = ovn_const.LSP_TYPE_LOCALPORT if utils.is_port_external(port): diff --git a/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py b/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py index d0d4aebac3a..4ae2c337551 100644 --- a/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py +++ b/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py @@ -16,6 +16,7 @@ import re from unittest import mock +from neutron_lib import constants from oslo_config import fixture as fixture_config from oslo_utils import uuidutils from ovsdbapp.backend.ovs_idl import event @@ -154,7 +155,9 @@ class TestMetadataAgent(base.TestOVNFunctionalBase): addresses='AA:AA:AA:AA:AA:AA 192.168.122.123', external_ids={ ovn_const.OVN_CIDRS_EXT_ID_KEY: '192.168.122.123/24', - ovn_const.OVN_DEVID_EXT_ID_KEY: 'ovnmeta-' + lswitch_name + ovn_const.OVN_DEVID_EXT_ID_KEY: 'ovnmeta-' + lswitch_name, + ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: + constants.DEVICE_OWNER_DISTRIBUTED })) def _update_metadata_port_ip(self, metadata_port_name): diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py index d75be57282d..1d7631137e1 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py @@ -15,6 +15,7 @@ import copy import uuid +from neutron_lib import constants from oslo_utils import uuidutils from ovsdbapp.backend.ovs_idl import connection from ovsdbapp import constants as const @@ -104,34 +105,62 @@ class TestSbApi(BaseOvnIdlTest): self.load_test_data() self.assertRaises(ValueError, self.api.get_chassis_and_physnets) - def _add_switch_port(self, chassis_name, - type=ovn_const.LSP_TYPE_LOCALPORT): - sname, pname = (utils.get_rand_device_name(prefix=p) - for p in ('switch', 'port')) + def _add_switch(self, chassis_name): + sname = utils.get_rand_device_name(prefix='switch') chassis = self.api.lookup('Chassis', chassis_name) + with self.nbapi.transaction(check_error=True) as txn: + switch = txn.add(self.nbapi.ls_add(sname)) + return chassis, switch.result + + def _add_port_to_switch( + self, switch, type=ovn_const.LSP_TYPE_LOCALPORT, + device_owner=constants.DEVICE_OWNER_DISTRIBUTED): + pname = utils.get_rand_device_name(prefix='port') row_event = events.WaitForCreatePortBindingEvent(pname) self.handler.watch_event(row_event) with self.nbapi.transaction(check_error=True) as txn: - switch = txn.add(self.nbapi.ls_add(sname)) - port = txn.add(self.nbapi.lsp_add(sname, pname, type=type)) + port = txn.add(self.nbapi.lsp_add( + switch.uuid, pname, type=type, + external_ids={ + ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: device_owner})) row_event.wait() - return chassis, switch.result, port.result, row_event.row + return port.result, row_event.row def test_get_metadata_port_network(self): - chassis, switch, port, binding = self._add_switch_port( - self.data['chassis'][0]['name']) + chassis, switch = self._add_switch(self.data['chassis'][0]['name']) + port, binding = self._add_port_to_switch(switch) result = self.api.get_metadata_port_network(str(binding.datapath.uuid)) self.assertEqual(binding, result) self.assertEqual(binding.datapath.external_ids['logical-switch'], str(switch.uuid)) + self.assertEqual( + port.external_ids[ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY], + constants.DEVICE_OWNER_DISTRIBUTED) + + def test_get_metadata_port_network_other_non_metadata_port(self): + chassis, switch = self._add_switch(self.data['chassis'][0]['name']) + port, binding = self._add_port_to_switch(switch) + port_lbhm, binding_port_lbhm = self._add_port_to_switch( + switch, device_owner=ovn_const.OVN_LB_HM_PORT_DISTRIBUTED) + result = self.api.get_metadata_port_network(str(binding.datapath.uuid)) + self.assertEqual(binding, result) + self.assertEqual(binding.datapath.external_ids['logical-switch'], + str(switch.uuid)) + self.assertEqual( + binding_port_lbhm.datapath.external_ids['logical-switch'], + str(switch.uuid)) + self.assertEqual( + port_lbhm.external_ids[ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY], + ovn_const.OVN_LB_HM_PORT_DISTRIBUTED) def test_get_metadata_port_network_missing(self): val = str(uuid.uuid4()) self.assertIsNone(self.api.get_metadata_port_network(val)) def _create_bound_port_with_ip(self): - chassis, switch, port, binding = self._add_switch_port( + chassis, switch = self._add_switch( self.data['chassis'][0]['name']) + port, binding = self._add_port_to_switch(switch) mac = 'de:ad:be:ef:4d:ad' ipaddr = '192.0.2.1' mac_ip = '%s %s' % (mac, ipaddr) @@ -165,8 +194,9 @@ class TestSbApi(BaseOvnIdlTest): self.assertEqual(1, len(result)) def test_get_ports_on_chassis(self): - chassis, switch, port, binding = self._add_switch_port( + chassis, switch = self._add_switch( self.data['chassis'][0]['name']) + port, binding = self._add_port_to_switch(switch) self.api.lsp_bind(port.name, chassis.name).execute(check_error=True) self.assertEqual([binding], self.api.get_ports_on_chassis(chassis.name)) diff --git a/neutron/tests/unit/common/ovn/test_utils.py b/neutron/tests/unit/common/ovn/test_utils.py index 440c0567d05..dbe8e252ff3 100644 --- a/neutron/tests/unit/common/ovn/test_utils.py +++ b/neutron/tests/unit/common/ovn/test_utils.py @@ -217,6 +217,21 @@ class TestUtils(base.BaseTestCase): self.assertFalse(utils.is_ovn_metadata_port(non_meta_port_0)) self.assertFalse(utils.is_ovn_metadata_port(non_meta_port_1)) + def test_is_ovn_lb_hm_port(self): + ovn_lb_hm_port = { + 'device_owner': constants.OVN_LB_HM_PORT_DISTRIBUTED, + 'device_id': 'ovn-lb-hm-12345'} + non_ovn_lb_hm_port_0 = { + 'device_owner': n_const.DEVICE_OWNER_DISTRIBUTED, + 'device_id': 'ovnmeta-12345'} + non_ovn_lb_hm_port_1 = { + 'device_owner': n_const.DEVICE_OWNER_DHCP, + 'device_id': 'dhcp-12345'} + + self.assertTrue(utils.is_ovn_lb_hm_port(ovn_lb_hm_port)) + self.assertFalse(utils.is_ovn_lb_hm_port(non_ovn_lb_hm_port_0)) + self.assertFalse(utils.is_ovn_lb_hm_port(non_ovn_lb_hm_port_1)) + class TestGateWayChassisValidity(base.BaseTestCase): diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index 48bc21a4e52..6d53548a619 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -1913,6 +1913,22 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase): self.assertEqual("address_scope_v4", options.address4_scope_id) self.assertEqual("address_scope_v6", options.address6_scope_id) + def test__get_port_options_with_ovn_lb_hm_port(self): + port = { + 'id': 'ovn-lb-hm-port', + 'mac_address': '00:00:00:00:00:00', + 'device_owner': ovn_const.OVN_LB_HM_PORT_DISTRIBUTED, + 'device_id': 'ovn-lb-hm-foo', + 'network_id': 'foo', + 'fixed_ips': [], + portbindings.HOST_ID: 'fake-src', + portbindings.PROFILE: { + ovn_const.MIGRATING_ATTR: 'fake-dest', + } + } + options = self.mech_driver._ovn_client._get_port_options(port) + self.assertEqual('fake-src', options.options['requested-chassis']) + def test__get_port_options_migrating_additional_chassis_missing(self): port = { 'id': 'virt-port', diff --git a/releasenotes/notes/new-device-owner-for-ovn-lb-hm-ports-f5a648c4d948c5c8.yaml b/releasenotes/notes/new-device-owner-for-ovn-lb-hm-ports-f5a648c4d948c5c8.yaml new file mode 100644 index 00000000000..b35aeaa4bd6 --- /dev/null +++ b/releasenotes/notes/new-device-owner-for-ovn-lb-hm-ports-f5a648c4d948c5c8.yaml @@ -0,0 +1,9 @@ +--- +other: + - | + The new value for 'device_owner' for OVN loadbalancer health monitor ports + (ovn-lb-hm:distributed) is now supported by Neutron, providing a LOCALPORT + behavior to these ports. The responsibility to define these ports with the + new value instead of the old one (network:distributed) is under the + OVN-Octavia Provider driver, which will take care of database conversion + for these ports.