Replace metaclass registry with explicit opt-in registry from oslo
This patch replaces Nova's magical metaclass object registry with the one from oslo_versionedobjects. Most of the changes here are related to test objects which previously (and annoyingly) were automatically registered. It takes a more explicit action to register these now, but also means we don't have to worry about the version hash tracking picking up such objects. There is a change to nova.exception here to copy the readonly exception from the library, which is purely a tactic to reduce the amount of stuff we have to change in this patch. The next patch will remove that and do the necessary test conversions. Related to blueprint use-oslo-objects Change-Id: If3465f995dd05d2c01f01469b3ad9166c8362e8c
This commit is contained in:
parent
3a037abdb0
commit
d95c05a8fd
|
@ -28,6 +28,7 @@ import sys
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
|
from oslo_versionedobjects import exception as ovo_exc
|
||||||
import six
|
import six
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
|
@ -1509,8 +1510,9 @@ class IncompatibleObjectVersion(NovaException):
|
||||||
'maximum supported version is: %(supported)s')
|
'maximum supported version is: %(supported)s')
|
||||||
|
|
||||||
|
|
||||||
class ReadOnlyFieldError(NovaException):
|
# FIXME(danms): Remove this and convert existing object tests to catch
|
||||||
msg_fmt = _('Cannot modify readonly field %(field)s')
|
# the oslo_versionedobjects exception themselves
|
||||||
|
ReadOnlyFieldError = ovo_exc.ReadOnlyFieldError
|
||||||
|
|
||||||
|
|
||||||
class ObjectActionError(NovaException):
|
class ObjectActionError(NovaException):
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
"""Nova common internal object model"""
|
"""Nova common internal object model"""
|
||||||
|
|
||||||
import collections
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -42,115 +41,16 @@ LOG = logging.getLogger('object')
|
||||||
|
|
||||||
def get_attrname(name):
|
def get_attrname(name):
|
||||||
"""Return the mangled name of the attribute's underlying storage."""
|
"""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):
|
class NovaObjectRegistry(ovoo_base.VersionedObjectRegistry):
|
||||||
# NOTE(danms/comstud): Inherit fields from super classes.
|
def registration_hook(self, cls, index):
|
||||||
# mro() returns the current class first and returns 'object' last, so
|
# NOTE(danms): Set the *latest* version of this class
|
||||||
# those can be skipped. Also be careful to not overwrite any fields
|
newest = self._registry._obj_classes[cls.obj_name()][0]
|
||||||
# that already exist. And make sure each cls has its own copy of
|
setattr(objects, cls.obj_name(), newest)
|
||||||
# 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)
|
|
||||||
|
|
||||||
|
|
||||||
# These are decorators that mark an object's method as remotable.
|
# These are decorators that mark an object's method as remotable.
|
||||||
|
@ -220,7 +120,6 @@ def remotable(fn):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(NovaObjectMetaclass)
|
|
||||||
class NovaObject(object):
|
class NovaObject(object):
|
||||||
"""Base class and object factory.
|
"""Base class and object factory.
|
||||||
|
|
||||||
|
@ -279,6 +178,9 @@ class NovaObject(object):
|
||||||
# since they were not added until version 1.2.
|
# since they were not added until version 1.2.
|
||||||
obj_relationships = {}
|
obj_relationships = {}
|
||||||
|
|
||||||
|
# Temporary until we inherit from o.vo.base.VersionedObject
|
||||||
|
indirection_api = None
|
||||||
|
|
||||||
def __init__(self, context=None, **kwargs):
|
def __init__(self, context=None, **kwargs):
|
||||||
self._changed_fields = set()
|
self._changed_fields = set()
|
||||||
self._context = context
|
self._context = context
|
||||||
|
@ -304,7 +206,7 @@ class NovaObject(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def obj_class_from_name(cls, objname, objver):
|
def obj_class_from_name(cls, objname, objver):
|
||||||
"""Returns a class from the registry based on a name and version."""
|
"""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 '
|
LOG.error(_LE('Unable to instantiate unregistered object type '
|
||||||
'%(objtype)s'), dict(objtype=objname))
|
'%(objtype)s'), dict(objtype=objname))
|
||||||
raise exception.UnsupportedObjectError(objtype=objname)
|
raise exception.UnsupportedObjectError(objtype=objname)
|
||||||
|
@ -315,7 +217,8 @@ class NovaObject(object):
|
||||||
# once below.
|
# once below.
|
||||||
compatible_match = None
|
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:
|
if objclass.VERSION == objver:
|
||||||
return objclass
|
return objclass
|
||||||
if (not compatible_match and
|
if (not compatible_match and
|
||||||
|
@ -326,7 +229,7 @@ class NovaObject(object):
|
||||||
return compatible_match
|
return compatible_match
|
||||||
|
|
||||||
# As mentioned above, latest version is always first in the list.
|
# 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,
|
raise exception.IncompatibleObjectVersion(objname=objname,
|
||||||
objver=objver,
|
objver=objver,
|
||||||
supported=latest_ver)
|
supported=latest_ver)
|
||||||
|
|
|
@ -233,7 +233,7 @@ class TestCase(testtools.TestCase):
|
||||||
# registry.
|
# registry.
|
||||||
objects_base.NovaObject.indirection_api = None
|
objects_base.NovaObject.indirection_api = None
|
||||||
self._base_test_obj_backup = copy.copy(
|
self._base_test_obj_backup = copy.copy(
|
||||||
objects_base.NovaObject._obj_classes)
|
objects_base.NovaObjectRegistry._registry._obj_classes)
|
||||||
self.addCleanup(self._restore_obj_registry)
|
self.addCleanup(self._restore_obj_registry)
|
||||||
|
|
||||||
# NOTE(mnaser): All calls to utils.is_neutron() are cached in
|
# NOTE(mnaser): All calls to utils.is_neutron() are cached in
|
||||||
|
@ -251,7 +251,8 @@ class TestCase(testtools.TestCase):
|
||||||
self.useFixture(nova_fixtures.PoisonFunctions())
|
self.useFixture(nova_fixtures.PoisonFunctions())
|
||||||
|
|
||||||
def _restore_obj_registry(self):
|
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):
|
def _clear_attrs(self):
|
||||||
# Delete attributes that don't start with _ so they don't pin
|
# Delete attributes that don't start with _ so they don't pin
|
||||||
|
|
|
@ -296,6 +296,8 @@ class CellsMessageClassesTestCase(test.TestCase):
|
||||||
"""
|
"""
|
||||||
fields = {'test': objects_fields.StringField()}
|
fields = {'test': objects_fields.StringField()}
|
||||||
|
|
||||||
|
objects_base.NovaObjectRegistry.register(CellsMsgingTestObject)
|
||||||
|
|
||||||
test_obj = CellsMsgingTestObject()
|
test_obj = CellsMsgingTestObject()
|
||||||
test_obj.test = 'meow'
|
test_obj.test = 'meow'
|
||||||
|
|
||||||
|
|
|
@ -420,6 +420,8 @@ class ConductorTestCase(_BaseTestCase, test.TestCase):
|
||||||
else:
|
else:
|
||||||
return 'test'
|
return 'test'
|
||||||
|
|
||||||
|
obj_base.NovaObjectRegistry.register(TestObject)
|
||||||
|
|
||||||
obj = TestObject()
|
obj = TestObject()
|
||||||
# NOTE(danms): After a trip over RPC, any tuple will be a list,
|
# 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
|
# 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.dict['foo'] = 'bar'
|
||||||
self.obj_reset_changes()
|
self.obj_reset_changes()
|
||||||
|
|
||||||
|
obj_base.NovaObjectRegistry.register(TestObject)
|
||||||
|
|
||||||
obj = TestObject()
|
obj = TestObject()
|
||||||
obj.dict = {}
|
obj.dict = {}
|
||||||
obj.obj_reset_changes()
|
obj.obj_reset_changes()
|
||||||
|
|
|
@ -725,6 +725,9 @@ class TestObject(TestField):
|
||||||
class OtherTestableObject(obj_base.NovaObject):
|
class OtherTestableObject(obj_base.NovaObject):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
obj_base.NovaObjectRegistry.register(TestableObject)
|
||||||
|
obj_base.NovaObjectRegistry.register(OtherTestableObject)
|
||||||
|
|
||||||
test_inst = TestableObject()
|
test_inst = TestableObject()
|
||||||
self._test_cls = TestableObject
|
self._test_cls = TestableObject
|
||||||
self.field = fields.Field(fields.Object('TestableObject'))
|
self.field = fields.Field(fields.Object('TestableObject'))
|
||||||
|
|
|
@ -199,10 +199,10 @@ class _TestInstanceObject(object):
|
||||||
).AndReturn(fake_inst2)
|
).AndReturn(fake_inst2)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
inst = instance.Instance.get_by_uuid(self.context, fake_uuid)
|
inst = instance.Instance.get_by_uuid(self.context, fake_uuid)
|
||||||
self.assertFalse(hasattr(inst, '_metadata'))
|
self.assertFalse(hasattr(inst, '_obj_metadata'))
|
||||||
meta = inst.metadata
|
meta = inst.metadata
|
||||||
self.assertEqual(meta, {'foo': 'bar'})
|
self.assertEqual(meta, {'foo': 'bar'})
|
||||||
self.assertTrue(hasattr(inst, '_metadata'))
|
self.assertTrue(hasattr(inst, '_obj_metadata'))
|
||||||
# Make sure we don't run load again
|
# Make sure we don't run load again
|
||||||
meta2 = inst.metadata
|
meta2 = inst.metadata
|
||||||
self.assertEqual(meta2, {'foo': 'bar'})
|
self.assertEqual(meta2, {'foo': 'bar'})
|
||||||
|
@ -1330,6 +1330,8 @@ class TestRemoteInstanceObject(test_objects._RemoteTest,
|
||||||
class OldInstance(objects.Instance):
|
class OldInstance(objects.Instance):
|
||||||
VERSION = '1.17'
|
VERSION = '1.17'
|
||||||
|
|
||||||
|
base.NovaObjectRegistry.register(OldInstance)
|
||||||
|
|
||||||
inst = OldInstance.get_by_uuid(self.context, inst.uuid)
|
inst = OldInstance.get_by_uuid(self.context, inst.uuid)
|
||||||
self.assertFalse(inst.obj_attr_is_set('system_metadata'))
|
self.assertFalse(inst.obj_attr_is_set('system_metadata'))
|
||||||
self.assertEqual('bar', inst.system_metadata['foo'])
|
self.assertEqual('bar', inst.system_metadata['foo'])
|
||||||
|
@ -1348,6 +1350,8 @@ class TestRemoteInstanceObject(test_objects._RemoteTest,
|
||||||
class OldInstance(objects.Instance):
|
class OldInstance(objects.Instance):
|
||||||
VERSION = '1.17'
|
VERSION = '1.17'
|
||||||
|
|
||||||
|
base.NovaObjectRegistry.register(OldInstance)
|
||||||
|
|
||||||
inst = OldInstance.get_by_uuid(self.context, inst.uuid,
|
inst = OldInstance.get_by_uuid(self.context, inst.uuid,
|
||||||
expected_attrs=['system_metadata'])
|
expected_attrs=['system_metadata'])
|
||||||
self.assertTrue(inst.obj_attr_is_set('system_metadata'))
|
self.assertTrue(inst.obj_attr_is_set('system_metadata'))
|
||||||
|
|
|
@ -21,19 +21,20 @@ import inspect
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
|
import fixtures
|
||||||
import mock
|
import mock
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
|
from oslo_versionedobjects import exception as ovo_exc
|
||||||
|
from oslo_versionedobjects import fixture
|
||||||
import six
|
import six
|
||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
|
|
||||||
from nova.conductor import rpcapi as conductor_rpcapi
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.objects import base
|
from nova.objects import base
|
||||||
from nova.objects import fields
|
from nova.objects import fields
|
||||||
from nova import rpc
|
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests import fixtures as nova_fixtures
|
from nova.tests import fixtures as nova_fixtures
|
||||||
from nova.tests.unit import fake_notifier
|
from nova.tests.unit import fake_notifier
|
||||||
|
@ -122,6 +123,10 @@ class MyObjDiffVers(MyObj):
|
||||||
|
|
||||||
|
|
||||||
class MyObj2(object):
|
class MyObj2(object):
|
||||||
|
fields = {
|
||||||
|
'bar': fields.StringField(),
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def obj_name(cls):
|
def obj_name(cls):
|
||||||
return 'MyObj'
|
return 'MyObj'
|
||||||
|
@ -136,78 +141,15 @@ class RandomMixInWithNoFields(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class TestSubclassedObject(RandomMixInWithNoFields, MyObj):
|
class TestSubclassedObject(RandomMixInWithNoFields, MyObj):
|
||||||
fields = {'new_field': fields.StringField()}
|
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):
|
class TestObjToPrimitive(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_obj_to_primitive_list(self):
|
def test_obj_to_primitive_list(self):
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class MyObjElement(base.NovaObject):
|
class MyObjElement(base.NovaObject):
|
||||||
fields = {'foo': fields.IntegerField()}
|
fields = {'foo': fields.IntegerField()}
|
||||||
|
|
||||||
|
@ -215,6 +157,7 @@ class TestObjToPrimitive(test.NoDBTestCase):
|
||||||
super(MyObjElement, self).__init__()
|
super(MyObjElement, self).__init__()
|
||||||
self.foo = foo
|
self.foo = foo
|
||||||
|
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class MyList(base.ObjectListBase, base.NovaObject):
|
class MyList(base.ObjectListBase, base.NovaObject):
|
||||||
fields = {'objects': fields.ListOfObjectsField('MyObjElement')}
|
fields = {'objects': fields.ListOfObjectsField('MyObjElement')}
|
||||||
|
|
||||||
|
@ -224,11 +167,14 @@ class TestObjToPrimitive(test.NoDBTestCase):
|
||||||
[x['foo'] for x in base.obj_to_primitive(mylist)])
|
[x['foo'] for x in base.obj_to_primitive(mylist)])
|
||||||
|
|
||||||
def test_obj_to_primitive_dict(self):
|
def test_obj_to_primitive_dict(self):
|
||||||
|
base.NovaObjectRegistry.register(MyObj)
|
||||||
myobj = MyObj(foo=1, bar='foo')
|
myobj = MyObj(foo=1, bar='foo')
|
||||||
self.assertEqual({'foo': 1, 'bar': 'foo'},
|
self.assertEqual({'foo': 1, 'bar': 'foo'},
|
||||||
base.obj_to_primitive(myobj))
|
base.obj_to_primitive(myobj))
|
||||||
|
|
||||||
def test_obj_to_primitive_recursive(self):
|
def test_obj_to_primitive_recursive(self):
|
||||||
|
base.NovaObjectRegistry.register(MyObj)
|
||||||
|
|
||||||
class MyList(base.ObjectListBase, base.NovaObject):
|
class MyList(base.ObjectListBase, base.NovaObject):
|
||||||
fields = {'objects': fields.ListOfObjectsField('MyObj')}
|
fields = {'objects': fields.ListOfObjectsField('MyObj')}
|
||||||
|
|
||||||
|
@ -239,6 +185,7 @@ class TestObjToPrimitive(test.NoDBTestCase):
|
||||||
base.obj_to_primitive(mylist))
|
base.obj_to_primitive(mylist))
|
||||||
|
|
||||||
def test_obj_to_primitive_with_ip_addr(self):
|
def test_obj_to_primitive_with_ip_addr(self):
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class TestObject(base.NovaObject):
|
class TestObject(base.NovaObject):
|
||||||
fields = {'addr': fields.IPAddressField(),
|
fields = {'addr': fields.IPAddressField(),
|
||||||
'cidr': fields.IPNetworkField()}
|
'cidr': fields.IPNetworkField()}
|
||||||
|
@ -316,6 +263,12 @@ class _BaseTestCase(test.TestCase):
|
||||||
fake_notifier.stub_notifier(self.stubs)
|
fake_notifier.stub_notifier(self.stubs)
|
||||||
self.addCleanup(fake_notifier.reset)
|
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,
|
def compare_obj(self, obj, db_obj, subs=None, allow_missing=None,
|
||||||
comparators=None):
|
comparators=None):
|
||||||
compare_obj(self, obj, db_obj, subs=subs, allow_missing=allow_missing,
|
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
|
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):
|
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):
|
def setUp(self):
|
||||||
super(_RemoteTest, self).setUp()
|
super(_RemoteTest, self).setUp()
|
||||||
self._testable_conductor()
|
self.useFixture(IndirectionFixture())
|
||||||
|
|
||||||
|
|
||||||
class _TestObject(object):
|
class _TestObject(object):
|
||||||
|
@ -494,6 +453,7 @@ class _TestObject(object):
|
||||||
self.assertEqual(obj.bar, 'loaded!')
|
self.assertEqual(obj.bar, 'loaded!')
|
||||||
|
|
||||||
def test_load_in_base(self):
|
def test_load_in_base(self):
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class Foo(base.NovaObject):
|
class Foo(base.NovaObject):
|
||||||
fields = {'foobar': fields.IntegerField()}
|
fields = {'foobar': fields.IntegerField()}
|
||||||
obj = Foo()
|
obj = Foo()
|
||||||
|
@ -586,6 +546,7 @@ class _TestObject(object):
|
||||||
self.assertIsInstance(obj.rel_object, MyOwnedObject)
|
self.assertIsInstance(obj.rel_object, MyOwnedObject)
|
||||||
|
|
||||||
def test_changed_with_sub_object(self):
|
def test_changed_with_sub_object(self):
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class ParentObject(base.NovaObject):
|
class ParentObject(base.NovaObject):
|
||||||
fields = {'foo': fields.IntegerField(),
|
fields = {'foo': fields.IntegerField(),
|
||||||
'bar': fields.ObjectField('MyObj'),
|
'bar': fields.ObjectField('MyObj'),
|
||||||
|
@ -734,6 +695,7 @@ class _TestObject(object):
|
||||||
self.assertEqual({}, obj.obj_get_changes())
|
self.assertEqual({}, obj.obj_get_changes())
|
||||||
|
|
||||||
def test_obj_fields(self):
|
def test_obj_fields(self):
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class TestObj(base.NovaObject):
|
class TestObj(base.NovaObject):
|
||||||
fields = {'foo': fields.IntegerField()}
|
fields = {'foo': fields.IntegerField()}
|
||||||
obj_extra_fields = ['bar']
|
obj_extra_fields = ['bar']
|
||||||
|
@ -754,7 +716,7 @@ class _TestObject(object):
|
||||||
def test_obj_read_only(self):
|
def test_obj_read_only(self):
|
||||||
obj = MyObj(context=self.context, foo=123, bar='abc')
|
obj = MyObj(context=self.context, foo=123, bar='abc')
|
||||||
obj.readonly = 1
|
obj.readonly = 1
|
||||||
self.assertRaises(exception.ReadOnlyFieldError, setattr,
|
self.assertRaises(ovo_exc.ReadOnlyFieldError, setattr,
|
||||||
obj, 'readonly', 2)
|
obj, 'readonly', 2)
|
||||||
|
|
||||||
def test_obj_mutable_default(self):
|
def test_obj_mutable_default(self):
|
||||||
|
@ -853,12 +815,14 @@ class _TestObject(object):
|
||||||
obj.obj_make_compatible, {}, '1.0')
|
obj.obj_make_compatible, {}, '1.0')
|
||||||
|
|
||||||
def test_obj_make_compatible_doesnt_skip_falsey_sub_objects(self):
|
def test_obj_make_compatible_doesnt_skip_falsey_sub_objects(self):
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class MyList(base.ObjectListBase, base.NovaObject):
|
class MyList(base.ObjectListBase, base.NovaObject):
|
||||||
VERSION = '1.2'
|
VERSION = '1.2'
|
||||||
fields = {'objects': fields.ListOfObjectsField('MyObjElement')}
|
fields = {'objects': fields.ListOfObjectsField('MyObjElement')}
|
||||||
|
|
||||||
mylist = MyList(objects=[])
|
mylist = MyList(objects=[])
|
||||||
|
|
||||||
|
@base.NovaObjectRegistry.register_if(False)
|
||||||
class MyOwner(base.NovaObject):
|
class MyOwner(base.NovaObject):
|
||||||
VERSION = '1.2'
|
VERSION = '1.2'
|
||||||
fields = {'mylist': fields.ObjectField('MyList')}
|
fields = {'mylist': fields.ObjectField('MyList')}
|
||||||
|
@ -978,6 +942,8 @@ class TestObjectSerializer(_BaseTestCase):
|
||||||
class MyTestObj(MyObj):
|
class MyTestObj(MyObj):
|
||||||
VERSION = my_version
|
VERSION = my_version
|
||||||
|
|
||||||
|
base.NovaObjectRegistry.register(MyTestObj)
|
||||||
|
|
||||||
obj = MyTestObj()
|
obj = MyTestObj()
|
||||||
obj.VERSION = obj_version
|
obj.VERSION = obj_version
|
||||||
primitive = obj.obj_to_primitive()
|
primitive = obj.obj_to_primitive()
|
||||||
|
@ -1135,8 +1101,6 @@ object_data = {
|
||||||
'KeyPairList': '1.2-60f984184dc5a8eba6e34e20cbabef04',
|
'KeyPairList': '1.2-60f984184dc5a8eba6e34e20cbabef04',
|
||||||
'Migration': '1.2-331b1f37d0b20b932614181b9832c860',
|
'Migration': '1.2-331b1f37d0b20b932614181b9832c860',
|
||||||
'MigrationList': '1.2-5e79c0693d7ebe4e9ac03b5db11ab243',
|
'MigrationList': '1.2-5e79c0693d7ebe4e9ac03b5db11ab243',
|
||||||
'MyObj': '1.6-ee7b607402fbfb3390a92ab7199e0d88',
|
|
||||||
'MyOwnedObject': '1.0-fec853730bd02d54cc32771dd67f08a0',
|
|
||||||
'NUMACell': '1.2-74fc993ac5c83005e76e34e8487f1c05',
|
'NUMACell': '1.2-74fc993ac5c83005e76e34e8487f1c05',
|
||||||
'NUMAPagesTopology': '1.0-c71d86317283266dc8364c149155e48e',
|
'NUMAPagesTopology': '1.0-c71d86317283266dc8364c149155e48e',
|
||||||
'NUMATopology': '1.2-c63fad38be73b6afd04715c9c1b29220',
|
'NUMATopology': '1.2-c63fad38be73b6afd04715c9c1b29220',
|
||||||
|
@ -1160,7 +1124,6 @@ object_data = {
|
||||||
'ServiceList': '1.10-2f49ab65571c0edcbf623f664da612c0',
|
'ServiceList': '1.10-2f49ab65571c0edcbf623f664da612c0',
|
||||||
'Tag': '1.0-616bf44af4a22e853c17b37a758ec73e',
|
'Tag': '1.0-616bf44af4a22e853c17b37a758ec73e',
|
||||||
'TagList': '1.0-e16d65894484b7530b720792ffbbbd02',
|
'TagList': '1.0-e16d65894484b7530b720792ffbbbd02',
|
||||||
'TestSubclassedObject': '1.6-716fc8b481c9374f7e222de03ba0a621',
|
|
||||||
'VirtCPUFeature': '1.0-3310718d8c72309259a6e39bdefe83ee',
|
'VirtCPUFeature': '1.0-3310718d8c72309259a6e39bdefe83ee',
|
||||||
'VirtCPUModel': '1.0-6a5cc9f322729fc70ddc6733bacd57d3',
|
'VirtCPUModel': '1.0-6a5cc9f322729fc70ddc6733bacd57d3',
|
||||||
'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563',
|
'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563',
|
||||||
|
@ -1191,12 +1154,10 @@ object_relationships = {
|
||||||
'InstanceNUMACell': {'VirtCPUTopology': '1.0'},
|
'InstanceNUMACell': {'VirtCPUTopology': '1.0'},
|
||||||
'InstanceNUMATopology': {'InstanceNUMACell': '1.2'},
|
'InstanceNUMATopology': {'InstanceNUMACell': '1.2'},
|
||||||
'InstancePCIRequests': {'InstancePCIRequest': '1.1'},
|
'InstancePCIRequests': {'InstancePCIRequest': '1.1'},
|
||||||
'MyObj': {'MyOwnedObject': '1.0'},
|
|
||||||
'NUMACell': {'NUMAPagesTopology': '1.0'},
|
'NUMACell': {'NUMAPagesTopology': '1.0'},
|
||||||
'NUMATopology': {'NUMACell': '1.2'},
|
'NUMATopology': {'NUMACell': '1.2'},
|
||||||
'SecurityGroupRule': {'SecurityGroup': '1.1'},
|
'SecurityGroupRule': {'SecurityGroup': '1.1'},
|
||||||
'Service': {'ComputeNode': '1.11'},
|
'Service': {'ComputeNode': '1.11'},
|
||||||
'TestSubclassedObject': {'MyOwnedObject': '1.0'},
|
|
||||||
'VirtCPUModel': {'VirtCPUFeature': '1.0', 'VirtCPUTopology': '1.0'},
|
'VirtCPUModel': {'VirtCPUFeature': '1.0', 'VirtCPUTopology': '1.0'},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1218,7 +1179,8 @@ class TestObjectVersions(test.NoDBTestCase):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_fingerprint(self, obj_name):
|
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 = obj_class.fields.items()
|
||||||
fields.sort()
|
fields.sort()
|
||||||
methods = []
|
methods = []
|
||||||
|
@ -1246,7 +1208,8 @@ class TestObjectVersions(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_versions(self):
|
def test_versions(self):
|
||||||
fingerprints = {}
|
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)
|
fingerprints[obj_name] = self._get_fingerprint(obj_name)
|
||||||
|
|
||||||
if os.getenv('GENERATE_HASHES'):
|
if os.getenv('GENERATE_HASHES'):
|
||||||
|
@ -1269,15 +1232,6 @@ class TestObjectVersions(test.NoDBTestCase):
|
||||||
'versions have been bumped, and then update their '
|
'versions have been bumped, and then update their '
|
||||||
'hashes here.')
|
'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):
|
def _get_object_field_name(self, field):
|
||||||
if isinstance(field._type, fields.Object):
|
if isinstance(field._type, fields.Object):
|
||||||
return field._type._obj_name
|
return field._type._obj_name
|
||||||
|
@ -1290,6 +1244,7 @@ class TestObjectVersions(test.NoDBTestCase):
|
||||||
if obj_name in tree:
|
if obj_name in tree:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
for name, field in obj_class.fields.items():
|
for name, field in obj_class.fields.items():
|
||||||
# Notes(yjiang5): ObjectListBase should be covered by
|
# Notes(yjiang5): ObjectListBase should be covered by
|
||||||
# child_versions test
|
# child_versions test
|
||||||
|
@ -1298,15 +1253,16 @@ class TestObjectVersions(test.NoDBTestCase):
|
||||||
continue
|
continue
|
||||||
sub_obj_name = self._get_object_field_name(field)
|
sub_obj_name = self._get_object_field_name(field)
|
||||||
if sub_obj_name:
|
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)
|
self._build_tree(tree, sub_obj_class)
|
||||||
tree.setdefault(obj_name, {})
|
tree.setdefault(obj_name, {})
|
||||||
tree[obj_name][sub_obj_name] = sub_obj_class.VERSION
|
tree[obj_name][sub_obj_name] = sub_obj_class.VERSION
|
||||||
|
|
||||||
def test_relationships(self):
|
def test_relationships(self):
|
||||||
tree = {}
|
tree = {}
|
||||||
for obj_name in base.NovaObject._obj_classes.keys():
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
self._build_tree(tree, base.NovaObject._obj_classes[obj_name][0])
|
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()])
|
stored = set([(x, str(y)) for x, y in object_relationships.items()])
|
||||||
computed = set([(x, str(y)) for x, y in tree.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
|
# This doesn't actually test the data conversions, but it at least
|
||||||
# makes sure the method doesn't blow up on something basic like
|
# makes sure the method doesn't blow up on something basic like
|
||||||
# expecting the wrong version format.
|
# expecting the wrong version format.
|
||||||
for obj_name in base.NovaObject._obj_classes:
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
obj_class = base.NovaObject._obj_classes[obj_name][0]
|
for obj_name in obj_classes:
|
||||||
|
obj_class = obj_classes[obj_name][0]
|
||||||
version = utils.convert_version_to_tuple(obj_class.VERSION)
|
version = utils.convert_version_to_tuple(obj_class.VERSION)
|
||||||
for n in range(version[1]):
|
for n in range(version[1]):
|
||||||
test_version = '%d.%d' % (version[0], n)
|
test_version = '%d.%d' % (version[0], n)
|
||||||
|
@ -1340,10 +1297,11 @@ class TestObjectVersions(test.NoDBTestCase):
|
||||||
|
|
||||||
def _get_obj_to_test(self, obj_class):
|
def _get_obj_to_test(self, obj_class):
|
||||||
obj = obj_class()
|
obj = obj_class()
|
||||||
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
for fname, ftype in obj.fields.items():
|
for fname, ftype in obj.fields.items():
|
||||||
if isinstance(ftype, fields.ObjectField):
|
if isinstance(ftype, fields.ObjectField):
|
||||||
fobjname = ftype.AUTO_TYPE._obj_name
|
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))
|
setattr(obj, fname, self._get_obj_to_test(fobjcls))
|
||||||
elif isinstance(ftype, fields.ListOfObjectsField):
|
elif isinstance(ftype, fields.ListOfObjectsField):
|
||||||
# FIXME(danms): This will result in no tests for this
|
# 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
|
# This doesn't actually test the data conversions, but it at least
|
||||||
# makes sure the method doesn't blow up on something basic like
|
# makes sure the method doesn't blow up on something basic like
|
||||||
# expecting the wrong version format.
|
# expecting the wrong version format.
|
||||||
for obj_name in base.NovaObject._obj_classes:
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
obj_class = base.NovaObject._obj_classes[obj_name][0]
|
for obj_name in obj_classes:
|
||||||
|
obj_class = obj_classes[obj_name][0]
|
||||||
if 'tests.unit' in obj_class.__module__:
|
if 'tests.unit' in obj_class.__module__:
|
||||||
# NOTE(danms): Skip test objects. When we move to
|
# NOTE(danms): Skip test objects. When we move to
|
||||||
# oslo.versionedobjects, we won't have to do this
|
# 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
|
# This doesn't actually test the data conversions, but it at least
|
||||||
# makes sure the method doesn't blow up on something basic like
|
# makes sure the method doesn't blow up on something basic like
|
||||||
# expecting the wrong version format.
|
# expecting the wrong version format.
|
||||||
for obj_name in base.NovaObject._obj_classes:
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
obj_class = base.NovaObject._obj_classes[obj_name][0]
|
for obj_name in obj_classes:
|
||||||
|
obj_class = obj_classes[obj_name][0]
|
||||||
for field, versions in obj_class.obj_relationships.items():
|
for field, versions in obj_class.obj_relationships.items():
|
||||||
last_my_version = (0, 0)
|
last_my_version = (0, 0)
|
||||||
last_child_version = (0, 0)
|
last_child_version = (0, 0)
|
||||||
|
|
Loading…
Reference in New Issue