Merge "objects: Hook migration object into Instance"

This commit is contained in:
Jenkins 2015-09-04 00:31:20 +00:00 committed by Gerrit Code Review
commit 3f0c42325a
6 changed files with 178 additions and 27 deletions

View File

@ -57,7 +57,8 @@ class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject,
# Version 1.12: Changed device_type field to BlockDeviceTypeField.
# Version 1.13: Instance version 1.21
# Version 1.14: Instance version 1.22
VERSION = '1.14'
# Version 1.15: Instance version 1.23
VERSION = '1.15'
fields = {
'id': fields.IntegerField(),
@ -84,7 +85,7 @@ class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject,
'instance': [('1.0', '1.13'), ('1.2', '1.14'), ('1.3', '1.15'),
('1.4', '1.16'), ('1.5', '1.17'), ('1.6', '1.18'),
('1.8', '1.19'), ('1.9', '1.20'), ('1.13', '1.21'),
('1.14', '1.22')],
('1.14', '1.22'), ('1.15', '1.23')],
}
@staticmethod
@ -272,7 +273,8 @@ class BlockDeviceMappingList(base.ObjectListBase, base.NovaObject):
# Version 1.13: BlockDeviceMapping <= version 1.12
# Version 1.14: BlockDeviceMapping <= version 1.13
# Version 1.15: BlockDeviceMapping <= version 1.14
VERSION = '1.15'
# Version 1.16: BlockDeviceMapping <= version 1.15
VERSION = '1.16'
fields = {
'objects': fields.ListOfObjectsField('BlockDeviceMapping'),
@ -283,7 +285,7 @@ class BlockDeviceMappingList(base.ObjectListBase, base.NovaObject):
('1.6', '1.5'), ('1.7', '1.6'), ('1.8', '1.7'),
('1.9', '1.8'), ('1.10', '1.9'), ('1.11', '1.10'),
('1.12', '1.11'), ('1.13', '1.12'), ('1.14', '1.13'),
('1.15', '1.14')],
('1.15', '1.14'), ('1.16', '1.15')],
}
@base.remotable_classmethod

View File

@ -43,7 +43,8 @@ class FixedIP(obj_base.NovaPersistentObject, obj_base.NovaObject,
# Version 1.10: Instance 1.20
# Version 1.11: Instance 1.21
# Version 1.12: Instance 1.22, FloatingIPList 1.9
VERSION = '1.12'
# Version 1.13: Instance 1.23, FloatingIPList 1.10
VERSION = '1.13'
fields = {
'id': fields.IntegerField(),
@ -70,10 +71,11 @@ class FixedIP(obj_base.NovaPersistentObject, obj_base.NovaObject,
'instance': [('1.0', '1.13'), ('1.2', '1.14'), ('1.3', '1.15'),
('1.6', '1.16'), ('1.7', '1.17'), ('1.8', '1.18'),
('1.9', '1.19'), ('1.10', '1.20'), ('1.11', '1.21'),
('1.12', '1.22')],
('1.12', '1.22'), ('1.13', '1.23')],
'network': [('1.0', '1.2')],
'virtual_interface': [('1.1', '1.0')],
'floating_ips': [('1.5', '1.7'), ('1.11', '1.8'), ('1.12', '1.9')],
'floating_ips': [('1.5', '1.7'), ('1.11', '1.8'), ('1.12', '1.9'),
('1.13', '1.10')],
}
def obj_make_compatible(self, primitive, target_version):
@ -222,7 +224,8 @@ class FixedIPList(obj_base.ObjectListBase, obj_base.NovaObject):
# Version 1.10: FixedIP <= version 1.10
# Version 1.11: FixedIP <= version 1.11
# Version 1.12: FixedIP <= version 1.12
VERSION = '1.12'
# Version 1.13: FixedIP <= version 1.13
VERSION = '1.13'
fields = {
'objects': fields.ListOfObjectsField('FixedIP'),
@ -232,7 +235,7 @@ class FixedIPList(obj_base.ObjectListBase, obj_base.NovaObject):
('1.3', '1.3'), ('1.4', '1.4'), ('1.5', '1.5'),
('1.6', '1.6'), ('1.7', '1.7'), ('1.8', '1.8'),
('1.9', '1.9'), ('1.10', '1.10'), ('1.11', '1.11'),
('1.12', '1.12')],
('1.12', '1.12'), ('1.13', '1.13')],
}
@obj_base.remotable_classmethod

View File

@ -34,7 +34,8 @@ class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject,
# Version 1.6: FixedIP <= version 1.6
# Version 1.7: FixedIP <= version 1.11
# Version 1.8: FixedIP <= version 1.12
VERSION = '1.8'
# Version 1.9: FixedIP <= version 1.13
VERSION = '1.9'
fields = {
'id': fields.IntegerField(),
'address': fields.IPAddressField(),
@ -50,7 +51,7 @@ class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject,
obj_relationships = {
'fixed_ip': [('1.0', '1.1'), ('1.2', '1.2'), ('1.3', '1.3'),
('1.4', '1.4'), ('1.5', '1.5'), ('1.6', '1.6'),
('1.7', '1.11'), ('1.8', '1.12')],
('1.7', '1.11'), ('1.8', '1.12'), ('1.9', '1.13')],
}
@staticmethod
@ -175,6 +176,7 @@ class FloatingIPList(obj_base.ObjectListBase, obj_base.NovaObject):
# Version 1.7: FloatingIP 1.6
# Version 1.8: FloatingIP 1.7
# Version 1.9: FloatingIP 1.8
# Version 1.10: FloatingIP 1.9
fields = {
'objects': fields.ListOfObjectsField('FloatingIP'),
}
@ -182,9 +184,9 @@ class FloatingIPList(obj_base.ObjectListBase, obj_base.NovaObject):
'objects': [('1.0', '1.0'), ('1.1', '1.1'), ('1.2', '1.1'),
('1.3', '1.2'), ('1.4', '1.3'), ('1.5', '1.4'),
('1.6', '1.5'), ('1.7', '1.6'), ('1.8', '1.7'),
('1.9', '1.8')],
('1.9', '1.8'), ('1.10', '1.9')],
}
VERSION = '1.9'
VERSION = '1.10'
@obj_base.remotable_classmethod
def get_all(cls, context):

View File

@ -26,7 +26,7 @@ from nova.cells import utils as cells_utils
from nova.compute import flavors
from nova import db
from nova import exception
from nova.i18n import _LE
from nova.i18n import _LE, _LW
from nova import notifications
from nova import objects
from nova.objects import base
@ -47,7 +47,7 @@ _INSTANCE_OPTIONAL_NON_COLUMN_FIELDS = ['fault', 'flavor', 'old_flavor',
'new_flavor', 'ec2_ids']
# These are fields that are optional and in instance_extra
_INSTANCE_EXTRA_FIELDS = ['numa_topology', 'pci_requests',
'flavor', 'vcpu_model']
'flavor', 'vcpu_model', 'migration_context']
# These are fields that can be specified as expected_attrs
INSTANCE_OPTIONAL_ATTRS = (_INSTANCE_OPTIONAL_JOINED_FIELDS +
@ -79,6 +79,9 @@ def _expected_cols(expected_attrs):
return simple_cols + complex_cols
_NO_DATA_SENTINEL = object()
# TODO(berrange): Remove NovaObjectDictCompat
@base.NovaObjectRegistry.register
class Instance(base.NovaPersistentObject, base.NovaObject,
@ -108,7 +111,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
# Version 1.20: Added ec2_ids
# Version 1.21: TagList 1.1
# Version 1.22: InstanceNUMATopology 1.2
VERSION = '1.22'
# Version 1.23: Added migration_context
VERSION = '1.23'
fields = {
'id': fields.IntegerField(),
@ -206,6 +210,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
'new_flavor': fields.ObjectField('Flavor', nullable=True),
'vcpu_model': fields.ObjectField('VirtCPUModel', nullable=True),
'ec2_ids': fields.ObjectField('EC2Ids'),
'migration_context': fields.ObjectField('MigrationContext',
nullable=True)
}
obj_extra_fields = ['name']
@ -223,6 +229,7 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
'new_flavor': [('1.18', '1.1')],
'vcpu_model': [('1.19', '1.0')],
'ec2_ids': [('1.20', '1.0')],
'migration_context': [('1.23', '1.0')],
}
def __init__(self, *args, **kwargs):
@ -384,6 +391,12 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
instance.vcpu_model = None
if 'ec2_ids' in expected_attrs:
instance._load_ec2_ids()
if 'migration_context' in expected_attrs:
if have_extra:
instance._load_migration_context(
db_inst['extra'].get('migration_context'))
else:
instance.migration_context = None
if 'info_cache' in expected_attrs:
if db_inst['info_cache'] is None:
instance.info_cache = None
@ -604,6 +617,14 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
# NOTE(hanlind): Read-only so no need to save this.
pass
def _save_migration_context(self, context):
if self.migration_context:
self.migration_context.instance_uuid = self.uuid
with self.migration_context.obj_alternate_context(context):
self.migration_context._save()
else:
objects.MigrationContext._destroy(context, self.uuid)
@base.remotable
def save(self, expected_vm_state=None,
expected_task_state=None, admin_state_reset=False):
@ -848,6 +869,42 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
def _load_ec2_ids(self):
self.ec2_ids = objects.EC2Ids.get_by_instance(self._context, self)
def _load_migration_context(self, db_context=_NO_DATA_SENTINEL):
if db_context is _NO_DATA_SENTINEL:
try:
self.migration_context = (
objects.MigrationContext.get_by_instance_uuid(
self._context, self.uuid))
except exception.MigrationContextNotFound:
self.migration_context = None
elif db_context is None:
self.migration_context = None
else:
self.migration_context = objects.MigrationContext.obj_from_db_obj(
db_context)
def apply_migration_context(self):
if self.migration_context:
self.numa_topology = self.migration_context.new_numa_topology
else:
LOG.warn(_LW("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
else:
LOG.warn(_LW("Trying to revert a migration context that does not "
"seem to be set for this instance"),
instance=self)
@base.remotable
def drop_migration_context(self):
if self.migration_context:
objects.MigrationContext._destroy(self._context, self.uuid)
self.migration_context = None
def obj_load_attr(self, attrname):
if attrname not in INSTANCE_OPTIONAL_ATTRS:
raise exception.ObjectActionError(
@ -876,6 +933,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
self._load_vcpu_model()
elif attrname == 'ec2_ids':
self._load_ec2_ids()
elif attrname == 'migration_context':
self._load_migration_context()
elif 'flavor' in attrname:
self._load_flavor()
else:
@ -1024,7 +1083,8 @@ class InstanceList(base.ObjectListBase, base.NovaObject):
# Version 1.19: Erronenous removal of get_hung_in_rebooting(). Reverted.
# Version 1.20: Instance <= version 1.22
# Version 1.21: New method get_by_grantee_security_group_ids()
VERSION = '1.21'
# Version 1.22: Instance <= version 1.23
VERSION = '1.22'
fields = {
'objects': fields.ListOfObjectsField('Instance'),
@ -1037,7 +1097,8 @@ class InstanceList(base.ObjectListBase, base.NovaObject):
('1.10', '1.16'), ('1.11', '1.16'), ('1.12', '1.16'),
('1.13', '1.17'), ('1.14', '1.18'), ('1.15', '1.19'),
('1.16', '1.19'), ('1.17', '1.20'), ('1.18', '1.21'),
('1.19', '1.21'), ('1.20', '1.22'), ('1.21', '1.22')],
('1.19', '1.21'), ('1.20', '1.22'), ('1.21', '1.22'),
('1.22', '1.23')],
}
@base.remotable_classmethod

View File

@ -40,6 +40,7 @@ from nova.tests.unit.objects import test_instance_fault
from nova.tests.unit.objects import test_instance_info_cache
from nova.tests.unit.objects import test_instance_numa_topology
from nova.tests.unit.objects import test_instance_pci_requests
from nova.tests.unit.objects import test_migration_context as test_mig_ctxt
from nova.tests.unit.objects import test_objects
from nova.tests.unit.objects import test_security_group
from nova.tests.unit.objects import test_vcpu_model
@ -132,9 +133,11 @@ class _TestInstanceObject(object):
exp_cols.remove('pci_requests')
exp_cols.remove('vcpu_model')
exp_cols.remove('ec2_ids')
exp_cols.remove('migration_context')
exp_cols = list(filter(lambda x: 'flavor' not in x, exp_cols))
exp_cols.extend(['extra', 'extra.numa_topology', 'extra.pci_requests',
'extra.flavor', 'extra.vcpu_model'])
'extra.flavor', 'extra.vcpu_model',
'extra.migration_context'])
fake_topology = (test_instance_numa_topology.
fake_db_topology['numa_topology'])
@ -145,12 +148,15 @@ class _TestInstanceObject(object):
'old': None, 'new': None})
fake_vcpu_model = jsonutils.dumps(
test_vcpu_model.fake_vcpumodel.obj_to_primitive())
fake_mig_context = jsonutils.dumps(
test_mig_ctxt.fake_migration_context_obj.obj_to_primitive())
fake_instance = dict(self.fake_instance,
extra={
'numa_topology': fake_topology,
'pci_requests': fake_requests,
'flavor': fake_flavor,
'vcpu_model': fake_vcpu_model,
'migration_context': fake_mig_context,
})
db.instance_get_by_uuid(
self.context, 'uuid',
@ -502,6 +508,28 @@ class _TestInstanceObject(object):
mock_update.assert_called_once_with(
self.context, inst.uuid, {'vcpu_model': None})
@mock.patch('nova.db.instance_extra_update_by_uuid')
def test_save_migration_context_model(self, mock_update):
inst = fake_instance.fake_instance_obj(self.context)
inst.migration_context = test_mig_ctxt.get_fake_migration_context_obj(
self.context)
inst.save()
self.assertTrue(mock_update.called)
self.assertEqual(mock_update.call_count, 1)
actual_args = mock_update.call_args
self.assertEqual(self.context, actual_args[0][0])
self.assertEqual(inst.uuid, actual_args[0][1])
self.assertEqual(list(actual_args[0][2].keys()), ['migration_context'])
self.assertIsInstance(
objects.MigrationContext.obj_from_db_obj(
actual_args[0][2]['migration_context']),
objects.MigrationContext)
mock_update.reset_mock()
inst.migration_context = None
inst.save()
mock_update.assert_called_once_with(
self.context, inst.uuid, {'migration_context': None})
def test_save_flavor_skips_unchanged_flavors(self):
inst = objects.Instance(context=self.context,
flavor=objects.Flavor())
@ -1160,6 +1188,61 @@ class _TestInstanceObject(object):
inst.ec2_ids
mock_load.assert_called_once_with()
def test_load_migration_context(self):
inst = instance.Instance(context=self.context,
uuid='fake-uuid')
with mock.patch.object(
objects.MigrationContext, 'get_by_instance_uuid',
return_value=test_mig_ctxt.fake_migration_context_obj
) as mock_get:
inst.migration_context
mock_get.assert_called_once_with(self.context, inst.uuid)
def test_load_migration_context_no_context(self):
inst = instance.Instance(context=self.context,
uuid='fake-uuid')
with mock.patch.object(
objects.MigrationContext, 'get_by_instance_uuid',
side_effect=exception.MigrationContextNotFound(
instance_uuid=inst.uuid)
) as mock_get:
mig_ctxt = inst.migration_context
mock_get.assert_called_once_with(self.context, inst.uuid)
self.assertIsNone(mig_ctxt)
def test_load_migration_context_no_data(self):
inst = instance.Instance(context=self.context,
uuid='fake-uuid')
with mock.patch.object(
objects.MigrationContext, 'get_by_instance_uuid') as mock_get:
loaded_ctxt = inst._load_migration_context(db_context=None)
self.assertFalse(mock_get.called)
self.assertIsNone(loaded_ctxt)
def test_apply_revert_migration_context(self):
inst = instance.Instance(context=self.context,
uuid='fake-uuid', numa_topology=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)
inst.revert_migration_context()
self.assertIsNone(inst.numa_topology)
def test_drop_migration_context(self):
inst = instance.Instance(context=self.context,
uuid='fake-uuid')
inst.migration_context = test_mig_ctxt.get_fake_migration_context_obj(
self.context)
inst.migration_context.instance_uuid = inst.uuid
inst.migration_context.id = 7
with mock.patch(
'nova.db.instance_extra_update_by_uuid') as update_extra:
inst.drop_migration_context()
self.assertIsNone(inst.migration_context)
update_extra.assert_called_once_with(self.context, inst.uuid,
{"migration_context": None})
@mock.patch.object(objects.Instance, 'get_by_uuid')
def test_load_generic(self, mock_get):
inst2 = instance.Instance(metadata={'foo': 'bar'})

View File

@ -1129,8 +1129,8 @@ object_data = {
'AggregateList': '1.2-fb6e19f3c3a3186b04eceb98b5dadbfa',
'BandwidthUsage': '1.2-c6e4c779c7f40f2407e3d70022e3cd1c',
'BandwidthUsageList': '1.2-5fe7475ada6fe62413cbfcc06ec70746',
'BlockDeviceMapping': '1.14-d44d8d694619e79c172a99b3c1d6261d',
'BlockDeviceMappingList': '1.15-6fa262c059dad1d519b9fe05b9e4f404',
'BlockDeviceMapping': '1.15-d44d8d694619e79c172a99b3c1d6261d',
'BlockDeviceMappingList': '1.16-6fa262c059dad1d519b9fe05b9e4f404',
'CellMapping': '1.0-7f1a7e85a22bbb7559fc730ab658b9bd',
'ComputeNode': '1.14-a396975707b66281c5f404a68fccd395',
'ComputeNodeList': '1.14-3b6f4f5ade621c40e70cb116db237844',
@ -1140,17 +1140,17 @@ object_data = {
'EC2InstanceMapping': '1.0-a4556eb5c5e94c045fe84f49cf71644f',
'EC2SnapshotMapping': '1.0-47e7ddabe1af966dce0cfd0ed6cd7cd1',
'EC2VolumeMapping': '1.0-5b713751d6f97bad620f3378a521020d',
'FixedIP': '1.12-b5818a33996228fc146f096d1403742c',
'FixedIPList': '1.12-87a39361c8f08f059004d6b15103cdfd',
'FixedIP': '1.13-b5818a33996228fc146f096d1403742c',
'FixedIPList': '1.13-87a39361c8f08f059004d6b15103cdfd',
'Flavor': '1.1-b6bb7a730a79d720344accefafacf7ee',
'FlavorList': '1.1-52b5928600e7ca973aa4fc1e46f3934c',
'FloatingIP': '1.8-52a67d52d85eb8b3f324a5b7935a335b',
'FloatingIPList': '1.9-7f2ba670714e1b7bab462ab3290f7159',
'FloatingIP': '1.9-52a67d52d85eb8b3f324a5b7935a335b',
'FloatingIPList': '1.10-7f2ba670714e1b7bab462ab3290f7159',
'HostMapping': '1.0-1a3390a696792a552ab7bd31a77ba9ac',
'HVSpec': '1.1-6b4f7c0f688cbd03e24142a44eb9010d',
'ImageMeta': '1.6-642d1b2eb3e880a367f37d72dd76162d',
'ImageMetaProps': '1.6-07a6d9f3576c4927220331584661ce45',
'Instance': '1.22-260d385315d4868b6397c61a13109841',
'Instance': '1.23-4e68422207667f4abff5fa730a5edc98',
'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914',
'InstanceActionEvent': '1.1-e56a64fa4710e43ef7af2ad9d6028b33',
'InstanceActionEventList': '1.1-13d92fb953030cdbfee56481756e02be',
@ -1161,7 +1161,7 @@ object_data = {
'InstanceGroup': '1.10-1a0c8c7447dc7ecb9da53849430c4a5f',
'InstanceGroupList': '1.7-be18078220513316abd0ae1b2d916873',
'InstanceInfoCache': '1.5-cd8b96fefe0fc8d4d337243ba0bf0e1e',
'InstanceList': '1.21-6c8ba6147cca3082b1e4643f795068bf',
'InstanceList': '1.22-6c8ba6147cca3082b1e4643f795068bf',
'InstanceMapping': '1.0-47ef26034dfcbea78427565d9177fe50',
'InstanceMappingList': '1.0-9e982e3de1613b9ada85e35f69b23d47',
'InstanceNUMACell': '1.2-535ef30e0de2d6a0d26a71bd58ecafc4',