From 07a90676f79935507487da247cbc77f6542cbd99 Mon Sep 17 00:00:00 2001 From: Gorka Eguileor Date: Tue, 30 May 2017 12:13:06 +0200 Subject: [PATCH] Update OVO devref On commit f0d34b7d9ba79af1b50c1e89e2cd3493075c2754 we changed the way we did backports for OVOs during upgrades removing the need to bump objects when related objects are modified. This patch updates the documentation to reflect this as well as adds a new example of backporting code for the case where we have added new fields in the latest version of an object. TrivialFix Change-Id: I327235fdafdcee2d36395c4c64372caf6b79d1e2 --- doc/source/devref/rolling.upgrades.rst | 63 +++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/doc/source/devref/rolling.upgrades.rst b/doc/source/devref/rolling.upgrades.rst index 5b4b9668193..7d64b8ee2d1 100644 --- a/doc/source/devref/rolling.upgrades.rst +++ b/doc/source/devref/rolling.upgrades.rst @@ -265,12 +265,6 @@ version bump is not required in this case it's because the actual data doesn't change, we are just removing magic string by an enumerate, but the strings used are exactly the same. -A case where we may not notice that a version bump is required is on chained -versioned objects. That is to say, when you we change the version of a -versioned object and that object is used in another versioned object with a -``fields.ObjectField``. In such a case all versioned objects that are -including changed versioned object will also require a version bump. - As mentioned before, you don't have to know all the rules, as we have a test that calculates the hash of all objects taking all these rules into consideration and will tell you exactly when you need to bump the version of a @@ -285,6 +279,17 @@ Then you'll see which versioned object requires a bump and you need to bump that version and update the object_data dictionary in the test file to reflect the new version as well as the new hash. +There is a very common false positive on the version bump test, and that is +when we have modified a versioned object that is being used by other objects +using the ``fields.ObjectField`` class. Due to the backporting mechanism +implemented in Cinder we don't require bumping the version for these cases and +we'll just need to update the hash used in the test. + +For example if we were to add a new field to the Volume object and then run the +test we may think that we need to bump Volume, Snapshot, Backup, RequestSpec, +and VolumeAttachment objects, but we really only need to bump the version of +the Volume object and update the hash for all the other objects. + Imagine that we (finally!) decide that :code:`request_spec` sent in :code:`create_volume` RPC cast is duplicating data and we want to start to remove redundant occurrences. When running in version-mixed environment older @@ -354,3 +359,49 @@ environment and without when all services are upgraded and will understand Note that o.vo layer is able to recursively downgrade all of its fields, so when `request_spec` will be used as a field in other object, it will be correctly downgraded. + +A more common case where we need backporting code is when we add new fields. +In such case the backporting consist on removing the newly added fields. For +example if we add 3 new fields to the Group object in version 1.1, then we need +to remove them if backporting to earlier versions:: + + from oslo_utils import versionutils + + def obj_make_compatible(self, primitive, target_version): + super(Group, self).obj_make_compatible(primitive, target_version) + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + for key in ('group_snapshot_id', 'source_group_id', + 'group_snapshots'): + primitive.pop(key, None) + +As time goes on we will be adding more and more new fields to our objects, so +we may end up with a long series of if and for statements like in the Volume +object:: + + from oslo_utils import versionutils + + def obj_make_compatible(self, primitive, target_version): + super(Volume, self).obj_make_compatible(primitive, target_version) + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 4): + for key in ('cluster', 'cluster_name'): + primitive.pop(key, None) + if target_version < (1, 5): + for key in ('group', 'group_id'): + primitive.pop(key, None) + +So a different pattern would be preferable as it will make the backporting +easier for future additions:: + + from oslo_utils import versionutils + + def obj_make_compatible(self, primitive, target_version): + added_fields = (((1, 4), ('cluster', 'cluster_name')), + ((1, 5), ('group', 'group_id'))) + super(Volume, self).obj_make_compatible(primitive, target_version) + target_version = versionutils.convert_version_to_tuple(target_version) + for version, remove_fields in added_fields: + if target_version < version: + for obj_field in remove_fields: + primitive.pop(obj_field, None)