Files
deb-cinder/cinder/api/contrib/qos_specs_manage.py
Gorka Eguileor a130387b45 Short-circuit notifications when not enabled
When running Cinder with notifications disabled we are still doing all
the work and calls to Oslo message notifier and it's there, at the very
last moment when it's going to send the message that it checks that
there's no actual extension loaded in the driver manager and skips the
actual send of the data.

This is considerably wasteful considering that for some of the
notifications we are actually querying the DB to get data, for example
volume attachments and glance metadata information when notifying about
volume usage.

This patch proposes short-circuiting notification methods as much as
possible to optimize code execution when Cinder has no notification
transport mechanism configured, as is the case when deployed as a
standalone SDS service.

Closes-Bug: #1660303
Change-Id: I77f655d3ef90088ce71304da5d4ea7b543991e90
2017-01-30 12:31:45 +01:00

397 lines
16 KiB
Python

# Copyright (c) 2013 eBay Inc.
# Copyright (c) 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""The QoS specs extension"""
from oslo_log import log as logging
import six
from six.moves import http_client
import webob
from cinder.api import common
from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder.api.views import qos_specs as view_qos_specs
from cinder import exception
from cinder.i18n import _
from cinder import rpc
from cinder import utils
from cinder.volume import qos_specs
LOG = logging.getLogger(__name__)
authorize = extensions.extension_authorizer('volume', 'qos_specs_manage')
def _check_specs(context, specs_id):
# Not found exception will be handled at the wsgi level
qos_specs.get_qos_specs(context, specs_id)
class QoSSpecsController(wsgi.Controller):
"""The volume type extra specs API controller for the OpenStack API."""
_view_builder_class = view_qos_specs.ViewBuilder
@staticmethod
@utils.if_notifications_enabled
def _notify_qos_specs_error(context, method, payload):
rpc.get_notifier('QoSSpecs').error(context,
method,
payload)
def index(self, req):
"""Returns the list of qos_specs."""
context = req.environ['cinder.context']
authorize(context)
params = req.params.copy()
marker, limit, offset = common.get_pagination_params(params)
sort_keys, sort_dirs = common.get_sort_params(params)
filters = params
allowed_search_options = ('id', 'name', 'consumer')
utils.remove_invalid_filter_options(context, filters,
allowed_search_options)
specs = qos_specs.get_all_specs(context, filters=filters,
marker=marker, limit=limit,
offset=offset, sort_keys=sort_keys,
sort_dirs=sort_dirs)
return self._view_builder.summary_list(req, specs)
def create(self, req, body=None):
context = req.environ['cinder.context']
authorize(context)
self.assert_valid_body(body, 'qos_specs')
specs = body['qos_specs']
name = specs.pop('name', None)
if name is None:
msg = _("Please specify a name for QoS specs.")
raise webob.exc.HTTPBadRequest(explanation=msg)
self.validate_string_length(name, 'name', min_length=1,
max_length=255, remove_whitespaces=True)
name = name.strip()
# Validate the key-value pairs in the qos spec.
utils.validate_dictionary_string_length(specs)
try:
spec = qos_specs.create(context, name, specs)
notifier_info = dict(name=name, specs=specs)
rpc.get_notifier('QoSSpecs').info(context,
'qos_specs.create',
notifier_info)
except exception.InvalidQoSSpecs as err:
notifier_err = dict(name=name, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.create',
notifier_err)
raise webob.exc.HTTPBadRequest(explanation=six.text_type(err))
except exception.QoSSpecsExists as err:
notifier_err = dict(name=name, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.create',
notifier_err)
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
except exception.QoSSpecsCreateFailed as err:
notifier_err = dict(name=name, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.create',
notifier_err)
raise webob.exc.HTTPInternalServerError(
explanation=six.text_type(err))
return self._view_builder.detail(req, spec)
def update(self, req, id, body=None):
context = req.environ['cinder.context']
authorize(context)
self.assert_valid_body(body, 'qos_specs')
specs = body['qos_specs']
try:
qos_specs.update(context, id, specs)
notifier_info = dict(id=id, specs=specs)
rpc.get_notifier('QoSSpecs').info(context,
'qos_specs.update',
notifier_info)
except (exception.QoSSpecsNotFound, exception.InvalidQoSSpecs) as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.update',
notifier_err)
# Not found exception will be handled at the wsgi level
raise
except exception.QoSSpecsUpdateFailed as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.update',
notifier_err)
raise webob.exc.HTTPInternalServerError(
explanation=six.text_type(err))
return body
def show(self, req, id):
"""Return a single qos spec item."""
context = req.environ['cinder.context']
authorize(context)
# Not found exception will be handled at the wsgi level
spec = qos_specs.get_qos_specs(context, id)
return self._view_builder.detail(req, spec)
def delete(self, req, id):
"""Deletes an existing qos specs."""
context = req.environ['cinder.context']
authorize(context)
# Convert string to bool type in strict manner
force = utils.get_bool_param('force', req.params)
LOG.debug("Delete qos_spec: %(id)s, force: %(force)s",
{'id': id, 'force': force})
try:
qos_specs.delete(context, id, force)
notifier_info = dict(id=id)
rpc.get_notifier('QoSSpecs').info(context,
'qos_specs.delete',
notifier_info)
except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.delete',
notifier_err)
# Not found exception will be handled at the wsgi level
raise
except exception.QoSSpecsInUse as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.delete',
notifier_err)
if force:
msg = _('Failed to disassociate qos specs.')
raise webob.exc.HTTPInternalServerError(explanation=msg)
msg = _('Qos specs still in use.')
raise webob.exc.HTTPBadRequest(explanation=msg)
return webob.Response(status_int=http_client.ACCEPTED)
def delete_keys(self, req, id, body):
"""Deletes specified keys in qos specs."""
context = req.environ['cinder.context']
authorize(context)
if not (body and 'keys' in body
and isinstance(body.get('keys'), list)):
raise webob.exc.HTTPBadRequest()
keys = body['keys']
LOG.debug("Delete_key spec: %(id)s, keys: %(keys)s",
{'id': id, 'keys': keys})
try:
qos_specs.delete_keys(context, id, keys)
notifier_info = dict(id=id)
rpc.get_notifier('QoSSpecs').info(context, 'qos_specs.delete_keys',
notifier_info)
except exception.NotFound as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.delete_keys',
notifier_err)
# Not found exception will be handled at the wsgi level
raise
return webob.Response(status_int=http_client.ACCEPTED)
def associations(self, req, id):
"""List all associations of given qos specs."""
context = req.environ['cinder.context']
authorize(context)
LOG.debug("Get associations for qos_spec id: %s", id)
try:
associates = qos_specs.get_associations(context, id)
notifier_info = dict(id=id)
rpc.get_notifier('QoSSpecs').info(context,
'qos_specs.associations',
notifier_info)
except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.associations',
notifier_err)
# Not found exception will be handled at the wsgi level
raise
except exception.CinderException as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.associations',
notifier_err)
raise webob.exc.HTTPInternalServerError(
explanation=six.text_type(err))
return self._view_builder.associations(req, associates)
def associate(self, req, id):
"""Associate a qos specs with a volume type."""
context = req.environ['cinder.context']
authorize(context)
type_id = req.params.get('vol_type_id', None)
if not type_id:
msg = _('Volume Type id must not be None.')
notifier_err = dict(id=id, error_message=msg)
self._notify_qos_specs_error(context,
'qos_specs.delete',
notifier_err)
raise webob.exc.HTTPBadRequest(explanation=msg)
LOG.debug("Associate qos_spec: %(id)s with type: %(type_id)s",
{'id': id, 'type_id': type_id})
try:
qos_specs.associate_qos_with_type(context, id, type_id)
notifier_info = dict(id=id, type_id=type_id)
rpc.get_notifier('QoSSpecs').info(context,
'qos_specs.associate',
notifier_info)
except exception.NotFound as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.associate',
notifier_err)
# Not found exception will be handled at the wsgi level
raise
except exception.InvalidVolumeType as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.associate',
notifier_err)
self._notify_qos_specs_error(context,
'qos_specs.associate',
notifier_err)
raise webob.exc.HTTPBadRequest(explanation=six.text_type(err))
except exception.QoSSpecsAssociateFailed as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.associate',
notifier_err)
raise webob.exc.HTTPInternalServerError(
explanation=six.text_type(err))
return webob.Response(status_int=http_client.ACCEPTED)
def disassociate(self, req, id):
"""Disassociate a qos specs from a volume type."""
context = req.environ['cinder.context']
authorize(context)
type_id = req.params.get('vol_type_id', None)
if not type_id:
msg = _('Volume Type id must not be None.')
notifier_err = dict(id=id, error_message=msg)
self._notify_qos_specs_error(context,
'qos_specs.delete',
notifier_err)
raise webob.exc.HTTPBadRequest(explanation=msg)
LOG.debug("Disassociate qos_spec: %(id)s from type: %(type_id)s",
{'id': id, 'type_id': type_id})
try:
qos_specs.disassociate_qos_specs(context, id, type_id)
notifier_info = dict(id=id, type_id=type_id)
rpc.get_notifier('QoSSpecs').info(context,
'qos_specs.disassociate',
notifier_info)
except exception.NotFound as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.disassociate',
notifier_err)
# Not found exception will be handled at the wsgi level
raise
except exception.QoSSpecsDisassociateFailed as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.disassociate',
notifier_err)
raise webob.exc.HTTPInternalServerError(
explanation=six.text_type(err))
return webob.Response(status_int=http_client.ACCEPTED)
def disassociate_all(self, req, id):
"""Disassociate a qos specs from all volume types."""
context = req.environ['cinder.context']
authorize(context)
LOG.debug("Disassociate qos_spec: %s from all.", id)
try:
qos_specs.disassociate_all(context, id)
notifier_info = dict(id=id)
rpc.get_notifier('QoSSpecs').info(context,
'qos_specs.disassociate_all',
notifier_info)
except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.disassociate_all',
notifier_err)
# Not found exception will be handled at the wsgi level
raise
except exception.QoSSpecsDisassociateFailed as err:
notifier_err = dict(id=id, error_message=err)
self._notify_qos_specs_error(context,
'qos_specs.disassociate_all',
notifier_err)
raise webob.exc.HTTPInternalServerError(
explanation=six.text_type(err))
return webob.Response(status_int=http_client.ACCEPTED)
class Qos_specs_manage(extensions.ExtensionDescriptor):
"""QoS specs support."""
name = "Qos_specs_manage"
alias = "qos-specs"
updated = "2013-08-02T00:00:00+00:00"
def get_resources(self):
resources = []
res = extensions.ResourceExtension(
Qos_specs_manage.alias,
QoSSpecsController(),
member_actions={"associations": "GET",
"associate": "GET",
"disassociate": "GET",
"disassociate_all": "GET",
"delete_keys": "PUT"})
resources.append(res)
return resources