objects: Add devices_metadata to instance object

Loading the InstanceDeviceMetadata object into the
instance will avoid the need to make multiple calls
to the db from the metadata service.

Partially implements blueprint virt-device-role-tagging
Change-Id: Iea73c26e48d02c9fda9426c8603d1627c0e8e692
This commit is contained in:
Vladik Romanovsky 2016-06-07 15:25:47 -04:00
parent 885cf20daf
commit 1aa494107e
6 changed files with 49 additions and 7 deletions

View File

@ -51,7 +51,7 @@ _INSTANCE_OPTIONAL_NON_COLUMN_FIELDS = ['fault', 'flavor', 'old_flavor',
# These are fields that are optional and in instance_extra
_INSTANCE_EXTRA_FIELDS = ['numa_topology', 'pci_requests',
'flavor', 'vcpu_model', 'migration_context',
'keypairs']
'keypairs', 'device_metadata']
# These are fields that applied/drooped by migration_context
_MIGRATION_CONTEXT_ATTRS = ['numa_topology', 'pci_requests',
'pci_devices']
@ -99,7 +99,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
# Version 2.0: Initial version
# Version 2.1: Added services
# Version 2.2: Added keypairs
VERSION = '2.2'
# Version 2.3: Added device_metadata
VERSION = '2.3'
fields = {
'id': fields.IntegerField(),
@ -190,6 +191,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
nullable=True),
'pci_requests': fields.ObjectField('InstancePCIRequests',
nullable=True),
'device_metadata': fields.ObjectField('InstanceDeviceMetadata',
nullable=True),
'tags': fields.ObjectField('TagList'),
'flavor': fields.ObjectField('Flavor'),
'old_flavor': fields.ObjectField('Flavor', nullable=True),
@ -206,6 +209,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
def obj_make_compatible(self, primitive, target_version):
super(Instance, self).obj_make_compatible(primitive, target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if target_version < (2, 3) and 'device_metadata' in primitive:
del primitive['device_metadata']
if target_version < (2, 2) and 'keypairs' in primitive:
del primitive['keypairs']
if target_version < (2, 1) and 'services' in primitive:
@ -336,6 +341,12 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
db_inst['extra'].get('pci_requests'))
else:
instance.pci_requests = None
if 'device_metadata' in expected_attrs:
if have_extra:
instance._load_device_metadata(
db_inst['extra'].get('device_metadata'))
else:
instance.device_metadata = None
if 'vcpu_model' in expected_attrs:
if have_extra:
instance._load_vcpu_model(
@ -456,6 +467,13 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
pci_requests.to_json())
else:
updates['extra']['pci_requests'] = None
device_metadata = updates.pop('device_metadata', None)
expected_attrs.append('device_metadata')
if device_metadata:
updates['extra']['device_metadata'] = (
device_metadata._to_json())
else:
updates['extra']['device_metadata'] = None
flavor = updates.pop('flavor', None)
if flavor:
expected_attrs.append('flavor')
@ -799,6 +817,16 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
objects.InstancePCIRequests.get_by_instance_uuid(
self._context, self.uuid)
def _load_device_metadata(self, db_requests=None):
if db_requests is not None:
self.device_metadata = \
objects.InstanceDeviceMetadata.obj_from_db(
self._context, db_requests)
else:
self.device_metadata = \
objects.InstanceDeviceMetadata.get_by_instance_uuid(
self._context, self.uuid)
def _load_flavor(self):
instance = self.__class__.get_by_uuid(
self._context, uuid=self.uuid,
@ -957,6 +985,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
self._load_fault()
elif attrname == 'numa_topology':
self._load_numa_topology()
elif attrname == 'device_metadata':
self._load_device_metadata()
elif attrname == 'pci_requests':
self._load_pci_requests()
elif attrname == 'vcpu_model':

View File

@ -84,7 +84,7 @@ class BuildRequestTestCase(test.NoDBTestCase):
'pci_devices', 'security_groups', 'info_cache',
'ec2_ids', 'migration_context', 'metadata',
'vcpu_model', 'services', 'system_metadata',
'tags', 'fault'],
'tags', 'fault', 'device_metadata'],
comparators={'flavor': obj_comp,
'created_at': date_comp,
'keypairs': obj_comp})

View File

@ -2911,6 +2911,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
instance.numa_topology = None
instance.pci_requests = None
instance.pci_devices = None
instance.device_metadata = None
instance.task_state = task_states.REBUILDING
instance.save(expected_task_state=[task_states.REBUILDING])
self.compute._rebuild_default_impl(self.context,

View File

@ -73,6 +73,7 @@ def fake_db_instance(**updates):
'flavor': flavorinfo,
'numa_topology': None,
'vcpu_model': None,
'device_metadata': None,
},
'tags': [],
'services': []

View File

@ -37,6 +37,7 @@ from nova.objects import pci_device
from nova.objects import security_group
from nova import test
from nova.tests.unit import fake_instance
from nova.tests.unit.objects import test_instance_device_metadata
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
@ -143,15 +144,19 @@ class _TestInstanceObject(object):
exp_cols.remove('ec2_ids')
exp_cols.remove('migration_context')
exp_cols.remove('keypairs')
exp_cols.remove('device_metadata')
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.migration_context', 'extra.keypairs'])
'extra.migration_context', 'extra.keypairs',
'extra.device_metadata'])
fake_topology = (test_instance_numa_topology.
fake_db_topology['numa_topology'])
fake_requests = jsonutils.dumps(test_instance_pci_requests.
fake_pci_requests)
fake_devices_metadata = \
test_instance_device_metadata.fake_devices_metadata
fake_flavor = jsonutils.dumps(
{'cur': objects.Flavor().obj_to_primitive(),
'old': None, 'new': None})
@ -176,6 +181,7 @@ class _TestInstanceObject(object):
extra={
'numa_topology': fake_topology,
'pci_requests': fake_requests,
'device_metadata': fake_devices_metadata,
'flavor': fake_flavor,
'vcpu_model': fake_vcpu_model,
'migration_context': fake_mig_context,
@ -976,6 +982,7 @@ class _TestInstanceObject(object):
'vcpu_model': None,
'numa_topology': None,
'pci_requests': None,
'device_metadata': None,
}}
fake_inst = fake_instance.fake_db_instance(**vals)
mock_create.return_value = fake_inst
@ -1003,6 +1010,7 @@ class _TestInstanceObject(object):
'vcpu_model': None,
'numa_topology': None,
'pci_requests': None,
'device_metadata': None,
}}
fake_inst = fake_instance.fake_db_instance(**vals)
mock_create.return_value = fake_inst
@ -1017,11 +1025,12 @@ class _TestInstanceObject(object):
def test_create(self, mock_create):
extras = {'vcpu_model': None,
'numa_topology': None,
'pci_requests': None}
'pci_requests': None,
'device_metadata': None}
mock_create.return_value = self.fake_instance
inst = objects.Instance(context=self.context)
inst.create()
self.assertEqual(self.fake_instance['id'], inst.id)
self.assertIsNotNone(inst.ec2_ids)
mock_create.assert_called_once_with(self.context, {'extra': extras})
@ -1096,6 +1105,7 @@ class _TestInstanceObject(object):
'vcpu_model': None,
'numa_topology': None,
'pci_requests': None,
'device_metadata': None,
},
})

View File

@ -1130,7 +1130,7 @@ object_data = {
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
'ImageMetaProps': '1.14-cc8f96454cdb0feadcb2fc9e63974016',
'Instance': '2.2-ab450ec9c1f4d429755c48d492b823f0',
'Instance': '2.3-4f98ab23f4b0a25fabb1040c8f5edecc',
'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914',
'InstanceActionEvent': '1.1-e56a64fa4710e43ef7af2ad9d6028b33',
'InstanceActionEventList': '1.1-13d92fb953030cdbfee56481756e02be',