cinder/cinder/volume/qos_specs.py
Jay S. Bryant 888dcd4372 [Doc] Handle Sphinx warnings as errors
We should be treating warnings from Sphinx as
errors as they usually result in bad HTML output.

Now that we have all of the existing warnings resolved we
want to turn on this option so that more bad docstrings, etc.
do not get merged.

One last bad docstring appears to have snuck in.  This patch
also fixes that docstring problem in cinder/volume/qos_specs.py .

We have a requirement on graphviz that was never handled and is
actually causing one of our web pages to be improperly rendered.
The patch adds the dependency to bindep.txt so that we do not
see a check failure and so the documentation builds properly.

Change-Id: I66a9282b63d884a390534b0c777be8433a7e9bb3
2017-08-29 16:00:09 -05:00

251 lines
8.6 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 Implementation"""
from oslo_db import exception as db_exc
from oslo_log import log as logging
from cinder import context
from cinder import db
from cinder import exception
from cinder.i18n import _
from cinder import objects
from cinder import utils
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
CONTROL_LOCATION = ['front-end', 'back-end', 'both']
def create(context, name, specs=None):
"""Creates qos_specs.
:param specs: Dictionary that contains specifications for QoS
Expected format of the input parameter:
.. code-block:: python
{
'consumer': 'front-end',
'total_iops_sec': 1000,
'total_bytes_sec': 1024000
}
"""
# Validate the key-value pairs in the qos spec.
utils.validate_dictionary_string_length(specs)
consumer = specs.get('consumer')
if consumer:
# If we need to modify specs, copy so we don't cause unintended
# consequences for the caller
specs = specs.copy()
del specs['consumer']
values = dict(name=name, consumer=consumer, specs=specs)
LOG.debug("Dict for qos_specs: %s", values)
qos_spec = objects.QualityOfServiceSpecs(context, **values)
qos_spec.create()
return qos_spec
def update(context, qos_specs_id, specs):
"""Update qos specs.
:param specs: dictionary that contains key/value pairs for updating
existing specs.
e.g. {'consumer': 'front-end',
'total_iops_sec': 500,
'total_bytes_sec': 512000,}
"""
LOG.debug('qos_specs.update(): specs %s', specs)
try:
utils.validate_dictionary_string_length(specs)
qos_spec = objects.QualityOfServiceSpecs.get_by_id(context,
qos_specs_id)
if 'consumer' in specs:
qos_spec.consumer = specs['consumer']
# If we need to modify specs, copy so we don't cause unintended
# consequences for the caller
specs = specs.copy()
del specs['consumer']
# Update any values in specs dict
qos_spec.specs.update(specs)
qos_spec.save()
except exception.InvalidInput as e:
raise exception.InvalidQoSSpecs(reason=e)
except db_exc.DBError:
LOG.exception('DB error:')
raise exception.QoSSpecsUpdateFailed(specs_id=qos_specs_id,
qos_specs=specs)
return qos_spec
def delete(context, qos_specs_id, force=False):
"""Marks qos specs as deleted.
'force' parameter is a flag to determine whether should destroy
should continue when there were entities associated with the qos specs.
force=True indicates caller would like to mark qos specs as deleted
even if there was entities associate with target qos specs.
Trying to delete a qos specs still associated with entities will
cause QoSSpecsInUse exception if force=False (default).
"""
if qos_specs_id is None:
msg = _("id cannot be None")
raise exception.InvalidQoSSpecs(reason=msg)
qos_spec = objects.QualityOfServiceSpecs.get_by_id(
context, qos_specs_id)
qos_spec.destroy(force)
def delete_keys(context, qos_specs_id, keys):
"""Marks specified key of target qos specs as deleted."""
if qos_specs_id is None:
msg = _("id cannot be None")
raise exception.InvalidQoSSpecs(reason=msg)
qos_spec = objects.QualityOfServiceSpecs.get_by_id(context, qos_specs_id)
# Previous behavior continued to delete keys until it hit first unset one,
# so for now will mimic that. In the future it would be useful to have all
# or nothing deletion of keys (or at least delete all set keys),
# especially since order of keys from CLI to API is not preserved currently
try:
for key in keys:
try:
del qos_spec.specs[key]
except KeyError:
raise exception.QoSSpecsKeyNotFound(
specs_key=key, specs_id=qos_specs_id)
finally:
qos_spec.save()
def get_associations(context, qos_specs_id):
"""Get all associations of given qos specs."""
try:
types = objects.VolumeTypeList.get_all_types_for_qos(context,
qos_specs_id)
except db_exc.DBError:
LOG.exception('DB error:')
msg = _('Failed to get all associations of '
'qos specs %s') % qos_specs_id
LOG.warning(msg)
raise exception.CinderException(message=msg)
result = []
for vol_type in types:
result.append({
'association_type': 'volume_type',
'name': vol_type.name,
'id': vol_type.id
})
return result
def associate_qos_with_type(context, specs_id, type_id):
"""Associate qos_specs with volume type.
Associate target qos specs with specific volume type.
:param specs_id: qos specs ID to associate with
:param type_id: volume type ID to associate with
:raises VolumeTypeNotFound: if volume type doesn't exist
:raises QoSSpecsNotFound: if qos specs doesn't exist
:raises InvalidVolumeType: if volume type is already associated
with qos specs other than given one.
:raises QoSSpecsAssociateFailed: if there was general DB error
"""
try:
get_qos_specs(context, specs_id)
res = volume_types.get_volume_type_qos_specs(type_id)
if res.get('qos_specs', None):
if res['qos_specs'].get('id') != specs_id:
msg = (_("Type %(type_id)s is already associated with another "
"qos specs: %(qos_specs_id)s") %
{'type_id': type_id,
'qos_specs_id': res['qos_specs']['id']})
raise exception.InvalidVolumeType(reason=msg)
else:
db.qos_specs_associate(context, specs_id, type_id)
except db_exc.DBError:
LOG.exception('DB error:')
LOG.warning('Failed to associate qos specs '
'%(id)s with type: %(vol_type_id)s',
dict(id=specs_id, vol_type_id=type_id))
raise exception.QoSSpecsAssociateFailed(specs_id=specs_id,
type_id=type_id)
def disassociate_qos_specs(context, specs_id, type_id):
"""Disassociate qos_specs from volume type."""
try:
get_qos_specs(context, specs_id)
db.qos_specs_disassociate(context, specs_id, type_id)
except db_exc.DBError:
LOG.exception('DB error:')
LOG.warning('Failed to disassociate qos specs '
'%(id)s with type: %(vol_type_id)s',
dict(id=specs_id, vol_type_id=type_id))
raise exception.QoSSpecsDisassociateFailed(specs_id=specs_id,
type_id=type_id)
def disassociate_all(context, specs_id):
"""Disassociate qos_specs from all entities."""
try:
get_qos_specs(context, specs_id)
db.qos_specs_disassociate_all(context, specs_id)
except db_exc.DBError:
LOG.exception('DB error:')
LOG.warning('Failed to disassociate qos specs %s.', specs_id)
raise exception.QoSSpecsDisassociateFailed(specs_id=specs_id,
type_id=None)
def get_all_specs(context, filters=None, marker=None, limit=None, offset=None,
sort_keys=None, sort_dirs=None):
"""Get all non-deleted qos specs."""
return objects.QualityOfServiceSpecsList.get_all(
context, filters=filters, marker=marker, limit=limit, offset=offset,
sort_keys=sort_keys, sort_dirs=sort_dirs)
def get_qos_specs(ctxt, spec_id):
"""Retrieves single qos specs by id."""
if spec_id is None:
msg = _("id cannot be None")
raise exception.InvalidQoSSpecs(reason=msg)
if ctxt is None:
ctxt = context.get_admin_context()
return objects.QualityOfServiceSpecs.get_by_id(ctxt, spec_id)