Support instance_extra fields in expected_attrs on Instance object
This makes the fields that are stored in instance_extra specifyable in expected_attrs for efficient queries. Related to blueprint flavor-from-sysmeta-to-blob Change-Id: I73567280dc1d35a3eb3badfafbe7271943e8ed57
This commit is contained in:
parent
d1d806e57f
commit
9fd9644fd2
@ -1768,7 +1768,10 @@ def _build_instance_get(context, session=None,
|
||||
if column in ['info_cache', 'security_groups']:
|
||||
# Already always joined above
|
||||
continue
|
||||
query = query.options(joinedload(column))
|
||||
if 'extra.' in column:
|
||||
query = query.options(undefer(column))
|
||||
else:
|
||||
query = query.options(joinedload(column))
|
||||
# NOTE(alaski) Stop lazy loading of columns not needed.
|
||||
for col in ['metadata', 'system_metadata']:
|
||||
if col not in columns_to_join:
|
||||
@ -2205,8 +2208,11 @@ def _instance_get_all_query(context, project_only=False,
|
||||
models.Instance,
|
||||
project_only=project_only,
|
||||
use_slave=use_slave)
|
||||
for join in joins:
|
||||
query = query.options(joinedload(join))
|
||||
for column in joins:
|
||||
if 'extra.' in column:
|
||||
query = query.options(undefer(column))
|
||||
else:
|
||||
query = query.options(joinedload(column))
|
||||
return query
|
||||
|
||||
|
||||
|
@ -39,12 +39,14 @@ _INSTANCE_OPTIONAL_JOINED_FIELDS = ['metadata', 'system_metadata',
|
||||
'info_cache', 'security_groups',
|
||||
'pci_devices']
|
||||
# These are fields that are optional but don't translate to db columns
|
||||
_INSTANCE_OPTIONAL_NON_COLUMN_FIELDS = ['fault', 'numa_topology',
|
||||
'pci_requests']
|
||||
_INSTANCE_OPTIONAL_NON_COLUMN_FIELDS = ['fault']
|
||||
# These are fields that are optional and in instance_extra
|
||||
_INSTANCE_EXTRA_FIELDS = ['numa_topology', 'pci_requests']
|
||||
|
||||
# These are fields that can be specified as expected_attrs
|
||||
INSTANCE_OPTIONAL_ATTRS = (_INSTANCE_OPTIONAL_JOINED_FIELDS +
|
||||
_INSTANCE_OPTIONAL_NON_COLUMN_FIELDS)
|
||||
_INSTANCE_OPTIONAL_NON_COLUMN_FIELDS +
|
||||
_INSTANCE_EXTRA_FIELDS)
|
||||
# These are fields that most query calls load by default
|
||||
INSTANCE_DEFAULT_FIELDS = ['metadata', 'system_metadata',
|
||||
'info_cache', 'security_groups']
|
||||
@ -54,8 +56,15 @@ def _expected_cols(expected_attrs):
|
||||
"""Return expected_attrs that are columns needing joining."""
|
||||
if not expected_attrs:
|
||||
return expected_attrs
|
||||
return [attr for attr in expected_attrs
|
||||
if attr in _INSTANCE_OPTIONAL_JOINED_FIELDS]
|
||||
simple_cols = [attr for attr in expected_attrs
|
||||
if attr in _INSTANCE_OPTIONAL_JOINED_FIELDS]
|
||||
|
||||
complex_cols = ['extra.%s' % field
|
||||
for field in _INSTANCE_EXTRA_FIELDS
|
||||
if field in expected_attrs]
|
||||
if complex_cols:
|
||||
simple_cols.append('extra')
|
||||
return simple_cols + complex_cols
|
||||
|
||||
|
||||
class Instance(base.NovaPersistentObject, base.NovaObject):
|
||||
@ -292,9 +301,11 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
|
||||
objects.InstanceFault.get_latest_for_instance(
|
||||
context, instance.uuid))
|
||||
if 'numa_topology' in expected_attrs:
|
||||
instance._load_numa_topology()
|
||||
instance._load_numa_topology(
|
||||
db_inst.get('extra').get('numa_topology'))
|
||||
if 'pci_requests' in expected_attrs:
|
||||
instance._load_pci_requests()
|
||||
instance._load_pci_requests(
|
||||
db_inst.get('extra').get('pci_requests'))
|
||||
|
||||
if 'info_cache' in expected_attrs:
|
||||
if db_inst['info_cache'] is None:
|
||||
@ -568,18 +579,28 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
|
||||
self.fault = objects.InstanceFault.get_latest_for_instance(
|
||||
self._context, self.uuid)
|
||||
|
||||
def _load_numa_topology(self):
|
||||
try:
|
||||
def _load_numa_topology(self, db_topology=None):
|
||||
if db_topology is not None:
|
||||
self.numa_topology = \
|
||||
objects.InstanceNUMATopology.get_by_instance_uuid(
|
||||
self._context, self.uuid)
|
||||
except exception.NumaTopologyNotFound:
|
||||
self.numa_topology = None
|
||||
objects.InstanceNUMATopology.obj_from_db_obj(self.uuid,
|
||||
db_topology)
|
||||
else:
|
||||
try:
|
||||
self.numa_topology = \
|
||||
objects.InstanceNUMATopology.get_by_instance_uuid(
|
||||
self._context, self.uuid)
|
||||
except exception.NumaTopologyNotFound:
|
||||
self.numa_topology = None
|
||||
|
||||
def _load_pci_requests(self):
|
||||
self.pci_requests = \
|
||||
objects.InstancePCIRequests.get_by_instance_uuid(
|
||||
self._context, self.uuid)
|
||||
def _load_pci_requests(self, db_requests=None):
|
||||
# FIXME: also do this if none!
|
||||
if db_requests is not None:
|
||||
self.pci_requests = objects.InstancePCIRequests.obj_from_db(
|
||||
self._context, self.uuid, db_requests)
|
||||
else:
|
||||
self.pci_requests = \
|
||||
objects.InstancePCIRequests.get_by_instance_uuid(
|
||||
self._context, self.uuid)
|
||||
|
||||
def obj_load_attr(self, attrname):
|
||||
if attrname not in INSTANCE_OPTIONAL_ATTRS:
|
||||
|
@ -45,6 +45,17 @@ class InstanceNUMATopology(base.NovaObject):
|
||||
'cells': fields.ListOfObjectsField('InstanceNUMACell'),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_db_obj(cls, instance_uuid, db_obj):
|
||||
topo = hardware.VirtNUMAInstanceTopology.from_json(db_obj)
|
||||
obj_topology = cls.obj_from_topology(topo)
|
||||
obj_topology.instance_uuid = instance_uuid
|
||||
# NOTE (ndipanov) not really needed as we never save, but left for
|
||||
# consistency
|
||||
obj_topology.id = 0
|
||||
obj_topology.obj_reset_changes()
|
||||
return obj_topology
|
||||
|
||||
@classmethod
|
||||
def obj_from_topology(cls, topology):
|
||||
if not isinstance(topology, hardware.VirtNUMAInstanceTopology):
|
||||
@ -86,20 +97,12 @@ class InstanceNUMATopology(base.NovaObject):
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_instance_uuid(cls, context, instance_uuid):
|
||||
db_topology = db.instance_extra_get_by_instance_uuid(
|
||||
db_extra = db.instance_extra_get_by_instance_uuid(
|
||||
context, instance_uuid, columns=['numa_topology'])
|
||||
if not db_topology:
|
||||
if not db_extra:
|
||||
raise exception.NumaTopologyNotFound(instance_uuid=instance_uuid)
|
||||
|
||||
if db_topology['numa_topology'] is None:
|
||||
if db_extra['numa_topology'] is None:
|
||||
return None
|
||||
|
||||
topo = hardware.VirtNUMAInstanceTopology.from_json(
|
||||
db_topology['numa_topology'])
|
||||
obj_topology = cls.obj_from_topology(topo)
|
||||
obj_topology.id = db_topology['id']
|
||||
obj_topology.instance_uuid = db_topology['instance_uuid']
|
||||
# NOTE (ndipanov) not really needed as we never save, but left for
|
||||
# consistency
|
||||
obj_topology.obj_reset_changes()
|
||||
return obj_topology
|
||||
return cls.obj_from_db_obj(instance_uuid, db_extra['numa_topology'])
|
||||
|
@ -67,30 +67,29 @@ class InstancePCIRequests(base.NovaObject):
|
||||
primitive['requests'][index]['nova_object.data'], '1.0')
|
||||
primitive['requests'][index]['nova_object.version'] = '1.0'
|
||||
|
||||
@classmethod
|
||||
def obj_from_db(cls, context, instance_uuid, db_requests):
|
||||
self = cls(context=context, requests=[],
|
||||
instance_uuid=instance_uuid)
|
||||
try:
|
||||
requests = jsonutils.loads(db_requests['pci_requests'])
|
||||
except TypeError:
|
||||
requests = []
|
||||
for request in requests:
|
||||
request_obj = InstancePCIRequest(
|
||||
count=request['count'], spec=request['spec'],
|
||||
alias_name=request['alias_name'], is_new=request['is_new'],
|
||||
request_id=request['request_id'])
|
||||
request_obj.obj_reset_changes()
|
||||
self.requests.append(request_obj)
|
||||
self.obj_reset_changes()
|
||||
return self
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_instance_uuid(cls, context, instance_uuid):
|
||||
obj_pci_requests = cls(instance_uuid=instance_uuid)
|
||||
obj_pci_requests.requests = []
|
||||
obj_pci_requests._context = context
|
||||
|
||||
db_pci_requests = db.instance_extra_get_by_instance_uuid(
|
||||
context, instance_uuid, columns=['pci_requests'])
|
||||
if db_pci_requests:
|
||||
try:
|
||||
requests = jsonutils.loads(db_pci_requests['pci_requests'])
|
||||
except TypeError:
|
||||
requests = []
|
||||
for request in requests:
|
||||
request_obj = InstancePCIRequest(
|
||||
count=request['count'], spec=request['spec'],
|
||||
alias_name=request['alias_name'], is_new=request['is_new'],
|
||||
request_id=request['request_id'])
|
||||
request_obj.obj_reset_changes()
|
||||
obj_pci_requests.requests.append(request_obj)
|
||||
|
||||
obj_pci_requests.obj_reset_changes()
|
||||
|
||||
return obj_pci_requests
|
||||
return cls.obj_from_db(context, instance_uuid, db_pci_requests)
|
||||
|
||||
@classmethod
|
||||
def get_by_instance_uuid_and_newness(cls, context, instance_uuid, is_new):
|
||||
|
@ -1882,6 +1882,8 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
"task_state": "",
|
||||
"vm_state": "",
|
||||
"root_device_name": inst.get('root_device_name', 'vda'),
|
||||
"extra": {"pci_requests": None,
|
||||
"numa_topology": None},
|
||||
})
|
||||
|
||||
self.instance_cache_by_id[instance['id']] = instance
|
||||
|
@ -1869,6 +1869,8 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
"vm_state": "",
|
||||
"root_device_name": inst.get('root_device_name', 'vda'),
|
||||
"security_groups": inst['security_groups'],
|
||||
"extra": {"pci_requests": None,
|
||||
"numa_topology": None},
|
||||
})
|
||||
|
||||
self.instance_cache_by_id[instance['id']] = instance
|
||||
|
@ -282,18 +282,23 @@ class BaseTestCase(test.TestCase):
|
||||
'image_ref': None,
|
||||
'root_device_name': None,
|
||||
}
|
||||
extra = {
|
||||
'id': 1, 'created_at': None, 'updated_at': None,
|
||||
'deleted_at': None, 'deleted': None,
|
||||
'instance_uuid': instance['uuid'],
|
||||
'numa_topology': None,
|
||||
'pci_requests': None,
|
||||
}
|
||||
|
||||
numa_topology = kwargs.pop('numa_topology', None)
|
||||
if numa_topology:
|
||||
numa_topology = {
|
||||
'id': 1, 'created_at': None, 'updated_at': None,
|
||||
'deleted_at': None, 'deleted': None,
|
||||
'instance_uuid': instance['uuid'],
|
||||
'numa_topology': numa_topology.to_json()
|
||||
}
|
||||
extra['numa_topology'] = numa_topology.to_json()
|
||||
|
||||
instance.update(kwargs)
|
||||
instance['extra'] = extra
|
||||
|
||||
self._instances[instance_uuid] = instance
|
||||
self._numa_topologies[instance_uuid] = numa_topology
|
||||
self._numa_topologies[instance_uuid] = extra
|
||||
return instance
|
||||
|
||||
def _fake_flavor_create(self, **kwargs):
|
||||
|
@ -54,7 +54,9 @@ def fake_db_instance(**updates):
|
||||
'metadata': {},
|
||||
'system_metadata': {},
|
||||
'root_gb': 0,
|
||||
'ephemeral_gb': 0
|
||||
'ephemeral_gb': 0,
|
||||
'extra': {'pci_requests': None,
|
||||
'numa_topology': None},
|
||||
}
|
||||
|
||||
for name, field in objects.Instance.fields.items():
|
||||
|
@ -18,6 +18,7 @@ import iso8601
|
||||
import mock
|
||||
import mox
|
||||
import netaddr
|
||||
from oslo.serialization import jsonutils
|
||||
from oslo.utils import timeutils
|
||||
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
@ -126,26 +127,26 @@ class _TestInstanceObject(object):
|
||||
exp_cols.remove('fault')
|
||||
exp_cols.remove('numa_topology')
|
||||
exp_cols.remove('pci_requests')
|
||||
exp_cols.extend(['extra', 'extra.numa_topology', 'extra.pci_requests'])
|
||||
|
||||
fake_topology = (test_instance_numa_topology.
|
||||
fake_db_topology['numa_topology'])
|
||||
fake_requests = jsonutils.dumps(test_instance_pci_requests.
|
||||
fake_pci_requests)
|
||||
fake_instance = dict(self.fake_instance,
|
||||
extra={
|
||||
'numa_topology': fake_topology,
|
||||
'pci_requests': fake_requests,
|
||||
})
|
||||
db.instance_get_by_uuid(
|
||||
self.context, 'uuid',
|
||||
columns_to_join=exp_cols,
|
||||
use_slave=False
|
||||
).AndReturn(self.fake_instance)
|
||||
).AndReturn(fake_instance)
|
||||
fake_faults = test_instance_fault.fake_faults
|
||||
db.instance_fault_get_by_instance_uuids(
|
||||
self.context, [self.fake_instance['uuid']]
|
||||
self.context, [fake_instance['uuid']]
|
||||
).AndReturn(fake_faults)
|
||||
fake_topology = test_instance_numa_topology.fake_db_topology
|
||||
db.instance_extra_get_by_instance_uuid(
|
||||
self.context, self.fake_instance['uuid'],
|
||||
columns=['numa_topology']
|
||||
).AndReturn(fake_topology)
|
||||
fake_requests = test_instance_pci_requests.fake_pci_requests
|
||||
db.instance_extra_get_by_instance_uuid(
|
||||
self.context, self.fake_instance['uuid'],
|
||||
columns=['pci_requests']
|
||||
).AndReturn(fake_requests)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
inst = instance.Instance.get_by_uuid(
|
||||
@ -950,6 +951,21 @@ class _TestInstanceObject(object):
|
||||
self.assertEqual(fake_fault['id'], fault.id)
|
||||
self.assertNotIn('metadata', inst.obj_what_changed())
|
||||
|
||||
def test_get_with_extras(self):
|
||||
pci_requests = objects.InstancePCIRequests(requests=[
|
||||
objects.InstancePCIRequest(count=123, spec=[])])
|
||||
inst = objects.Instance(context=self.context,
|
||||
user_id=self.context.user_id,
|
||||
project_id=self.context.project_id,
|
||||
pci_requests=pci_requests)
|
||||
inst.create()
|
||||
uuid = inst.uuid
|
||||
inst = objects.Instance.get_by_uuid(self.context, uuid)
|
||||
self.assertFalse(inst.obj_attr_is_set('pci_requests'))
|
||||
inst = objects.Instance.get_by_uuid(
|
||||
self.context, uuid, expected_attrs=['pci_requests'])
|
||||
self.assertTrue(inst.obj_attr_is_set('pci_requests'))
|
||||
|
||||
|
||||
class TestInstanceObject(test_objects._LocalTest,
|
||||
_TestInstanceObject):
|
||||
@ -1254,3 +1270,8 @@ class TestInstanceObjectMisc(test.NoDBTestCase):
|
||||
self.stubs.Set(instance, '_INSTANCE_OPTIONAL_JOINED_FIELDS', ['bar'])
|
||||
self.assertEqual(['bar'], instance._expected_cols(['foo', 'bar']))
|
||||
self.assertIsNone(instance._expected_cols(None))
|
||||
|
||||
def test_expected_cols_extra(self):
|
||||
self.assertEqual(['metadata', 'extra', 'extra.numa_topology'],
|
||||
instance._expected_cols(['metadata',
|
||||
'numa_topology']))
|
||||
|
@ -49,7 +49,7 @@ class _TestInstanceNUMATopology(object):
|
||||
def test_get_by_instance_uuid(self, mock_get):
|
||||
mock_get.return_value = fake_db_topology
|
||||
numa_topology = objects.InstanceNUMATopology.get_by_instance_uuid(
|
||||
self.context, 'fake_uuid')
|
||||
self.context, fake_db_topology['instance_uuid'])
|
||||
self.assertEqual(fake_db_topology['instance_uuid'],
|
||||
numa_topology.instance_uuid)
|
||||
for obj_cell, topo_cell in zip(
|
||||
|
Loading…
Reference in New Issue
Block a user