Merge "Replace metaclass registry with explicit opt-in registry from oslo"

This commit is contained in:
Jenkins 2015-06-15 10:04:25 +00:00 committed by Gerrit Code Review
commit 07c93c897e
8 changed files with 133 additions and 254 deletions

View File

@ -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
@ -1510,8 +1511,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):

View File

@ -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)

View File

@ -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

View File

@ -295,6 +295,8 @@ class CellsMessageClassesTestCase(test.TestCase):
"""
fields = {'test': objects_fields.StringField()}
objects_base.NovaObjectRegistry.register(CellsMsgingTestObject)
test_obj = CellsMsgingTestObject()
test_obj.test = 'meow'

View File

@ -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()

View File

@ -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'))

View File

@ -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'})
@ -1351,6 +1351,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'])
@ -1369,6 +1371,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'))

View File

@ -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.11-d1728430a30700c143e542b7c75f65b0',
'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)