Merge "Only return latest instance fault for instances"

This commit is contained in:
Jenkins 2017-01-04 21:37:14 +00:00 committed by Gerrit Code Review
commit 2fb05c5b5e
7 changed files with 96 additions and 18 deletions

View File

@ -1903,9 +1903,11 @@ def instance_fault_create(context, values):
return IMPL.instance_fault_create(context, values)
def instance_fault_get_by_instance_uuids(context, instance_uuids):
def instance_fault_get_by_instance_uuids(context, instance_uuids,
latest=False):
"""Get all instance faults for the provided instance_uuids."""
return IMPL.instance_fault_get_by_instance_uuids(context, instance_uuids)
return IMPL.instance_fault_get_by_instance_uuids(context, instance_uuids,
latest=latest)
####################

View File

@ -6047,24 +6047,63 @@ def instance_fault_create(context, values):
@pick_context_manager_reader
def instance_fault_get_by_instance_uuids(context, instance_uuids):
"""Get all instance faults for the provided instance_uuids."""
def instance_fault_get_by_instance_uuids(context, instance_uuids,
latest=False):
"""Get all instance faults for the provided instance_uuids.
:param instance_uuids: List of UUIDs of instances to grab faults for
:param latest: Optional boolean indicating we should only return the latest
fault for the instance
"""
if not instance_uuids:
return {}
rows = model_query(context, models.InstanceFault, read_deleted='no').\
filter(models.InstanceFault.instance_uuid.in_(
instance_uuids)).\
order_by(desc("created_at"), desc("id")).\
all()
faults_tbl = models.InstanceFault.__table__
# NOTE(rpodolyaka): filtering by instance_uuids is performed in both
# code branches below for the sake of a better query plan. On change,
# make sure to update the other one as well.
query = model_query(context, models.InstanceFault,
[faults_tbl],
read_deleted='no')
if latest:
# NOTE(jaypipes): We join instance_faults to a derived table of the
# latest faults per instance UUID. The SQL produced below looks like
# this:
#
# SELECT instance_faults.*
# FROM instance_faults
# JOIN (
# SELECT instance_uuid, MAX(id) AS max_id
# FROM instance_faults
# WHERE instance_uuid IN ( ... )
# AND deleted = 0
# GROUP BY instance_uuid
# ) AS latest_faults
# ON instance_faults.id = latest_faults.max_id;
latest_faults = model_query(
context, models.InstanceFault,
[faults_tbl.c.instance_uuid,
sql.func.max(faults_tbl.c.id).label('max_id')],
read_deleted='no'
).filter(
faults_tbl.c.instance_uuid.in_(instance_uuids)
).group_by(
faults_tbl.c.instance_uuid
).subquery(name="latest_faults")
query = query.join(latest_faults,
faults_tbl.c.id == latest_faults.c.max_id)
else:
query = query.filter(models.InstanceFault.instance_uuid.in_(
instance_uuids)).order_by(desc("id"))
output = {}
for instance_uuid in instance_uuids:
output[instance_uuid] = []
for row in rows:
data = dict(row)
output[row['instance_uuid']].append(data)
for row in query:
output[row.instance_uuid].append(row._asdict())
return output

View File

@ -1346,12 +1346,11 @@ class InstanceList(base.ObjectListBase, base.NovaObject):
:returns: A list of instance uuids for which faults were found.
"""
uuids = [inst.uuid for inst in self]
faults = objects.InstanceFaultList.get_by_instance_uuids(
faults = objects.InstanceFaultList.get_latest_by_instance_uuids(
self._context, uuids)
faults_by_uuid = {}
for fault in faults:
if fault.instance_uuid not in faults_by_uuid:
faults_by_uuid[fault.instance_uuid] = fault
faults_by_uuid[fault.instance_uuid] = fault
for instance in self:
if instance.uuid in faults_by_uuid:

View File

@ -96,12 +96,22 @@ class InstanceFaultList(base.ObjectListBase, base.NovaObject):
# Version 1.0: Initial version
# InstanceFault <= version 1.1
# Version 1.1: InstanceFault version 1.2
VERSION = '1.1'
# Version 1.2: Added get_latest_by_instance_uuids() method
VERSION = '1.2'
fields = {
'objects': fields.ListOfObjectsField('InstanceFault'),
}
@base.remotable_classmethod
def get_latest_by_instance_uuids(cls, context, instance_uuids):
db_faultdict = db.instance_fault_get_by_instance_uuids(context,
instance_uuids,
latest=True)
db_faultlist = itertools.chain(*db_faultdict.values())
return base.obj_make_list(context, cls(context), objects.InstanceFault,
db_faultlist)
@base.remotable_classmethod
def get_by_instance_uuids(cls, context, instance_uuids):
db_faultdict = db.instance_fault_get_by_instance_uuids(context,

View File

@ -4225,6 +4225,33 @@ class InstanceFaultTestCase(test.TestCase, ModelsObjectComparatorMixin):
for uuid in uuids:
self._assertEqualOrderedListOfObjects(expected[uuid], faults[uuid])
def test_instance_fault_get_latest_by_instance(self):
"""Ensure we can retrieve only latest faults for instance."""
uuids = [uuidsentinel.uuid1, uuidsentinel.uuid2]
fault_codes = [404, 500]
expected = {}
# Create faults
for uuid in uuids:
db.instance_create(self.ctxt, {'uuid': uuid})
expected[uuid] = []
for code in fault_codes:
fault_values = self._create_fault_values(uuid, code)
fault = db.instance_fault_create(self.ctxt, fault_values)
expected[uuid].append(fault)
# We are only interested in the latest fault for each instance
for uuid in expected:
expected[uuid] = expected[uuid][-1:]
# Ensure faults are saved
faults = db.instance_fault_get_by_instance_uuids(self.ctxt, uuids,
latest=True)
self.assertEqual(len(expected), len(faults))
for uuid in uuids:
self._assertEqualListsOfObjects(expected[uuid], faults[uuid])
def test_instance_faults_get_by_instance_uuids_no_faults(self):
uuid = uuidsentinel.uuid1
# None should be returned when no faults exist.

View File

@ -1835,7 +1835,8 @@ class _TestInstanceListObject(object):
self.assertEqual(set(), inst.obj_what_changed())
mock_fault_get.assert_called_once_with(self.context,
[x.uuid for x in insts])
[x.uuid for x in insts],
latest=True)
@mock.patch('nova.objects.instance.Instance.obj_make_compatible')
def test_get_by_security_group(self, mock_compat):

View File

@ -1104,7 +1104,7 @@ object_data = {
'InstanceDeviceMetadata': '1.0-74d78dd36aa32d26d2769a1b57caf186',
'InstanceExternalEvent': '1.1-6e446ceaae5f475ead255946dd443417',
'InstanceFault': '1.2-7ef01f16f1084ad1304a513d6d410a38',
'InstanceFaultList': '1.1-f8ec07cbe3b60f5f07a8b7a06311ac0d',
'InstanceFaultList': '1.2-6bb72de2872fe49ded5eb937a93f2451',
'InstanceGroup': '1.10-1a0c8c7447dc7ecb9da53849430c4a5f',
'InstanceGroupList': '1.7-be18078220513316abd0ae1b2d916873',
'InstanceInfoCache': '1.5-cd8b96fefe0fc8d4d337243ba0bf0e1e',