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:
Moshe Levi 2016-04-18 14:33:59 +03:00
parent c9a3d86803
commit c2c3b97259
13 changed files with 240 additions and 211 deletions

View File

@ -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

View File

@ -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()

View File

@ -46,6 +46,7 @@ Possible Values:
Services which consume this:
* nova-api
* nova-compute
Related options:

View File

@ -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):

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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,

View File

@ -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))

View File

@ -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.

View File

@ -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)

View File

@ -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',

View File

@ -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()