Add assert_min_rpc_version decorator

We have multiple places in RPC APIs where we're blocking sending RPC
calls if version cap is set to value lower than our minimal version.
This commit implements this consistently by adding a reusable decorator
that can be used with RPC API methods.

Change-Id: I11c0d79c45294bd412feffd4b301359254d2e143
This commit is contained in:
Michał Dulko 2017-01-24 15:17:41 +01:00
parent e0bf4ce7af
commit 0b7c4cc54e
3 changed files with 32 additions and 44 deletions

View File

@ -31,10 +31,11 @@ from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_utils import importutils
profiler = importutils.try_import('osprofiler.profiler')
import six
import cinder.context
import cinder.exception
from cinder.i18n import _LE, _LI
from cinder.i18n import _, _LE, _LI
from cinder import objects
from cinder.objects import base
@ -170,6 +171,28 @@ def get_notifier(service=None, host=None, publisher_id=None):
return NOTIFIER.prepare(publisher_id=publisher_id)
def assert_min_rpc_version(min_ver, exc=None):
"""Decorator to block RPC calls when version cap is lower than min_ver."""
if exc is None:
exc = cinder.exception.ServiceTooOld
def decorator(f):
@six.wraps(f)
def _wrapper(self, *args, **kwargs):
if not self.client.can_send_version(min_ver):
msg = _('One of %(binary)s services is too old to accept '
'%(method)s request. Required RPC API version is '
'%(version)s. Are you running mixed versions of '
'%(binary)ss?') % {'binary': self.BINARY,
'version': min_ver,
'method': f.__name__}
raise exc(msg)
return f(self, *args, **kwargs)
return _wrapper
return decorator
LAST_RPC_VERSIONS = {}
LAST_OBJ_VERSIONS = {}

View File

@ -20,8 +20,6 @@ from oslo_serialization import jsonutils
from oslo_utils import timeutils
from cinder.common import constants
from cinder import exception
from cinder.i18n import _
from cinder import rpc
@ -148,13 +146,10 @@ class SchedulerAPI(rpc.RPCAPI):
}
return cctxt.cast(ctxt, 'manage_existing', **msg_args)
@rpc.assert_min_rpc_version('3.2')
def extend_volume(self, ctxt, volume, new_size, reservations,
request_spec, filter_properties=None):
cctxt = self._get_cctxt()
if not cctxt.can_send_version('3.2'):
msg = _('extend_volume requires cinder-scheduler '
'RPC API version >= 3.2.')
raise exception.ServiceTooOld(msg)
request_spec_p = jsonutils.to_primitive(request_spec)
msg_args = {
@ -194,14 +189,9 @@ class SchedulerAPI(rpc.RPCAPI):
cctxt = self._get_cctxt(fanout=True, version=version)
cctxt.cast(ctxt, 'update_service_capabilities', **msg_args)
@rpc.assert_min_rpc_version('3.1')
def notify_service_capabilities(self, ctxt, service_name,
backend, capabilities, timestamp=None):
version = '3.1'
if not self.client.can_send_version(version):
msg = _('notify_service_capabilities requires cinder-scheduler '
'RPC API version >= 3.1.')
raise exception.ServiceTooOld(msg)
parameters = {'service_name': service_name,
'capabilities': capabilities}
if self.client.can_send_version('3.5'):
@ -209,32 +199,23 @@ class SchedulerAPI(rpc.RPCAPI):
parameters.update(backend=backend,
timestamp=self.prepare_timestamp(timestamp))
else:
version = '3.1'
parameters['host'] = backend
cctxt = self._get_cctxt(version=version)
cctxt.cast(ctxt, 'notify_service_capabilities', **parameters)
@rpc.assert_min_rpc_version('3.4')
def work_cleanup(self, ctxt, cleanup_request):
"""Generate individual service cleanup requests from user request."""
if not self.client.can_send_version('3.4'):
msg = _('One of cinder-scheduler services is too old to accept '
'such request. Are you running mixed Newton-Ocata'
'cinder-schedulers?')
raise exception.ServiceTooOld(msg)
cctxt = self.client.prepare(version='3.4')
# Response will have services that are receiving the cleanup request
# and services that couldn't receive it since they are down.
return cctxt.call(ctxt, 'work_cleanup',
cleanup_request=cleanup_request)
@rpc.assert_min_rpc_version('3.4')
def do_cleanup(self, ctxt, cleanup_request):
"""Perform this scheduler's resource cleanup as per cleanup_request."""
if not self.client.can_send_version('3.4'):
msg = _('One of cinder-scheduler services is too old to accept '
'such request. Are you running mixed Newton-Ocata'
'cinder-schedulers?')
raise exception.ServiceTooOld(msg)
cctxt = self.client.prepare(version='3.4')
cctxt.cast(ctxt, 'do_cleanup', cleanup_request=cleanup_request)

View File

@ -14,8 +14,6 @@
from cinder.common import constants
from cinder import exception
from cinder.i18n import _
from cinder import objects
from cinder import quota
from cinder import rpc
@ -407,13 +405,8 @@ class VolumeAPI(rpc.RPCAPI):
cctxt.cast(ctxt, 'delete_group_snapshot',
group_snapshot=group_snapshot)
@rpc.assert_min_rpc_version('3.9')
def attachment_update(self, ctxt, vref, connector, attachment_id):
if not self.client.can_send_version('3.9'):
msg = _('One of cinder-volume services is too old to accept '
'such request. Are you running mixed Newton-Ocata'
'cinder-schedulers?')
raise exception.ServiceTooOld(msg)
version = self._compat_ver('3.9')
cctxt = self._get_cctxt(vref.host, version=version)
return cctxt.call(ctxt,
@ -422,13 +415,8 @@ class VolumeAPI(rpc.RPCAPI):
connector=connector,
attachment_id=attachment_id)
@rpc.assert_min_rpc_version('3.9')
def attachment_delete(self, ctxt, attachment_id, vref):
if not self.client.can_send_version('3.9'):
msg = _('One of cinder-volume services is too old to accept '
'such request. Are you running mixed Newton-Ocata'
'cinder-schedulers?')
raise exception.ServiceTooOld(msg)
version = self._compat_ver('3.9')
cctxt = self._get_cctxt(vref.host, version=version)
return cctxt.call(ctxt,
@ -436,13 +424,9 @@ class VolumeAPI(rpc.RPCAPI):
attachment_id=attachment_id,
vref=vref)
@rpc.assert_min_rpc_version('3.7')
def do_cleanup(self, ctxt, cleanup_request):
"""Perform this service/cluster resource cleanup as requested."""
if not self.client.can_send_version('3.7'):
msg = _('One of cinder-volume services is too old to accept such '
'a request. Are you running mixed Newton-Ocata services?')
raise exception.ServiceTooOld(msg)
destination = cleanup_request.service_topic_queue
cctxt = self._get_cctxt(destination, '3.7')
# NOTE(geguileo): This call goes to do_cleanup code in