def obj_to_primitive(self, target_version=None, version_manifest=None): """Simple base-case dehydration. This calls to_primitive() for each item in fields. """ if target_version is None: target_version = self.VERSION if (utils.convert_version_to_tuple(target_version) > utils.convert_version_to_tuple(self.VERSION)): raise exception.InvalidTargetVersion(version=target_version) 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)) # NOTE(danms): If we know we're being asked for a different version, # then do the compat step. However, even if we think we're not, # we may have sub-objects that need it, so if we have a manifest we # have to traverse this object just in case. Previously, we # required a parent version bump for any child, so the target # check was enough. if target_version != self.VERSION or version_manifest: self.obj_make_compatible_from_manifest(primitive, target_version, version_manifest) obj = {self._obj_primitive_key('name'): self.obj_name(), self._obj_primitive_key('namespace'): ( self.OBJ_PROJECT_NAMESPACE), self._obj_primitive_key('version'): target_version, self._obj_primitive_key('data'): primitive} if self.obj_what_changed(): obj[self._obj_primitive_key('changes')] = list( self.obj_what_changed()) return obj
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, VersionedObject): obj.obj_make_compatible( obj._obj_primitive_field(primitive[field], 'data'), to_version) ver_key = obj._obj_primitive_key('version') primitive[field][ver_key] = to_version elif isinstance(obj, list): for i, element in enumerate(obj): element.obj_make_compatible( element._obj_primitive_field(primitive[field][i], 'data'), to_version) ver_key = element._obj_primitive_key('version') primitive[field][i][ver_key] = 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 _test_relationships_in_order(self, obj_class): for field, versions in obj_class.obj_relationships.items(): last_my_version = (0, 0) last_child_version = (0, 0) for my_version, child_version in versions: _my_version = utils.convert_version_to_tuple(my_version) _ch_version = utils.convert_version_to_tuple(child_version) assert (last_my_version < _my_version and last_child_version <= _ch_version), \ ('Object %s relationship ' '%s->%s for field %s is out of order') % ( obj_class.obj_name(), my_version, child_version, field) last_my_version = _my_version last_child_version = _ch_version
def _test_object_compatibility(self, obj_class): version = utils.convert_version_to_tuple(obj_class.VERSION) for n in range(version[1] + 1): test_version = '%d.%d' % (version[0], n) LOG.info('testing obj: %s version: %s' % (obj_class.obj_name(), test_version)) obj_class().obj_to_primitive(target_version=test_version)
def _get_subobject_version(tgt_version, relationships, backport_func): """Get the version to which we need to convert a subobject. This uses the relationships between a parent and a subobject, along with the target parent version, to decide the version we need to convert a subobject to. If the subobject did not exist in the parent at the target version, TargetBeforeChildExistedException is raised. If there is a need to backport, backport_func is called and the subobject version to backport to is passed in. :param tgt_version: The version we are converting the parent to :param relationships: A list of (parent, subobject) version tuples :param backport_func: A backport function that takes in the subobject version :returns: The version we need to convert the subobject to """ tgt = utils.convert_version_to_tuple(tgt_version) for index, versions in enumerate(relationships): parent, child = versions parent = utils.convert_version_to_tuple(parent) if tgt < parent: if index == 0: # We're backporting to a version of the parent that did # not contain this subobject raise exception.TargetBeforeSubobjectExistedException( target_version=tgt_version) else: # We're in a gap between index-1 and index, so set the desired # version to the previous index's version child = relationships[index - 1][1] backport_func(child) return elif tgt == parent: # We found the version we want, so backport to it backport_func(child) return
def test_convert_version_to_tuple(self): self.assertEqual(utils.convert_version_to_tuple('6.7.0'), (6, 7, 0))