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)
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(),
'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):
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)
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
'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',
'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.')