diff --git a/neutron/cmd/sanity/checks.py b/neutron/cmd/sanity/checks.py index 768414493ac..8bbf00703e2 100644 --- a/neutron/cmd/sanity/checks.py +++ b/neutron/cmd/sanity/checks.py @@ -52,6 +52,7 @@ MINIMUM_DIBBLER_VERSION = '1.0.1' CONNTRACK_GRE_MODULE = 'nf_conntrack_proto_gre' OVN_NB_DB_SCHEMA_PORT_GROUP = '5.11' OVN_NB_DB_SCHEMA_STATELESS_NAT = '5.17' +OVN_SB_DB_SCHEMA_VIRTUAL_PORT = '2.5' class OVNCheckType(enum.Enum): @@ -612,3 +613,17 @@ def ovn_nb_db_schema_stateless_nat_supported(): 'Exception: %s', e) return False return True + + +def ovn_sb_db_schema_virtual_port_supported(): + try: + ver = _get_ovn_version(OVNCheckType.sb_db_schema) + minver = versionutils.convert_version_to_tuple( + OVN_SB_DB_SCHEMA_VIRTUAL_PORT) + if ver < minver: + return False + except (OSError, RuntimeError, ValueError) as e: + LOG.debug('Exception while checking OVN DB schema version. ' + 'Exception: %s', e) + return False + return True diff --git a/neutron/cmd/sanity_check.py b/neutron/cmd/sanity_check.py index 601c01be83d..db19f15cdee 100644 --- a/neutron/cmd/sanity_check.py +++ b/neutron/cmd/sanity_check.py @@ -319,6 +319,14 @@ def check_ovn_nb_db_schema_stateless_nat(): return result +def check_ovn_sb_db_schema_virtual_port(): + result = checks.ovn_sb_db_schema_virtual_port_supported() + if not result: + LOG.warning('OVN SB DB schema does not support virtual ports. This ' + 'support was added in DB schema version 2.5.') + return result + + # Define CLI opts to test specific features, with a callback for the test OPTS = [ BoolOptCallback('ovs_vxlan', check_ovs_vxlan, default=False, @@ -391,6 +399,10 @@ OPTS = [ check_ovn_nb_db_schema_stateless_nat, help=_('Check OVN NB DB schema support stateless NAT'), default=False), + BoolOptCallback('ovn_sb_db_schema_virtual_port_support', + check_ovn_sb_db_schema_virtual_port, + help=_('Check OVN SB DB schema support virtual ports'), + default=False), ] @@ -440,6 +452,7 @@ def enable_tests_from_config(): if 'ovn' in cfg.CONF.ml2.mechanism_drivers: cfg.CONF.set_default('ovn_nb_db_schema_port_group_support', True) cfg.CONF.set_default('ovn_nb_db_schema_stateless_nat_support', True) + cfg.CONF.set_default('ovn_sb_db_schema_virtual_port_support', True) def all_tests_passed(): 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 3c7aa018313..432c9cc547e 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 @@ -105,11 +105,6 @@ class OVNClient(object): for cmd in commands: txn.add(cmd) - def _is_virtual_port_supported(self): - # TODO(lucasagomes): Remove this method in the future. The - # "virtual" port type was added in the version 2.12 of OVN - return self._sb_idl.is_col_present('Port_Binding', 'virtual_parent') - def is_external_ports_supported(self): return self._nb_idl.is_col_present( 'Logical_Switch_Port', 'ha_chassis_group') @@ -259,8 +254,7 @@ class OVNClient(object): subnet['cidr'].split('/')[1]) # Check if the port being created is a virtual port - if (self._is_virtual_port_supported() and - not port['device_owner']): + if not port['device_owner']: parents = self.get_virtual_port_parents(ip_addr, port) if parents: port_type = ovn_const.LSP_TYPE_VIRTUAL @@ -470,8 +464,7 @@ class OVNClient(object): # Check if the parent port was created with the # allowed_address_pairs already set allowed_address_pairs = port.get('allowed_address_pairs', []) - if (self._is_virtual_port_supported() and - allowed_address_pairs and + if (allowed_address_pairs and port_info.type != ovn_const.LSP_TYPE_VIRTUAL): addrs = [addr['ip_address'] for addr in allowed_address_pairs] self._set_unset_virtual_port_type(context, txn, port, addrs) @@ -595,8 +588,7 @@ class OVNClient(object): ovn_port = self._nb_idl.lookup('Logical_Switch_Port', port['id']) addr_pairs_diff = utils.compute_address_pairs_diff(ovn_port, port) - if (self._is_virtual_port_supported() and - port_info.type != ovn_const.LSP_TYPE_VIRTUAL): + if port_info.type != ovn_const.LSP_TYPE_VIRTUAL: self._set_unset_virtual_port_type( context, txn, port, addr_pairs_diff.added) self._set_unset_virtual_port_type( @@ -683,8 +675,7 @@ class OVNClient(object): self.add_txns_to_remove_port_dns_records(txn, port_object) # Check if the port being deleted is a virtual parent - if (ovn_port.type != ovn_const.LSP_TYPE_VIRTUAL and - self._is_virtual_port_supported()): + if ovn_port.type != ovn_const.LSP_TYPE_VIRTUAL: ls = self._nb_idl.ls_get(ovn_network_name).execute( check_error=True) cmd = self._nb_idl.unset_lswitch_port_to_virtual_type diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index 25c4471aec0..2e00db13e31 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -207,9 +207,6 @@ class TestNetworkMTUUpdate(base.TestOVNFunctionalBase): second_revision.updated_at) -@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.' - 'ovsdb.ovn_client.OVNClient._is_virtual_port_supported', - lambda *args: True) class TestVirtualPorts(base.TestOVNFunctionalBase): def setUp(self): diff --git a/neutron/tests/unit/db/test_db_base_plugin_v2.py b/neutron/tests/unit/db/test_db_base_plugin_v2.py index 23f85f594c8..b0d807aa860 100644 --- a/neutron/tests/unit/db/test_db_base_plugin_v2.py +++ b/neutron/tests/unit/db/test_db_base_plugin_v2.py @@ -62,6 +62,7 @@ from neutron.ipam.drivers.neutrondb_ipam import driver as ipam_driver from neutron.ipam import exceptions as ipam_exc from neutron.objects import network as network_obj from neutron.objects import router as l3_obj +from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client from neutron import policy from neutron import quota from neutron.quota import resource_registry @@ -1009,6 +1010,13 @@ class TestV2HTTPResponse(NeutronDbPluginV2TestCase): class TestPortsV2(NeutronDbPluginV2TestCase): + + def setUp(self, **kwargs): + super().setUp(**kwargs) + self.mock_vp_parents = mock.patch.object( + ovn_client.OVNClient, 'get_virtual_port_parents', + return_value=None).start() + def test_create_port_json(self): keys = [('admin_state_up', True), ('status', self.port_create_status)] with self.network(shared=True) as network: diff --git a/neutron/tests/unit/extensions/test_portsecurity.py b/neutron/tests/unit/extensions/test_portsecurity.py index e9a97d21380..dc0539d12da 100644 --- a/neutron/tests/unit/extensions/test_portsecurity.py +++ b/neutron/tests/unit/extensions/test_portsecurity.py @@ -29,6 +29,7 @@ from neutron.db import db_base_plugin_v2 from neutron.db import portsecurity_db from neutron.db import securitygroups_db from neutron.extensions import securitygroup as ext_sg +from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client from neutron import quota from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_securitygroup @@ -186,6 +187,9 @@ class TestPortSecurity(PortSecurityDBTestCase): commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation') self.mock_quota_make_res = make_res.start() self.mock_quota_commit_res = commit_res.start() + self.mock_vp_parents = mock.patch.object( + ovn_client.OVNClient, 'get_virtual_port_parents', + return_value=None).start() def test_create_network_with_portsecurity_mac(self): res = self._create_network('json', 'net1', True) 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 8c4d3b75707..6afc1049632 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 @@ -133,6 +133,9 @@ class TestOVNMechanismDriverBase(MechDriverSetupBase, p = mock.patch.object(ovn_revision_numbers_db, 'bump_revision') p.start() self.addCleanup(p.stop) + self.mock_vp_parents = mock.patch.object( + ovn_client.OVNClient, 'get_virtual_port_parents', + return_value=None).start() def test_delete_mac_binding_entries(self): self.config(group='ovn', ovn_sb_private_key=None) @@ -159,6 +162,13 @@ class TestOVNMechanismDriverBase(MechDriverSetupBase, class TestOVNMechanismDriver(TestOVNMechanismDriverBase): + + def setUp(self): + super().setUp() + self.mock_vp_parents = mock.patch.object( + ovn_client.OVNClient, 'get_virtual_port_parents', + return_value=None).start() + @mock.patch.object(ovsdb_monitor.OvnInitPGNbIdl, 'from_server') @mock.patch.object(ovsdb_monitor, 'short_living_ovsdb_api') def test__create_neutron_pg_drop_non_existing( @@ -2214,6 +2224,9 @@ class OVNMechanismDriverTestCase(MechDriverSetupBase, p = mock.patch.object(ovn_utils, 'get_revision_number', return_value=1) p.start() self.addCleanup(p.stop) + self.mock_vp_parents = mock.patch.object( + ovn_client.OVNClient, 'get_virtual_port_parents', + return_value=None).start() class TestOVNMechanismDriverBasicGet(test_plugin.TestMl2BasicGet, @@ -3209,6 +3222,9 @@ class TestOVNMechanismDriverSecurityGroup(MechDriverSetupBase, super(TestOVNMechanismDriverSecurityGroup, self).setUp() self.ctx = context.get_admin_context() revision_plugin.RevisionPlugin() + self.mock_vp_parents = mock.patch.object( + ovn_client.OVNClient, 'get_virtual_port_parents', + return_value=None).start() def _delete_default_sg_rules(self, security_group_id): res = self._list( @@ -3714,8 +3730,6 @@ class TestOVNVtepPortBinding(OVNMechanismDriverTestCase): ovn_port_info.options["vtep-logical-switch"]) -@mock.patch.object(ovn_client.OVNClient, '_is_virtual_port_supported', - lambda *args: True) class TestOVNVVirtualPort(OVNMechanismDriverTestCase): def setUp(self): diff --git a/releasenotes/notes/ovn-support-virtual-ports-3da6dc89937a63c7.yaml b/releasenotes/notes/ovn-support-virtual-ports-3da6dc89937a63c7.yaml new file mode 100644 index 00000000000..8bc74236713 --- /dev/null +++ b/releasenotes/notes/ovn-support-virtual-ports-3da6dc89937a63c7.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Virtual ports are supported in OVN since version 2.12. Since Yoga, this + support is mandatory. The minimum OVN SB schema version must be 2.5.