From 9f677abd1e09bb5e9a7d25aac5f42bc49597c92c Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Tue, 18 Jul 2023 00:42:45 +0000 Subject: [PATCH] [OVN][Trunk] Set the subports correct host during live migration During the trunk migration, the parent port multiple port binding will contain the destination host. Because this update is done before the migration is done (in other words, the parent port still has two port binding registers), the method setting the binding profile of the subport will use the destination host in advance. At the end of the live migration, the subports host will point to the correct hostname. Related-Bug: #2027605 Change-Id: I2370ea2f96e2e31dbd43bf232a63394388e6945f (cherry picked from commit 7ed79c1f7890456488a4b44769ed84fea23c9a39) (cherry picked from commit 6d3c29723e5d48eefd6ba73dc152ed6c99f70ffa) --- .../trunk/drivers/ovn/trunk_driver.py | 13 +++++-- .../trunk/drivers/ovn/test_trunk_driver.py | 37 ++++++++++++++++--- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/neutron/services/trunk/drivers/ovn/trunk_driver.py b/neutron/services/trunk/drivers/ovn/trunk_driver.py index bc8ab0e422e..ff3e33f10fd 100644 --- a/neutron/services/trunk/drivers/ovn/trunk_driver.py +++ b/neutron/services/trunk/drivers/ovn/trunk_driver.py @@ -14,6 +14,7 @@ from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources +from neutron_lib import constants as n_const from neutron_lib import context as n_context from neutron_lib.db import api as db_api from neutron_lib import exceptions as n_exc @@ -49,7 +50,11 @@ class OVNTrunkHandler(object): context = n_context.get_admin_context() db_parent_port = port_obj.Port.get_object(context, id=parent_port) parent_port_status = db_parent_port.status - parent_port_bindings = db_parent_port.bindings[0] + try: + parent_port_bindings = [pb for pb in db_parent_port.bindings + if pb.status == n_const.ACTIVE][-1] + except IndexError: + parent_port_bindings = None for subport in subports: with db_api.CONTEXT_WRITER.using(context), ( txn(check_error=True)) as ovn_txn: @@ -83,8 +88,10 @@ class OVNTrunkHandler(object): db_port.id, db_port, ovn_const.TYPE_PORTS) ovn_txn.add(check_rev_cmd) parent_binding_host = '' - if parent_port_bindings.host: - parent_binding_host = parent_port_bindings.host + if parent_port_bindings and parent_port_bindings.host: + migrating_to = parent_port_bindings.profile.get( + ovn_const.MIGRATING_ATTR) + parent_binding_host = migrating_to or parent_port_bindings.host try: # NOTE(flaviof): We expect binding's host to be set. Otherwise, # sub-port will not transition from DOWN to ACTIVE. diff --git a/neutron/tests/functional/services/trunk/drivers/ovn/test_trunk_driver.py b/neutron/tests/functional/services/trunk/drivers/ovn/test_trunk_driver.py index 1c19b38b282..0b3d08f0e40 100644 --- a/neutron/tests/functional/services/trunk/drivers/ovn/test_trunk_driver.py +++ b/neutron/tests/functional/services/trunk/drivers/ovn/test_trunk_driver.py @@ -96,13 +96,23 @@ class TestOVNTrunkDriver(base.TestOVNFunctionalBase): if trunk.get('status'): self.assertEqual(trunk_consts.TRUNK_ACTIVE_STATUS, trunk['status']) - def _bind_port(self, port_id, host): + def _bind_port(self, port_id, host_source, host_dest=None): with db_api.CONTEXT_WRITER.using(self.context): - pb = port_obj.PortBinding.get_object(self.context, - port_id=port_id, host='') - pb.delete() - port_obj.PortBinding(self.context, port_id=port_id, host=host, - vif_type=portbindings.VIF_TYPE_OVS).create() + for pb in port_obj.PortBinding.get_objects(self.context, + port_id=port_id): + pb.delete() + profile = {} + if host_dest: + # When "host_dest" there are 2 port bindings, as in a live + # migration; the second one (destination host) is inactive. + profile[ovn_const.MIGRATING_ATTR] = host_dest + port_obj.PortBinding( + self.context, port_id=port_id, host=host_dest, + vif_type=portbindings.VIF_TYPE_OVS, + status=n_consts.INACTIVE).create() + port_obj.PortBinding( + self.context, port_id=port_id, host=host_source, + profile=profile, vif_type=portbindings.VIF_TYPE_OVS).create() def test_trunk_create(self): with self.trunk() as trunk: @@ -128,6 +138,21 @@ class TestOVNTrunkDriver(base.TestOVNFunctionalBase): self._verify_trunk_info(new_trunk, has_items=True, host='host1') + def test_subport_add_live_migration_multiple_port_binding(self): + with self.subport() as subport: + with self.trunk() as trunk: + self.trunk_plugin.add_subports(self.context, trunk['id'], + {'sub_ports': [subport]}) + new_trunk = self.trunk_plugin.get_trunk(self.context, + trunk['id']) + self._verify_trunk_info(new_trunk, has_items=True) + # Bind parent port. That will trigger the binding of the + # trunk subports too, using the same host ID. + self._bind_port(trunk['port_id'], 'host1', host_dest='host2') + self.mech_driver.set_port_status_up(trunk['port_id']) + self._verify_trunk_info(new_trunk, has_items=True, + host='host2') + def test_subport_delete(self): with self.subport() as subport: with self.trunk([subport]) as trunk: