Require List objects to be able to backlevel their contents
Right now, a client declares its supported version of a given object automatically in the remoted calls it makes to conductor. However, in the case of things like InstanceList.get_by_foo(), they are reporting the version of their InstanceList object, not their Instance object. Conductor fills a version-matching InstanceList object with brand new Instance objects, which the client, of course, barfs on. There may be a better way to handle this going forward, but for now, stop the bleeding by requiring a version bump to the corresponding List object whenever the object type it contains takes a version bump. This adds a test to validate that all the objects registered have a suitable mapping for the current version in the tree. Since this actually caused a breakage in the Instance object recently, this also bumps the InstanceList version so that conductors running this commit (or later) will properly send version 1.9 Instance objects to Havana clients and version 1.10+ to newer ones. Change-Id: I2668dead4784fbd0411d1b6372a9a8006eeb2e84 Related-Bug: #1258256 Closes-Bug: #1254902
This commit is contained in:
parent
bfd8378c6a
commit
ff221c6cc6
|
@ -147,11 +147,17 @@ class Aggregate(base.NovaPersistentObject, base.NovaObject):
|
|||
class AggregateList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Added key argument to get_by_host()
|
||||
# Aggregate <= version 1.1
|
||||
VERSION = '1.1'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Aggregate'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.1',
|
||||
'1.1': '1.1',
|
||||
# NOTE(danms): Aggregate was at 1.1 before we added this
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_all(cls, context):
|
||||
|
|
|
@ -435,6 +435,11 @@ class ObjectListBase(object):
|
|||
'objects': fields.ListOfObjectsField('NovaObject'),
|
||||
}
|
||||
|
||||
# This is a dictionary of my_version:child_version mappings so that
|
||||
# we can support backleveling our contents based on the version
|
||||
# requested of the list object.
|
||||
child_versions = {}
|
||||
|
||||
def __iter__(self):
|
||||
"""List iterator interface."""
|
||||
return iter(self.objects)
|
||||
|
@ -478,6 +483,15 @@ class ObjectListBase(object):
|
|||
objects.append(obj)
|
||||
return objects
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
primitives = primitive['objects']
|
||||
child_target_version = self.child_versions.get(target_version, '1.0')
|
||||
for index, item in enumerate(self.objects):
|
||||
self.objects[index].obj_make_compatible(
|
||||
primitives[index]['nova_object.data'],
|
||||
child_target_version)
|
||||
primitives[index]['nova_object.version'] = child_target_version
|
||||
|
||||
|
||||
class NovaObjectSerializer(nova.openstack.common.rpc.serializer.Serializer):
|
||||
"""A NovaObject-aware Serializer.
|
||||
|
|
|
@ -91,9 +91,16 @@ class ComputeNode(base.NovaPersistentObject, base.NovaObject):
|
|||
|
||||
|
||||
class ComputeNodeList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# ComputeNode <= version 1.2
|
||||
VERSION = '1.0'
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('ComputeNode'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.2',
|
||||
# NOTE(danms): ComputeNode was at 1.2 before we added this
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_all(cls, context):
|
||||
|
|
|
@ -168,6 +168,9 @@ class FlavorList(base.ObjectListBase, base.NovaObject):
|
|||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Flavor'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.0',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_all(cls, context, inactive=False, filters=None,
|
||||
|
|
|
@ -531,11 +531,18 @@ def _make_instance_list(context, inst_list, db_inst_list, expected_attrs):
|
|||
class InstanceList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Added use_slave to get_by_host
|
||||
VERSION = '1.1'
|
||||
# Instance <= version 1.9
|
||||
# Version 1.2: Instance <= version 1.11
|
||||
VERSION = '1.2'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Instance'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.1': '1.9',
|
||||
# NOTE(danms): Instance was at 1.9 before we added this
|
||||
'1.2': '1.11',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_filters(cls, context, filters,
|
||||
|
|
|
@ -77,9 +77,16 @@ class InstanceAction(base.NovaPersistentObject, base.NovaObject):
|
|||
|
||||
|
||||
class InstanceActionList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# InstanceAction <= version 1.1
|
||||
VERSION = '1.0'
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('InstanceAction'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.1',
|
||||
# NOTE(danms): InstanceAction was at 1.1 before we added this
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_instance_uuid(cls, context, instance_uuid):
|
||||
|
@ -157,6 +164,9 @@ class InstanceActionEventList(base.ObjectListBase, base.NovaObject):
|
|||
fields = {
|
||||
'objects': fields.ListOfObjectsField('InstanceActionEvent'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.0',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_action(cls, context, action_id):
|
||||
|
|
|
@ -58,9 +58,17 @@ def _make_fault_list(faultlist, db_faultlist):
|
|||
|
||||
|
||||
class InstanceFaultList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# InstanceFault <= version 1.1
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('InstanceFault'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.1',
|
||||
# NOTE(danms): InstanceFault was at 1.1 before we added this
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_instance_uuids(cls, context, instance_uuids):
|
||||
|
|
|
@ -123,9 +123,17 @@ def _make_instance_group_list(context, inst_list, db_list):
|
|||
|
||||
|
||||
class InstanceGroupList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# InstanceGroup <= version 1.3
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('InstanceGroup'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.3',
|
||||
# NOTE(danms): InstanceGroup was at 1.3 before we added this
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_project_id(cls, context, project_id):
|
||||
|
|
|
@ -59,9 +59,17 @@ class KeyPair(base.NovaPersistentObject, base.NovaObject):
|
|||
|
||||
|
||||
class KeyPairList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# KeyPair <= version 1.1
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('KeyPair'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.1',
|
||||
# NOTE(danms): KeyPair was at 1.1 before we added this
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_user(cls, context, user_id):
|
||||
|
|
|
@ -87,12 +87,18 @@ def _make_list(context, list_obj, item_cls, db_list):
|
|||
|
||||
class MigrationList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# Migration <= 1.1
|
||||
# Version 1.1: Added use_slave to get_unconfirmed_by_dest_compute
|
||||
VERSION = '1.1'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Migration'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.1',
|
||||
# NOTE(danms): Migration was at 1.1 before we added this
|
||||
'1.1': '1.1',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_unconfirmed_by_dest_compute(cls, context, confirm_window,
|
||||
|
|
|
@ -251,9 +251,17 @@ def _make_pci_list(context, pci_list, db_list):
|
|||
|
||||
|
||||
class PciDeviceList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# PciDevice <= 1.1
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('PciDevice'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.1',
|
||||
# NOTE(danms): PciDevice was at 1.1 before we added this
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super(PciDeviceList, self).__init__()
|
||||
|
|
|
@ -82,9 +82,17 @@ def _make_secgroup_list(context, secgroup_list, db_secgroup_list):
|
|||
|
||||
|
||||
class SecurityGroupList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# SecurityGroup <= version 1.1
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('SecurityGroup'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.1',
|
||||
# NOTE(danms): SecurityGroup was at 1.1 before we added this
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super(SecurityGroupList, self).__init__()
|
||||
|
|
|
@ -64,6 +64,9 @@ class SecurityGroupRuleList(base.ObjectListBase, base.NovaObject):
|
|||
fields = {
|
||||
'objects': fields.ListOfObjectsField('SecurityGroupRule'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.0',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_security_group_id(cls, context, secgroup_id):
|
||||
|
|
|
@ -124,9 +124,17 @@ class Service(base.NovaPersistentObject, base.NovaObject):
|
|||
|
||||
|
||||
class ServiceList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
# Service <= version 1.2
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Service'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.2',
|
||||
# NOTE(danms): Service was at 1.2 before we added this
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_topic(cls, context, topic):
|
||||
|
|
|
@ -721,6 +721,27 @@ class TestObjectListBase(test.TestCase):
|
|||
self.assertEqual([x.foo for x in obj],
|
||||
[y.foo for y in obj2])
|
||||
|
||||
def _test_object_list_version_mappings(self, list_obj_class):
|
||||
# Figure out what sort of object this list is for
|
||||
list_field = list_obj_class.fields['objects']
|
||||
item_obj_field = list_field._type._element_type
|
||||
item_obj_name = item_obj_field._type._obj_name
|
||||
|
||||
# Look through all object classes of this type and make sure that
|
||||
# the versions we find are covered by the parent list class
|
||||
for item_class in base.NovaObject._obj_classes[item_obj_name]:
|
||||
self.assertIn(
|
||||
item_class.VERSION,
|
||||
list_obj_class.child_versions.values())
|
||||
|
||||
def test_object_version_mappings(self):
|
||||
# Find all object list classes and make sure that they at least handle
|
||||
# all the current object versions
|
||||
for obj_classes in base.NovaObject._obj_classes.values():
|
||||
for obj_class in obj_classes:
|
||||
if issubclass(obj_class, base.ObjectListBase):
|
||||
self._test_object_list_version_mappings(obj_class)
|
||||
|
||||
|
||||
class TestObjectSerializer(_BaseTestCase):
|
||||
def test_serialize_entity_primitive(self):
|
||||
|
|
Loading…
Reference in New Issue