]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Switch to oslo_versionedobjects
authorThang Pham <thang.g.pham@gmail.com>
Sun, 1 Mar 2015 22:46:02 +0000 (17:46 -0500)
committerThang Pham <thang.g.pham@gmail.com>
Wed, 13 May 2015 15:36:53 +0000 (08:36 -0700)
oslo_versionedobjects was not made available until very
late in the Kilo cycle (i.e. near the end of kilo-3).
In order to make progress on cinder objects, a fork of
nova objects was made, so that proper trial and testing
could be done. The following patch makes the switch to
use oslo_versionedobjects.

Implements: blueprint cinder-objects
Change-Id: I883f387c8247e8d79da82016a624cef2180cde88

14 files changed:
cinder/exception.py
cinder/objects/base.py
cinder/objects/fields.py [deleted file]
cinder/objects/snapshot.py
cinder/objects/volume.py
cinder/tests/unit/fake_snapshot.py
cinder/tests/unit/fake_volume.py
cinder/tests/unit/objects/test_fields.py [deleted file]
cinder/tests/unit/objects/test_objects.py [deleted file]
cinder/tests/unit/objects/test_snapshot.py
cinder/tests/unit/objects/test_volume.py
cinder/volume/api.py
cinder/volume/manager.py
requirements.txt

index 5ceab0990734c52e2a6bab361214f2e379d33064..5f29fe485f7c1cef083ea6445f4e8b5aceb451ab 100644 (file)
@@ -26,6 +26,7 @@ import sys
 
 from oslo_config import cfg
 from oslo_log import log as logging
+from oslo_versionedobjects import exception as obj_exc
 import six
 import webob.exc
 
@@ -663,28 +664,12 @@ class EvaluatorParseException(Exception):
     message = _("Error during evaluator parsing: %(reason)s")
 
 
-class ObjectActionError(CinderException):
-    msg_fmt = _('Object action %(action)s failed because: %(reason)s')
-
-
-class ObjectFieldInvalid(CinderException):
-    msg_fmt = _('Field %(field)s of %(objname)s is not an instance of Field')
-
-
-class UnsupportedObjectError(CinderException):
-    msg_fmt = _('Unsupported object type %(objtype)s')
-
-
-class OrphanedObjectError(CinderException):
-    msg_fmt = _('Cannot call %(method)s on orphaned %(objtype)s object')
-
-
-class IncompatibleObjectVersion(CinderException):
-    msg_fmt = _('Version %(objver)s of %(objname)s is not supported')
-
-
-class ReadOnlyFieldError(CinderException):
-    msg_fmt = _('Cannot modify readonly field %(field)s')
+UnsupportedObjectError = obj_exc.UnsupportedObjectError
+OrphanedObjectError = obj_exc.OrphanedObjectError
+IncompatibleObjectVersion = obj_exc.IncompatibleObjectVersion
+ReadOnlyFieldError = obj_exc.ReadOnlyFieldError
+ObjectActionError = obj_exc.ObjectActionError
+ObjectFieldInvalid = obj_exc.ObjectFieldInvalid
 
 
 class VolumeGroupNotFound(CinderException):
index 946f10c8e16d114c1dea213a2c6a77f8341b3062..dc984e44c739912bb13995ddfbe903981af908ee 100644 (file)
 
 """Cinder common internal object model"""
 
-import collections
 import contextlib
-import copy
 import datetime
 import functools
 import traceback
 
-import netaddr
 from oslo_log import log as logging
-import oslo_messaging as messaging
 from oslo_utils import timeutils
+from oslo_versionedobjects import base
+from oslo_versionedobjects import fields
 import six
 
-from cinder import context
 from cinder import exception
-from cinder.i18n import _, _LE
 from cinder import objects
-from cinder.objects import fields
-from cinder.openstack.common import versionutils
-from cinder import utils
 
 
 LOG = logging.getLogger('object')
+remotable = base.remotable
+remotable_classmethod = base.remotable_classmethod
+obj_make_list = base.obj_make_list
 
 
-class NotSpecifiedSentinel(object):
-    pass
-
-
-def get_attrname(name):
-    """Return the mangled name of the attribute's underlying storage."""
-    return '_' + 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 cls.fields.iteritems():
-        if not isinstance(field, 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
-
-        setattr(cls, name, property(getter, setter))
-
-
-class CinderObjectMetaclass(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 'CinderObject' 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 CinderObject._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 cinder.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
-                    # cinder.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 cinder.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.
-# If the metaclass is configured to forward object methods to an
-# indirection service, these will result in making an RPC call
-# instead of directly calling the implementation in the object. Instead,
-# the object implementation on the remote end will perform the
-# requested action and the result will be returned here.
-def remotable_classmethod(fn):
-    """Decorator for remotable classmethods."""
-    @functools.wraps(fn)
-    def wrapper(cls, context, *args, **kwargs):
-        if CinderObject.indirection_api:
-            result = CinderObject.indirection_api.object_class_action(
-                context, cls.obj_name(), fn.__name__, cls.VERSION,
-                args, kwargs)
-        else:
-            result = fn(cls, context, *args, **kwargs)
-            if isinstance(result, CinderObject):
-                result._context = context
-        return result
-
-    # NOTE(danms): Make this discoverable
-    wrapper.remotable = True
-    wrapper.original_fn = fn
-    return classmethod(wrapper)
-
-
-# See comment above for remotable_classmethod()
-#
-# Note that this will use either the provided context, or the one
-# stashed in the object. If neither are present, the object is
-# "orphaned" and remotable methods cannot be called.
-def remotable(fn):
-    """Decorator for remotable object methods."""
-    @functools.wraps(fn)
-    def wrapper(self, *args, **kwargs):
-        ctxt = self._context
-        try:
-            if isinstance(args[0], (context.RequestContext)):
-                ctxt = args[0]
-                args = args[1:]
-        except IndexError:
-            pass
-        if ctxt is None:
-            raise exception.OrphanedObjectError(method=fn.__name__,
-                                                objtype=self.obj_name())
-        # Force this to be set if it wasn't before.
-        self._context = ctxt
-        if CinderObject.indirection_api:
-            updates, result = CinderObject.indirection_api.object_action(
-                ctxt, self, fn.__name__, args, kwargs)
-            for key, value in updates.iteritems():
-                if key in self.fields:
-                    field = self.fields[key]
-                    # NOTE(ndipanov): Since CinderObjectSerializer will have
-                    # deserialized any object fields into objects already,
-                    # we do not try to deserialize them again here.
-                    if isinstance(value, CinderObject):
-                        self[key] = value
-                    else:
-                        self[key] = field.from_primitive(self, key, value)
-            self.obj_reset_changes()
-            self._changed_fields = set(updates.get('obj_what_changed', []))
-            return result
-        else:
-            return fn(self, ctxt, *args, **kwargs)
-
-    wrapper.remotable = True
-    wrapper.original_fn = fn
-    return wrapper
-
-
-@six.add_metaclass(CinderObjectMetaclass)
-class CinderObject(object):
-    """Base class and object factory.
-
-    This forms the base of all objects that can be remoted or instantiated
-    via RPC. Simply defining a class that inherits from this base class
-    will make it remotely instantiatable. Objects should implement the
-    necessary "get" classmethod routines as well as "save" object methods
-    as appropriate.
-    """
-
-    # Object versioning rules
-    #
-    # Each service has its set of objects, each with a version attached. When
-    # a client attempts to call an object method, the server checks to see if
-    # the version of that object matches (in a compatible way) its object
-    # implementation. If so, cool, and if not, fail.
-    #
-    # This version is allowed to have three parts, X.Y.Z, where the .Z element
-    # is reserved for stable branch backports. The .Z is ignored for the
-    # purposes of triggering a backport, which means anything changed under
-    # a .Z must be additive and non-destructive such that a node that knows
-    # about X.Y can consider X.Y.Z equivalent.
-    VERSION = '1.0'
-
-    # The fields present in this object as key:field pairs. For example:
-    #
-    # fields = { 'foo': fields.IntegerField(),
-    #            'bar': fields.StringField(),
-    #          }
-    fields = {}
-    obj_extra_fields = []
+class CinderObjectRegistry(base.VersionedObjectRegistry):
+    def registration_hook(self, cls, index):
+        setattr(objects, cls.obj_name(), cls)
 
-    # Table of sub-object versioning information
-    #
-    # This contains a list of version mappings, by the field name of
-    # the subobject. The mappings must be in order of oldest to
-    # newest, and are tuples of (my_version, subobject_version). A
-    # request to backport this object to $my_version will cause the
-    # subobject to be backported to $subobject_version.
-    #
-    # obj_relationships = {
-    #     'subobject1': [('1.2', '1.1'), ('1.4', '1.2')],
-    #     'subobject2': [('1.2', '1.0')],
-    # }
-    #
-    # In the above example:
-    #
-    # - If we are asked to backport our object to version 1.3,
-    #   subobject1 will be backported to version 1.1, since it was
-    #   bumped to version 1.2 when our version was 1.4.
-    # - If we are asked to backport our object to version 1.5,
-    #   no changes will be made to subobject1 or subobject2, since
-    #   they have not changed since version 1.4.
-    # - If we are asked to backlevel our object to version 1.1, we
-    #   will remove both subobject1 and subobject2 from the primitive,
-    #   since they were not added until version 1.2.
-    obj_relationships = {}
-
-    def __init__(self, context=None, **kwargs):
-        self._changed_fields = set()
-        self._context = context
-        for key in kwargs.keys():
-            setattr(self, key, kwargs[key])
-
-    def __repr__(self):
-        return '%s(%s)' % (
-            self.obj_name(),
-            ','.join(['%s=%s' % (name,
-                                 (self.obj_attr_is_set(name) and
-                                  field.stringify(getattr(self, name)) or
-                                  '<?>'))
-                      for name, field in sorted(self.fields.items())]))
-
-    @classmethod
-    def obj_name(cls):
-        """Return a canonical name for this object.
-
-        The canonical name will be used over the wire for remote hydration.
-        """
-        return cls.__name__
-
-    @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:
-            LOG.error(_LE('Unable to instantiate unregistered object type '
-                          '%(objtype)s'), dict(objtype=objname))
-            raise exception.UnsupportedObjectError(objtype=objname)
-
-        # NOTE(comstud): If there's not an exact match, return the highest
-        # compatible version. The objects stored in the class are sorted
-        # such that highest version is first, so only set compatible_match
-        # once below.
-        compatible_match = None
-
-        for objclass in cls._obj_classes[objname]:
-            if objclass.VERSION == objver:
-                return objclass
-            if (not compatible_match and
-                    versionutils.is_compatible(objver, objclass.VERSION)):
-                compatible_match = objclass
-
-        if compatible_match:
-            return compatible_match
-
-        # As mentioned above, latest version is always first in the list.
-        latest_ver = cls._obj_classes[objname][0].VERSION
-        raise exception.IncompatibleObjectVersion(objname=objname,
-                                                  objver=objver,
-                                                  supported=latest_ver)
-
-    @classmethod
-    def _obj_from_primitive(cls, context, objver, primitive):
-        self = cls()
-        self._context = context
-        self.VERSION = objver
-        objdata = primitive['cinder_object.data']
-        changes = primitive.get('cinder_object.changes', [])
-        for name, field in self.fields.items():
-            if name in objdata:
-                setattr(self, name, field.from_primitive(self, name,
-                                                         objdata[name]))
-        self._changed_fields = set([x for x in changes if x in self.fields])
-        return self
-
-    @classmethod
-    def obj_from_primitive(cls, primitive, context=None):
-        """Object field-by-field hydration."""
-        if primitive['cinder_object.namespace'] != 'cinder':
-            # NOTE(danms): We don't do anything with this now, but it's
-            # there for "the future"
-            raise exception.UnsupportedObjectError(
-                objtype='%s.%s' % (primitive['cinder_object.namespace'],
-                                   primitive['cinder_object.name']))
-        objname = primitive['cinder_object.name']
-        objver = primitive['cinder_object.version']
-        objclass = cls.obj_class_from_name(objname, objver)
-        return objclass._obj_from_primitive(context, objver, primitive)
-
-    def __deepcopy__(self, memo):
-        """Efficiently make a deep copy of this object."""
-
-        # NOTE(danms): A naive deepcopy would copy more than we need,
-        # and since we have knowledge of the volatile bits of the
-        # object, we can be smarter here. Also, nested entities within
-        # some objects may be uncopyable, so we can avoid those sorts
-        # of issues by copying only our field data.
-
-        nobj = self.__class__()
-        nobj._context = self._context
-        for name in self.fields:
-            if self.obj_attr_is_set(name):
-                nval = copy.deepcopy(getattr(self, name), memo)
-                setattr(nobj, name, nval)
-        nobj._changed_fields = set(self._changed_fields)
-        return nobj
-
-    def obj_clone(self):
-        """Create a copy."""
-        return copy.deepcopy(self)
-
-    def _obj_make_obj_compatible(self, primitive, target_version, field):
-        """Backlevel a sub-object based on our versioning rules.
-
-        This is responsible for backporting objects contained within
-        this object's primitive according to a set of rules we
-        maintain about version dependencies between objects. This
-        requires that the obj_relationships table in this object is
-        correct and up-to-date.
-
-        :param:primitive: The primitive version of this object
-        :param:target_version: The version string requested for this object
-        :param:field: The name of the field in this object containing the
-                      sub-object to be backported
-        """
-
-        def _do_backport(to_version):
-            obj = getattr(self, field)
-            if not obj:
-                return
-            if isinstance(obj, CinderObject):
-                obj.obj_make_compatible(
-                    primitive[field]['cinder_object.data'],
-                    to_version)
-                primitive[field]['cinder_object.version'] = to_version
-            elif isinstance(obj, list):
-                for i, element in enumerate(obj):
-                    element.obj_make_compatible(
-                        primitive[field][i]['cinder_object.data'],
-                        to_version)
-                    primitive[field][i]['cinder_object.version'] = to_version
-
-        target_version = utils.convert_version_to_tuple(target_version)
-        for index, versions in enumerate(self.obj_relationships[field]):
-            my_version, child_version = versions
-            my_version = utils.convert_version_to_tuple(my_version)
-            if target_version < my_version:
-                if index == 0:
-                    # We're backporting to a version from before this
-                    # subobject was added: delete it from the primitive.
-                    del primitive[field]
-                else:
-                    # We're in the gap between index-1 and index, so
-                    # backport to the older version
-                    last_child_version = \
-                        self.obj_relationships[field][index - 1][1]
-                    _do_backport(last_child_version)
-                return
-            elif target_version == my_version:
-                # This is the first mapping that satisfies the
-                # target_version request: backport the object.
-                _do_backport(child_version)
-                return
-
-    def obj_make_compatible(self, primitive, target_version):
-        """Make an object representation compatible with a target version.
-
-        This is responsible for taking the primitive representation of
-        an object and making it suitable for the given target_version.
-        This may mean converting the format of object attributes, removing
-        attributes that have been added since the target version, etc. In
-        general:
-
-        - If a new version of an object adds a field, this routine
-          should remove it for older versions.
-        - If a new version changed or restricted the format of a field, this
-          should convert it back to something a client knowing only of the
-          older version will tolerate.
-        - If an object that this object depends on is bumped, then this
-          object should also take a version bump. Then, this routine should
-          backlevel the dependent object (by calling its obj_make_compatible())
-          if the requested version of this object is older than the version
-          where the new dependent object was added.
-
-        :param:primitive: The result of self.obj_to_primitive()
-        :param:target_version: The version string requested by the recipient
-        of the object
-        :raises: cinder.exception.UnsupportedObjectError if conversion
-        is not possible for some reason
-        """
-        for key, field in self.fields.items():
-            if not isinstance(field, (fields.ObjectField,
-                                      fields.ListOfObjectsField)):
-                continue
-            if not self.obj_attr_is_set(key):
-                continue
-            if key not in self.obj_relationships:
-                # NOTE(danms): This is really a coding error and shouldn't
-                # happen unless we miss something
-                raise exception.ObjectActionError(
-                    action='obj_make_compatible',
-                    reason='No rule for %s' % key)
-            self._obj_make_obj_compatible(primitive, target_version, key)
-
-    def obj_to_primitive(self, target_version=None):
-        """Simple base-case dehydration.
-
-        This calls to_primitive() for each item in fields.
-        """
-        primitive = dict()
-        for name, field in self.fields.items():
-            if self.obj_attr_is_set(name):
-                primitive[name] = field.to_primitive(self, name,
-                                                     getattr(self, name))
-        if target_version:
-            self.obj_make_compatible(primitive, target_version)
-        obj = {'cinder_object.name': self.obj_name(),
-               'cinder_object.namespace': 'cinder',
-               'cinder_object.version': target_version or self.VERSION,
-               'cinder_object.data': primitive}
-        if self.obj_what_changed():
-            obj['cinder_object.changes'] = sorted(self.obj_what_changed())
-        return obj
-
-    def obj_set_defaults(self, *attrs):
-        if not attrs:
-            attrs = [name for name, field in self.fields.items()
-                     if field.default != fields.UnspecifiedDefault]
-
-        for attr in attrs:
-            default = self.fields[attr].default
-            if default is fields.UnspecifiedDefault:
-                raise exception.ObjectActionError(
-                    action='set_defaults',
-                    reason='No default set for field %s' % attr)
-            setattr(self, attr, default)
-
-    def obj_load_attr(self, attrname):
-        """Load an additional attribute from the real object."""
-        raise NotImplementedError(
-            _("Cannot load '%s' in the base class") % attrname)
-
-    def save(self, context):
-        """Save the changed fields back to the store.
-
-        This is optional for subclasses, but is presented here in the base
-        class for consistency among those that do.
-        """
-        raise NotImplementedError('Cannot save anything in the base class')
 
-    def obj_what_changed(self):
-        """Returns a set of fields that have been modified."""
-        changes = set(self._changed_fields)
-        for field in self.fields:
-            if (self.obj_attr_is_set(field) and
-                    isinstance(getattr(self, field), CinderObject) and
-                    getattr(self, field).obj_what_changed()):
-                changes.add(field)
-        return changes
+@CinderObjectRegistry.register
+class CinderObject(base.VersionedObject):
+    # NOTE(thangp): OBJ_PROJECT_NAMESPACE needs to be set so that nova,
+    # cinder, and other objects can exist on the same bus and be distinguished
+    # from one another.
+    OBJ_PROJECT_NAMESPACE = 'cinder'
 
-    def obj_get_changes(self):
-        """Returns a dict of changed fields and their new values."""
-        changes = {}
-        for key in self.obj_what_changed():
-            changes[key] = getattr(self, key)
-        return changes
 
-    def obj_reset_changes(self, fields=None):
-        """Reset the list of fields that have been changed.
-
-        Note that this is NOT "revert to previous values"
-        """
-        if fields:
-            self._changed_fields -= set(fields)
-        else:
-            self._changed_fields.clear()
-
-    def obj_attr_is_set(self, attrname):
-        """Test object to see if attrname is present.
-
-        Returns True if the named attribute has a value set, or
-        False if not. Raises AttributeError if attrname is not
-        a valid attribute for this object.
-        """
-        if attrname not in self.obj_fields:
-            raise AttributeError(
-                _("%(objname)s object has no attribute '%(attrname)s'") %
-                {'objname': self.obj_name(), 'attrname': attrname})
-        return hasattr(self, get_attrname(attrname))
-
-    @property
-    def obj_fields(self):
-        return self.fields.keys() + self.obj_extra_fields
-
-
-class CinderObjectDictCompat(object):
-    """Mix-in to provide dictionary key access compat
+class CinderObjectDictCompat(base.VersionedObjectDictCompat):
+    """Mix-in to provide dictionary key access compat.
 
     If an object needs to support attribute access using
     dictionary items instead of object attributes, inherit
@@ -568,44 +60,7 @@ class CinderObjectDictCompat(object):
     NOTE(berrange) This class will eventually be deleted.
     """
 
-    # dictish syntactic sugar
-    def iteritems(self):
-        """For backwards-compatibility with dict-based objects.
-
-        NOTE(danms): May be removed in the future.
-        """
-        for name in self.obj_fields:
-            if (self.obj_attr_is_set(name) or
-                    name in self.obj_extra_fields):
-                yield name, getattr(self, name)
-
-    items = lambda self: list(self.iteritems())
-
-    def __getitem__(self, name):
-        """For backwards-compatibility with dict-based objects.
-
-        NOTE(danms): May be removed in the future.
-        """
-        return getattr(self, name)
-
-    def __setitem__(self, name, value):
-        """For backwards-compatibility with dict-based objects.
-
-        NOTE(danms): May be removed in the future.
-        """
-        setattr(self, name, value)
-
-    def __contains__(self, name):
-        """For backwards-compatibility with dict-based objects.
-
-        NOTE(danms): May be removed in the future.
-        """
-        try:
-            return self.obj_attr_is_set(name)
-        except AttributeError:
-            return False
-
-    def get(self, key, value=NotSpecifiedSentinel):
+    def get(self, key, value=base._NotSpecifiedSentinel):
         """For backwards-compatibility with dict-based objects.
 
         NOTE(danms): May be removed in the future.
@@ -621,19 +76,12 @@ class CinderObjectDictCompat(object):
                       {'object_name': self.__class__.__name__,
                        'attribute_name': key})
             return None
-        if value != NotSpecifiedSentinel and not self.obj_attr_is_set(key):
+        if (value != base._NotSpecifiedSentinel and
+                not self.obj_attr_is_set(key)):
             return value
         else:
             return getattr(self, key)
 
-    def update(self, updates):
-        """For backwards-compatibility with dict-base objects.
-
-        NOTE(danms): May be removed in the future.
-        """
-        for key, value in updates.items():
-            setattr(self, key, value)
-
 
 class CinderPersistentObject(object):
     """Mixin class for Persistent objects.
@@ -656,7 +104,6 @@ class CinderPersistentObject(object):
 
            with obj.obj_as_admin():
                obj.save()
-
         """
         if self._context is None:
             raise exception.OrphanedObjectError(method='obj_as_admin',
@@ -670,185 +117,12 @@ class CinderPersistentObject(object):
             self._context = original_context
 
 
-class ObjectListBase(object):
-    """Mixin class for lists of objects.
-
-    This mixin class can be added as a base class for an object that
-    is implementing a list of objects. It adds a single field of 'objects',
-    which is the list store, and behaves like a list itself. It supports
-    serialization of the list of objects automatically.
-    """
-    fields = {
-        'objects': fields.ListOfObjectsField('CinderObject'),
-    }
-
-    # This is a dictionary of my_version:child_version mappings so that
-    # we can support backleveling our contents based on the version
-    # requested of the list object.
-    child_versions = {}
-
-    def __init__(self, *args, **kwargs):
-        super(ObjectListBase, self).__init__(*args, **kwargs)
-        if 'objects' not in kwargs:
-            self.objects = []
-            self._changed_fields.discard('objects')
-
-    def __iter__(self):
-        """List iterator interface."""
-        return iter(self.objects)
-
-    def __len__(self):
-        """List length."""
-        return len(self.objects)
-
-    def __getitem__(self, index):
-        """List index access."""
-        if isinstance(index, slice):
-            new_obj = self.__class__()
-            new_obj.objects = self.objects[index]
-            # NOTE(danms): We must be mixed in with a CinderObject!
-            new_obj.obj_reset_changes()
-            new_obj._context = self._context
-            return new_obj
-        return self.objects[index]
-
-    def __contains__(self, value):
-        """List membership test."""
-        return value in self.objects
-
-    def count(self, value):
-        """List count of value occurrences."""
-        return self.objects.count(value)
-
-    def index(self, value):
-        """List index of value."""
-        return self.objects.index(value)
-
-    def sort(self, cmp=None, key=None, reverse=False):
-        self.objects.sort(cmp=cmp, key=key, reverse=reverse)
-
-    def obj_make_compatible(self, primitive, target_version):
-        primitives = primitive['objects']
-        child_target_version = self.child_versions.get(target_version, '1.0')
-        for index, item in enumerate(self.objects):
-            self.objects[index].obj_make_compatible(
-                primitives[index]['cinder_object.data'],
-                child_target_version)
-            primitives[index]['cinder_object.version'] = child_target_version
-
-    def obj_what_changed(self):
-        changes = set(self._changed_fields)
-        for child in self.objects:
-            if child.obj_what_changed():
-                changes.add('objects')
-        return changes
-
-
-class CinderObjectSerializer(messaging.NoOpSerializer):
-    """A CinderObject-aware Serializer.
-
-    This implements the Oslo Serializer interface and provides the
-    ability to serialize and deserialize CinderObject entities. Any service
-    that needs to accept or return CinderObjects as arguments or result values
-    should pass this to its RPCClient and RPCServer objects.
-    """
-
-    def _process_object(self, context, objprim):
-        try:
-            objinst = CinderObject.obj_from_primitive(objprim, context=context)
-        except exception.IncompatibleObjectVersion:
-            objver = objprim['cinder_object.version']
-            if objver.count('.') == 2:
-                # NOTE(danms): For our purposes, the .z part of the version
-                # should be safe to accept without requiring a backport
-                objprim['cinder_object.version'] = \
-                    '.'.join(objver.split('.')[:2])
-                return self._process_object(context, objprim)
-            raise
-
-        return objinst
-
-    def _process_iterable(self, context, action_fn, values):
-        """Process an iterable, taking an action on each value.
-        :param:context: Request context
-        :param:action_fn: Action to take on each item in values
-        :param:values: Iterable container of things to take action on
-        :returns: A new container of the same type (except set) with
-                  items from values having had action applied.
-        """
-        iterable = values.__class__
-        if issubclass(iterable, dict):
-            return iterable(**{k: action_fn(context, v)
-                            for k, v in six.iteritems(values)})
-        else:
-            # NOTE(danms): A set can't have an unhashable value inside, such as
-            # a dict. Convert sets to tuples, which is fine, since we can't
-            # send them over RPC anyway.
-            if iterable == set:
-                iterable = tuple
-            return iterable([action_fn(context, value) for value in values])
-
-    def serialize_entity(self, context, entity):
-        if isinstance(entity, (tuple, list, set, dict)):
-            entity = self._process_iterable(context, self.serialize_entity,
-                                            entity)
-        elif (hasattr(entity, 'obj_to_primitive') and
-              callable(entity.obj_to_primitive)):
-            entity = entity.obj_to_primitive()
-        return entity
-
-    def deserialize_entity(self, context, entity):
-        if isinstance(entity, dict) and 'cinder_object.name' in entity:
-            entity = self._process_object(context, entity)
-        elif isinstance(entity, (tuple, list, set, dict)):
-            entity = self._process_iterable(context, self.deserialize_entity,
-                                            entity)
-        return entity
-
-
-def obj_to_primitive(obj):
-    """Recursively turn an object into a python primitive.
-
-    A CinderObject becomes a dict, and anything that implements ObjectListBase
-    becomes a list.
-    """
-    if isinstance(obj, ObjectListBase):
-        return [obj_to_primitive(x) for x in obj]
-    elif isinstance(obj, CinderObject):
-        result = {}
-        for key in obj.obj_fields:
-            if obj.obj_attr_is_set(key) or key in obj.obj_extra_fields:
-                result[key] = obj_to_primitive(getattr(obj, key))
-        return result
-    elif isinstance(obj, netaddr.IPAddress):
-        return str(obj)
-    elif isinstance(obj, netaddr.IPNetwork):
-        return str(obj)
-    else:
-        return obj
-
-
-def obj_make_list(context, list_obj, item_cls, db_list, **extra_args):
-    """Construct an object list from a list of primitives.
+class ObjectListBase(base.ObjectListBase):
+    pass
 
-    This calls item_cls._from_db_object() on each item of db_list, and
-    adds the resulting object to list_obj.
 
-    :param:context: Request contextr
-    :param:list_obj: An ObjectListBase object
-    :param:item_cls: The CinderObject class of the objects within the list
-    :param:db_list: The list of primitives to convert to objects
-    :param:extra_args: Extra arguments to pass to _from_db_object()
-    :returns: list_obj
-    """
-    list_obj.objects = []
-    for db_item in db_list:
-        item = item_cls._from_db_object(context, item_cls(), db_item,
-                                        **extra_args)
-        list_obj.objects.append(item)
-    list_obj._context = context
-    list_obj.obj_reset_changes()
-    return list_obj
+class CinderObjectSerializer(base.VersionedObjectSerializer):
+    OBJ_BASE_CLASS = CinderObject
 
 
 def serialize_args(fn):
diff --git a/cinder/objects/fields.py b/cinder/objects/fields.py
deleted file mode 100644 (file)
index 39986db..0000000
+++ /dev/null
@@ -1,607 +0,0 @@
-#    Copyright 2015 Red Hat, Inc.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import abc
-import datetime
-
-import iso8601
-import netaddr
-from oslo_utils import timeutils
-import six
-
-from cinder.i18n import _
-
-
-class KeyTypeError(TypeError):
-    def __init__(self, expected, value):
-        super(KeyTypeError, self).__init__(
-            _('Key %(key)s must be of type %(expected)s not %(actual)s'
-              ) % {'key': repr(value),
-                   'expected': expected.__name__,
-                   'actual': value.__class__.__name__,
-                   })
-
-
-class ElementTypeError(TypeError):
-    def __init__(self, expected, key, value):
-        super(ElementTypeError, self).__init__(
-            _('Element %(key)s:%(val)s must be of type %(expected)s'
-              ' not %(actual)s'
-              ) % {'key': key,
-                   'val': repr(value),
-                   'expected': expected,
-                   'actual': value.__class__.__name__,
-                   })
-
-
-@six.add_metaclass(abc.ABCMeta)
-class AbstractFieldType(object):
-    @abc.abstractmethod
-    def coerce(self, obj, attr, value):
-        """This is called to coerce (if possible) a value on assignment.
-
-        This method should convert the value given into the designated type,
-        or throw an exception if this is not possible.
-
-        :param:obj: The CinderObject on which an attribute is being set
-        :param:attr: The name of the attribute being set
-        :param:value: The value being set
-        :returns: A properly-typed value
-        """
-        pass
-
-    @abc.abstractmethod
-    def from_primitive(self, obj, attr, value):
-        """This is called to deserialize a value.
-
-        This method should deserialize a value from the form given by
-        to_primitive() to the designated type.
-
-        :param:obj: The CinderObject on which the value is to be set
-        :param:attr: The name of the attribute which will hold the value
-        :param:value: The serialized form of the value
-        :returns: The natural form of the value
-        """
-        pass
-
-    @abc.abstractmethod
-    def to_primitive(self, obj, attr, value):
-        """This is called to serialize a value.
-
-        This method should serialize a value to the form expected by
-        from_primitive().
-
-        :param:obj: The CinderObject on which the value is set
-        :param:attr: The name of the attribute holding the value
-        :param:value: The natural form of the value
-        :returns: The serialized form of the value
-        """
-        pass
-
-    @abc.abstractmethod
-    def describe(self):
-        """Returns a string describing the type of the field."""
-        pass
-
-    @abc.abstractmethod
-    def stringify(self, value):
-        """Returns a short stringified version of a value."""
-        pass
-
-
-class FieldType(AbstractFieldType):
-    @staticmethod
-    def coerce(obj, attr, value):
-        return value
-
-    @staticmethod
-    def from_primitive(obj, attr, value):
-        return value
-
-    @staticmethod
-    def to_primitive(obj, attr, value):
-        return value
-
-    def describe(self):
-        return self.__class__.__name__
-
-    def stringify(self, value):
-        return str(value)
-
-
-class UnspecifiedDefault(object):
-    pass
-
-
-class Field(object):
-    def __init__(self, field_type, nullable=False,
-                 default=UnspecifiedDefault, read_only=False):
-        self._type = field_type
-        self._nullable = nullable
-        self._default = default
-        self._read_only = read_only
-
-    def __repr__(self):
-        args = {
-            'nullable': self._nullable,
-            'default': self._default,
-        }
-        return '%s(%s)' % (self._type.__class__.__name__,
-                           ','.join(['%s=%s' % (k, v)
-                                     for k, v in args.items()]))
-
-    @property
-    def nullable(self):
-        return self._nullable
-
-    @property
-    def default(self):
-        return self._default
-
-    @property
-    def read_only(self):
-        return self._read_only
-
-    def _null(self, obj, attr):
-        if self.nullable:
-            return None
-        elif self._default != UnspecifiedDefault:
-            # NOTE(danms): We coerce the default value each time the field
-            # is set to None as our contract states that we'll let the type
-            # examine the object and attribute name at that time.
-            return self._type.coerce(obj, attr, self._default)
-        else:
-            raise ValueError(_("Field `%s' cannot be None") % attr)
-
-    def coerce(self, obj, attr, value):
-        """Coerce a value to a suitable type.
-
-        This is called any time you set a value on an object, like:
-
-          foo.myint = 1
-
-        and is responsible for making sure that the value (1 here) is of
-        the proper type, or can be sanely converted.
-
-        This also handles the potentially nullable or defaultable
-        nature of the field and calls the coerce() method on a
-        FieldType to actually do the coercion.
-
-        :param:obj: The object being acted upon
-        :param:attr: The name of the attribute/field being set
-        :param:value: The value being set
-        :returns: The properly-typed value
-        """
-        if value is None:
-            return self._null(obj, attr)
-        else:
-            return self._type.coerce(obj, attr, value)
-
-    def from_primitive(self, obj, attr, value):
-        """Deserialize a value from primitive form.
-
-        This is responsible for deserializing a value from primitive
-        into regular form. It calls the from_primitive() method on a
-        FieldType to do the actual deserialization.
-
-        :param:obj: The object being acted upon
-        :param:attr: The name of the attribute/field being deserialized
-        :param:value: The value to be deserialized
-        :returns: The deserialized value
-        """
-        if value is None:
-            return None
-        else:
-            return self._type.from_primitive(obj, attr, value)
-
-    def to_primitive(self, obj, attr, value):
-        """Serialize a value to primitive form.
-
-        This is responsible for serializing a value to primitive
-        form. It calls to_primitive() on a FieldType to do the actual
-        serialization.
-
-        :param:obj: The object being acted upon
-        :param:attr: The name of the attribute/field being serialized
-        :param:value: The value to be serialized
-        :returns: The serialized value
-        """
-        if value is None:
-            return None
-        else:
-            return self._type.to_primitive(obj, attr, value)
-
-    def describe(self):
-        """Return a short string describing the type of this field."""
-        name = self._type.describe()
-        prefix = self.nullable and 'Nullable' or ''
-        return prefix + name
-
-    def stringify(self, value):
-        if value is None:
-            return 'None'
-        else:
-            return self._type.stringify(value)
-
-
-class String(FieldType):
-    @staticmethod
-    def coerce(obj, attr, value):
-        # FIXME(danms): We should really try to avoid the need to do this
-        if isinstance(value, (six.string_types, int, long, float,
-                              datetime.datetime)):
-            return six.text_type(value)
-        else:
-            raise ValueError(_('A string is required here, not %s') %
-                             value.__class__.__name__)
-
-    @staticmethod
-    def stringify(value):
-        return "'%s'" % value
-
-
-class UUID(FieldType):
-    @staticmethod
-    def coerce(obj, attr, value):
-        # FIXME(danms): We should actually verify the UUIDness here
-        return six.text_type(value)
-
-
-class Integer(FieldType):
-    @staticmethod
-    def coerce(obj, attr, value):
-        return int(value)
-
-
-class Float(FieldType):
-    def coerce(self, obj, attr, value):
-        return float(value)
-
-
-class Boolean(FieldType):
-    @staticmethod
-    def coerce(obj, attr, value):
-        return bool(value)
-
-
-class DateTime(FieldType):
-    @staticmethod
-    def coerce(obj, attr, value):
-        if isinstance(value, six.string_types):
-            # NOTE(danms): Being tolerant of isotime strings here will help us
-            # during our objects transition
-            value = timeutils.parse_isotime(value)
-        elif not isinstance(value, datetime.datetime):
-            raise ValueError(_('A datetime.datetime is required here'))
-
-        if value.utcoffset() is None:
-            # NOTE(danms): Legacy objects from sqlalchemy are stored in UTC,
-            # but are returned without a timezone attached.
-            # As a transitional aid, assume a tz-naive object is in UTC.
-            value = value.replace(tzinfo=iso8601.iso8601.Utc())
-        return value
-
-    def from_primitive(self, obj, attr, value):
-        return self.coerce(obj, attr, timeutils.parse_isotime(value))
-
-    @staticmethod
-    def to_primitive(obj, attr, value):
-        return timeutils.isotime(value)
-
-    @staticmethod
-    def stringify(value):
-        return timeutils.isotime(value)
-
-
-class IPAddress(FieldType):
-    @staticmethod
-    def coerce(obj, attr, value):
-        try:
-            return netaddr.IPAddress(value)
-        except netaddr.AddrFormatError as e:
-            raise ValueError(six.text_type(e))
-
-    def from_primitive(self, obj, attr, value):
-        return self.coerce(obj, attr, value)
-
-    @staticmethod
-    def to_primitive(obj, attr, value):
-        return six.text_type(value)
-
-
-class IPV4Address(IPAddress):
-    @staticmethod
-    def coerce(obj, attr, value):
-        result = IPAddress.coerce(obj, attr, value)
-        if result.version != 4:
-            raise ValueError(_('Network "%s" is not valid') % value)
-        return result
-
-
-class IPV6Address(IPAddress):
-    @staticmethod
-    def coerce(obj, attr, value):
-        result = IPAddress.coerce(obj, attr, value)
-        if result.version != 6:
-            raise ValueError(_('Network "%s" is not valid') % value)
-        return result
-
-
-class IPV4AndV6Address(IPAddress):
-    @staticmethod
-    def coerce(obj, attr, value):
-        result = IPAddress.coerce(obj, attr, value)
-        if result.version != 4 and result.version != 6:
-            raise ValueError(_('Network "%s" is not valid') % value)
-        return result
-
-
-class IPNetwork(IPAddress):
-    @staticmethod
-    def coerce(obj, attr, value):
-        try:
-            return netaddr.IPNetwork(value)
-        except netaddr.AddrFormatError as e:
-            raise ValueError(six.text_type(e))
-
-
-class IPV4Network(IPNetwork):
-    @staticmethod
-    def coerce(obj, attr, value):
-        try:
-            return netaddr.IPNetwork(value, version=4)
-        except netaddr.AddrFormatError as e:
-            raise ValueError(six.text_type(e))
-
-
-class IPV6Network(IPNetwork):
-    @staticmethod
-    def coerce(obj, attr, value):
-        try:
-            return netaddr.IPNetwork(value, version=6)
-        except netaddr.AddrFormatError as e:
-            raise ValueError(six.text_type(e))
-
-
-class CompoundFieldType(FieldType):
-    def __init__(self, element_type, **field_args):
-        self._element_type = Field(element_type, **field_args)
-
-
-class List(CompoundFieldType):
-    def coerce(self, obj, attr, value):
-        if not isinstance(value, list):
-            raise ValueError(_('A list is required here'))
-        for index, element in enumerate(list(value)):
-            value[index] = self._element_type.coerce(
-                obj, '%s[%i]' % (attr, index), element)
-        return value
-
-    def to_primitive(self, obj, attr, value):
-        return [self._element_type.to_primitive(obj, attr, x) for x in value]
-
-    def from_primitive(self, obj, attr, value):
-        return [self._element_type.from_primitive(obj, attr, x) for x in value]
-
-    def stringify(self, value):
-        return '[%s]' % (
-            ','.join([self._element_type.stringify(x) for x in value]))
-
-
-class Dict(CompoundFieldType):
-    def coerce(self, obj, attr, value):
-        if not isinstance(value, dict):
-            raise ValueError(_('A dict is required here'))
-        for key, element in value.items():
-            if not isinstance(key, six.string_types):
-                # NOTE(guohliu) In order to keep compatibility with python3
-                # we need to use six.string_types rather than basestring here,
-                # since six.string_types is a tuple, so we need to pass the
-                # real type in.
-                raise KeyTypeError(six.string_types[0], key)
-            value[key] = self._element_type.coerce(
-                obj, '%s["%s"]' % (attr, key), element)
-        return value
-
-    def to_primitive(self, obj, attr, value):
-        primitive = {}
-        for key, element in value.items():
-            primitive[key] = self._element_type.to_primitive(
-                obj, '%s["%s"]' % (attr, key), element)
-        return primitive
-
-    def from_primitive(self, obj, attr, value):
-        concrete = {}
-        for key, element in value.items():
-            concrete[key] = self._element_type.from_primitive(
-                obj, '%s["%s"]' % (attr, key), element)
-        return concrete
-
-    def stringify(self, value):
-        return '{%s}' % (
-            ','.join(['%s=%s' % (key, self._element_type.stringify(val))
-                      for key, val in sorted(value.items())]))
-
-
-class DictProxyField(object):
-    """Descriptor allowing us to assign pinning data as a dict of key_types.
-
-    This allows us to have an object field that will be a dict of key_type
-    keys, allowing that will convert back to string-keyed dict.
-
-    This will take care of the conversion while the dict field will make sure
-    that we store the raw json-serializable data on the object.
-
-    key_type should return a type that unambiguously responds to six.text_type
-    so that calling key_type on it yields the same thing.
-    """
-    def __init__(self, dict_field_name, key_type=int):
-        self._fld_name = dict_field_name
-        self._key_type = key_type
-
-    def __get__(self, obj, obj_type=None):
-        if obj is None:
-            return self
-        if getattr(obj, self._fld_name) is None:
-            return
-        return {self._key_type(k): v
-                for k, v in six.iteritems(getattr(obj, self._fld_name))}
-
-    def __set__(self, obj, val):
-        if val is None:
-            setattr(obj, self._fld_name, val)
-        else:
-            setattr(obj, self._fld_name, {six.text_type(k): v
-                                          for k, v in six.iteritems(val)})
-
-
-class Set(CompoundFieldType):
-    def coerce(self, obj, attr, value):
-        if not isinstance(value, set):
-            raise ValueError(_('A set is required here'))
-
-        coerced = set()
-        for element in value:
-            coerced.add(self._element_type.coerce(
-                obj, '%s["%s"]' % (attr, element), element))
-        return coerced
-
-    def to_primitive(self, obj, attr, value):
-        return tuple(
-            self._element_type.to_primitive(obj, attr, x) for x in value)
-
-    def from_primitive(self, obj, attr, value):
-        return set([self._element_type.from_primitive(obj, attr, x)
-                    for x in value])
-
-    def stringify(self, value):
-        return 'set([%s])' % (
-            ','.join([self._element_type.stringify(x) for x in value]))
-
-
-class Object(FieldType):
-    def __init__(self, obj_name, **kwargs):
-        self._obj_name = obj_name
-        super(Object, self).__init__(**kwargs)
-
-    def coerce(self, obj, attr, value):
-        try:
-            obj_name = value.obj_name()
-        except AttributeError:
-            obj_name = ""
-
-        if obj_name != self._obj_name:
-            raise ValueError(_('An object of type %s is required here') %
-                             self._obj_name)
-        return value
-
-    @staticmethod
-    def to_primitive(obj, attr, value):
-        return value.obj_to_primitive()
-
-    @staticmethod
-    def from_primitive(obj, attr, value):
-        # FIXME(danms): Avoid circular import from base.py
-        from cinder.objects import base as obj_base
-        # NOTE (ndipanov): If they already got hydrated by the serializer, just
-        # pass them back unchanged
-        if isinstance(value, obj_base.CinderObject):
-            return value
-        return obj_base.CinderObject.obj_from_primitive(value, obj._context)
-
-    def describe(self):
-        return "Object<%s>" % self._obj_name
-
-    def stringify(self, value):
-        if 'uuid' in value.fields:
-            ident = '(%s)' % (value.obj_attr_is_set('uuid') and value.uuid or
-                              'UNKNOWN')
-        elif 'id' in value.fields:
-            ident = '(%s)' % (value.obj_attr_is_set('id') and value.id or
-                              'UNKNOWN')
-        else:
-            ident = ''
-
-        return '%s%s' % (self._obj_name, ident)
-
-
-class AutoTypedField(Field):
-    AUTO_TYPE = None
-
-    def __init__(self, **kwargs):
-        super(AutoTypedField, self).__init__(self.AUTO_TYPE, **kwargs)
-
-
-class StringField(AutoTypedField):
-    AUTO_TYPE = String()
-
-
-class UUIDField(AutoTypedField):
-    AUTO_TYPE = UUID()
-
-
-class IntegerField(AutoTypedField):
-    AUTO_TYPE = Integer()
-
-
-class FloatField(AutoTypedField):
-    AUTO_TYPE = Float()
-
-
-class BooleanField(AutoTypedField):
-    AUTO_TYPE = Boolean()
-
-
-class DateTimeField(AutoTypedField):
-    AUTO_TYPE = DateTime()
-
-
-class DictOfStringsField(AutoTypedField):
-    AUTO_TYPE = Dict(String())
-
-
-class DictOfNullableStringsField(AutoTypedField):
-    AUTO_TYPE = Dict(String(), nullable=True)
-
-
-class DictOfIntegersField(AutoTypedField):
-    AUTO_TYPE = Dict(Integer())
-
-
-class ListOfStringsField(AutoTypedField):
-    AUTO_TYPE = List(String())
-
-
-class SetOfIntegersField(AutoTypedField):
-    AUTO_TYPE = Set(Integer())
-
-
-class ListOfSetsOfIntegersField(AutoTypedField):
-    AUTO_TYPE = List(Set(Integer()))
-
-
-class ListOfDictOfNullableStringsField(AutoTypedField):
-    AUTO_TYPE = List(Dict(String(), nullable=True))
-
-
-class ObjectField(AutoTypedField):
-    def __init__(self, objtype, **kwargs):
-        self.AUTO_TYPE = Object(objtype)
-        super(ObjectField, self).__init__(**kwargs)
-
-
-class ListOfObjectsField(AutoTypedField):
-    def __init__(self, objtype, **kwargs):
-        self.AUTO_TYPE = List(Object(objtype))
-        super(ListOfObjectsField, self).__init__(**kwargs)
index cbca84342ca861130b47dcc35df20411eefa6e71..d0ce81f61031037b9570da0806b73abdb4fe02ff 100644 (file)
 
 from oslo_config import cfg
 from oslo_log import log as logging
+from oslo_versionedobjects import fields
 
 from cinder import db
 from cinder import exception
 from cinder.i18n import _
 from cinder import objects
 from cinder.objects import base
-from cinder.objects import fields
 from cinder import utils
 
 CONF = cfg.CONF
@@ -30,6 +30,7 @@ OPTIONAL_FIELDS = ['volume', 'metadata']
 LOG = logging.getLogger(__name__)
 
 
+@base.CinderObjectRegistry.register
 class Snapshot(base.CinderPersistentObject, base.CinderObject,
                base.CinderObjectDictCompat):
     # Version 1.0: Initial version
@@ -129,7 +130,7 @@ class Snapshot(base.CinderPersistentObject, base.CinderObject,
                                    expected_attrs=['metadata'])
 
     @base.remotable
-    def create(self, context):
+    def create(self):
         if self.obj_attr_is_set('id'):
             raise exception.ObjectActionError(action='create',
                                               reason=_('already created'))
@@ -139,11 +140,11 @@ class Snapshot(base.CinderPersistentObject, base.CinderObject,
             raise exception.ObjectActionError(action='create',
                                               reason=_('volume assigned'))
 
-        db_snapshot = db.snapshot_create(context, updates)
-        self._from_db_object(context, self, db_snapshot)
+        db_snapshot = db.snapshot_create(self._context, updates)
+        self._from_db_object(self._context, self, db_snapshot)
 
     @base.remotable
-    def save(self, context):
+    def save(self):
         updates = self.obj_get_changes()
         if updates:
             if 'volume' in updates:
@@ -154,16 +155,17 @@ class Snapshot(base.CinderPersistentObject, base.CinderObject,
                 # Metadata items that are not specified in the
                 # self.metadata will be deleted
                 metadata = updates.pop('metadata', None)
-                self.metadata = db.snapshot_metadata_update(context, self.id,
-                                                            metadata, True)
+                self.metadata = db.snapshot_metadata_update(self._context,
+                                                            self.id, metadata,
+                                                            True)
 
-            db.snapshot_update(context, self.id, updates)
+            db.snapshot_update(self._context, self.id, updates)
 
         self.obj_reset_changes()
 
     @base.remotable
-    def destroy(self, context):
-        db.snapshot_destroy(context, self.id)
+    def destroy(self):
+        db.snapshot_destroy(self._context, self.id)
 
     def obj_load_attr(self, attrname):
         if attrname not in OPTIONAL_FIELDS:
@@ -191,6 +193,7 @@ class Snapshot(base.CinderPersistentObject, base.CinderObject,
             self.obj_reset_changes(['metadata'])
 
 
+@base.CinderObjectRegistry.register
 class SnapshotList(base.ObjectListBase, base.CinderObject):
     VERSION = '1.0'
 
index 6296ed1901bb83e4b0efee12e0976ce80567cd40..d32aff26af73ce9d18b4e641d71fd901e260e64b 100644 (file)
 
 from oslo_config import cfg
 from oslo_log import log as logging
+from oslo_versionedobjects import fields
 
 from cinder import db
 from cinder import exception
 from cinder.i18n import _
 from cinder import objects
 from cinder.objects import base
-from cinder.objects import fields
 from cinder import utils
 
 CONF = cfg.CONF
@@ -28,6 +28,7 @@ OPTIONAL_FIELDS = []
 LOG = logging.getLogger(__name__)
 
 
+@base.CinderObjectRegistry.register
 class Volume(base.CinderPersistentObject, base.CinderObject,
              base.CinderObjectDictCompat):
     # Version 1.0: Initial version
@@ -116,27 +117,28 @@ class Volume(base.CinderPersistentObject, base.CinderObject,
         return cls._from_db_object(context, cls(context), db_volume)
 
     @base.remotable
-    def create(self, context):
+    def create(self):
         if self.obj_attr_is_set('id'):
             raise exception.ObjectActionError(action='create',
                                               reason=_('already created'))
         updates = self.obj_get_changes()
-        db_volume = db.volume_create(context, updates)
-        self._from_db_object(context, self, db_volume)
+        db_volume = db.volume_create(self._context, updates)
+        self._from_db_object(self._context, self, db_volume)
 
     @base.remotable
-    def save(self, context):
+    def save(self):
         updates = self.obj_get_changes()
         if updates:
-            db.volume_update(context, self.id, updates)
+            db.volume_update(self._context, self.id, updates)
 
         self.obj_reset_changes()
 
     @base.remotable
-    def destroy(self, context):
-        db.volume_destroy(context, self.id)
+    def destroy(self):
+        db.volume_destroy(self._context, self.id)
 
 
+@base.CinderObjectRegistry.register
 class VolumeList(base.ObjectListBase, base.CinderObject):
     VERSION = '1.0'
 
index 0d37c85edb6d2dc52271a747faee859e0fc7d0c5..2b395677d45b89af915f4bdd1cac72dd9838f856 100644 (file)
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_versionedobjects import fields
+
 from cinder import objects
-from cinder.objects import fields
 
 
 def fake_db_volume(**updates):
index 5aba19012d7b5f85e364ee719975b3b278c7d2e8..46af555095e3aaf2c23d457629ae34d3fd546e53 100644 (file)
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_versionedobjects import fields
+
 from cinder import objects
-from cinder.objects import fields
 
 
 def fake_db_volume(**updates):
diff --git a/cinder/tests/unit/objects/test_fields.py b/cinder/tests/unit/objects/test_fields.py
deleted file mode 100644 (file)
index 1edd584..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-#    Copyright 2015 Red Hat, Inc.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import datetime
-
-import iso8601
-from oslo_utils import timeutils
-
-from cinder.objects import base as obj_base
-from cinder.objects import fields
-from cinder import test
-
-
-class FakeFieldType(fields.FieldType):
-    def coerce(self, obj, attr, value):
-        return '*%s*' % value
-
-    def to_primitive(self, obj, attr, value):
-        return '!%s!' % value
-
-    def from_primitive(self, obj, attr, value):
-        return value[1:-1]
-
-
-class TestField(test.TestCase):
-    def setUp(self):
-        super(TestField, self).setUp()
-        self.field = fields.Field(FakeFieldType())
-        self.coerce_good_values = [('foo', '*foo*')]
-        self.coerce_bad_values = []
-        self.to_primitive_values = [('foo', '!foo!')]
-        self.from_primitive_values = [('!foo!', 'foo')]
-
-    def test_coerce_good_values(self):
-        for in_val, out_val in self.coerce_good_values:
-            self.assertEqual(out_val, self.field.coerce('obj', 'attr', in_val))
-
-    def test_coerce_bad_values(self):
-        for in_val in self.coerce_bad_values:
-            self.assertRaises((TypeError, ValueError),
-                              self.field.coerce, 'obj', 'attr', in_val)
-
-    def test_to_primitive(self):
-        for in_val, prim_val in self.to_primitive_values:
-            self.assertEqual(prim_val, self.field.to_primitive('obj', 'attr',
-                                                               in_val))
-
-    def test_from_primitive(self):
-        class ObjectLikeThing(object):
-            _context = 'context'
-
-        for prim_val, out_val in self.from_primitive_values:
-            self.assertEqual(out_val, self.field.from_primitive(
-                ObjectLikeThing, 'attr', prim_val))
-
-    def test_stringify(self):
-        self.assertEqual('123', self.field.stringify(123))
-
-
-class TestString(TestField):
-    def setUp(self):
-        super(TestField, self).setUp()
-        self.field = fields.StringField()
-        self.coerce_good_values = [('foo', 'foo'), (1, '1'), (1L, '1'),
-                                   (True, 'True')]
-        self.coerce_bad_values = [None]
-        self.to_primitive_values = self.coerce_good_values[0:1]
-        self.from_primitive_values = self.coerce_good_values[0:1]
-
-    def test_stringify(self):
-        self.assertEqual("'123'", self.field.stringify(123))
-
-
-class TestInteger(TestField):
-    def setUp(self):
-        super(TestField, self).setUp()
-        self.field = fields.IntegerField()
-        self.coerce_good_values = [(1, 1), ('1', 1)]
-        self.coerce_bad_values = ['foo', None]
-        self.to_primitive_values = self.coerce_good_values[0:1]
-        self.from_primitive_values = self.coerce_good_values[0:1]
-
-
-class TestFloat(TestField):
-    def setUp(self):
-        super(TestFloat, self).setUp()
-        self.field = fields.FloatField()
-        self.coerce_good_values = [(1.1, 1.1), ('1.1', 1.1)]
-        self.coerce_bad_values = ['foo', None]
-        self.to_primitive_values = self.coerce_good_values[0:1]
-        self.from_primitive_values = self.coerce_good_values[0:1]
-
-
-class TestBoolean(TestField):
-    def setUp(self):
-        super(TestField, self).setUp()
-        self.field = fields.BooleanField()
-        self.coerce_good_values = [(True, True), (False, False), (1, True),
-                                   ('foo', True), (0, False), ('', False)]
-        self.coerce_bad_values = []
-        self.to_primitive_values = self.coerce_good_values[0:2]
-        self.from_primitive_values = self.coerce_good_values[0:2]
-
-
-class TestDateTime(TestField):
-    def setUp(self):
-        super(TestDateTime, self).setUp()
-        self.dt = datetime.datetime(1955, 11, 5, tzinfo=iso8601.iso8601.Utc())
-        self.field = fields.DateTimeField()
-        self.coerce_good_values = [(self.dt, self.dt),
-                                   (timeutils.isotime(self.dt), self.dt)]
-        self.coerce_bad_values = [1, 'foo']
-        self.to_primitive_values = [(self.dt, timeutils.isotime(self.dt))]
-        self.from_primitive_values = [(timeutils.isotime(self.dt), self.dt)]
-
-    def test_stringify(self):
-        self.assertEqual(
-            '1955-11-05T18:00:00Z',
-            self.field.stringify(
-                datetime.datetime(1955, 11, 5, 18, 0, 0,
-                                  tzinfo=iso8601.iso8601.Utc())))
-
-
-class TestDict(TestField):
-    def setUp(self):
-        super(TestDict, self).setUp()
-        self.field = fields.Field(fields.Dict(FakeFieldType()))
-        self.coerce_good_values = [({'foo': 'bar'}, {'foo': '*bar*'}),
-                                   ({'foo': 1}, {'foo': '*1*'})]
-        self.coerce_bad_values = [{1: 'bar'}, 'foo']
-        self.to_primitive_values = [({'foo': 'bar'}, {'foo': '!bar!'})]
-        self.from_primitive_values = [({'foo': '!bar!'}, {'foo': 'bar'})]
-
-    def test_stringify(self):
-        self.assertEqual("{key=val}", self.field.stringify({'key': 'val'}))
-
-
-class TestDictOfStrings(TestField):
-    def setUp(self):
-        super(TestDictOfStrings, self).setUp()
-        self.field = fields.DictOfStringsField()
-        self.coerce_good_values = [({'foo': 'bar'}, {'foo': 'bar'}),
-                                   ({'foo': 1}, {'foo': '1'})]
-        self.coerce_bad_values = [{1: 'bar'}, {'foo': None}, 'foo']
-        self.to_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})]
-        self.from_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})]
-
-    def test_stringify(self):
-        self.assertEqual("{key='val'}", self.field.stringify({'key': 'val'}))
-
-
-class TestDictOfIntegers(TestField):
-    def setUp(self):
-        super(TestDictOfIntegers, self).setUp()
-        self.field = fields.DictOfIntegersField()
-        self.coerce_good_values = [({'foo': '42'}, {'foo': 42}),
-                                   ({'foo': 4.2}, {'foo': 4})]
-        self.coerce_bad_values = [{1: 'bar'}, {'foo': 'boo'},
-                                  'foo', {'foo': None}]
-        self.to_primitive_values = [({'foo': 42}, {'foo': 42})]
-        self.from_primitive_values = [({'foo': 42}, {'foo': 42})]
-
-    def test_stringify(self):
-        self.assertEqual("{key=42}", self.field.stringify({'key': 42}))
-
-
-class TestDictOfStringsNone(TestField):
-    def setUp(self):
-        super(TestDictOfStringsNone, self).setUp()
-        self.field = fields.DictOfNullableStringsField()
-        self.coerce_good_values = [({'foo': 'bar'}, {'foo': 'bar'}),
-                                   ({'foo': 1}, {'foo': '1'}),
-                                   ({'foo': None}, {'foo': None})]
-        self.coerce_bad_values = [{1: 'bar'}, 'foo']
-        self.to_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})]
-        self.from_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})]
-
-    def test_stringify(self):
-        self.assertEqual("{k2=None,key='val'}",
-                         self.field.stringify({'k2': None,
-                                               'key': 'val'}))
-
-
-class TestListOfDictOfNullableStringsField(TestField):
-    def setUp(self):
-        super(TestListOfDictOfNullableStringsField, self).setUp()
-        self.field = fields.ListOfDictOfNullableStringsField()
-        self.coerce_good_values = [([{'f': 'b', 'f1': 'b1'}, {'f2': 'b2'}],
-                                    [{'f': 'b', 'f1': 'b1'}, {'f2': 'b2'}]),
-                                   ([{'f': 1}, {'f1': 'b1'}],
-                                    [{'f': '1'}, {'f1': 'b1'}]),
-                                   ([{'foo': None}], [{'foo': None}])]
-        self.coerce_bad_values = [[{1: 'a'}], ['ham', 1], ['eggs']]
-        self.to_primitive_values = [([{'f': 'b'}, {'f1': 'b1'}, {'f2': None}],
-                                     [{'f': 'b'}, {'f1': 'b1'}, {'f2': None}])]
-        self.from_primitive_values = [([{'f': 'b'}, {'f1': 'b1'},
-                                        {'f2': None}],
-                                       [{'f': 'b'}, {'f1': 'b1'},
-                                        {'f2': None}])]
-
-    def test_stringify(self):
-        self.assertEqual("[{f=None,f1='b1'},{f2='b2'}]",
-                         self.field.stringify(
-                             [{'f': None, 'f1': 'b1'}, {'f2': 'b2'}]))
-
-
-class TestList(TestField):
-    def setUp(self):
-        super(TestList, self).setUp()
-        self.field = fields.Field(fields.List(FakeFieldType()))
-        self.coerce_good_values = [(['foo', 'bar'], ['*foo*', '*bar*'])]
-        self.coerce_bad_values = ['foo']
-        self.to_primitive_values = [(['foo'], ['!foo!'])]
-        self.from_primitive_values = [(['!foo!'], ['foo'])]
-
-    def test_stringify(self):
-        self.assertEqual('[123]', self.field.stringify([123]))
-
-
-class TestListOfStrings(TestField):
-    def setUp(self):
-        super(TestListOfStrings, self).setUp()
-        self.field = fields.ListOfStringsField()
-        self.coerce_good_values = [(['foo', 'bar'], ['foo', 'bar'])]
-        self.coerce_bad_values = ['foo']
-        self.to_primitive_values = [(['foo'], ['foo'])]
-        self.from_primitive_values = [(['foo'], ['foo'])]
-
-    def test_stringify(self):
-        self.assertEqual("['abc']", self.field.stringify(['abc']))
-
-
-class TestSet(TestField):
-    def setUp(self):
-        super(TestSet, self).setUp()
-        self.field = fields.Field(fields.Set(FakeFieldType()))
-        self.coerce_good_values = [(set(['foo', 'bar']),
-                                    set(['*foo*', '*bar*']))]
-        self.coerce_bad_values = [['foo'], {'foo': 'bar'}]
-        self.to_primitive_values = [(set(['foo']), tuple(['!foo!']))]
-        self.from_primitive_values = [(tuple(['!foo!']), set(['foo']))]
-
-    def test_stringify(self):
-        self.assertEqual('set([123])', self.field.stringify(set([123])))
-
-
-class TestSetOfIntegers(TestField):
-    def setUp(self):
-        super(TestSetOfIntegers, self).setUp()
-        self.field = fields.SetOfIntegersField()
-        self.coerce_good_values = [(set(['1', 2]),
-                                    set([1, 2]))]
-        self.coerce_bad_values = [set(['foo'])]
-        self.to_primitive_values = [(set([1]), tuple([1]))]
-        self.from_primitive_values = [(tuple([1]), set([1]))]
-
-    def test_stringify(self):
-        self.assertEqual('set([1,2])', self.field.stringify(set([1, 2])))
-
-
-class TestListOfSetsOfIntegers(TestField):
-    def setUp(self):
-        super(TestListOfSetsOfIntegers, self).setUp()
-        self.field = fields.ListOfSetsOfIntegersField()
-        self.coerce_good_values = [([set(['1', 2]), set([3, '4'])],
-                                    [set([1, 2]), set([3, 4])])]
-        self.coerce_bad_values = [[set(['foo'])]]
-        self.to_primitive_values = [([set([1])], [tuple([1])])]
-        self.from_primitive_values = [([tuple([1])], [set([1])])]
-
-    def test_stringify(self):
-        self.assertEqual('[set([1,2])]', self.field.stringify([set([1, 2])]))
-
-
-class TestObject(TestField):
-    def setUp(self):
-        super(TestObject, self).setUp()
-
-        class TestableObject(obj_base.CinderObject):
-            fields = {
-                'uuid': fields.StringField(),
-            }
-
-            def __eq__(self, value):
-                # NOTE(danms): Be rather lax about this equality thing to
-                # satisfy the assertEqual() in test_from_primitive(). We
-                # just want to make sure the right type of object is re-created
-                return value.__class__.__name__ == TestableObject.__name__
-
-        class OtherTestableObject(obj_base.CinderObject):
-            pass
-
-        test_inst = TestableObject()
-        self._test_cls = TestableObject
-        self.field = fields.Field(fields.Object('TestableObject'))
-        self.coerce_good_values = [(test_inst, test_inst)]
-        self.coerce_bad_values = [OtherTestableObject(), 1, 'foo']
-        self.to_primitive_values = [(test_inst, test_inst.obj_to_primitive())]
-        self.from_primitive_values = [(test_inst.obj_to_primitive(),
-                                       test_inst), (test_inst, test_inst)]
-
-    def test_stringify(self):
-        obj = self._test_cls(uuid='fake-uuid')
-        self.assertEqual('TestableObject(fake-uuid)',
-                         self.field.stringify(obj))
diff --git a/cinder/tests/unit/objects/test_objects.py b/cinder/tests/unit/objects/test_objects.py
deleted file mode 100644 (file)
index 74f8d79..0000000
+++ /dev/null
@@ -1,966 +0,0 @@
-#    Copyright 2015 IBM Corp.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import contextlib
-import copy
-import datetime
-
-import mock
-from oslo_serialization import jsonutils
-from oslo_utils import timeutils
-import six
-from testtools import matchers
-
-from cinder import context
-from cinder import exception
-from cinder import objects
-from cinder.objects import base
-from cinder.objects import fields
-from cinder import test
-from cinder.tests.unit import fake_notifier
-
-
-class MyOwnedObject(base.CinderPersistentObject, base.CinderObject):
-    VERSION = '1.0'
-    fields = {'baz': fields.Field(fields.Integer())}
-
-
-class MyObj(base.CinderPersistentObject, base.CinderObject,
-            base.CinderObjectDictCompat):
-    VERSION = '1.6'
-    fields = {'foo': fields.Field(fields.Integer(), default=1),
-              'bar': fields.Field(fields.String()),
-              'missing': fields.Field(fields.String()),
-              'readonly': fields.Field(fields.Integer(), read_only=True),
-              'rel_object': fields.ObjectField('MyOwnedObject', nullable=True),
-              'rel_objects': fields.ListOfObjectsField('MyOwnedObject',
-                                                       nullable=True),
-              }
-
-    @staticmethod
-    def _from_db_object(context, obj, db_obj):
-        self = MyObj()
-        self.foo = db_obj['foo']
-        self.bar = db_obj['bar']
-        self.missing = db_obj['missing']
-        self.readonly = 1
-        return self
-
-    def obj_load_attr(self, attrname):
-        setattr(self, attrname, 'loaded!')
-
-    @base.remotable_classmethod
-    def query(cls, context):
-        obj = cls(context=context, foo=1, bar='bar')
-        obj.obj_reset_changes()
-        return obj
-
-    @base.remotable
-    def marco(self, context):
-        return 'polo'
-
-    @base.remotable
-    def _update_test(self, context):
-        if context.project_id == 'alternate':
-            self.bar = 'alternate-context'
-        else:
-            self.bar = 'updated'
-
-    @base.remotable
-    def save(self, context):
-        self.obj_reset_changes()
-
-    @base.remotable
-    def refresh(self, context):
-        self.foo = 321
-        self.bar = 'refreshed'
-        self.obj_reset_changes()
-
-    @base.remotable
-    def modify_save_modify(self, context):
-        self.bar = 'meow'
-        self.save()
-        self.foo = 42
-        self.rel_object = MyOwnedObject(baz=42)
-
-    def obj_make_compatible(self, primitive, target_version):
-        super(MyObj, self).obj_make_compatible(primitive, target_version)
-        # NOTE(danms): Simulate an older version that had a different
-        # format for the 'bar' attribute
-        if target_version == '1.1' and 'bar' in primitive:
-            primitive['bar'] = 'old%s' % primitive['bar']
-
-
-class MyObjDiffVers(MyObj):
-    VERSION = '1.5'
-
-    @classmethod
-    def obj_name(cls):
-        return 'MyObj'
-
-
-class MyObj2(object):
-    @classmethod
-    def obj_name(cls):
-        return 'MyObj'
-
-    @base.remotable_classmethod
-    def query(cls, *args, **kwargs):
-        pass
-
-
-class RandomMixInWithNoFields(object):
-    """Used to test object inheritance using a mixin that has no fields."""
-    pass
-
-
-class TestSubclassedObject(RandomMixInWithNoFields, MyObj):
-    fields = {'new_field': fields.Field(fields.String())}
-
-
-class TestMetaclass(test.TestCase):
-    def test_obj_tracking(self):
-
-        @six.add_metaclass(base.CinderObjectMetaclass)
-        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.CinderObject):
-                VERSION = '1.5'
-                fields = {'foo': field()}
-            return TestField
-
-        create_class(fields.BooleanField)
-        self.assertRaises(exception.ObjectFieldInvalid,
-                          create_class, fields.Boolean)
-        self.assertRaises(exception.ObjectFieldInvalid,
-                          create_class, int)
-
-
-class TestObjToPrimitive(test.TestCase):
-
-    def test_obj_to_primitive_list(self):
-        class MyObjElement(base.CinderObject):
-            fields = {'foo': fields.IntegerField()}
-
-            def __init__(self, foo):
-                super(MyObjElement, self).__init__()
-                self.foo = foo
-
-        class MyList(base.ObjectListBase, base.CinderObject):
-            fields = {'objects': fields.ListOfObjectsField('MyObjElement')}
-
-        mylist = MyList()
-        mylist.objects = [MyObjElement(1), MyObjElement(2), MyObjElement(3)]
-        self.assertEqual([1, 2, 3],
-                         [x['foo'] for x in base.obj_to_primitive(mylist)])
-
-    def test_obj_to_primitive_dict(self):
-        myobj = MyObj(foo=1, bar='foo')
-        self.assertEqual({'foo': 1, 'bar': 'foo'},
-                         base.obj_to_primitive(myobj))
-
-    def test_obj_to_primitive_recursive(self):
-        class MyList(base.ObjectListBase, base.CinderObject):
-            fields = {'objects': fields.ListOfObjectsField('MyObj')}
-
-        mylist = MyList(objects=[MyObj(), MyObj()])
-        for i, value in enumerate(mylist):
-            value.foo = i
-        self.assertEqual([{'foo': 0}, {'foo': 1}],
-                         base.obj_to_primitive(mylist))
-
-
-class TestObjMakeList(test.TestCase):
-
-    def test_obj_make_list(self):
-        class MyList(base.ObjectListBase, base.CinderObject):
-            pass
-
-        db_objs = [{'foo': 1, 'bar': 'baz', 'missing': 'banana'},
-                   {'foo': 2, 'bar': 'bat', 'missing': 'apple'},
-                   ]
-        mylist = base.obj_make_list('ctxt', MyList(), MyObj, db_objs)
-        self.assertEqual(2, len(mylist))
-        self.assertEqual('ctxt', mylist._context)
-        for index, item in enumerate(mylist):
-            self.assertEqual(db_objs[index]['foo'], item.foo)
-            self.assertEqual(db_objs[index]['bar'], item.bar)
-            self.assertEqual(db_objs[index]['missing'], item.missing)
-
-
-def compare_obj(test, obj, db_obj, subs=None, allow_missing=None,
-                comparators=None):
-    """Compare a CinderObject and a dict-like database object.
-
-    This automatically converts TZ-aware datetimes and iterates over
-    the fields of the object.
-
-    :param:test: The TestCase doing the comparison
-    :param:obj: The CinderObject to examine
-    :param:db_obj: The dict-like database object to use as reference
-    :param:subs: A dict of objkey=dbkey field substitutions
-    :param:allow_missing: A list of fields that may not be in db_obj
-    :param:comparators: Map of comparator functions to use for certain fields
-    """
-
-    if subs is None:
-        subs = {}
-    if allow_missing is None:
-        allow_missing = []
-    if comparators is None:
-        comparators = {}
-
-    for key in obj.fields:
-        if key in allow_missing and not obj.obj_attr_is_set(key):
-            continue
-        obj_val = getattr(obj, key)
-        db_key = subs.get(key, key)
-        db_val = db_obj[db_key]
-        if isinstance(obj_val, datetime.datetime):
-            obj_val = obj_val.replace(tzinfo=None)
-
-        if key in comparators:
-            comparator = comparators[key]
-            comparator(db_val, obj_val)
-        else:
-            test.assertEqual(db_val, obj_val)
-
-
-class _BaseTestCase(test.TestCase):
-    def setUp(self):
-        super(_BaseTestCase, self).setUp()
-        self.remote_object_calls = list()
-        self.user_id = 'fake-user'
-        self.project_id = 'fake-project'
-        self.context = context.RequestContext(self.user_id, self.project_id)
-        fake_notifier.stub_notifier(self.stubs)
-        self.addCleanup(fake_notifier.reset)
-
-    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,
-                    comparators=comparators)
-
-    def json_comparator(self, expected, obj_val):
-        # json-ify an object field for comparison with its db str
-        # equivalent
-        self.assertEqual(expected, jsonutils.dumps(obj_val))
-
-    def str_comparator(self, expected, obj_val):
-        """Compare an object field to a string in the db by performing
-        a simple coercion on the object field value.
-        """
-        self.assertEqual(expected, str(obj_val))
-
-    def assertNotIsInstance(self, obj, cls, msg=None):
-        """Python < v2.7 compatibility.  Assert 'not isinstance(obj, cls)."""
-        try:
-            f = super(_BaseTestCase, self).assertNotIsInstance
-        except AttributeError:
-            self.assertThat(obj,
-                            matchers.Not(matchers.IsInstance(cls)),
-                            message=msg or '')
-        else:
-            f(obj, cls, msg=msg)
-
-
-class _LocalTest(_BaseTestCase):
-    def setUp(self):
-        super(_LocalTest, self).setUp()
-        # Just in case
-        base.CinderObject.indirection_api = None
-
-    def assertRemotes(self):
-        self.assertEqual(self.remote_object_calls, [])
-
-
-@contextlib.contextmanager
-def things_temporarily_local():
-    _api = base.CinderObject.indirection_api
-    base.CinderObject.indirection_api = None
-    yield
-    base.CinderObject.indirection_api = _api
-
-
-class _TestObject(object):
-    def test_object_attrs_in_init(self):
-        # Now check the test one in this file. Should be newest version
-        self.assertEqual('1.6', objects.MyObj.VERSION)
-
-    def test_hydration_type_error(self):
-        primitive = {'cinder_object.name': 'MyObj',
-                     'cinder_object.namespace': 'cinder',
-                     'cinder_object.version': '1.5',
-                     'cinder_object.data': {'foo': 'a'}}
-        self.assertRaises(ValueError, MyObj.obj_from_primitive, primitive)
-
-    def test_hydration(self):
-        primitive = {'cinder_object.name': 'MyObj',
-                     'cinder_object.namespace': 'cinder',
-                     'cinder_object.version': '1.5',
-                     'cinder_object.data': {'foo': 1}}
-        real_method = MyObj._obj_from_primitive
-
-        def _obj_from_primitive(*args):
-            return real_method(*args)
-
-        with mock.patch.object(MyObj, '_obj_from_primitive') as ofp:
-            ofp.side_effect = _obj_from_primitive
-            obj = MyObj.obj_from_primitive(primitive)
-            ofp.assert_called_once_with(None, '1.5', primitive)
-        self.assertEqual(obj.foo, 1)
-
-    def test_hydration_version_different(self):
-        primitive = {'cinder_object.name': 'MyObj',
-                     'cinder_object.namespace': 'cinder',
-                     'cinder_object.version': '1.2',
-                     'cinder_object.data': {'foo': 1}}
-        obj = MyObj.obj_from_primitive(primitive)
-        self.assertEqual(obj.foo, 1)
-        self.assertEqual('1.2', obj.VERSION)
-
-    def test_hydration_bad_ns(self):
-        primitive = {'cinder_object.name': 'MyObj',
-                     'cinder_object.namespace': 'foo',
-                     'cinder_object.version': '1.5',
-                     'cinder_object.data': {'foo': 1}}
-        self.assertRaises(exception.UnsupportedObjectError,
-                          MyObj.obj_from_primitive, primitive)
-
-    def test_hydration_additional_unexpected_stuff(self):
-        primitive = {'cinder_object.name': 'MyObj',
-                     'cinder_object.namespace': 'cinder',
-                     'cinder_object.version': '1.5.1',
-                     'cinder_object.data': {
-                         'foo': 1,
-                         'unexpected_thing': 'foobar'}}
-        obj = MyObj.obj_from_primitive(primitive)
-        self.assertEqual(1, obj.foo)
-        self.assertFalse(hasattr(obj, 'unexpected_thing'))
-        # NOTE(danms): If we call obj_from_primitive() directly
-        # with a version containing .z, we'll get that version
-        # in the resulting object. In reality, when using the
-        # serializer, we'll get that snipped off (tested
-        # elsewhere)
-        self.assertEqual('1.5.1', obj.VERSION)
-
-    def test_dehydration(self):
-        expected = {'cinder_object.name': 'MyObj',
-                    'cinder_object.namespace': 'cinder',
-                    'cinder_object.version': '1.6',
-                    'cinder_object.data': {'foo': 1}}
-        obj = MyObj(foo=1)
-        obj.obj_reset_changes()
-        self.assertEqual(obj.obj_to_primitive(), expected)
-
-    def test_object_property(self):
-        obj = MyObj(foo=1)
-        self.assertEqual(obj.foo, 1)
-
-    def test_object_property_type_error(self):
-        obj = MyObj()
-
-        def fail():
-            obj.foo = 'a'
-        self.assertRaises(ValueError, fail)
-
-    def test_object_dict_syntax(self):
-        obj = MyObj(foo=123, bar='bar')
-        self.assertEqual(obj['foo'], 123)
-        self.assertEqual(sorted(obj.items(), key=lambda x: x[0]),
-                         [('bar', 'bar'), ('foo', 123)])
-        self.assertEqual(sorted(list(obj.iteritems()), key=lambda x: x[0]),
-                         [('bar', 'bar'), ('foo', 123)])
-
-    def test_load(self):
-        obj = MyObj()
-        self.assertEqual(obj.bar, 'loaded!')
-
-    def test_load_in_base(self):
-        class Foo(base.CinderObject):
-            fields = {'foobar': fields.Field(fields.Integer())}
-        obj = Foo()
-        with self.assertRaisesRegex(NotImplementedError, ".*foobar.*"):
-            obj.foobar
-
-    def test_loaded_in_primitive(self):
-        obj = MyObj(foo=1)
-        obj.obj_reset_changes()
-        self.assertEqual(obj.bar, 'loaded!')
-        expected = {'cinder_object.name': 'MyObj',
-                    'cinder_object.namespace': 'cinder',
-                    'cinder_object.version': '1.6',
-                    'cinder_object.changes': ['bar'],
-                    'cinder_object.data': {'foo': 1,
-                                           'bar': 'loaded!'}}
-        self.assertEqual(obj.obj_to_primitive(), expected)
-
-    def test_changes_in_primitive(self):
-        obj = MyObj(foo=123)
-        self.assertEqual(obj.obj_what_changed(), set(['foo']))
-        primitive = obj.obj_to_primitive()
-        self.assertIn('cinder_object.changes', primitive)
-        obj2 = MyObj.obj_from_primitive(primitive)
-        self.assertEqual(obj2.obj_what_changed(), set(['foo']))
-        obj2.obj_reset_changes()
-        self.assertEqual(obj2.obj_what_changed(), set())
-
-    def test_obj_class_from_name(self):
-        obj = base.CinderObject.obj_class_from_name('MyObj', '1.5')
-        self.assertEqual('1.5', obj.VERSION)
-
-    def test_obj_class_from_name_latest_compatible(self):
-        obj = base.CinderObject.obj_class_from_name('MyObj', '1.1')
-        self.assertEqual('1.6', obj.VERSION)
-
-    def test_unknown_objtype(self):
-        self.assertRaises(exception.UnsupportedObjectError,
-                          base.CinderObject.obj_class_from_name, 'foo', '1.0')
-
-    def test_obj_class_from_name_supported_version(self):
-        error = None
-        try:
-            base.CinderObject.obj_class_from_name('MyObj', '1.25')
-        except exception.IncompatibleObjectVersion as error:
-            pass
-
-        self.assertIsNotNone(error)
-        self.assertEqual('1.6', error.kwargs['supported'])
-
-    def test_with_alternate_context(self):
-        ctxt1 = context.RequestContext('foo', 'foo')
-        ctxt2 = context.RequestContext('bar', 'alternate')
-        obj = MyObj.query(ctxt1)
-        obj._update_test(ctxt2)
-        self.assertEqual(obj.bar, 'alternate-context')
-        self.assertRemotes()
-
-    def test_orphaned_object(self):
-        obj = MyObj.query(self.context)
-        obj._context = None
-        self.assertRaises(exception.OrphanedObjectError,
-                          obj._update_test)
-        self.assertRemotes()
-
-    def test_changed_1(self):
-        obj = MyObj.query(self.context)
-        obj.foo = 123
-        self.assertEqual(obj.obj_what_changed(), set(['foo']))
-        obj._update_test(self.context)
-        self.assertEqual(obj.obj_what_changed(), set(['foo', 'bar']))
-        self.assertEqual(obj.foo, 123)
-        self.assertRemotes()
-
-    def test_changed_2(self):
-        obj = MyObj.query(self.context)
-        obj.foo = 123
-        self.assertEqual(obj.obj_what_changed(), set(['foo']))
-        obj.save()
-        self.assertEqual(obj.obj_what_changed(), set([]))
-        self.assertEqual(obj.foo, 123)
-        self.assertRemotes()
-
-    def test_changed_3(self):
-        obj = MyObj.query(self.context)
-        obj.foo = 123
-        self.assertEqual(obj.obj_what_changed(), set(['foo']))
-        obj.refresh()
-        self.assertEqual(obj.obj_what_changed(), set([]))
-        self.assertEqual(obj.foo, 321)
-        self.assertEqual(obj.bar, 'refreshed')
-        self.assertRemotes()
-
-    def test_changed_4(self):
-        obj = MyObj.query(self.context)
-        obj.bar = 'something'
-        self.assertEqual(obj.obj_what_changed(), set(['bar']))
-        obj.modify_save_modify(self.context)
-        self.assertEqual(obj.obj_what_changed(), set(['foo', 'rel_object']))
-        self.assertEqual(obj.foo, 42)
-        self.assertEqual(obj.bar, 'meow')
-        self.assertIsInstance(obj.rel_object, MyOwnedObject)
-        self.assertRemotes()
-
-    def test_changed_with_sub_object(self):
-        class ParentObject(base.CinderObject):
-            fields = {'foo': fields.IntegerField(),
-                      'bar': fields.ObjectField('MyObj'),
-                      }
-        obj = ParentObject()
-        self.assertEqual(set(), obj.obj_what_changed())
-        obj.foo = 1
-        self.assertEqual(set(['foo']), obj.obj_what_changed())
-        bar = MyObj()
-        obj.bar = bar
-        self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
-        obj.obj_reset_changes()
-        self.assertEqual(set(), obj.obj_what_changed())
-        bar.foo = 1
-        self.assertEqual(set(['bar']), obj.obj_what_changed())
-
-    def test_static_result(self):
-        obj = MyObj.query(self.context)
-        self.assertEqual(obj.bar, 'bar')
-        result = obj.marco()
-        self.assertEqual(result, 'polo')
-        self.assertRemotes()
-
-    def test_updates(self):
-        obj = MyObj.query(self.context)
-        self.assertEqual(obj.foo, 1)
-        obj._update_test()
-        self.assertEqual(obj.bar, 'updated')
-        self.assertRemotes()
-
-    def test_base_attributes(self):
-        dt = datetime.datetime(1955, 11, 5)
-        obj = MyObj(created_at=dt, updated_at=dt, deleted_at=None,
-                    deleted=False)
-        expected = {'cinder_object.name': 'MyObj',
-                    'cinder_object.namespace': 'cinder',
-                    'cinder_object.version': '1.6',
-                    'cinder_object.changes':
-                        ['created_at', 'deleted', 'deleted_at', 'updated_at'],
-                    'cinder_object.data':
-                        {'created_at': timeutils.isotime(dt),
-                         'updated_at': timeutils.isotime(dt),
-                         'deleted_at': None,
-                         'deleted': False,
-                         }
-                    }
-        self.assertEqual(obj.obj_to_primitive(), expected)
-
-    def test_contains(self):
-        obj = MyObj()
-        self.assertNotIn('foo', obj)
-        obj.foo = 1
-        self.assertIn('foo', obj)
-        self.assertNotIn('does_not_exist', obj)
-
-    def test_obj_attr_is_set(self):
-        obj = MyObj(foo=1)
-        self.assertTrue(obj.obj_attr_is_set('foo'))
-        self.assertFalse(obj.obj_attr_is_set('bar'))
-        self.assertRaises(AttributeError, obj.obj_attr_is_set, 'bang')
-
-    def test_get(self):
-        obj = MyObj(foo=1)
-        # Foo has value, should not get the default
-        self.assertEqual(1, obj.get('foo', 2))
-        # Foo has value, should return the value without error
-        self.assertEqual(1, obj.get('foo'))
-        # Bar is not loaded, so we should get the default
-        self.assertEqual('not-loaded', obj.get('bar', 'not-loaded'))
-        # Bar without a default should lazy-load
-        self.assertEqual('loaded!', obj.get('bar'))
-        # Bar now has a default, but loaded value should be returned
-        self.assertEqual('loaded!', obj.get('bar', 'not-loaded'))
-        # Invalid attribute should return None
-        self.assertEqual(None, obj.get('nothing'))
-
-    def test_object_inheritance(self):
-        base_fields = base.CinderPersistentObject.fields.keys()
-        myobj_fields = (['foo', 'bar', 'missing',
-                         'readonly', 'rel_object', 'rel_objects'] +
-                        base_fields)
-        myobj3_fields = ['new_field']
-        self.assertTrue(issubclass(TestSubclassedObject, MyObj))
-        self.assertEqual(len(myobj_fields), len(MyObj.fields))
-        self.assertEqual(set(myobj_fields), set(MyObj.fields.keys()))
-        self.assertEqual(len(myobj_fields) + len(myobj3_fields),
-                         len(TestSubclassedObject.fields))
-        self.assertEqual(set(myobj_fields) | set(myobj3_fields),
-                         set(TestSubclassedObject.fields.keys()))
-
-    def test_obj_as_admin(self):
-        obj = MyObj(context=self.context)
-
-        def fake(*args, **kwargs):
-            self.assertTrue(obj._context.is_admin)
-
-        with mock.patch.object(obj, 'obj_reset_changes') as mock_fn:
-            mock_fn.side_effect = fake
-            with obj.obj_as_admin():
-                obj.save()
-            self.assertTrue(mock_fn.called)
-
-        self.assertFalse(obj._context.is_admin)
-
-    def test_obj_as_admin_orphaned(self):
-        def testme():
-            obj = MyObj()
-            with obj.obj_as_admin():
-                pass
-        self.assertRaises(exception.OrphanedObjectError, testme)
-
-    def test_get_changes(self):
-        obj = MyObj()
-        self.assertEqual({}, obj.obj_get_changes())
-        obj.foo = 123
-        self.assertEqual({'foo': 123}, obj.obj_get_changes())
-        obj.bar = 'test'
-        self.assertEqual({'foo': 123, 'bar': 'test'}, obj.obj_get_changes())
-        obj.obj_reset_changes()
-        self.assertEqual({}, obj.obj_get_changes())
-
-    def test_obj_fields(self):
-        class TestObj(base.CinderObject):
-            fields = {'foo': fields.Field(fields.Integer())}
-            obj_extra_fields = ['bar']
-
-            @property
-            def bar(self):
-                return 'this is bar'
-
-        obj = TestObj()
-        self.assertEqual(['foo', 'bar'], obj.obj_fields)
-
-    def test_obj_constructor(self):
-        obj = MyObj(context=self.context, foo=123, bar='abc')
-        self.assertEqual(123, obj.foo)
-        self.assertEqual('abc', obj.bar)
-        self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
-
-    def test_obj_read_only(self):
-        obj = MyObj(context=self.context, foo=123, bar='abc')
-        obj.readonly = 1
-        self.assertRaises(exception.ReadOnlyFieldError, setattr,
-                          obj, 'readonly', 2)
-
-    def test_obj_repr(self):
-        obj = MyObj(foo=123)
-        self.assertEqual('MyObj(bar=<?>,created_at=<?>,deleted=<?>,'
-                         'deleted_at=<?>,foo=123,missing=<?>,readonly=<?>,'
-                         'rel_object=<?>,rel_objects=<?>,updated_at=<?>)',
-                         repr(obj))
-
-    def test_obj_make_obj_compatible(self):
-        subobj = MyOwnedObject(baz=1)
-        obj = MyObj(rel_object=subobj)
-        obj.obj_relationships = {
-            'rel_object': [('1.5', '1.1'), ('1.7', '1.2')],
-        }
-        primitive = obj.obj_to_primitive()['cinder_object.data']
-        with mock.patch.object(subobj, 'obj_make_compatible') as mock_compat:
-            obj._obj_make_obj_compatible(copy.copy(primitive), '1.8',
-                                         'rel_object')
-            self.assertFalse(mock_compat.called)
-
-        with mock.patch.object(subobj, 'obj_make_compatible') as mock_compat:
-            obj._obj_make_obj_compatible(copy.copy(primitive),
-                                         '1.7', 'rel_object')
-            mock_compat.assert_called_once_with(
-                primitive['rel_object']['cinder_object.data'], '1.2')
-            self.assertEqual('1.2',
-                             primitive['rel_object']['cinder_object.version'])
-
-        with mock.patch.object(subobj, 'obj_make_compatible') as mock_compat:
-            obj._obj_make_obj_compatible(copy.copy(primitive),
-                                         '1.6', 'rel_object')
-            mock_compat.assert_called_once_with(
-                primitive['rel_object']['cinder_object.data'], '1.1')
-            self.assertEqual('1.1',
-                             primitive['rel_object']['cinder_object.version'])
-
-        with mock.patch.object(subobj, 'obj_make_compatible') as mock_compat:
-            obj._obj_make_obj_compatible(copy.copy(primitive), '1.5',
-                                         'rel_object')
-            mock_compat.assert_called_once_with(
-                primitive['rel_object']['cinder_object.data'], '1.1')
-            self.assertEqual('1.1',
-                             primitive['rel_object']['cinder_object.version'])
-
-        with mock.patch.object(subobj, 'obj_make_compatible') as mock_compat:
-            _prim = copy.copy(primitive)
-            obj._obj_make_obj_compatible(_prim, '1.4', 'rel_object')
-            self.assertFalse(mock_compat.called)
-            self.assertNotIn('rel_object', _prim)
-
-    def test_obj_make_compatible_hits_sub_objects(self):
-        subobj = MyOwnedObject(baz=1)
-        obj = MyObj(foo=123, rel_object=subobj)
-        obj.obj_relationships = {'rel_object': [('1.0', '1.0')]}
-        with mock.patch.object(obj, '_obj_make_obj_compatible') as mock_compat:
-            obj.obj_make_compatible({'rel_object': 'foo'}, '1.10')
-            mock_compat.assert_called_once_with({'rel_object': 'foo'}, '1.10',
-                                                'rel_object')
-
-    def test_obj_make_compatible_skips_unset_sub_objects(self):
-        obj = MyObj(foo=123)
-        obj.obj_relationships = {'rel_object': [('1.0', '1.0')]}
-        with mock.patch.object(obj, '_obj_make_obj_compatible') as mock_compat:
-            obj.obj_make_compatible({'rel_object': 'foo'}, '1.10')
-            self.assertFalse(mock_compat.called)
-
-    def test_obj_make_compatible_complains_about_missing_rules(self):
-        subobj = MyOwnedObject(baz=1)
-        obj = MyObj(foo=123, rel_object=subobj)
-        obj.obj_relationships = {}
-        self.assertRaises(exception.ObjectActionError,
-                          obj.obj_make_compatible, {}, '1.0')
-
-    def test_obj_make_compatible_handles_list_of_objects(self):
-        subobj = MyOwnedObject(baz=1)
-        obj = MyObj(rel_objects=[subobj])
-        obj.obj_relationships = {'rel_objects': [('1.0', '1.123')]}
-
-        def fake_make_compat(primitive, version):
-            self.assertEqual('1.123', version)
-            self.assertIn('baz', primitive)
-
-        with mock.patch.object(subobj, 'obj_make_compatible') as mock_mc:
-            mock_mc.side_effect = fake_make_compat
-            obj.obj_to_primitive('1.0')
-            self.assertTrue(mock_mc.called)
-
-
-class TestObject(_LocalTest, _TestObject):
-    def test_set_defaults(self):
-        obj = MyObj()
-        obj.obj_set_defaults('foo')
-        self.assertTrue(obj.obj_attr_is_set('foo'))
-        self.assertEqual(1, obj.foo)
-
-    def test_set_defaults_no_default(self):
-        obj = MyObj()
-        self.assertRaises(exception.ObjectActionError,
-                          obj.obj_set_defaults, 'bar')
-
-    def test_set_all_defaults(self):
-        obj = MyObj()
-        obj.obj_set_defaults()
-        self.assertEqual(set(['deleted', 'foo']), obj.obj_what_changed())
-        self.assertEqual(1, obj.foo)
-
-
-class TestObjectListBase(test.TestCase):
-    def test_list_like_operations(self):
-        class MyElement(base.CinderObject):
-            fields = {'foo': fields.IntegerField()}
-
-            def __init__(self, foo):
-                super(MyElement, self).__init__()
-                self.foo = foo
-
-        class Foo(base.ObjectListBase, base.CinderObject):
-            fields = {'objects': fields.ListOfObjectsField('MyElement')}
-
-        objlist = Foo(context='foo',
-                      objects=[MyElement(1), MyElement(2), MyElement(3)])
-        self.assertEqual(list(objlist), objlist.objects)
-        self.assertEqual(len(objlist), 3)
-        self.assertIn(objlist.objects[0], objlist)
-        self.assertEqual(list(objlist[:1]), [objlist.objects[0]])
-        self.assertEqual(objlist[:1]._context, 'foo')
-        self.assertEqual(objlist[2], objlist.objects[2])
-        self.assertEqual(objlist.count(objlist.objects[0]), 1)
-        self.assertEqual(objlist.index(objlist.objects[1]), 1)
-        objlist.sort(key=lambda x: x.foo, reverse=True)
-        self.assertEqual([3, 2, 1],
-                         [x.foo for x in objlist])
-
-    def test_serialization(self):
-        class Foo(base.ObjectListBase, base.CinderObject):
-            fields = {'objects': fields.ListOfObjectsField('Bar')}
-
-        class Bar(base.CinderObject):
-            fields = {'foo': fields.Field(fields.String())}
-
-        obj = Foo(objects=[])
-        for i in 'abc':
-            bar = Bar(foo=i)
-            obj.objects.append(bar)
-
-        obj2 = base.CinderObject.obj_from_primitive(obj.obj_to_primitive())
-        self.assertFalse(obj is obj2)
-        self.assertEqual([x.foo for x in obj],
-                         [y.foo for y in obj2])
-
-    def test_list_changes(self):
-        class Foo(base.ObjectListBase, base.CinderObject):
-            fields = {'objects': fields.ListOfObjectsField('Bar')}
-
-        class Bar(base.CinderObject):
-            fields = {'foo': fields.StringField()}
-
-        obj = Foo(objects=[])
-        self.assertEqual(set(['objects']), obj.obj_what_changed())
-        obj.objects.append(Bar(foo='test'))
-        self.assertEqual(set(['objects']), obj.obj_what_changed())
-        obj.obj_reset_changes()
-        # This should still look dirty because the child is dirty
-        self.assertEqual(set(['objects']), obj.obj_what_changed())
-        obj.objects[0].obj_reset_changes()
-        # This should now look clean because the child is clean
-        self.assertEqual(set(), obj.obj_what_changed())
-
-    def test_initialize_objects(self):
-        class Foo(base.ObjectListBase, base.CinderObject):
-            fields = {'objects': fields.ListOfObjectsField('Bar')}
-
-        class Bar(base.CinderObject):
-            fields = {'foo': fields.StringField()}
-
-        obj = Foo()
-        self.assertEqual([], obj.objects)
-        self.assertEqual(set(), obj.obj_what_changed())
-
-    def test_obj_repr(self):
-        class Foo(base.ObjectListBase, base.CinderObject):
-            fields = {'objects': fields.ListOfObjectsField('Bar')}
-
-        class Bar(base.CinderObject):
-            fields = {'uuid': fields.StringField()}
-
-        obj = Foo(objects=[Bar(uuid='fake-uuid')])
-        self.assertEqual('Foo(objects=[Bar(fake-uuid)])', repr(obj))
-
-
-class TestObjectSerializer(_BaseTestCase):
-    def test_serialize_entity_primitive(self):
-        ser = base.CinderObjectSerializer()
-        for thing in (1, 'foo', [1, 2], {'foo': 'bar'}):
-            self.assertEqual(thing, ser.serialize_entity(None, thing))
-
-    def test_deserialize_entity_primitive(self):
-        ser = base.CinderObjectSerializer()
-        for thing in (1, 'foo', [1, 2], {'foo': 'bar'}):
-            self.assertEqual(thing, ser.deserialize_entity(None, thing))
-
-    def _test_deserialize_entity_newer(self, obj_version, backported_to,
-                                       my_version='1.6'):
-        ser = base.CinderObjectSerializer()
-        ser._conductor = mock.Mock()
-        ser._conductor.object_backport.return_value = 'backported'
-
-        class MyTestObj(MyObj):
-            VERSION = my_version
-
-        obj = MyTestObj()
-        obj.VERSION = obj_version
-        primitive = obj.obj_to_primitive()
-        ser.deserialize_entity(self.context, primitive)
-        if backported_to is None:
-            self.assertFalse(ser._conductor.object_backport.called)
-
-    def test_deserialize_entity_newer_revision_does_not_backport_zero(self):
-        self._test_deserialize_entity_newer('1.6.0', None)
-
-    def test_deserialize_entity_newer_revision_does_not_backport(self):
-        self._test_deserialize_entity_newer('1.6.1', None)
-
-    def test_deserialize_dot_z_with_extra_stuff(self):
-        primitive = {'cinder_object.name': 'MyObj',
-                     'cinder_object.namespace': 'cinder',
-                     'cinder_object.version': '1.6.1',
-                     'cinder_object.data': {
-                         'foo': 1,
-                         'unexpected_thing': 'foobar'}}
-        ser = base.CinderObjectSerializer()
-        obj = ser.deserialize_entity(self.context, primitive)
-        self.assertEqual(1, obj.foo)
-        self.assertFalse(hasattr(obj, 'unexpected_thing'))
-        # NOTE(danms): The serializer is where the logic lives that
-        # avoids backports for cases where only a .z difference in
-        # the received object version is detected. As a result, we
-        # end up with a version of what we expected, effectively the
-        # .0 of the object.
-        self.assertEqual('1.6', obj.VERSION)
-
-    def test_object_serialization(self):
-        ser = base.CinderObjectSerializer()
-        obj = MyObj()
-        primitive = ser.serialize_entity(self.context, obj)
-        self.assertIn('cinder_object.name', primitive)
-        obj2 = ser.deserialize_entity(self.context, primitive)
-        self.assertIsInstance(obj2, MyObj)
-        self.assertEqual(self.context, obj2._context)
-
-    def test_object_serialization_iterables(self):
-        ser = base.CinderObjectSerializer()
-        obj = MyObj()
-        for iterable in (list, tuple, set):
-            thing = iterable([obj])
-            primitive = ser.serialize_entity(self.context, thing)
-            self.assertEqual(1, len(primitive))
-            for item in primitive:
-                self.assertNotIsInstance(item, base.CinderObject)
-            thing2 = ser.deserialize_entity(self.context, primitive)
-            self.assertEqual(1, len(thing2))
-            for item in thing2:
-                self.assertIsInstance(item, MyObj)
-        # dict case
-        thing = {'key': obj}
-        primitive = ser.serialize_entity(self.context, thing)
-        self.assertEqual(1, len(primitive))
-        for item in primitive.itervalues():
-            self.assertNotIsInstance(item, base.CinderObject)
-        thing2 = ser.deserialize_entity(self.context, primitive)
-        self.assertEqual(1, len(thing2))
-        for item in thing2.itervalues():
-            self.assertIsInstance(item, MyObj)
-
-        # object-action updates dict case
-        thing = {'foo': obj.obj_to_primitive()}
-        primitive = ser.serialize_entity(self.context, thing)
-        self.assertEqual(thing, primitive)
-        thing2 = ser.deserialize_entity(self.context, thing)
-        self.assertIsInstance(thing2['foo'], base.CinderObject)
index 48068a8905b777ebb7eaa6158d4944b50cbdec38..e1a76e7f015f7bd50da54ac15ea62406dc8bf394 100644 (file)
 #    under the License.
 
 import mock
+from oslo_versionedobjects.tests import test_objects
 
 from cinder.objects import snapshot as snapshot_obj
 from cinder.objects import volume as volume_obj
 from cinder.tests.unit import fake_volume
-from cinder.tests.unit.objects import test_objects
 
 fake_snapshot = {
     'id': '1',
@@ -68,7 +68,7 @@ class TestSnapshot(test_objects._LocalTest):
         snapshot = snapshot_obj.Snapshot._from_db_object(
             self.context, snapshot_obj.Snapshot(), fake_snapshot)
         snapshot.display_name = 'foobar'
-        snapshot.save(self.context)
+        snapshot.save()
         snapshot_update.assert_called_once_with(self.context, snapshot.id,
                                                 {'display_name': 'foobar'})
 
@@ -84,7 +84,7 @@ class TestSnapshot(test_objects._LocalTest):
         self.assertEqual({'display_name': 'foobar',
                           'metadata': {'key1': 'value1'}},
                          snapshot.obj_get_changes())
-        snapshot.save(self.context)
+        snapshot.save()
         snapshot_update.assert_called_once_with(self.context, snapshot.id,
                                                 {'display_name': 'foobar'})
         snapshot_metadata_update.assert_called_once_with(self.context, '1',
@@ -128,7 +128,7 @@ class TestSnapshot(test_objects._LocalTest):
 
 class TestSnapshotList(test_objects._LocalTest):
     @mock.patch('cinder.db.snapshot_metadata_get', return_value={})
-    @mock.patch('cinder.objects.Volume.get_by_id')
+    @mock.patch('cinder.objects.volume.Volume.get_by_id')
     @mock.patch('cinder.db.snapshot_get_all', return_value=[fake_snapshot])
     def test_get_all(self, snapshot_get_all, volume_get_by_id,
                      snapshot_metadata_get):
@@ -140,7 +140,7 @@ class TestSnapshotList(test_objects._LocalTest):
         TestSnapshot._compare(self, fake_snapshot, snapshots[0])
 
     @mock.patch('cinder.db.snapshot_metadata_get', return_value={})
-    @mock.patch('cinder.objects.Volume.get_by_id')
+    @mock.patch('cinder.objects.volume.Volume.get_by_id')
     @mock.patch('cinder.db.snapshot_get_all_by_project',
                 return_value=[fake_snapshot])
     def test_get_all_by_project(self, get_all_by_project, volume_get_by_id,
@@ -149,12 +149,12 @@ class TestSnapshotList(test_objects._LocalTest):
         volume_get_by_id.return_value = fake_volume_obj
 
         snapshots = snapshot_obj.SnapshotList.get_all_by_project(
-            self.context, self.context.project_id)
+            self.context, self.project_id)
         self.assertEqual(1, len(snapshots))
         TestSnapshot._compare(self, fake_snapshot, snapshots[0])
 
     @mock.patch('cinder.db.snapshot_metadata_get', return_value={})
-    @mock.patch('cinder.objects.Volume.get_by_id')
+    @mock.patch('cinder.objects.volume.Volume.get_by_id')
     @mock.patch('cinder.db.snapshot_get_all_for_volume',
                 return_value=[fake_snapshot])
     def test_get_all_for_volume(self, get_all_for_volume, volume_get_by_id,
index d6f407a703f2cbea06afe6117c136b535feb4457..6d8f2dc503c04f44050b8b534813fcf2da05777e 100644 (file)
 #    under the License.
 
 import mock
+from oslo_versionedobjects.tests import test_objects
 
 from cinder import objects
 from cinder.tests.unit import fake_volume
-from cinder.tests.unit.objects import test_objects
 
 
 class TestVolume(test_objects._LocalTest):
index 56379bfc80babc6343a14c4de09198d6cf8aec9a..b51a560d7ad74008709f6474c5becd2305a0150f 100644 (file)
@@ -105,7 +105,7 @@ def check_policy(context, action, target_obj=None):
 
     if isinstance(target_obj, objects_base.CinderObject):
         # Turn object into dict so target.update can work
-        target.update(objects_base.obj_to_primitive(target_obj) or {})
+        target.update(target_obj.obj_to_primitive() or {})
     else:
         target.update(target_obj or {})
 
@@ -920,7 +920,7 @@ class API(base.Base):
 
         snapshot_obj = self.get_snapshot(context, snapshot['id'])
         snapshot_obj.status = 'deleting'
-        snapshot_obj.save(context)
+        snapshot_obj.save()
 
         volume = self.db.volume_get(context, snapshot_obj.volume_id)
         self.volume_rpcapi.delete_snapshot(context, snapshot_obj,
@@ -931,7 +931,7 @@ class API(base.Base):
     @wrap_check_policy
     def update_snapshot(self, context, snapshot, fields):
         snapshot.update(fields)
-        snapshot.save(context)
+        snapshot.save()
 
     @wrap_check_policy
     def get_volume_metadata(self, context, volume):
@@ -1078,7 +1078,7 @@ class API(base.Base):
         self._check_metadata_properties(_metadata)
 
         snapshot.metadata = _metadata
-        snapshot.save(context)
+        snapshot.save()
 
         # TODO(jdg): Implement an RPC call for drivers that may use this info
 
index 220a9a9a1fffa60f41cb4300e2014a385f16d545..63034a6701086b222a5d31d878fd7720ae49510a 100644 (file)
@@ -643,12 +643,12 @@ class VolumeManager(manager.SchedulerDependentManager):
             model_update = self.driver.create_snapshot(snapshot)
             if model_update:
                 snapshot.update(model_update)
-                snapshot.save(context)
+                snapshot.save()
 
         except Exception:
             with excutils.save_and_reraise_exception():
                 snapshot.status = 'error'
-                snapshot.save(context)
+                snapshot.save()
 
         vol_ref = self.db.volume_get(context, volume_id)
         if vol_ref.bootable:
@@ -666,12 +666,12 @@ class VolumeManager(manager.SchedulerDependentManager):
                                   " %(volume_id)s metadata"),
                               {'volume_id': volume_id}, resource=snapshot)
                 snapshot.status = 'error'
-                snapshot.save(context)
+                snapshot.save()
                 raise exception.MetadataCopyFailure(reason=six.text_type(ex))
 
         snapshot.status = 'available'
         snapshot.progress = '100%'
-        snapshot.save(context)
+        snapshot.save()
 
         self._notify_about_snapshot_usage(context, snapshot, "create.end")
         LOG.info(_LI("Create snapshot completed successfully"),
@@ -682,6 +682,7 @@ class VolumeManager(manager.SchedulerDependentManager):
     def delete_snapshot(self, context, snapshot):
         """Deletes and unexports snapshot."""
         context = context.elevated()
+        snapshot._context = context
         project_id = snapshot.project_id
 
         self._notify_about_snapshot_usage(
@@ -731,7 +732,7 @@ class VolumeManager(manager.SchedulerDependentManager):
             LOG.exception(_LE("Update snapshot usages failed."),
                           resource=snapshot)
         self.db.volume_glance_metadata_delete_by_snapshot(context, snapshot.id)
-        snapshot.destroy(context)
+        snapshot.destroy()
         self._notify_about_snapshot_usage(context, snapshot, "delete.end")
 
         # Commit the reservations
index 7fa73aa09123b5edf9ec7425e755f1dc7ec0c26b..b8a21532ba5555853dc5c7296343f5d4b92f6a89 100644 (file)
@@ -22,6 +22,7 @@ oslo.middleware>=1.0.0                  # Apache-2.0
 oslo.rootwrap>=1.6.0  # Apache-2.0
 oslo.serialization>=1.4.0               # Apache-2.0
 oslo.utils>=1.4.0                       # Apache-2.0
+oslo.versionedobjects>=0.1.1
 osprofiler>=0.3.0                       # Apache-2.0
 paramiko>=1.13.0
 Paste