# Copyright 2013 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. """Zun common internal object model""" from oslo_versionedobjects import base as ovoo_base from oslo_versionedobjects import fields as ovoo_fields from zun.objects import fields as obj_fields remotable_classmethod = ovoo_base.remotable_classmethod remotable = ovoo_base.remotable class ZunObjectRegistry(ovoo_base.VersionedObjectRegistry): pass class ZunObject(ovoo_base.VersionedObject): """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. """ OBJ_SERIAL_NAMESPACE = 'zun_object' OBJ_PROJECT_NAMESPACE = 'zun' def as_dict(self): return {k: getattr(self, k) for k in self.fields if self.obj_attr_is_set(k)} class ZunPersistentObject(object): """Mixin class for Persistent objects. This adds the fields that we use in common for all persistent objects. """ fields = { 'created_at': ovoo_fields.DateTimeField(nullable=True, tzinfo_aware=False), 'updated_at': ovoo_fields.DateTimeField(nullable=True, tzinfo_aware=False), } class ZunObjectSerializer(ovoo_base.VersionedObjectSerializer): # Base class to use for object hydration OBJ_BASE_CLASS = ZunObject class ObjectListBase(ovoo_base.ObjectListBase): # NOTE: These are for transition to using the oslo # base object and can be removed when we move to it. @classmethod def _obj_primitive_key(cls, field): return 'zun_object.%s' % field @classmethod def _obj_primitive_field(cls, primitive, field, default=obj_fields.UnspecifiedDefault): key = cls._obj_primitive_key(field) if default == obj_fields.UnspecifiedDefault: return primitive[key] else: return primitive.get(key, default) def obj_to_primitive(obj): """Recursively turn an object into a python primitive. A ZunObject 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, ZunObject): 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 else: return obj def obj_equal_prims(obj_1, obj_2, ignore=None): """Compare two primitives for equivalence ignoring some keys. This operation tests the primitives of two objects for equivalence. Object primitives may contain a list identifying fields that have been changed - this is ignored in the comparison. The ignore parameter lists any other keys to be ignored. :param:obj1: The first object in the comparison :param:obj2: The second object in the comparison :param:ignore: A list of fields to ignore :returns: True if the primitives are equal ignoring changes and specified fields, otherwise False. """ def _strip(prim, keys): if isinstance(prim, dict): for k in keys: prim.pop(k, None) for v in prim.values(): _strip(v, keys) if isinstance(prim, list): for v in prim: _strip(v, keys) return prim if ignore is not None: keys = ['zun_object.changes'] + ignore else: keys = ['zun_object.changes'] prim_1 = _strip(obj_1.obj_to_primitive(), keys) prim_2 = _strip(obj_2.obj_to_primitive(), keys) return prim_1 == prim_2