diff --git a/nova/pci/manager.py b/nova/pci/manager.py index ff161e38cafb..3084643f5e8a 100644 --- a/nova/pci/manager.py +++ b/nova/pci/manager.py @@ -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. diff --git a/nova/tests/unit/pci/test_manager.py b/nova/tests/unit/pci/test_manager.py index 2ea986dc197e..fe7d918e27b9 100644 --- a/nova/tests/unit/pci/test_manager.py +++ b/nova/tests/unit/pci/test_manager.py @@ -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):