Skip to content

Commit 84bb00a

Browse files
committed
Move get_pci_mapping_for_migration to MigrationContext
In order to fix Bug #1809095, it is required to update PCI related VIFs with the original PCI address on the source host to allow virt driver to properly unplug the VIF from hypervisor, e.g allow the proper VF representor to be unplugged from the integration bridge in case of a hardware offloaded OVS. To do so, some preliminary work is needed to allow code-sharing between nova.network.neutronv2 and nova.compute.manager This change: - Moves common logic to retrieve the PCI mapping between the source and destination node from nova.network.neutronv2 to objects.migration_context. - Makes code adjustments to methods in nova.network.neutronv2 to accomodate the former. Change-Id: I9a5118373548c525b2b1c2271e7d210cc92e4f4c Partial-Bug: #1809095 (cherry picked from commit 5a1c385)
1 parent 3689fbf commit 84bb00a

File tree

4 files changed

+85
-81
lines changed

4 files changed

+85
-81
lines changed

nova/network/neutronv2/api.py

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3253,45 +3253,14 @@ def cleanup_instance_network_on_host(self, context, instance, host):
32533253
# device_id field on the port which is not what we'd want for shelve.
32543254
pass
32553255

3256-
def _get_pci_devices_from_migration_context(self, migration_context,
3257-
migration):
3258-
if migration and migration.get('status') == 'reverted':
3259-
# In case of revert, swap old and new devices to
3260-
# update the ports back to the original devices.
3261-
return (migration_context.new_pci_devices,
3262-
migration_context.old_pci_devices)
3263-
return (migration_context.old_pci_devices,
3264-
migration_context.new_pci_devices)
3265-
3266-
def _get_pci_mapping_for_migration(self, context, instance, migration):
3267-
"""Get the mapping between the old PCI devices and the new PCI
3268-
devices that have been allocated during this migration. The
3269-
correlation is based on PCI request ID which is unique per PCI
3270-
devices for SR-IOV ports.
3271-
3272-
:param context: The request context.
3273-
:param instance: Get PCI mapping for this instance.
3274-
:param migration: The migration for this instance.
3275-
:Returns: dictionary of mapping {'<old pci address>': <New PciDevice>}
3276-
"""
3277-
migration_context = instance.migration_context
3278-
if not migration_context:
3256+
def _get_pci_mapping_for_migration(self, instance, migration):
3257+
if not instance.migration_context:
32793258
return {}
3280-
3281-
old_pci_devices, new_pci_devices = \
3282-
self._get_pci_devices_from_migration_context(migration_context,
3283-
migration)
3284-
if old_pci_devices and new_pci_devices:
3285-
LOG.debug("Determining PCI devices mapping using migration "
3286-
"context: old_pci_devices: %(old)s, "
3287-
"new_pci_devices: %(new)s",
3288-
{'old': [dev for dev in old_pci_devices],
3289-
'new': [dev for dev in new_pci_devices]})
3290-
return {old.address: new
3291-
for old in old_pci_devices
3292-
for new in new_pci_devices
3293-
if old.request_id == new.request_id}
3294-
return {}
3259+
# In case of revert, swap old and new devices to
3260+
# update the ports back to the original devices.
3261+
revert = (migration and
3262+
migration.get('status') == 'reverted')
3263+
return instance.migration_context.get_pci_mapping_for_migration(revert)
32953264

32963265
def _update_port_binding_for_instance(self, context, instance, host,
32973266
migration=None):
@@ -3337,7 +3306,7 @@ def _update_port_binding_for_instance(self, context, instance, host,
33373306
if (vnic_type in network_model.VNIC_TYPES_SRIOV
33383307
and migration is not None):
33393308
if not pci_mapping:
3340-
pci_mapping = self._get_pci_mapping_for_migration(context,
3309+
pci_mapping = self._get_pci_mapping_for_migration(
33413310
instance, migration)
33423311

33433312
pci_slot = binding_profile.get('pci_slot')

nova/objects/migration_context.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414

15+
from oslo_log import log as logging
1516
from oslo_serialization import jsonutils
1617
from oslo_utils import versionutils
1718

@@ -20,6 +21,8 @@
2021
from nova.objects import base
2122
from nova.objects import fields
2223

24+
LOG = logging.getLogger(__name__)
25+
2326

2427
@base.NovaObjectRegistry.register
2528
class MigrationContext(base.NovaPersistentObject, base.NovaObject):
@@ -80,3 +83,32 @@ def get_by_instance_uuid(cls, context, instance_uuid):
8083
return None
8184

8285
return cls.obj_from_db_obj(db_extra['migration_context'])
86+
87+
def get_pci_mapping_for_migration(self, revert):
88+
"""Get the mapping between the old PCI devices and the new PCI
89+
devices that have been allocated during this migration. The
90+
correlation is based on PCI request ID which is unique per PCI
91+
devices for SR-IOV ports.
92+
93+
:param revert: If True, return a reverse mapping i.e
94+
mapping between new PCI devices and old PCI devices.
95+
:returns: dictionary of PCI mapping.
96+
if revert==False:
97+
{'<old pci address>': <New PciDevice>}
98+
if revert==True:
99+
{'<new pci address>': <Old PciDevice>}
100+
"""
101+
step = -1 if revert else 1
102+
current_pci_devs, updated_pci_devs = (self.old_pci_devices,
103+
self.new_pci_devices)[::step]
104+
if current_pci_devs and updated_pci_devs:
105+
LOG.debug("Determining PCI devices mapping using migration "
106+
"context: current_pci_devs: %(cur)s, "
107+
"updated_pci_devs: %(upd)s",
108+
{'cur': [dev for dev in current_pci_devs],
109+
'upd': [dev for dev in updated_pci_devs]})
110+
return {curr_dev.address: upd_dev
111+
for curr_dev in current_pci_devs
112+
for upd_dev in updated_pci_devs
113+
if curr_dev.request_id == upd_dev.request_id}
114+
return {}

nova/tests/unit/network/test_neutronv2.py

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4681,56 +4681,29 @@ def test_update_port_bindings_for_instance_with_diff_host_unbound_vif_type(
46814681
def test_get_pci_mapping_for_migration(self):
46824682
instance = fake_instance.fake_instance_obj(self.context)
46834683
instance.migration_context = objects.MigrationContext()
4684-
old_pci_devices = objects.PciDeviceList(
4685-
objects=[objects.PciDevice(vendor_id='1377',
4686-
product_id='0047',
4687-
address='0000:0a:00.1',
4688-
compute_node_id=1,
4689-
request_id='1234567890')])
4690-
4691-
new_pci_devices = objects.PciDeviceList(
4692-
objects=[objects.PciDevice(vendor_id='1377',
4693-
product_id='0047',
4694-
address='0000:0b:00.1',
4695-
compute_node_id=2,
4696-
request_id='1234567890')])
4697-
4698-
instance.migration_context.old_pci_devices = old_pci_devices
4699-
instance.migration_context.new_pci_devices = new_pci_devices
4700-
instance.pci_devices = instance.migration_context.old_pci_devices
47014684
migration = {'status': 'confirmed'}
47024685

4703-
pci_mapping = self.api._get_pci_mapping_for_migration(
4704-
self.context, instance, migration)
4705-
self.assertEqual(
4706-
{old_pci_devices[0].address: new_pci_devices[0]}, pci_mapping)
4686+
with mock.patch.object(instance.migration_context,
4687+
'get_pci_mapping_for_migration') as map_func:
4688+
self.api._get_pci_mapping_for_migration(instance, migration)
4689+
map_func.assert_called_with(False)
47074690

47084691
def test_get_pci_mapping_for_migration_reverted(self):
47094692
instance = fake_instance.fake_instance_obj(self.context)
47104693
instance.migration_context = objects.MigrationContext()
4711-
old_pci_devices = objects.PciDeviceList(
4712-
objects=[objects.PciDevice(vendor_id='1377',
4713-
product_id='0047',
4714-
address='0000:0a:00.1',
4715-
compute_node_id=1,
4716-
request_id='1234567890')])
4717-
4718-
new_pci_devices = objects.PciDeviceList(
4719-
objects=[objects.PciDevice(vendor_id='1377',
4720-
product_id='0047',
4721-
address='0000:0b:00.1',
4722-
compute_node_id=2,
4723-
request_id='1234567890')])
4724-
4725-
instance.migration_context.old_pci_devices = old_pci_devices
4726-
instance.migration_context.new_pci_devices = new_pci_devices
4727-
instance.pci_devices = instance.migration_context.old_pci_devices
47284694
migration = {'status': 'reverted'}
47294695

4696+
with mock.patch.object(instance.migration_context,
4697+
'get_pci_mapping_for_migration') as map_func:
4698+
self.api._get_pci_mapping_for_migration(instance, migration)
4699+
map_func.assert_called_with(True)
4700+
4701+
def test_get_pci_mapping_for_migration_no_migration_context(self):
4702+
instance = fake_instance.fake_instance_obj(self.context)
4703+
instance.migration_context = None
47304704
pci_mapping = self.api._get_pci_mapping_for_migration(
4731-
self.context, instance, migration)
4732-
self.assertEqual(
4733-
{new_pci_devices[0].address: old_pci_devices[0]}, pci_mapping)
4705+
instance, None)
4706+
self.assertDictEqual({}, pci_mapping)
47344707

47354708
@mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
47364709
def test_update_port_profile_for_migration_teardown_false(

nova/tests/unit/objects/test_migration_context.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@ def get_fake_migration_context_obj(ctxt):
5252
return obj
5353

5454

55+
def get_fake_migration_context_with_pci_devs(ctxt=None):
56+
obj = get_fake_migration_context_obj(ctxt)
57+
obj.old_pci_devices = objects.PciDeviceList(
58+
objects=[objects.PciDevice(vendor_id='1377',
59+
product_id='0047',
60+
address='0000:0a:00.1',
61+
compute_node_id=1,
62+
request_id=uuids.pcidev)])
63+
obj.new_pci_devices = objects.PciDeviceList(
64+
objects=[objects.PciDevice(vendor_id='1377',
65+
product_id='0047',
66+
address='0000:0b:00.1',
67+
compute_node_id=2,
68+
request_id=uuids.pcidev)])
69+
return obj
70+
71+
5572
class _TestMigrationContext(object):
5673

5774
def _test_get_by_instance_uuid(self, db_data):
@@ -104,7 +121,20 @@ def test_get_by_instance_uuid_missing(self, mock_get):
104121

105122

106123
class TestMigrationContext(test_objects._LocalTest, _TestMigrationContext):
107-
pass
124+
125+
def test_pci_mapping_for_migration(self):
126+
mig_ctx = get_fake_migration_context_with_pci_devs()
127+
pci_mapping = mig_ctx.get_pci_mapping_for_migration(False)
128+
self.assertDictEqual(
129+
{mig_ctx.old_pci_devices[0].address: mig_ctx.new_pci_devices[0]},
130+
pci_mapping)
131+
132+
def test_pci_mapping_for_migration_revert(self):
133+
mig_ctx = get_fake_migration_context_with_pci_devs()
134+
pci_mapping = mig_ctx.get_pci_mapping_for_migration(True)
135+
self.assertDictEqual(
136+
{mig_ctx.new_pci_devices[0].address: mig_ctx.old_pci_devices[0]},
137+
pci_mapping)
108138

109139

110140
class TestMigrationContextRemote(test_objects._RemoteTest,

0 commit comments

Comments
 (0)