Separate methods to free claimed and allocated devs

The existing PCI manager exposes a single method to free PCI resources
free_instance(), which frees both claimed and allocated PCI resources
for an instance.

This change proposes to extend the PCI manager API with two methods:
  1. free_instance_claims() : free PCI resources claims for instance.
  2. free_instance_allocations() : free PCI resources allocations for
     instance.

This change refactors free_instance() to use (1) and (2) from above.

This change is required to enable SR-IOV live migration as it is
required to free instance PCI allocations on the source node
in case of a successful migration and free instance PCI claims
on the destination node in case of an unsuccessful migration.

Change-Id: Id961f0fc219f32a2cf0282859f228e87cb36ffeb
Partial-Implements: blueprint libvirt-neutron-sriov-livemigration
This commit is contained in:
Adrian Chiris 2018-11-05 11:49:57 +02:00
parent 64b4f41b24
commit 2a3179affb
2 changed files with 84 additions and 12 deletions

View File

@ -314,23 +314,43 @@ class PciDevTracker(object):
for dev in freed_devs:
self.stats.add_device(dev)
def _free_instance(self, instance):
def free_instance_allocations(self, context, instance):
"""Free devices that are in ALLOCATED state for instance.
:param context: user request context (nova.context.RequestContext)
:param instance: instance object
"""
if self.allocations.pop(instance['uuid'], None):
for dev in self.pci_devs:
if (dev.status == fields.PciDeviceStatus.ALLOCATED and
dev.instance_uuid == instance['uuid']):
self._free_device(dev)
def free_instance_claims(self, context, instance):
"""Free devices that are in CLAIMED state for instance.
:param context: user request context (nova.context.RequestContext)
:param instance: instance object
"""
if self.claims.pop(instance['uuid'], None):
for dev in self.pci_devs:
if (dev.status == fields.PciDeviceStatus.CLAIMED and
dev.instance_uuid == instance['uuid']):
self._free_device(dev)
def free_instance(self, context, instance):
"""Free devices that are in CLAIMED or ALLOCATED state for instance.
:param context: user request context (nova.context.RequestContext)
:param instance: instance object
"""
# Note(yjiang5): When an instance is resized, the devices in the
# destination node are claimed to the instance in prep_resize stage.
# However, the instance contains only allocated devices
# information, not the claimed one. So we can't use
# instance['pci_devices'] to check the devices to be freed.
for dev in self.pci_devs:
if dev.status in (fields.PciDeviceStatus.CLAIMED,
fields.PciDeviceStatus.ALLOCATED):
if dev.instance_uuid == instance['uuid']:
self._free_device(dev)
def free_instance(self, context, instance):
if self.allocations.pop(instance['uuid'], None):
self._free_instance(instance)
elif self.claims.pop(instance['uuid'], None):
self._free_instance(instance)
self.free_instance_allocations(context, instance)
self.free_instance_claims(context, instance)
def update_pci_for_instance(self, context, instance, sign):
"""Update PCI usage information if devices are de/allocated.

View File

@ -546,6 +546,58 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
self.assertIn(pci_device.id, free_pci_device_ids)
self.assertIsNone(self.tracker.allocations.get(instance_uuid))
def test_free_instance_claims(self):
# Create an InstancePCIRequest object
pci_requests_obj = self._create_pci_requests_object(
[{'count': 1, 'spec': [{'vendor_id': 'v'}]}])
# Claim a single PCI device
claimed_devs = self.tracker.claim_instance(mock.sentinel.context,
pci_requests_obj, None)
# Assert we have exactly one claimed device for the given instance.
claimed_dev = claimed_devs[0]
instance_uuid = self.inst['uuid']
self.assertEqual(1, len(self.tracker.claims.get(instance_uuid)))
self.assertIn(claimed_dev.id,
[pci_dev.id for pci_dev in
self.tracker.claims.get(instance_uuid)])
self.assertIsNone(self.tracker.allocations.get(instance_uuid))
# Free instance claims
self.tracker.free_instance_claims(mock.sentinel.context, self.inst)
# Assert no claims for instance and all PCI devices are free
self.assertIsNone(self.tracker.claims.get(instance_uuid))
free_devs = self.tracker.pci_stats.get_free_devs()
self.assertEqual(len(fake_db_devs), len(free_devs))
def test_free_instance_allocations(self):
# Create an InstancePCIRequest object
pci_requests_obj = self._create_pci_requests_object(
[{'count': 1, 'spec': [{'vendor_id': 'v'}]}])
# Allocate a single PCI device
allocated_devs = self.tracker.claim_instance(mock.sentinel.context,
pci_requests_obj, None)
self.tracker.allocate_instance(self.inst)
# Assert we have exactly one allocated device for the given instance.
allocated_dev = allocated_devs[0]
instance_uuid = self.inst['uuid']
self.assertIsNone(self.tracker.claims.get(instance_uuid))
self.assertEqual(1, len(self.tracker.allocations.get(instance_uuid)))
self.assertIn(allocated_dev.id,
[pci_dev.id for pci_dev in
self.tracker.allocations.get(instance_uuid)])
# Free instance allocations and assert claims did not change
self.tracker.free_instance_allocations(mock.sentinel.context,
self.inst)
# Assert all PCI devices are free.
self.assertIsNone(self.tracker.allocations.get(instance_uuid))
free_devs = self.tracker.pci_stats.get_free_devs()
self.assertEqual(len(fake_db_devs), len(free_devs))
class PciGetInstanceDevs(test.NoDBTestCase):