Pin RPC and object version to lowest running

This commit adds detection and pinning to lowest RPC API version to all
rpcapi modules. The version pin is determined by a DB call done once per
service lifetime thanks to caching. Handling the compatibility is
guaranteed by oslo.messaging and our shims in rpcapi modules.

To achieve o.vo compatibility, a similar approach is implemented. Custom
oslo.messaging serializer is implemented that backports objects to the
lowest running version on sending.

During the process of upgrade it may happen that manager receives an
object in the version lower than current one. Handling of such
situations is up to the manager and it should do that explicitely by
checking obj.VERSION.

The patch also adds required methods to db.api and Service object.

Co-Authored-By: Thang Pham <thang.g.pham@gmail.com>

Change-Id: I649892da64f9734928a6cf0f004a369aa7aa375f
Partial-Implements: blueprint rpc-object-compatibility
This commit is contained in:
Michał Dulko
2016-01-14 20:57:12 +01:00
parent 4226d37272
commit e6d525e56d
15 changed files with 339 additions and 50 deletions

View File

@@ -27,14 +27,19 @@ __all__ = [
]
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_serialization import jsonutils
from osprofiler import profiler
import cinder.context
import cinder.exception
from cinder.i18n import _LI
from cinder import objects
from cinder.objects import base
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
TRANSPORT = None
NOTIFIER = None
@@ -160,3 +165,70 @@ def get_notifier(service=None, host=None, publisher_id=None):
if not publisher_id:
publisher_id = "%s.%s" % (service, host or CONF.host)
return NOTIFIER.prepare(publisher_id=publisher_id)
LAST_RPC_VERSIONS = {}
LAST_OBJ_VERSIONS = {}
class RPCAPI(object):
"""Mixin class aggregating methods related to RPC API compatibility."""
RPC_API_VERSION = '1.0'
TOPIC = ''
BINARY = ''
def __init__(self):
target = messaging.Target(topic=self.TOPIC,
version=self.RPC_API_VERSION)
obj_version_cap = self._determine_obj_version_cap()
serializer = base.CinderObjectSerializer(obj_version_cap)
rpc_version_cap = self._determine_rpc_version_cap()
self.client = get_client(target, version_cap=rpc_version_cap,
serializer=serializer)
def _determine_rpc_version_cap(self):
global LAST_RPC_VERSIONS
if self.BINARY in LAST_RPC_VERSIONS:
return LAST_RPC_VERSIONS[self.BINARY]
version_cap = objects.Service.get_minimum_rpc_version(
cinder.context.get_admin_context(), self.BINARY)
if version_cap == 'liberty':
# NOTE(dulek): This means that one of the services is Liberty,
# we should cap to it's RPC version.
version_cap = LIBERTY_RPC_VERSIONS[self.BINARY]
LOG.info(_LI('Automatically selected %(binary)s RPC version '
'%(version)s as minimum service version.'),
{'binary': self.BINARY, 'version': version_cap})
LAST_RPC_VERSIONS[self.BINARY] = version_cap
return version_cap
def _determine_obj_version_cap(self):
global LAST_OBJ_VERSIONS
if self.BINARY in LAST_OBJ_VERSIONS:
return LAST_OBJ_VERSIONS[self.BINARY]
version_cap = objects.Service.get_minimum_obj_version(
cinder.context.get_admin_context(), self.BINARY)
LOG.info(_LI('Automatically selected %(binary)s objects version '
'%(version)s as minimum service version.'),
{'binary': self.BINARY, 'version': version_cap})
LAST_OBJ_VERSIONS[self.BINARY] = version_cap
return version_cap
# FIXME(dulek): Liberty haven't reported its RPC versions, so we need to have
# them hardcoded. This dict may go away as soon as we drop compatibility with
# L, which should be in early N.
#
# This is the only time we need to have such dictionary. We don't need to add
# similar ones for any release following Liberty.
LIBERTY_RPC_VERSIONS = {
'cinder-volume': '1.30',
'cinder-scheduler': '1.8',
# NOTE(dulek) backup.manager had specified version '1.2', but backup.rpcapi
# was really only sending messages up to '1.1'.
'cinder-backup': '1.1',
}