From 82f06feebe190064c7a37cc30c144de71052f920 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) --- .../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 e404ee0b1f7..dcfa88b25cf 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 @@ -51,7 +52,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: @@ -85,8 +90,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 099c7d63bdb..91317f31ea4 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 @@ -97,13 +97,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: @@ -148,6 +158,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: