pci: Move PCI devices and PCI requests into migration context
When resizing guest to flavor with pci passthrogth, we need to drop the old pci devices and allocate new ones. To be able to do that we are leveraging the migration context (that used only for NUMA). Adds old and new PCI devices/ PCI requests into the MigrationContext object and uses the nova.pci.request.get_pci_requests_from_flavor() function to grab the set of requested PCI devices during a migration. Then, in the resource tracker's _update_usage_from_migration() call, we use the old and new PCI devices and PCI requests stored in the MigrationContext to properly account for changes. Closes-Bug: #1368201 Co-Authored-by: Jay Pipes <jaypipes@gmail.com> Change-Id: Ie8690f2b7235d677ebe15fabaae81b0a6bda29de
This commit is contained in:
parent
c9a3d86803
commit
c2c3b97259
@ -22,7 +22,6 @@ from oslo_log import log as logging
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.i18n import _LI
|
||||
from nova.i18n import _LW
|
||||
from nova import objects
|
||||
from nova.virt import hardware
|
||||
|
||||
@ -201,9 +200,7 @@ class Claim(NopClaim):
|
||||
if host_topology:
|
||||
host_topology = objects.NUMATopology.obj_from_db_obj(
|
||||
host_topology)
|
||||
pci_requests = objects.InstancePCIRequests.get_by_instance_uuid(
|
||||
self.context, self.instance.uuid)
|
||||
|
||||
pci_requests = self._pci_requests
|
||||
pci_stats = None
|
||||
if pci_requests.requests:
|
||||
pci_stats = self.tracker.pci_tracker.stats
|
||||
@ -300,18 +297,3 @@ class MoveClaim(Claim):
|
||||
self.tracker.drop_move_claim(
|
||||
self.context,
|
||||
self.instance, instance_type=self.instance_type)
|
||||
|
||||
def create_migration_context(self):
|
||||
if not self.migration:
|
||||
LOG.warning(
|
||||
_LW("Can't create a migration_context record without a "
|
||||
"migration object specified."),
|
||||
instance=self.instance)
|
||||
return
|
||||
|
||||
mig_context = objects.MigrationContext(
|
||||
context=self.context, instance_uuid=self.instance.uuid,
|
||||
migration_id=self.migration.id,
|
||||
old_numa_topology=self.instance.numa_topology,
|
||||
new_numa_topology=self.claimed_numa_topology)
|
||||
return mig_context
|
||||
|
@ -35,6 +35,7 @@ from nova import objects
|
||||
from nova.objects import base as obj_base
|
||||
from nova.objects import migration as migration_obj
|
||||
from nova.pci import manager as pci_manager
|
||||
from nova.pci import request as pci_request
|
||||
from nova import rpc
|
||||
from nova.scheduler import client as scheduler_client
|
||||
from nova import utils
|
||||
@ -217,15 +218,49 @@ class ResourceTracker(object):
|
||||
"GB", {'flavor': instance.root_gb,
|
||||
'overhead': overhead.get('disk_gb', 0)})
|
||||
|
||||
pci_requests = objects.InstancePCIRequests.\
|
||||
get_by_instance_uuid_and_newness(
|
||||
context, instance.uuid, True)
|
||||
# TODO(moshele): we are recreating the pci requests even if
|
||||
# there was no change on resize. This will cause allocating
|
||||
# the old/new pci device in the resize phase. In the future
|
||||
# we would like to optimise this.
|
||||
new_pci_requests = pci_request.get_pci_requests_from_flavor(
|
||||
new_instance_type)
|
||||
new_pci_requests.instance_uuid = instance.uuid
|
||||
# PCI requests come from two sources: instance flavor and
|
||||
# SR-IOV ports. SR-IOV ports pci_request don't have an alias_name.
|
||||
# On resize merge the SR-IOV ports pci_requests with the new
|
||||
# instance flavor pci_requests.
|
||||
if instance.pci_requests:
|
||||
for request in instance.pci_requests.requests:
|
||||
if request.alias_name is None:
|
||||
new_pci_requests.requests.append(request)
|
||||
claim = claims.MoveClaim(context, instance, new_instance_type,
|
||||
image_meta, self, self.compute_node,
|
||||
pci_requests, overhead=overhead,
|
||||
new_pci_requests, overhead=overhead,
|
||||
limits=limits)
|
||||
|
||||
claim.migration = migration
|
||||
instance.migration_context = claim.create_migration_context()
|
||||
claimed_pci_devices_objs = []
|
||||
if self.pci_tracker:
|
||||
# NOTE(jaypipes): ComputeNode.pci_device_pools is set below
|
||||
# in _update_usage_from_instance().
|
||||
claimed_pci_devices_objs = self.pci_tracker.claim_instance(
|
||||
context, new_pci_requests, claim.claimed_numa_topology)
|
||||
claimed_pci_devices = objects.PciDeviceList(
|
||||
objects=claimed_pci_devices_objs)
|
||||
|
||||
# TODO(jaypipes): Move claimed_numa_topology out of the Claim's
|
||||
# constructor flow so the Claim constructor only tests whether
|
||||
# resources can be claimed, not consume the resources directly.
|
||||
mig_context = objects.MigrationContext(
|
||||
context=context, instance_uuid=instance.uuid,
|
||||
migration_id=migration.id,
|
||||
old_numa_topology=instance.numa_topology,
|
||||
new_numa_topology=claim.claimed_numa_topology,
|
||||
old_pci_devices=instance.pci_devices,
|
||||
new_pci_devices=claimed_pci_devices,
|
||||
old_pci_requests=instance.pci_requests,
|
||||
new_pci_requests=new_pci_requests)
|
||||
instance.migration_context = mig_context
|
||||
instance.save()
|
||||
|
||||
# Mark the resources in-use for the resize landing on this
|
||||
@ -323,9 +358,12 @@ class ResourceTracker(object):
|
||||
usage = self._get_usage_dict(
|
||||
itype, numa_topology=numa_topology)
|
||||
if self.pci_tracker:
|
||||
self.pci_tracker.update_pci_for_migration(context,
|
||||
instance,
|
||||
sign=-1)
|
||||
# free old allocated pci devices
|
||||
old_pci_devices = self._get_migration_context_resource(
|
||||
'pci_devices', instance, prefix='old_')
|
||||
if old_pci_devices:
|
||||
for pci_device in old_pci_devices:
|
||||
self.pci_tracker.free_device(pci_device, instance)
|
||||
self._update_usage(usage, sign=-1)
|
||||
|
||||
ctxt = context.elevated()
|
||||
@ -685,8 +723,9 @@ class ResourceTracker(object):
|
||||
def _get_migration_context_resource(self, resource, instance,
|
||||
prefix='new_', itype=None):
|
||||
migration_context = instance.migration_context
|
||||
if migration_context:
|
||||
return getattr(migration_context, prefix + resource)
|
||||
resource = prefix + resource
|
||||
if migration_context and resource in migration_context:
|
||||
return getattr(migration_context, resource)
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -710,7 +749,7 @@ class ResourceTracker(object):
|
||||
record = self.tracked_instances.get(uuid, None)
|
||||
itype = None
|
||||
numa_topology = None
|
||||
|
||||
sign = 0
|
||||
if same_node:
|
||||
# same node resize. record usage for whichever instance type the
|
||||
# instance is *not* in:
|
||||
@ -720,6 +759,7 @@ class ResourceTracker(object):
|
||||
migration)
|
||||
numa_topology = self._get_migration_context_resource(
|
||||
'numa_topology', instance)
|
||||
sign = 1
|
||||
else:
|
||||
# instance record already has new flavor, hold space for a
|
||||
# possible revert to the old instance type:
|
||||
@ -752,8 +792,9 @@ class ResourceTracker(object):
|
||||
if itype:
|
||||
usage = self._get_usage_dict(
|
||||
itype, numa_topology=numa_topology)
|
||||
if self.pci_tracker:
|
||||
self.pci_tracker.update_pci_for_migration(context, instance)
|
||||
if self.pci_tracker and sign:
|
||||
self.pci_tracker.update_pci_for_instance(
|
||||
context, instance, sign=sign)
|
||||
self._update_usage(usage)
|
||||
if self.pci_tracker:
|
||||
obj = self.pci_tracker.stats.to_device_pools_obj()
|
||||
|
@ -46,6 +46,7 @@ Possible Values:
|
||||
|
||||
Services which consume this:
|
||||
|
||||
* nova-api
|
||||
* nova-compute
|
||||
|
||||
Related options:
|
||||
|
@ -52,6 +52,9 @@ _INSTANCE_OPTIONAL_NON_COLUMN_FIELDS = ['fault', 'flavor', 'old_flavor',
|
||||
_INSTANCE_EXTRA_FIELDS = ['numa_topology', 'pci_requests',
|
||||
'flavor', 'vcpu_model', 'migration_context',
|
||||
'keypairs']
|
||||
# These are fields that applied/drooped by migration_context
|
||||
_MIGRATION_CONTEXT_ATTRS = ['numa_topology', 'pci_requests',
|
||||
'pci_devices']
|
||||
|
||||
# These are fields that can be specified as expected_attrs
|
||||
INSTANCE_OPTIONAL_ATTRS = (_INSTANCE_OPTIONAL_JOINED_FIELDS +
|
||||
@ -878,18 +881,27 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
||||
|
||||
def apply_migration_context(self):
|
||||
if self.migration_context:
|
||||
self.numa_topology = self.migration_context.new_numa_topology
|
||||
self._set_migration_context_to_instance(prefix='new_')
|
||||
else:
|
||||
LOG.debug("Trying to apply a migration context that does not "
|
||||
"seem to be set for this instance", instance=self)
|
||||
|
||||
def revert_migration_context(self):
|
||||
if self.migration_context:
|
||||
self.numa_topology = self.migration_context.old_numa_topology
|
||||
self._set_migration_context_to_instance(prefix='old_')
|
||||
else:
|
||||
LOG.debug("Trying to revert a migration context that does not "
|
||||
"seem to be set for this instance", instance=self)
|
||||
|
||||
def _set_migration_context_to_instance(self, prefix):
|
||||
for inst_attr_name in _MIGRATION_CONTEXT_ATTRS:
|
||||
setattr(self, inst_attr_name, None)
|
||||
attr_name = prefix + inst_attr_name
|
||||
if attr_name in self.migration_context:
|
||||
attr_value = getattr(
|
||||
self.migration_context, attr_name)
|
||||
setattr(self, inst_attr_name, attr_value)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mutated_migration_context(self):
|
||||
"""Context manager to temporarily apply the migration context.
|
||||
@ -898,12 +910,15 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
||||
context will be saved which can cause incorrect resource tracking, and
|
||||
should be avoided.
|
||||
"""
|
||||
current_numa_topo = self.numa_topology
|
||||
current_values = {}
|
||||
for attr_name in _MIGRATION_CONTEXT_ATTRS:
|
||||
current_values[attr_name] = getattr(self, attr_name)
|
||||
self.apply_migration_context()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.numa_topology = current_numa_topo
|
||||
for attr_name in _MIGRATION_CONTEXT_ATTRS:
|
||||
setattr(self, attr_name, current_values[attr_name])
|
||||
|
||||
@base.remotable
|
||||
def drop_migration_context(self):
|
||||
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import versionutils
|
||||
|
||||
from nova import db
|
||||
from nova import exception
|
||||
@ -33,7 +34,8 @@ class MigrationContext(base.NovaPersistentObject, base.NovaObject):
|
||||
"""
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
# Version 1.1: Add old/new pci_devices and pci_requests
|
||||
VERSION = '1.1'
|
||||
|
||||
fields = {
|
||||
'instance_uuid': fields.UUIDField(),
|
||||
@ -42,8 +44,25 @@ class MigrationContext(base.NovaPersistentObject, base.NovaObject):
|
||||
nullable=True),
|
||||
'old_numa_topology': fields.ObjectField('InstanceNUMATopology',
|
||||
nullable=True),
|
||||
'new_pci_devices': fields.ObjectField('PciDeviceList',
|
||||
nullable=True),
|
||||
'old_pci_devices': fields.ObjectField('PciDeviceList',
|
||||
nullable=True),
|
||||
'new_pci_requests': fields.ObjectField('InstancePCIRequests',
|
||||
nullable=True),
|
||||
'old_pci_requests': fields.ObjectField('InstancePCIRequests',
|
||||
nullable=True),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_make_compatible(cls, primitive, target_version):
|
||||
target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
if target_version < (1, 1):
|
||||
primitive.pop('old_pci_devices', None)
|
||||
primitive.pop('new_pci_devices', None)
|
||||
primitive.pop('old_pci_requests', None)
|
||||
primitive.pop('new_pci_requests', None)
|
||||
|
||||
@classmethod
|
||||
def obj_from_db_obj(cls, db_obj):
|
||||
primitive = jsonutils.loads(db_obj)
|
||||
|
@ -26,7 +26,6 @@ from nova import objects
|
||||
from nova.objects import fields
|
||||
from nova.pci import stats
|
||||
from nova.pci import whitelist
|
||||
from nova.virt import hardware
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -200,16 +199,27 @@ class PciDevTracker(object):
|
||||
self.allocations[instance['uuid']] += devs
|
||||
|
||||
def claim_instance(self, context, pci_requests, instance_numa_topology):
|
||||
if not self.pci_devs or not pci_requests.requests:
|
||||
return
|
||||
devs = []
|
||||
if self.pci_devs and pci_requests.requests:
|
||||
instance_uuid = pci_requests.instance_uuid
|
||||
devs = self._claim_instance(context, pci_requests,
|
||||
instance_numa_topology)
|
||||
if devs:
|
||||
self.claims[instance_uuid] = devs
|
||||
return devs
|
||||
|
||||
instance_uuid = pci_requests.instance_uuid
|
||||
devs = self._claim_instance(context, pci_requests,
|
||||
instance_numa_topology)
|
||||
if devs:
|
||||
self.claims[instance_uuid] = devs
|
||||
return devs
|
||||
return None
|
||||
def free_device(self, dev, instance):
|
||||
"""Free device from pci resource tracker
|
||||
|
||||
:param dev: cloned pci device object that needs to be free
|
||||
:param instance: the instance that this pci device
|
||||
is allocated to
|
||||
"""
|
||||
for pci_dev in self.pci_devs:
|
||||
# find the matching pci device in the pci resource tracker
|
||||
# pci device. Once found one free it.
|
||||
if dev == pci_dev and dev.instance_uuid == instance['uuid']:
|
||||
self._free_device(pci_dev)
|
||||
|
||||
def _free_device(self, dev, instance=None):
|
||||
freed_devs = dev.free(instance)
|
||||
@ -248,27 +258,6 @@ class PciDevTracker(object):
|
||||
if sign == 1:
|
||||
self.allocate_instance(instance)
|
||||
|
||||
def update_pci_for_migration(self, context, instance, sign=1):
|
||||
"""Update instance's pci usage information when it is migrated.
|
||||
|
||||
The caller should hold the COMPUTE_RESOURCE_SEMAPHORE lock.
|
||||
|
||||
:param sign: claim devices for instance when sign is 1, remove
|
||||
the claims when sign is -1
|
||||
"""
|
||||
uuid = instance['uuid']
|
||||
pci_requests = objects.InstancePCIRequests.get_by_instance(
|
||||
context, instance)
|
||||
instance_numa_topology = hardware.instance_topology_from_instance(
|
||||
instance)
|
||||
if sign == 1 and uuid not in self.claims:
|
||||
devs = self._claim_instance(context, pci_requests,
|
||||
instance_numa_topology)
|
||||
if devs:
|
||||
self.claims[uuid] = devs
|
||||
if sign == -1 and uuid in self.claims:
|
||||
self._free_instance(instance)
|
||||
|
||||
def clean_usage(self, instances, migrations, orphans):
|
||||
"""Remove all usages for instances not passed in the parameter.
|
||||
|
||||
|
@ -62,6 +62,7 @@ class ClaimTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(ClaimTestCase, self).setUp()
|
||||
self.context = context.RequestContext('fake-user', 'fake-project')
|
||||
self.instance = None
|
||||
self.resources = self._fake_resources()
|
||||
self.tracker = DummyTracker()
|
||||
self.empty_requests = objects.InstancePCIRequests(
|
||||
@ -224,34 +225,29 @@ class ClaimTestCase(test.NoDBTestCase):
|
||||
|
||||
@mock.patch('nova.pci.stats.PciDeviceStats.support_requests',
|
||||
return_value=True)
|
||||
def test_pci_pass(self, mock_supports):
|
||||
def test_pci_pass(self, mock_pci_supports_requests):
|
||||
request = objects.InstancePCIRequest(count=1,
|
||||
spec=[{'vendor_id': 'v', 'product_id': 'p'}])
|
||||
requests = objects.InstancePCIRequests(requests=[request])
|
||||
|
||||
# Claim.__init__() would raise ComputeResourcesUnavailable
|
||||
# if Claim._test_pci() did not return None.
|
||||
self._claim(requests=requests)
|
||||
mock_supports.assert_called_once_with(requests.requests)
|
||||
mock_pci_supports_requests.assert_called_once_with([request])
|
||||
|
||||
@mock.patch('nova.pci.stats.PciDeviceStats.support_requests',
|
||||
return_value=False)
|
||||
def test_pci_fail(self, mock_supports):
|
||||
def test_pci_fail(self, mock_pci_supports_requests):
|
||||
request = objects.InstancePCIRequest(count=1,
|
||||
spec=[{'vendor_id': 'v', 'product_id': 'p'}])
|
||||
requests = objects.InstancePCIRequests(requests=[request])
|
||||
self.assertRaisesRegex(
|
||||
exception.ComputeResourcesUnavailable,
|
||||
'Claim pci failed.',
|
||||
self._claim, requests=requests)
|
||||
mock_pci_supports_requests.assert_called_once_with([request])
|
||||
|
||||
self.assertRaises(exception.ComputeResourcesUnavailable,
|
||||
self._claim, requests=requests)
|
||||
mock_supports.assert_called_once_with(requests.requests)
|
||||
|
||||
@mock.patch('nova.pci.stats.PciDeviceStats.support_requests',
|
||||
return_value=True)
|
||||
def test_pci_pass_no_requests(self, mock_supports):
|
||||
# Claim.__init__() would raise ComputeResourcesUnavailable
|
||||
# if Claim._test_pci() did not return None.
|
||||
@mock.patch('nova.pci.stats.PciDeviceStats.support_requests')
|
||||
def test_pci_pass_no_requests(self, mock_pci_supports_requests):
|
||||
self._claim()
|
||||
self.assertFalse(mock_supports.called)
|
||||
self.assertFalse(mock_pci_supports_requests.called)
|
||||
|
||||
def test_numa_topology_no_limit(self):
|
||||
huge_instance = objects.InstanceNUMATopology(
|
||||
@ -422,30 +418,6 @@ class MoveClaimTestCase(ClaimTestCase):
|
||||
claim = self._abort()
|
||||
self.assertTrue(claim.tracker.rcalled)
|
||||
|
||||
def test_create_migration_context(self):
|
||||
numa_topology = objects.InstanceNUMATopology(
|
||||
cells=[objects.InstanceNUMACell(
|
||||
id=1, cpuset=set([1, 2]), memory=512)])
|
||||
claim = self._claim(numa_topology=numa_topology)
|
||||
migration = objects.Migration(context=self.context, id=42)
|
||||
claim.migration = migration
|
||||
fake_mig_context = mock.Mock(spec=objects.MigrationContext)
|
||||
|
||||
@mock.patch('nova.db.instance_extra_get_by_instance_uuid',
|
||||
return_value=None)
|
||||
@mock.patch('nova.objects.MigrationContext',
|
||||
return_value=fake_mig_context)
|
||||
def _test(ctxt_mock, mock_get_extra):
|
||||
claim.create_migration_context()
|
||||
ctxt_mock.assert_called_once_with(
|
||||
context=self.context, instance_uuid=self.instance.uuid,
|
||||
migration_id=42, old_numa_topology=None,
|
||||
new_numa_topology=mock.ANY)
|
||||
self.assertIsInstance(ctxt_mock.call_args[1]['new_numa_topology'],
|
||||
objects.InstanceNUMATopology)
|
||||
self.assertEqual(migration, claim.migration)
|
||||
_test()
|
||||
|
||||
def test_image_meta(self):
|
||||
claim = self._claim()
|
||||
self.assertIsInstance(claim.image_meta, objects.ImageMeta)
|
||||
|
@ -2929,6 +2929,8 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
instance.migration_context = None
|
||||
instance.numa_topology = None
|
||||
instance.pci_requests = None
|
||||
instance.pci_devices = None
|
||||
instance.task_state = task_states.REBUILDING
|
||||
instance.save(expected_task_state=[task_states.REBUILDING])
|
||||
self.compute._rebuild_default_impl(self.context,
|
||||
|
@ -26,8 +26,10 @@ from nova.compute import vm_states
|
||||
from nova import exception as exc
|
||||
from nova import objects
|
||||
from nova.objects import base as obj_base
|
||||
from nova.objects import pci_device
|
||||
from nova.pci import manager as pci_manager
|
||||
from nova import test
|
||||
from nova.tests.unit.objects import test_pci_device as fake_pci_device
|
||||
|
||||
_HOSTNAME = 'fake-host'
|
||||
_NODENAME = 'fake-node'
|
||||
@ -168,6 +170,8 @@ _INSTANCE_FIXTURES = [
|
||||
root_gb=_INSTANCE_TYPE_FIXTURES[1]['root_gb'],
|
||||
ephemeral_gb=_INSTANCE_TYPE_FIXTURES[1]['ephemeral_gb'],
|
||||
numa_topology=_INSTANCE_NUMA_TOPOLOGIES['2mb'],
|
||||
pci_requests=None,
|
||||
pci_devices=None,
|
||||
instance_type_id=1,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
power_state=power_state.RUNNING,
|
||||
@ -188,6 +192,8 @@ _INSTANCE_FIXTURES = [
|
||||
root_gb=_INSTANCE_TYPE_FIXTURES[2]['root_gb'],
|
||||
ephemeral_gb=_INSTANCE_TYPE_FIXTURES[2]['ephemeral_gb'],
|
||||
numa_topology=None,
|
||||
pci_requests=None,
|
||||
pci_devices=None,
|
||||
instance_type_id=2,
|
||||
vm_state=vm_states.DELETED,
|
||||
power_state=power_state.SHUTDOWN,
|
||||
@ -267,6 +273,8 @@ _MIGRATION_INSTANCE_FIXTURES = {
|
||||
root_gb=_INSTANCE_TYPE_FIXTURES[1]['root_gb'],
|
||||
ephemeral_gb=_INSTANCE_TYPE_FIXTURES[1]['ephemeral_gb'],
|
||||
numa_topology=_INSTANCE_NUMA_TOPOLOGIES['2mb'],
|
||||
pci_requests=None,
|
||||
pci_devices=None,
|
||||
instance_type_id=1,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
power_state=power_state.RUNNING,
|
||||
@ -289,6 +297,8 @@ _MIGRATION_INSTANCE_FIXTURES = {
|
||||
root_gb=_INSTANCE_TYPE_FIXTURES[2]['root_gb'],
|
||||
ephemeral_gb=_INSTANCE_TYPE_FIXTURES[2]['ephemeral_gb'],
|
||||
numa_topology=None,
|
||||
pci_requests=None,
|
||||
pci_devices=None,
|
||||
instance_type_id=2,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
power_state=power_state.RUNNING,
|
||||
@ -311,6 +321,8 @@ _MIGRATION_INSTANCE_FIXTURES = {
|
||||
root_gb=_INSTANCE_TYPE_FIXTURES[2]['root_gb'],
|
||||
ephemeral_gb=_INSTANCE_TYPE_FIXTURES[2]['ephemeral_gb'],
|
||||
numa_topology=None,
|
||||
pci_requests=None,
|
||||
pci_devices=None,
|
||||
instance_type_id=2,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
power_state=power_state.RUNNING,
|
||||
@ -333,6 +345,8 @@ _MIGRATION_INSTANCE_FIXTURES = {
|
||||
root_gb=_INSTANCE_TYPE_FIXTURES[2]['root_gb'],
|
||||
ephemeral_gb=_INSTANCE_TYPE_FIXTURES[2]['ephemeral_gb'],
|
||||
numa_topology=None,
|
||||
pci_requests=None,
|
||||
pci_devices=None,
|
||||
instance_type_id=2,
|
||||
vm_state=vm_states.ACTIVE,
|
||||
power_state=power_state.RUNNING,
|
||||
@ -1384,11 +1398,9 @@ class TestInstanceClaim(BaseTestCase):
|
||||
|
||||
@mock.patch('nova.pci.stats.PciDeviceStats.support_requests',
|
||||
return_value=True)
|
||||
@mock.patch('nova.pci.manager.PciDevTracker.claim_instance')
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance_uuid')
|
||||
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
|
||||
def test_claim_with_pci(self, migr_mock, pci_mock,
|
||||
pci_manager_mock, pci_stats_mock):
|
||||
def test_claim_with_pci(self, migr_mock, pci_mock, pci_stats_mock):
|
||||
# Test that a claim involving PCI requests correctly claims
|
||||
# PCI devices on the host and sends an updated pci_device_pools
|
||||
# attribute of the ComputeNode object.
|
||||
@ -1398,12 +1410,17 @@ class TestInstanceClaim(BaseTestCase):
|
||||
# upon the resource tracker being initialized...
|
||||
self.rt.pci_tracker = pci_manager.PciDevTracker(mock.sentinel.ctx)
|
||||
|
||||
pci_pools = objects.PciDevicePoolList()
|
||||
pci_manager_mock.return_value = pci_pools
|
||||
pci_dev = pci_device.PciDevice.create(
|
||||
None, fake_pci_device.dev_dict)
|
||||
pci_devs = [pci_dev]
|
||||
self.rt.pci_tracker.pci_devs = objects.PciDeviceList(objects=pci_devs)
|
||||
|
||||
request = objects.InstancePCIRequest(count=1,
|
||||
spec=[{'vendor_id': 'v', 'product_id': 'p'}])
|
||||
pci_mock.return_value = objects.InstancePCIRequests(requests=[request])
|
||||
pci_requests = objects.InstancePCIRequests(
|
||||
requests=[request],
|
||||
instance_uuid=self.instance.uuid)
|
||||
pci_mock.return_value = pci_requests
|
||||
|
||||
disk_used = self.instance.root_gb + self.instance.ephemeral_gb
|
||||
expected = copy.deepcopy(_COMPUTE_NODE_FIXTURES[0])
|
||||
@ -1414,7 +1431,7 @@ class TestInstanceClaim(BaseTestCase):
|
||||
"free_ram_mb": expected.memory_mb - self.instance.memory_mb,
|
||||
'running_vms': 1,
|
||||
'vcpus_used': 1,
|
||||
'pci_device_pools': pci_pools,
|
||||
'pci_device_pools': objects.PciDevicePoolList(),
|
||||
'stats': {
|
||||
'io_workload': 0,
|
||||
'num_instances': 1,
|
||||
@ -1429,9 +1446,7 @@ class TestInstanceClaim(BaseTestCase):
|
||||
with mock.patch.object(self.instance, 'save'):
|
||||
self.rt.instance_claim(self.ctx, self.instance, None)
|
||||
update_mock.assert_called_once_with(self.elevated)
|
||||
pci_manager_mock.assert_called_once_with(mock.ANY, # context...
|
||||
pci_mock.return_value,
|
||||
None)
|
||||
pci_stats_mock.assert_called_once_with([request])
|
||||
self.assertTrue(obj_base.obj_equal_prims(expected,
|
||||
self.rt.compute_node))
|
||||
|
||||
|
@ -1266,13 +1266,22 @@ class _TestInstanceObject(object):
|
||||
|
||||
def test_apply_revert_migration_context(self):
|
||||
inst = instance.Instance(context=self.context, uuid=uuids.instance,
|
||||
numa_topology=None)
|
||||
numa_topology=None, pci_requests=None,
|
||||
pci_devices=None)
|
||||
inst.migration_context = test_mig_ctxt.get_fake_migration_context_obj(
|
||||
self.context)
|
||||
inst.apply_migration_context()
|
||||
self.assertIsInstance(inst.numa_topology, objects.InstanceNUMATopology)
|
||||
attrs_type = {'numa_topology': objects.InstanceNUMATopology,
|
||||
'pci_requests': objects.InstancePCIRequests,
|
||||
'pci_devices': objects.PciDeviceList}
|
||||
|
||||
for attr_name in instance._MIGRATION_CONTEXT_ATTRS:
|
||||
value = getattr(inst, attr_name)
|
||||
self.assertIsInstance(value, attrs_type[attr_name])
|
||||
inst.revert_migration_context()
|
||||
self.assertIsNone(inst.numa_topology)
|
||||
for attr_name in instance._MIGRATION_CONTEXT_ATTRS:
|
||||
value = getattr(inst, attr_name)
|
||||
self.assertIsNone(value)
|
||||
|
||||
def test_drop_migration_context(self):
|
||||
inst = instance.Instance(context=self.context, uuid=uuids.instance)
|
||||
@ -1292,15 +1301,29 @@ class _TestInstanceObject(object):
|
||||
fake_obj_numa_topology.obj_clone())
|
||||
numa_topology.cells[0].memory = 1024
|
||||
numa_topology.cells[1].memory = 1024
|
||||
pci_requests = objects.InstancePCIRequests(requests=[
|
||||
objects.InstancePCIRequest(count=1, spec=[])])
|
||||
pci_devices = pci_device.PciDeviceList()
|
||||
|
||||
inst = instance.Instance(context=self.context, uuid=uuids.instance,
|
||||
numa_topology=numa_topology)
|
||||
numa_topology=numa_topology,
|
||||
pci_requests=pci_requests,
|
||||
pci_devices=pci_devices)
|
||||
expected_objs = {'numa_topology': numa_topology,
|
||||
'pci_requests': pci_requests,
|
||||
'pci_devices': pci_devices}
|
||||
inst.migration_context = test_mig_ctxt.get_fake_migration_context_obj(
|
||||
self.context)
|
||||
with inst.mutated_migration_context():
|
||||
self.assertIs(inst.numa_topology,
|
||||
inst.migration_context.new_numa_topology)
|
||||
self.assertIs(numa_topology, inst.numa_topology)
|
||||
for attr_name in instance._MIGRATION_CONTEXT_ATTRS:
|
||||
inst_value = getattr(inst, attr_name)
|
||||
migration_context_value = (
|
||||
getattr(inst.migration_context, 'new_' + attr_name))
|
||||
self.assertIs(inst_value, migration_context_value)
|
||||
|
||||
for attr_name in instance._MIGRATION_CONTEXT_ATTRS:
|
||||
inst_value = getattr(inst, attr_name)
|
||||
self.assertIs(expected_objs[attr_name], inst_value)
|
||||
|
||||
def test_clear_numa_topology(self):
|
||||
numa_topology = (test_instance_numa_topology.
|
||||
|
@ -29,6 +29,12 @@ fake_migration_context_obj.migration_id = 42
|
||||
fake_migration_context_obj.new_numa_topology = (
|
||||
test_instance_numa_topology.fake_obj_numa_topology.obj_clone())
|
||||
fake_migration_context_obj.old_numa_topology = None
|
||||
fake_migration_context_obj.new_pci_devices = objects.PciDeviceList()
|
||||
fake_migration_context_obj.old_pci_devices = None
|
||||
fake_migration_context_obj.new_pci_requests = (
|
||||
objects.InstancePCIRequests(requests=[
|
||||
objects.InstancePCIRequest(count=123, spec=[])]))
|
||||
fake_migration_context_obj.old_pci_requests = None
|
||||
|
||||
fake_db_context = {
|
||||
'created_at': None,
|
||||
@ -48,6 +54,7 @@ def get_fake_migration_context_obj(ctxt):
|
||||
|
||||
|
||||
class _TestMigrationContext(object):
|
||||
|
||||
def _test_get_by_instance_uuid(self, db_data):
|
||||
mig_context = objects.MigrationContext.get_by_instance_uuid(
|
||||
self.context, fake_db_context['instance_uuid'])
|
||||
@ -65,6 +72,14 @@ class _TestMigrationContext(object):
|
||||
mig_context.new_numa_topology.__class__)
|
||||
self.assertIsInstance(expected_mig_context.old_numa_topology,
|
||||
mig_context.old_numa_topology.__class__)
|
||||
self.assertIsInstance(expected_mig_context.new_pci_devices,
|
||||
mig_context.new_pci_devices.__class__)
|
||||
self.assertIsInstance(expected_mig_context.old_pci_devices,
|
||||
mig_context.old_pci_devices.__class__)
|
||||
self.assertIsInstance(expected_mig_context.new_pci_requests,
|
||||
mig_context.new_pci_requests.__class__)
|
||||
self.assertIsInstance(expected_mig_context.old_pci_requests,
|
||||
mig_context.old_pci_requests.__class__)
|
||||
else:
|
||||
self.assertIsNone(mig_context)
|
||||
|
||||
|
@ -1157,7 +1157,7 @@ object_data = {
|
||||
'KeyPair': '1.4-1244e8d1b103cc69d038ed78ab3a8cc6',
|
||||
'KeyPairList': '1.2-58b94f96e776bedaf1e192ddb2a24c4e',
|
||||
'Migration': '1.4-17979b9f2ae7f28d97043a220b2a8350',
|
||||
'MigrationContext': '1.0-d8c2f10069e410f639c49082b5932c92',
|
||||
'MigrationContext': '1.1-9fb17b0b521370957a884636499df52d',
|
||||
'MigrationList': '1.3-55595bfc1a299a5962614d0821a3567e',
|
||||
'MonitorMetric': '1.1-53b1db7c4ae2c531db79761e7acc52ba',
|
||||
'MonitorMetricList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
|
@ -101,7 +101,7 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
def _fake_pci_device_destroy(self, ctxt, node_id, address):
|
||||
self.destroy_called += 1
|
||||
|
||||
def _create_pci_requests_object(self, mock_get, requests,
|
||||
def _create_pci_requests_object(self, requests,
|
||||
instance_uuid=None):
|
||||
instance_uuid = instance_uuid or uuidsentinel.instance1
|
||||
pci_reqs = []
|
||||
@ -109,7 +109,7 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
pci_req_obj = objects.InstancePCIRequest(count=request['count'],
|
||||
spec=request['spec'])
|
||||
pci_reqs.append(pci_req_obj)
|
||||
mock_get.return_value = objects.InstancePCIRequests(
|
||||
return objects.InstancePCIRequests(
|
||||
instance_uuid=instance_uuid,
|
||||
requests=pci_reqs)
|
||||
|
||||
@ -182,11 +182,11 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
if dev.status == 'removed']),
|
||||
2)
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_set_hvdev_changed_stal(self, mock_get):
|
||||
self._create_pci_requests_object(mock_get,
|
||||
def test_set_hvdev_changed_stal(self):
|
||||
pci_requests_obj = self._create_pci_requests_object(
|
||||
[{'count': 1, 'spec': [{'vendor_id': 'v1'}]}])
|
||||
self.tracker._claim_instance(None, mock_get.return_value, None)
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj, None)
|
||||
fake_pci_3 = dict(fake_pci, address='0000:00:00.2', vendor_id='v2')
|
||||
fake_pci_devs = [copy.deepcopy(fake_pci), copy.deepcopy(fake_pci_2),
|
||||
copy.deepcopy(fake_pci_3)]
|
||||
@ -194,11 +194,10 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
self.assertEqual(len(self.tracker.stale), 1)
|
||||
self.assertEqual(self.tracker.stale['0000:00:00.2']['vendor_id'], 'v2')
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_update_pci_for_instance_active(self, mock_get):
|
||||
|
||||
self._create_pci_requests_object(mock_get, fake_pci_requests)
|
||||
self.tracker.claim_instance(None, mock_get.return_value, None)
|
||||
def test_update_pci_for_instance_active(self):
|
||||
pci_requests_obj = self._create_pci_requests_object(fake_pci_requests)
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj, None)
|
||||
self.assertEqual(len(self.tracker.claims[self.inst['uuid']]), 2)
|
||||
self.tracker.update_pci_for_instance(None, self.inst, sign=1)
|
||||
self.assertEqual(len(self.tracker.allocations[self.inst['uuid']]), 2)
|
||||
@ -206,12 +205,12 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
self.assertEqual(len(free_devs), 1)
|
||||
self.assertEqual(free_devs[0].vendor_id, 'v')
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_update_pci_for_instance_fail(self, mock_get):
|
||||
def test_update_pci_for_instance_fail(self):
|
||||
pci_requests = copy.deepcopy(fake_pci_requests)
|
||||
pci_requests[0]['count'] = 4
|
||||
self._create_pci_requests_object(mock_get, pci_requests)
|
||||
self.tracker.claim_instance(None, mock_get.return_value, None)
|
||||
pci_requests_obj = self._create_pci_requests_object(pci_requests)
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj, None)
|
||||
self.assertEqual(len(self.tracker.claims[self.inst['uuid']]), 0)
|
||||
devs = self.tracker.update_pci_for_instance(None,
|
||||
self.inst,
|
||||
@ -219,8 +218,7 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
self.assertEqual(len(self.tracker.allocations[self.inst['uuid']]), 0)
|
||||
self.assertIsNone(devs)
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_pci_claim_instance_with_numa(self, mock_get):
|
||||
def test_pci_claim_instance_with_numa(self):
|
||||
fake_db_dev_3 = dict(fake_db_dev_1, id=4, address='0000:00:00.4')
|
||||
fake_devs_numa = copy.deepcopy(fake_db_devs)
|
||||
fake_devs_numa.append(fake_db_dev_3)
|
||||
@ -228,31 +226,32 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
self.tracker._set_hvdevs(fake_devs_numa)
|
||||
pci_requests = copy.deepcopy(fake_pci_requests)[:1]
|
||||
pci_requests[0]['count'] = 2
|
||||
self._create_pci_requests_object(mock_get, pci_requests)
|
||||
pci_requests_obj = self._create_pci_requests_object(pci_requests)
|
||||
self.inst.numa_topology = objects.InstanceNUMATopology(
|
||||
cells=[objects.InstanceNUMACell(
|
||||
id=1, cpuset=set([1, 2]), memory=512)])
|
||||
self.tracker.claim_instance(None, mock_get.return_value,
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj,
|
||||
self.inst.numa_topology)
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(2, len(free_devs))
|
||||
self.assertEqual('v1', free_devs[0].vendor_id)
|
||||
self.assertEqual('v1', free_devs[1].vendor_id)
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_pci_claim_instance_with_numa_fail(self, mock_get):
|
||||
self._create_pci_requests_object(mock_get, fake_pci_requests)
|
||||
def test_pci_claim_instance_with_numa_fail(self):
|
||||
pci_requests_obj = self._create_pci_requests_object(fake_pci_requests)
|
||||
self.inst.numa_topology = objects.InstanceNUMATopology(
|
||||
cells=[objects.InstanceNUMACell(
|
||||
id=1, cpuset=set([1, 2]), memory=512)])
|
||||
self.assertIsNone(self.tracker.claim_instance(
|
||||
None, mock_get.return_value,
|
||||
self.inst.numa_topology))
|
||||
mock.sentinel.context,
|
||||
pci_requests_obj,
|
||||
self.inst.numa_topology))
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_update_pci_for_instance_deleted(self, mock_get):
|
||||
self._create_pci_requests_object(mock_get, fake_pci_requests)
|
||||
self.tracker.claim_instance(None, mock_get.return_value, None)
|
||||
def test_update_pci_for_instance_deleted(self):
|
||||
pci_requests_obj = self._create_pci_requests_object(fake_pci_requests)
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj, None)
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(len(free_devs), 1)
|
||||
self.inst.vm_state = vm_states.DELETED
|
||||
@ -263,25 +262,6 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
dev in self.tracker.pci_devs]),
|
||||
set(['v', 'v1']))
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_update_pci_for_migration_in(self, mock_get):
|
||||
self._create_pci_requests_object(mock_get, fake_pci_requests)
|
||||
self.tracker.update_pci_for_migration(None, self.inst)
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(len(free_devs), 1)
|
||||
self.assertEqual(free_devs[0].vendor_id, 'v')
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_update_pci_for_migration_out(self, mock_get):
|
||||
self._create_pci_requests_object(mock_get, fake_pci_requests)
|
||||
self.tracker.update_pci_for_migration(None, self.inst)
|
||||
self.tracker.update_pci_for_migration(None, self.inst, sign=-1)
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(len(free_devs), 3)
|
||||
self.assertEqual(set([dev.vendor_id for
|
||||
dev in self.tracker.pci_devs]),
|
||||
set(['v', 'v1']))
|
||||
|
||||
@mock.patch.object(objects.PciDevice, 'should_migrate_data',
|
||||
return_value=False)
|
||||
def test_save(self, migrate_mock):
|
||||
@ -312,21 +292,22 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
self.assertEqual(len(self.tracker.pci_devs), 2)
|
||||
self.assertEqual(self.destroy_called, 1)
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_clean_usage(self, mock_get):
|
||||
def test_clean_usage(self):
|
||||
inst_2 = copy.copy(self.inst)
|
||||
inst_2.uuid = uuidsentinel.instance2
|
||||
migr = {'instance_uuid': 'uuid2', 'vm_state': vm_states.BUILDING}
|
||||
orph = {'uuid': 'uuid3', 'vm_state': vm_states.BUILDING}
|
||||
|
||||
self._create_pci_requests_object(mock_get,
|
||||
pci_requests_obj = self._create_pci_requests_object(
|
||||
[{'count': 1, 'spec': [{'vendor_id': 'v'}]}])
|
||||
self.tracker.claim_instance(None, mock_get.return_value, None)
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj, None)
|
||||
self.tracker.update_pci_for_instance(None, self.inst, sign=1)
|
||||
self._create_pci_requests_object(mock_get,
|
||||
pci_requests_obj = self._create_pci_requests_object(
|
||||
[{'count': 1, 'spec': [{'vendor_id': 'v1'}]}],
|
||||
instance_uuid=inst_2.uuid)
|
||||
self.tracker.claim_instance(None, mock_get.return_value, None)
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj, None)
|
||||
self.tracker.update_pci_for_instance(None, inst_2, sign=1)
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(len(free_devs), 1)
|
||||
@ -339,37 +320,11 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
set([dev.vendor_id for dev in free_devs]),
|
||||
set(['v', 'v1']))
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_clean_usage_claims(self, mock_get):
|
||||
inst_2 = copy.copy(self.inst)
|
||||
inst_2.uuid = uuidsentinel.instance2
|
||||
migr = {'instance_uuid': 'uuid2', 'vm_state': vm_states.BUILDING}
|
||||
orph = {'uuid': 'uuid3', 'vm_state': vm_states.BUILDING}
|
||||
|
||||
self._create_pci_requests_object(mock_get,
|
||||
[{'count': 1, 'spec': [{'vendor_id': 'v'}]}])
|
||||
self.tracker.claim_instance(None, mock_get.return_value, None)
|
||||
self.tracker.update_pci_for_instance(None, self.inst, sign=1)
|
||||
self._create_pci_requests_object(mock_get,
|
||||
[{'count': 1, 'spec': [{'vendor_id': 'v1'}]}],
|
||||
instance_uuid=inst_2.uuid)
|
||||
self.tracker.update_pci_for_migration(None, inst_2)
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(len(free_devs), 1)
|
||||
self.tracker.clean_usage([self.inst], [migr], [orph])
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(len(free_devs), 2)
|
||||
self.assertEqual(
|
||||
set([dev.vendor_id for dev in free_devs]),
|
||||
set(['v', 'v1']))
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_clean_usage_no_request_match_no_claims(self, mock_get):
|
||||
def test_clean_usage_no_request_match_no_claims(self):
|
||||
# Tests the case that there is no match for the request so the
|
||||
# claims mapping is set to None for the instance when the tracker
|
||||
# calls clean_usage.
|
||||
self._create_pci_requests_object(mock_get, [])
|
||||
self.tracker.update_pci_for_migration(None, instance=self.inst, sign=1)
|
||||
self.tracker.update_pci_for_instance(None, self.inst, sign=1)
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
self.assertEqual(3, len(free_devs))
|
||||
self.tracker.clean_usage([], [], [])
|
||||
@ -379,11 +334,11 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
|
||||
set([dev.address for dev in free_devs]),
|
||||
set(['0000:00:00.1', '0000:00:00.2', '0000:00:00.3']))
|
||||
|
||||
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
|
||||
def test_free_devices(self, mock_get):
|
||||
self._create_pci_requests_object(mock_get,
|
||||
def test_free_devices(self):
|
||||
pci_requests_obj = self._create_pci_requests_object(
|
||||
[{'count': 1, 'spec': [{'vendor_id': 'v'}]}])
|
||||
self.tracker.claim_instance(None, mock_get.return_value, None)
|
||||
self.tracker.claim_instance(mock.sentinel.context,
|
||||
pci_requests_obj, None)
|
||||
self.tracker.update_pci_for_instance(None, self.inst, sign=1)
|
||||
|
||||
free_devs = self.tracker.pci_stats.get_free_devs()
|
||||
|
Loading…
Reference in New Issue
Block a user