]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Report RPC and objects versions
authorMichał Dulko <michal.dulko@intel.com>
Thu, 14 Jan 2016 18:37:18 +0000 (19:37 +0100)
committerMichał Dulko <michal.dulko@intel.com>
Fri, 29 Jan 2016 13:35:13 +0000 (08:35 -0500)
While doing a rolling upgrade we will have services running in various
versions. In order to determine how to downgrade the RPC request and
payload (objects) to the lowest common version we need to actually
report versions of RPC servers (managers).

This commit implements such reporting in generic cinder.service module.
It is using DB columns that were merged in Liberty to save this
information.

To have a single version string identify a set of o.vo versions we need
to have dictionary with objects versions history. In that purpose a
dict-like CinderObjectVersionsHistory class and OBJ_VERSIONS instance of
it is added to cinder.objects.base. A unit test enforcing bumping the
versions is also included with the patch.

Change-Id: Ic3b57450e9d6f155a7eb805d224389f5f09eae18
Partial-Implements: blueprint rpc-object-compatibility

cinder/objects/base.py
cinder/objects/service.py
cinder/service.py
cinder/tests/unit/objects/test_objects.py

index ef8c1150af0d68d9840229010fa0c905a15bc589..f55ea4c6f9816da4c0f872812b39588b4bcd63ef 100644 (file)
@@ -33,6 +33,71 @@ remotable_classmethod = base.remotable_classmethod
 obj_make_list = base.obj_make_list
 
 
+class CinderObjectVersionsHistory(dict):
+    """Helper class that maintains objects version history.
+
+    Current state of object versions is aggregated in a single version number
+    that explicitily identifies a set of object versions. That way a service
+    is able to report what objects it supports using a single string and all
+    the newer services will know exactly what that mean for a single object.
+    """
+
+    def __init__(self):
+        super(CinderObjectVersionsHistory, self).__init__()
+        # NOTE(dulek): This is our pre-history and a starting point - Liberty.
+        # We want Mitaka to be able to talk to Liberty services, so we need to
+        # handle backporting to these objects versions (although I don't expect
+        # we've made a lot of incompatible changes inside the objects).
+        #
+        # If an object doesn't exist in Liberty, RPC API compatibility layer
+        # shouldn't send it or convert it to a dictionary.
+        #
+        # Please note that we do not need to add similar entires for each
+        # release. Liberty is here just for historical reasons.
+        self.versions = ['liberty']
+        self['liberty'] = {
+            'Backup': '1.1',
+            'BackupImport': '1.1',
+            'BackupList': '1.0',
+            'ConsistencyGroup': '1.1',
+            'ConsistencyGroupList': '1.0',
+            'Service': '1.0',
+            'ServiceList': '1.0',
+            'Snapshot': '1.0',
+            'SnapshotList': '1.0',
+            'Volume': '1.1',
+            'VolumeAttachment': '1.0',
+            'VolumeAttachmentList': '1.0',
+            'VolumeList': '1.1',
+            'VolumeType': '1.0',
+            'VolumeTypeList': '1.0',
+        }
+
+    def get_current(self):
+        return self.versions[-1]
+
+    def get_current_versions(self):
+        return self[self.get_current()]
+
+    def add(self, ver, updates):
+        self[ver] = self[self.get_current()].copy()
+        self.versions.append(ver)
+        self[ver].update(updates)
+
+
+OBJ_VERSIONS = CinderObjectVersionsHistory()
+# NOTE(dulek): You should add a new version here each time you bump a version
+# of any object. As a second parameter you need to specify only what changed.
+#
+# When dropping backward compatibility with an OpenStack release we can rework
+# this and remove some history while keeping the versions order.
+OBJ_VERSIONS.add('1.0', {'Backup': '1.3', 'BackupImport': '1.3',
+                         'CGSnapshot': '1.0', 'CGSnapshotList': '1.0',
+                         'ConsistencyGroup': '1.2',
+                         'ConsistencyGroupList': '1.1', 'Service': '1.1',
+                         'Volume': '1.3', 'VolumeTypeList': '1.1'})
+
+
 class CinderObjectRegistry(base.VersionedObjectRegistry):
     def registration_hook(self, cls, index):
         setattr(objects, cls.obj_name(), cls)
index bc4f0d848e3b9f65d3e127ed250ce769913df138..ab4847db3391416567a0598ec4e1e62a6bdf16b7 100644 (file)
@@ -32,7 +32,8 @@ class Service(base.CinderPersistentObject, base.CinderObject,
               base.CinderObjectDictCompat,
               base.CinderComparableObject):
     # Version 1.0: Initial version
-    VERSION = '1.0'
+    # Version 1.1: Add rpc_current_version and object_current_version fields
+    VERSION = '1.1'
 
     fields = {
         'id': fields.IntegerField(),
@@ -46,6 +47,8 @@ class Service(base.CinderPersistentObject, base.CinderObject,
         'disabled_reason': fields.StringField(nullable=True),
 
         'modified_at': fields.DateTimeField(nullable=True),
+        'rpc_current_version': fields.StringField(nullable=True),
+        'object_current_version': fields.StringField(nullable=True),
     }
 
     def obj_make_compatible(self, primitive, target_version):
index 66c015e212582c89b94c77b9b5e26835577e66ba..4871b524a17ead7a9b2ee8578130d99a145e9a7c 100644 (file)
@@ -150,6 +150,10 @@ class Service(service.Service):
         try:
             service_ref = objects.Service.get_by_args(
                 ctxt, self.host, self.binary)
+            service_ref.rpc_current_version = self.manager.RPC_API_VERSION
+            obj_version = objects_base.OBJ_VERSIONS.get_current()
+            service_ref.object_current_version = obj_version
+            service_ref.save()
             self.service_id = service_ref.id
         except exception.NotFound:
             self._create_service_ref(ctxt)
@@ -203,11 +207,15 @@ class Service(service.Service):
 
     def _create_service_ref(self, context):
         zone = CONF.storage_availability_zone
-        kwargs = {'host': self.host,
-                  'binary': self.binary,
-                  'topic': self.topic,
-                  'report_count': 0,
-                  'availability_zone': zone}
+        kwargs = {
+            'host': self.host,
+            'binary': self.binary,
+            'topic': self.topic,
+            'report_count': 0,
+            'availability_zone': zone,
+            'rpc_current_version': self.manager.RPC_API_VERSION,
+            'object_current_version': objects_base.OBJ_VERSIONS.get_current(),
+        }
         service_ref = objects.Service(context=context, **kwargs)
         service_ref.create()
         self.service_id = service_ref.id
index bda91f30b2bf0090e4c7e086418d76d690475b34..8908c25f9a86038068d598c8a0f38b73a5841a1f 100644 (file)
@@ -28,7 +28,7 @@ object_data = {
     'CGSnapshotList': '1.0-e8c3f4078cd0ee23487b34d173eec776',
     'ConsistencyGroup': '1.2-ed7f90a6871991a19af716ade7337fc9',
     'ConsistencyGroupList': '1.1-73916823b697dfa0c7f02508d87e0f28',
-    'Service': '1.0-64baeb4911dbab1153064dd1c87edb9f',
+    'Service': '1.1-9eb00cbd8e2bfb7371343429af54d6e8',
     'ServiceList': '1.0-d242d3384b68e5a5a534e090ff1d5161',
     'Snapshot': '1.0-a6c33eefeadefb324d79f72f66c54e9a',
     'SnapshotList': '1.0-71661e7180ef6cc51501704a9bea4bf1',
@@ -51,3 +51,20 @@ class TestObjectVersions(test.TestCase):
                          'Some objects have changed; please make sure the '
                          'versions have been bumped, and then update their '
                          'hashes in the object_data map in this test module.')
+
+    def test_versions_history(self):
+        classes = base.CinderObjectRegistry.obj_classes()
+        versions = base.OBJ_VERSIONS.get_current_versions()
+        expected = {}
+        actual = {}
+        for name, cls in classes.items():
+            if name not in versions:
+                expected[name] = cls[0].VERSION
+            elif cls[0].VERSION != versions[name]:
+                expected[name] = cls[0].VERSION
+                actual[name] = versions[name]
+
+        self.assertEqual(expected, actual,
+                         'Some objects versions have changed; please make '
+                         'sure a new objects history version was added in '
+                         'cinder.objects.base.OBJ_VERSIONS.')