diff --git a/nova/exception.py b/nova/exception.py index bba76200c0ff..c78136b9b805 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -28,6 +28,7 @@ import sys from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils +from oslo_versionedobjects import exception as ovo_exc import six import webob.exc @@ -1509,8 +1510,9 @@ class IncompatibleObjectVersion(NovaException): 'maximum supported version is: %(supported)s') -class ReadOnlyFieldError(NovaException): - msg_fmt = _('Cannot modify readonly field %(field)s') +# FIXME(danms): Remove this and convert existing object tests to catch +# the oslo_versionedobjects exception themselves +ReadOnlyFieldError = ovo_exc.ReadOnlyFieldError class ObjectActionError(NovaException): diff --git a/nova/objects/base.py b/nova/objects/base.py index 0cf23cf0b4bb..3e6592cc19b1 100644 --- a/nova/objects/base.py +++ b/nova/objects/base.py @@ -14,7 +14,6 @@ """Nova common internal object model""" -import collections import contextlib import copy import datetime @@ -42,115 +41,16 @@ LOG = logging.getLogger('object') def get_attrname(name): """Return the mangled name of the attribute's underlying storage.""" - return '_' + name + # FIXME(danms): This is just until we use o.vo's class properties + # and object base. + return '_obj_' + name -def make_class_properties(cls): - # NOTE(danms/comstud): Inherit fields from super classes. - # mro() returns the current class first and returns 'object' last, so - # those can be skipped. Also be careful to not overwrite any fields - # that already exist. And make sure each cls has its own copy of - # fields and that it is not sharing the dict with a super class. - cls.fields = dict(cls.fields) - for supercls in cls.mro()[1:-1]: - if not hasattr(supercls, 'fields'): - continue - for name, field in supercls.fields.items(): - if name not in cls.fields: - cls.fields[name] = field - for name, field in six.iteritems(cls.fields): - if not isinstance(field, obj_fields.Field): - raise exception.ObjectFieldInvalid( - field=name, objname=cls.obj_name()) - - def getter(self, name=name): - attrname = get_attrname(name) - if not hasattr(self, attrname): - self.obj_load_attr(name) - return getattr(self, attrname) - - def setter(self, value, name=name, field=field): - attrname = get_attrname(name) - field_value = field.coerce(self, name, value) - if field.read_only and hasattr(self, attrname): - # Note(yjiang5): _from_db_object() may iterate - # every field and write, no exception in such situation. - if getattr(self, attrname) != field_value: - raise exception.ReadOnlyFieldError(field=name) - else: - return - - self._changed_fields.add(name) - try: - return setattr(self, attrname, field_value) - except Exception: - attr = "%s.%s" % (self.obj_name(), name) - LOG.exception(_LE('Error setting %(attr)s'), {'attr': attr}) - raise - - def deleter(self, name=name): - attrname = get_attrname(name) - if not hasattr(self, attrname): - raise AttributeError('No such attribute `%s' % name) - delattr(self, get_attrname(name)) - - setattr(cls, name, property(getter, setter, deleter)) - - -# NOTE(danms): This is transitional to get the registration decorator -# on everything before we make a cut over -class NovaObjectRegistry(object): - classes = [] - - @classmethod - def register(cls, obj_cls): - cls.classes.append(obj_cls.obj_name()) - return obj_cls - - -class NovaObjectMetaclass(type): - """Metaclass that allows tracking of object classes.""" - - # NOTE(danms): This is what controls whether object operations are - # remoted. If this is not None, use it to remote things over RPC. - indirection_api = None - - def __init__(cls, names, bases, dict_): - if not hasattr(cls, '_obj_classes'): - # This means this is a base class using the metaclass. I.e., - # the 'NovaObject' class. - cls._obj_classes = collections.defaultdict(list) - return - - def _vers_tuple(obj): - return tuple([int(x) for x in obj.VERSION.split(".")]) - - # Add the subclass to NovaObject._obj_classes. If the - # same version already exists, replace it. Otherwise, - # keep the list with newest version first. - make_class_properties(cls) - obj_name = cls.obj_name() - for i, obj in enumerate(cls._obj_classes[obj_name]): - if cls.VERSION == obj.VERSION: - cls._obj_classes[obj_name][i] = cls - # Update nova.objects with this newer class. - setattr(objects, obj_name, cls) - break - if _vers_tuple(cls) > _vers_tuple(obj): - # Insert before. - cls._obj_classes[obj_name].insert(i, cls) - if i == 0: - # Later version than we've seen before. Update - # nova.objects. - setattr(objects, obj_name, cls) - break - else: - cls._obj_classes[obj_name].append(cls) - # Either this is the first time we've seen the object or it's - # an older version than anything we'e seen. Update nova.objects - # only if it's the first time we've seen this object name. - if not hasattr(objects, obj_name): - setattr(objects, obj_name, cls) +class NovaObjectRegistry(ovoo_base.VersionedObjectRegistry): + def registration_hook(self, cls, index): + # NOTE(danms): Set the *latest* version of this class + newest = self._registry._obj_classes[cls.obj_name()][0] + setattr(objects, cls.obj_name(), newest) # These are decorators that mark an object's method as remotable. @@ -220,7 +120,6 @@ def remotable(fn): return wrapper -@six.add_metaclass(NovaObjectMetaclass) class NovaObject(object): """Base class and object factory. @@ -279,6 +178,9 @@ class NovaObject(object): # since they were not added until version 1.2. obj_relationships = {} + # Temporary until we inherit from o.vo.base.VersionedObject + indirection_api = None + def __init__(self, context=None, **kwargs): self._changed_fields = set() self._context = context @@ -304,7 +206,7 @@ class NovaObject(object): @classmethod def obj_class_from_name(cls, objname, objver): """Returns a class from the registry based on a name and version.""" - if objname not in cls._obj_classes: + if objname not in NovaObjectRegistry.obj_classes(): LOG.error(_LE('Unable to instantiate unregistered object type ' '%(objtype)s'), dict(objtype=objname)) raise exception.UnsupportedObjectError(objtype=objname) @@ -315,7 +217,8 @@ class NovaObject(object): # once below. compatible_match = None - for objclass in cls._obj_classes[objname]: + obj_classes = NovaObjectRegistry.obj_classes() + for objclass in obj_classes[objname]: if objclass.VERSION == objver: return objclass if (not compatible_match and @@ -326,7 +229,7 @@ class NovaObject(object): return compatible_match # As mentioned above, latest version is always first in the list. - latest_ver = cls._obj_classes[objname][0].VERSION + latest_ver = obj_classes[objname][0].VERSION raise exception.IncompatibleObjectVersion(objname=objname, objver=objver, supported=latest_ver) diff --git a/nova/test.py b/nova/test.py index a7455e013a2a..4399299eabe7 100644 --- a/nova/test.py +++ b/nova/test.py @@ -233,7 +233,7 @@ class TestCase(testtools.TestCase): # registry. objects_base.NovaObject.indirection_api = None self._base_test_obj_backup = copy.copy( - objects_base.NovaObject._obj_classes) + objects_base.NovaObjectRegistry._registry._obj_classes) self.addCleanup(self._restore_obj_registry) # NOTE(mnaser): All calls to utils.is_neutron() are cached in @@ -251,7 +251,8 @@ class TestCase(testtools.TestCase): self.useFixture(nova_fixtures.PoisonFunctions()) def _restore_obj_registry(self): - objects_base.NovaObject._obj_classes = self._base_test_obj_backup + objects_base.NovaObjectRegistry._registry._obj_classes = \ + self._base_test_obj_backup def _clear_attrs(self): # Delete attributes that don't start with _ so they don't pin diff --git a/nova/tests/unit/cells/test_cells_messaging.py b/nova/tests/unit/cells/test_cells_messaging.py index 626f88809b48..29effdeb94af 100644 --- a/nova/tests/unit/cells/test_cells_messaging.py +++ b/nova/tests/unit/cells/test_cells_messaging.py @@ -296,6 +296,8 @@ class CellsMessageClassesTestCase(test.TestCase): """ fields = {'test': objects_fields.StringField()} + objects_base.NovaObjectRegistry.register(CellsMsgingTestObject) + test_obj = CellsMsgingTestObject() test_obj.test = 'meow' diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py index 0df9c89562b0..3c3f20e5b210 100644 --- a/nova/tests/unit/conductor/test_conductor.py +++ b/nova/tests/unit/conductor/test_conductor.py @@ -420,6 +420,8 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): else: return 'test' + obj_base.NovaObjectRegistry.register(TestObject) + obj = TestObject() # NOTE(danms): After a trip over RPC, any tuple will be a list, # so use a list here to make sure we can handle it @@ -456,6 +458,8 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): self.dict['foo'] = 'bar' self.obj_reset_changes() + obj_base.NovaObjectRegistry.register(TestObject) + obj = TestObject() obj.dict = {} obj.obj_reset_changes() diff --git a/nova/tests/unit/objects/test_fields.py b/nova/tests/unit/objects/test_fields.py index 605ee5c4bf95..a5387bd20e54 100644 --- a/nova/tests/unit/objects/test_fields.py +++ b/nova/tests/unit/objects/test_fields.py @@ -725,6 +725,9 @@ class TestObject(TestField): class OtherTestableObject(obj_base.NovaObject): pass + obj_base.NovaObjectRegistry.register(TestableObject) + obj_base.NovaObjectRegistry.register(OtherTestableObject) + test_inst = TestableObject() self._test_cls = TestableObject self.field = fields.Field(fields.Object('TestableObject')) diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index 1ccdcfe6f9b6..7f49155d6545 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -199,10 +199,10 @@ class _TestInstanceObject(object): ).AndReturn(fake_inst2) self.mox.ReplayAll() inst = instance.Instance.get_by_uuid(self.context, fake_uuid) - self.assertFalse(hasattr(inst, '_metadata')) + self.assertFalse(hasattr(inst, '_obj_metadata')) meta = inst.metadata self.assertEqual(meta, {'foo': 'bar'}) - self.assertTrue(hasattr(inst, '_metadata')) + self.assertTrue(hasattr(inst, '_obj_metadata')) # Make sure we don't run load again meta2 = inst.metadata self.assertEqual(meta2, {'foo': 'bar'}) @@ -1330,6 +1330,8 @@ class TestRemoteInstanceObject(test_objects._RemoteTest, class OldInstance(objects.Instance): VERSION = '1.17' + base.NovaObjectRegistry.register(OldInstance) + inst = OldInstance.get_by_uuid(self.context, inst.uuid) self.assertFalse(inst.obj_attr_is_set('system_metadata')) self.assertEqual('bar', inst.system_metadata['foo']) @@ -1348,6 +1350,8 @@ class TestRemoteInstanceObject(test_objects._RemoteTest, class OldInstance(objects.Instance): VERSION = '1.17' + base.NovaObjectRegistry.register(OldInstance) + inst = OldInstance.get_by_uuid(self.context, inst.uuid, expected_attrs=['system_metadata']) self.assertTrue(inst.obj_attr_is_set('system_metadata')) diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index eb08bea95929..8c17ca045b9d 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -21,19 +21,20 @@ import inspect import os import pprint +import fixtures import mock from oslo_log import log from oslo_utils import timeutils +from oslo_versionedobjects import exception as ovo_exc +from oslo_versionedobjects import fixture import six from testtools import matchers -from nova.conductor import rpcapi as conductor_rpcapi from nova import context from nova import exception from nova import objects from nova.objects import base from nova.objects import fields -from nova import rpc from nova import test from nova.tests import fixtures as nova_fixtures from nova.tests.unit import fake_notifier @@ -122,6 +123,10 @@ class MyObjDiffVers(MyObj): class MyObj2(object): + fields = { + 'bar': fields.StringField(), + } + @classmethod def obj_name(cls): return 'MyObj' @@ -136,78 +141,15 @@ class RandomMixInWithNoFields(object): pass +@base.NovaObjectRegistry.register_if(False) class TestSubclassedObject(RandomMixInWithNoFields, MyObj): fields = {'new_field': fields.StringField()} -class TestMetaclass(test.NoDBTestCase): - def test_obj_tracking(self): - - @six.add_metaclass(base.NovaObjectMetaclass) - class NewBaseClass(object): - VERSION = '1.0' - fields = {} - - @classmethod - def obj_name(cls): - return cls.__name__ - - class Fake1TestObj1(NewBaseClass): - @classmethod - def obj_name(cls): - return 'fake1' - - class Fake1TestObj2(Fake1TestObj1): - pass - - class Fake1TestObj3(Fake1TestObj1): - VERSION = '1.1' - - class Fake2TestObj1(NewBaseClass): - @classmethod - def obj_name(cls): - return 'fake2' - - class Fake1TestObj4(Fake1TestObj3): - VERSION = '1.2' - - class Fake2TestObj2(Fake2TestObj1): - VERSION = '1.1' - - class Fake1TestObj5(Fake1TestObj1): - VERSION = '1.1' - - # Newest versions first in the list. Duplicate versions take the - # newest object. - expected = {'fake1': [Fake1TestObj4, Fake1TestObj5, Fake1TestObj2], - 'fake2': [Fake2TestObj2, Fake2TestObj1]} - self.assertEqual(expected, NewBaseClass._obj_classes) - # The following should work, also. - self.assertEqual(expected, Fake1TestObj1._obj_classes) - self.assertEqual(expected, Fake1TestObj2._obj_classes) - self.assertEqual(expected, Fake1TestObj3._obj_classes) - self.assertEqual(expected, Fake1TestObj4._obj_classes) - self.assertEqual(expected, Fake1TestObj5._obj_classes) - self.assertEqual(expected, Fake2TestObj1._obj_classes) - self.assertEqual(expected, Fake2TestObj2._obj_classes) - - def test_field_checking(self): - def create_class(field): - class TestField(base.NovaObject): - VERSION = '1.5' - fields = {'foo': field()} - return TestField - - create_class(fields.IPV4AndV6AddressField) - self.assertRaises(exception.ObjectFieldInvalid, - create_class, fields.IPV4AndV6Address) - self.assertRaises(exception.ObjectFieldInvalid, - create_class, int) - - class TestObjToPrimitive(test.NoDBTestCase): def test_obj_to_primitive_list(self): + @base.NovaObjectRegistry.register_if(False) class MyObjElement(base.NovaObject): fields = {'foo': fields.IntegerField()} @@ -215,6 +157,7 @@ class TestObjToPrimitive(test.NoDBTestCase): super(MyObjElement, self).__init__() self.foo = foo + @base.NovaObjectRegistry.register_if(False) class MyList(base.ObjectListBase, base.NovaObject): fields = {'objects': fields.ListOfObjectsField('MyObjElement')} @@ -224,11 +167,14 @@ class TestObjToPrimitive(test.NoDBTestCase): [x['foo'] for x in base.obj_to_primitive(mylist)]) def test_obj_to_primitive_dict(self): + base.NovaObjectRegistry.register(MyObj) myobj = MyObj(foo=1, bar='foo') self.assertEqual({'foo': 1, 'bar': 'foo'}, base.obj_to_primitive(myobj)) def test_obj_to_primitive_recursive(self): + base.NovaObjectRegistry.register(MyObj) + class MyList(base.ObjectListBase, base.NovaObject): fields = {'objects': fields.ListOfObjectsField('MyObj')} @@ -239,6 +185,7 @@ class TestObjToPrimitive(test.NoDBTestCase): base.obj_to_primitive(mylist)) def test_obj_to_primitive_with_ip_addr(self): + @base.NovaObjectRegistry.register_if(False) class TestObject(base.NovaObject): fields = {'addr': fields.IPAddressField(), 'cidr': fields.IPNetworkField()} @@ -316,6 +263,12 @@ class _BaseTestCase(test.TestCase): fake_notifier.stub_notifier(self.stubs) self.addCleanup(fake_notifier.reset) + # NOTE(danms): register these here instead of at import time + # so that they're not always present + base.NovaObjectRegistry.register(MyObj) + base.NovaObjectRegistry.register(MyObjDiffVers) + base.NovaObjectRegistry.register(MyOwnedObject) + def compare_obj(self, obj, db_obj, subs=None, allow_missing=None, comparators=None): compare_obj(self, obj, db_obj, subs=subs, allow_missing=allow_missing, @@ -356,51 +309,57 @@ def things_temporarily_local(): base.NovaObject.indirection_api = _api +class FakeIndirectionHack(fixture.FakeIndirectionAPI): + def object_action(self, context, objinst, objmethod, args, kwargs): + objinst = self._ser.deserialize_entity( + context, self._ser.serialize_entity( + context, objinst)) + objmethod = six.text_type(objmethod) + args = self._ser.deserialize_entity( + None, self._ser.serialize_entity(None, args)) + kwargs = self._ser.deserialize_entity( + None, self._ser.serialize_entity(None, kwargs)) + original = objinst.obj_clone() + with mock.patch('nova.objects.base.NovaObject.' + 'indirection_api', new=None): + result = getattr(objinst, objmethod)(*args, **kwargs) + updates = self._get_changes(original, objinst) + updates['obj_what_changed'] = objinst.obj_what_changed() + return updates, result + + def object_class_action(self, context, objname, objmethod, objver, + args, kwargs): + objname = six.text_type(objname) + objmethod = six.text_type(objmethod) + objver = six.text_type(objver) + args = self._ser.deserialize_entity( + None, self._ser.serialize_entity(None, args)) + kwargs = self._ser.deserialize_entity( + None, self._ser.serialize_entity(None, kwargs)) + cls = base.NovaObject.obj_class_from_name(objname, objver) + with mock.patch('nova.objects.base.NovaObject.' + 'indirection_api', new=None): + result = getattr(cls, objmethod)(context, *args, **kwargs) + return (base.NovaObject.obj_from_primitive( + result.obj_to_primitive(target_version=objver), + context=context) + if isinstance(result, base.NovaObject) else result) + + +class IndirectionFixture(fixtures.Fixture): + def setUp(self): + super(IndirectionFixture, self).setUp() + ser = base.NovaObjectSerializer() + self.indirection_api = FakeIndirectionHack(serializer=ser) + self.useFixture(fixtures.MonkeyPatch( + 'nova.objects.base.NovaObject.indirection_api', + self.indirection_api)) + + class _RemoteTest(_BaseTestCase): - def _testable_conductor(self): - self.conductor_service = self.start_service( - 'conductor', manager='nova.conductor.manager.ConductorManager') - self.remote_object_calls = list() - - orig_object_class_action = \ - self.conductor_service.manager.object_class_action - orig_object_action = \ - self.conductor_service.manager.object_action - - def fake_object_class_action(*args, **kwargs): - self.remote_object_calls.append((kwargs.get('objname'), - kwargs.get('objmethod'))) - with things_temporarily_local(): - result = orig_object_class_action(*args, **kwargs) - return (base.NovaObject.obj_from_primitive(result, context=args[0]) - if isinstance(result, base.NovaObject) else result) - self.stubs.Set(self.conductor_service.manager, 'object_class_action', - fake_object_class_action) - - def fake_object_action(*args, **kwargs): - self.remote_object_calls.append((kwargs.get('objinst'), - kwargs.get('objmethod'))) - with things_temporarily_local(): - result = orig_object_action(*args, **kwargs) - return result - self.stubs.Set(self.conductor_service.manager, 'object_action', - fake_object_action) - - # Things are remoted by default in this session - self.useFixture(nova_fixtures.IndirectionAPIFixture( - conductor_rpcapi.ConductorAPI())) - - # To make sure local and remote contexts match - self.stubs.Set(rpc.RequestContextSerializer, - 'serialize_context', - lambda s, c: c) - self.stubs.Set(rpc.RequestContextSerializer, - 'deserialize_context', - lambda s, c: c) - def setUp(self): super(_RemoteTest, self).setUp() - self._testable_conductor() + self.useFixture(IndirectionFixture()) class _TestObject(object): @@ -494,6 +453,7 @@ class _TestObject(object): self.assertEqual(obj.bar, 'loaded!') def test_load_in_base(self): + @base.NovaObjectRegistry.register_if(False) class Foo(base.NovaObject): fields = {'foobar': fields.IntegerField()} obj = Foo() @@ -586,6 +546,7 @@ class _TestObject(object): self.assertIsInstance(obj.rel_object, MyOwnedObject) def test_changed_with_sub_object(self): + @base.NovaObjectRegistry.register_if(False) class ParentObject(base.NovaObject): fields = {'foo': fields.IntegerField(), 'bar': fields.ObjectField('MyObj'), @@ -734,6 +695,7 @@ class _TestObject(object): self.assertEqual({}, obj.obj_get_changes()) def test_obj_fields(self): + @base.NovaObjectRegistry.register_if(False) class TestObj(base.NovaObject): fields = {'foo': fields.IntegerField()} obj_extra_fields = ['bar'] @@ -754,7 +716,7 @@ class _TestObject(object): def test_obj_read_only(self): obj = MyObj(context=self.context, foo=123, bar='abc') obj.readonly = 1 - self.assertRaises(exception.ReadOnlyFieldError, setattr, + self.assertRaises(ovo_exc.ReadOnlyFieldError, setattr, obj, 'readonly', 2) def test_obj_mutable_default(self): @@ -853,12 +815,14 @@ class _TestObject(object): obj.obj_make_compatible, {}, '1.0') def test_obj_make_compatible_doesnt_skip_falsey_sub_objects(self): + @base.NovaObjectRegistry.register_if(False) class MyList(base.ObjectListBase, base.NovaObject): VERSION = '1.2' fields = {'objects': fields.ListOfObjectsField('MyObjElement')} mylist = MyList(objects=[]) + @base.NovaObjectRegistry.register_if(False) class MyOwner(base.NovaObject): VERSION = '1.2' fields = {'mylist': fields.ObjectField('MyList')} @@ -978,6 +942,8 @@ class TestObjectSerializer(_BaseTestCase): class MyTestObj(MyObj): VERSION = my_version + base.NovaObjectRegistry.register(MyTestObj) + obj = MyTestObj() obj.VERSION = obj_version primitive = obj.obj_to_primitive() @@ -1135,8 +1101,6 @@ object_data = { 'KeyPairList': '1.2-60f984184dc5a8eba6e34e20cbabef04', 'Migration': '1.2-331b1f37d0b20b932614181b9832c860', 'MigrationList': '1.2-5e79c0693d7ebe4e9ac03b5db11ab243', - 'MyObj': '1.6-ee7b607402fbfb3390a92ab7199e0d88', - 'MyOwnedObject': '1.0-fec853730bd02d54cc32771dd67f08a0', 'NUMACell': '1.2-74fc993ac5c83005e76e34e8487f1c05', 'NUMAPagesTopology': '1.0-c71d86317283266dc8364c149155e48e', 'NUMATopology': '1.2-c63fad38be73b6afd04715c9c1b29220', @@ -1160,7 +1124,6 @@ object_data = { 'ServiceList': '1.10-2f49ab65571c0edcbf623f664da612c0', 'Tag': '1.0-616bf44af4a22e853c17b37a758ec73e', 'TagList': '1.0-e16d65894484b7530b720792ffbbbd02', - 'TestSubclassedObject': '1.6-716fc8b481c9374f7e222de03ba0a621', 'VirtCPUFeature': '1.0-3310718d8c72309259a6e39bdefe83ee', 'VirtCPUModel': '1.0-6a5cc9f322729fc70ddc6733bacd57d3', 'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563', @@ -1191,12 +1154,10 @@ object_relationships = { 'InstanceNUMACell': {'VirtCPUTopology': '1.0'}, 'InstanceNUMATopology': {'InstanceNUMACell': '1.2'}, 'InstancePCIRequests': {'InstancePCIRequest': '1.1'}, - 'MyObj': {'MyOwnedObject': '1.0'}, 'NUMACell': {'NUMAPagesTopology': '1.0'}, 'NUMATopology': {'NUMACell': '1.2'}, 'SecurityGroupRule': {'SecurityGroup': '1.1'}, 'Service': {'ComputeNode': '1.11'}, - 'TestSubclassedObject': {'MyOwnedObject': '1.0'}, 'VirtCPUModel': {'VirtCPUFeature': '1.0', 'VirtCPUTopology': '1.0'}, } @@ -1218,7 +1179,8 @@ class TestObjectVersions(test.NoDBTestCase): return None def _get_fingerprint(self, obj_name): - obj_class = base.NovaObject._obj_classes[obj_name][0] + obj_classes = base.NovaObjectRegistry.obj_classes() + obj_class = obj_classes[obj_name][0] fields = obj_class.fields.items() fields.sort() methods = [] @@ -1246,7 +1208,8 @@ class TestObjectVersions(test.NoDBTestCase): def test_versions(self): fingerprints = {} - for obj_name in base.NovaObject._obj_classes: + obj_classes = base.NovaObjectRegistry.obj_classes() + for obj_name in obj_classes: fingerprints[obj_name] = self._get_fingerprint(obj_name) if os.getenv('GENERATE_HASHES'): @@ -1269,15 +1232,6 @@ class TestObjectVersions(test.NoDBTestCase): 'versions have been bumped, and then update their ' 'hashes here.') - def test_registry_matches_metaclass(self): - reference = set(object_data.keys()) - actual = set(base.NovaObjectRegistry.classes) - test_objects = set(['MyObj', 'MyOwnedObject', 'TestSubclassedObject']) - # NOTE(danms): In the new registry, we don't implicitly track test - # objects, so make sure that the difference between the metaclass and - # the opt-in registry is the set of test objects. - self.assertEqual(test_objects, reference.symmetric_difference(actual)) - def _get_object_field_name(self, field): if isinstance(field._type, fields.Object): return field._type._obj_name @@ -1290,6 +1244,7 @@ class TestObjectVersions(test.NoDBTestCase): if obj_name in tree: return + obj_classes = base.NovaObjectRegistry.obj_classes() for name, field in obj_class.fields.items(): # Notes(yjiang5): ObjectListBase should be covered by # child_versions test @@ -1298,15 +1253,16 @@ class TestObjectVersions(test.NoDBTestCase): continue sub_obj_name = self._get_object_field_name(field) if sub_obj_name: - sub_obj_class = base.NovaObject._obj_classes[sub_obj_name][0] + sub_obj_class = obj_classes[sub_obj_name][0] self._build_tree(tree, sub_obj_class) tree.setdefault(obj_name, {}) tree[obj_name][sub_obj_name] = sub_obj_class.VERSION def test_relationships(self): tree = {} - for obj_name in base.NovaObject._obj_classes.keys(): - self._build_tree(tree, base.NovaObject._obj_classes[obj_name][0]) + obj_classes = base.NovaObjectRegistry.obj_classes() + for obj_name in obj_classes.keys(): + self._build_tree(tree, obj_classes[obj_name][0]) stored = set([(x, str(y)) for x, y in object_relationships.items()]) computed = set([(x, str(y)) for x, y in tree.items()]) @@ -1329,8 +1285,9 @@ class TestObjectVersions(test.NoDBTestCase): # This doesn't actually test the data conversions, but it at least # makes sure the method doesn't blow up on something basic like # expecting the wrong version format. - for obj_name in base.NovaObject._obj_classes: - obj_class = base.NovaObject._obj_classes[obj_name][0] + obj_classes = base.NovaObjectRegistry.obj_classes() + for obj_name in obj_classes: + obj_class = obj_classes[obj_name][0] version = utils.convert_version_to_tuple(obj_class.VERSION) for n in range(version[1]): test_version = '%d.%d' % (version[0], n) @@ -1340,10 +1297,11 @@ class TestObjectVersions(test.NoDBTestCase): def _get_obj_to_test(self, obj_class): obj = obj_class() + obj_classes = base.NovaObjectRegistry.obj_classes() for fname, ftype in obj.fields.items(): if isinstance(ftype, fields.ObjectField): fobjname = ftype.AUTO_TYPE._obj_name - fobjcls = base.NovaObject._obj_classes[fobjname][0] + fobjcls = obj_classes[fobjname][0] setattr(obj, fname, self._get_obj_to_test(fobjcls)) elif isinstance(ftype, fields.ListOfObjectsField): # FIXME(danms): This will result in no tests for this @@ -1385,8 +1343,9 @@ class TestObjectVersions(test.NoDBTestCase): # This doesn't actually test the data conversions, but it at least # makes sure the method doesn't blow up on something basic like # expecting the wrong version format. - for obj_name in base.NovaObject._obj_classes: - obj_class = base.NovaObject._obj_classes[obj_name][0] + obj_classes = base.NovaObjectRegistry.obj_classes() + for obj_name in obj_classes: + obj_class = obj_classes[obj_name][0] if 'tests.unit' in obj_class.__module__: # NOTE(danms): Skip test objects. When we move to # oslo.versionedobjects, we won't have to do this @@ -1407,8 +1366,9 @@ class TestObjectVersions(test.NoDBTestCase): # This doesn't actually test the data conversions, but it at least # makes sure the method doesn't blow up on something basic like # expecting the wrong version format. - for obj_name in base.NovaObject._obj_classes: - obj_class = base.NovaObject._obj_classes[obj_name][0] + obj_classes = base.NovaObjectRegistry.obj_classes() + for obj_name in obj_classes: + obj_class = obj_classes[obj_name][0] for field, versions in obj_class.obj_relationships.items(): last_my_version = (0, 0) last_child_version = (0, 0)