a55a6b5c71
Log messages are no longer being translated. This removes all use of the _LE, _LI, and _LW translation markers to simplify logging and to avoid confusion with new contributions. See: http://lists.openstack.org/pipermail/openstack-i18n/2016-November/002574.html http://lists.openstack.org/pipermail/openstack-dev/2017-March/113365.html Change-Id: I4c96f3590d46205c45d12ee4ead8c208e11c52c5
204 lines
7.8 KiB
Python
204 lines
7.8 KiB
Python
# 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.
|
|
|
|
from oslo_db import exception as db_exc
|
|
from oslo_log import log as logging
|
|
|
|
from cinder import db
|
|
from cinder import exception
|
|
from cinder.i18n import _
|
|
from cinder import objects
|
|
from cinder.objects import base
|
|
from cinder.objects import fields as c_fields
|
|
from oslo_versionedobjects import fields
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
@base.CinderObjectRegistry.register
|
|
class QualityOfServiceSpecs(base.CinderPersistentObject,
|
|
base.CinderObject,
|
|
base.CinderObjectDictCompat,
|
|
base.CinderComparableObject):
|
|
# Version
|
|
# 1.0: Initial version
|
|
VERSION = "1.0"
|
|
|
|
OPTIONAL_FIELDS = ['volume_types']
|
|
|
|
fields = {
|
|
'id': fields.UUIDField(),
|
|
'name': fields.StringField(),
|
|
'consumer': c_fields.QoSConsumerField(
|
|
default=c_fields.QoSConsumerValues.BACK_END),
|
|
'specs': fields.DictOfNullableStringsField(nullable=True),
|
|
'volume_types': fields.ObjectField('VolumeTypeList', nullable=True),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(QualityOfServiceSpecs, self).__init__(*args, **kwargs)
|
|
self._init_specs = {}
|
|
|
|
def __setattr__(self, name, value):
|
|
try:
|
|
super(QualityOfServiceSpecs, self).__setattr__(name, value)
|
|
except ValueError:
|
|
if name == 'consumer':
|
|
# Give more descriptive error message for invalid 'consumer'
|
|
msg = (_("Valid consumer of QoS specs are: %s") %
|
|
c_fields.QoSConsumerField())
|
|
raise exception.InvalidQoSSpecs(reason=msg)
|
|
else:
|
|
raise
|
|
|
|
def obj_reset_changes(self, fields=None, recursive=False):
|
|
super(QualityOfServiceSpecs, self).obj_reset_changes(fields, recursive)
|
|
if fields is None or 'specs' in fields:
|
|
self._init_specs = self.specs.copy() if self.specs else {}
|
|
|
|
def obj_what_changed(self):
|
|
changes = super(QualityOfServiceSpecs, self).obj_what_changed()
|
|
|
|
# Do comparison of what's in the dict vs. reference to the specs object
|
|
if self.obj_attr_is_set('id'):
|
|
if self.specs != self._init_specs:
|
|
changes.add('specs')
|
|
else:
|
|
# If both dicts are equal don't consider anything gets changed
|
|
if 'specs' in changes:
|
|
changes.remove('specs')
|
|
|
|
return changes
|
|
|
|
def obj_get_changes(self):
|
|
changes = super(QualityOfServiceSpecs, self).obj_get_changes()
|
|
if 'specs' in changes:
|
|
# For specs, we only want what has changed in the dictionary,
|
|
# because otherwise we'll individually overwrite the DB value for
|
|
# every key in 'specs' even if it hasn't changed
|
|
specs_changes = {}
|
|
for key, val in self.specs.items():
|
|
if val != self._init_specs.get(key):
|
|
specs_changes[key] = val
|
|
changes['specs'] = specs_changes
|
|
|
|
specs_keys_removed = (set(self._init_specs.keys()) -
|
|
set(self.specs.keys()))
|
|
if specs_keys_removed:
|
|
# Special key notifying which specs keys have been deleted
|
|
changes['specs_keys_removed'] = specs_keys_removed
|
|
|
|
return changes
|
|
|
|
def obj_load_attr(self, attrname):
|
|
if attrname not in self.OPTIONAL_FIELDS:
|
|
raise exception.ObjectActionError(
|
|
action='obj_load_attr',
|
|
reason=_('attribute %s not lazy-loadable') % attrname)
|
|
if not self._context:
|
|
raise exception.OrphanedObjectError(method='obj_load_attr',
|
|
objtype=self.obj_name())
|
|
|
|
if attrname == 'volume_types':
|
|
self.volume_types = objects.VolumeTypeList.get_all_types_for_qos(
|
|
self._context, self.id)
|
|
|
|
@classmethod
|
|
def _from_db_object(cls, context, qos_spec, db_qos_spec,
|
|
expected_attrs=None):
|
|
if expected_attrs is None:
|
|
expected_attrs = []
|
|
|
|
for name, field in qos_spec.fields.items():
|
|
if name not in cls.OPTIONAL_FIELDS:
|
|
value = db_qos_spec.get(name)
|
|
# 'specs' could be null if only a consumer is given, so make
|
|
# it an empty dict instead of None
|
|
if not value and isinstance(field, fields.DictOfStringsField):
|
|
value = {}
|
|
setattr(qos_spec, name, value)
|
|
|
|
if 'volume_types' in expected_attrs:
|
|
volume_types = objects.VolumeTypeList.get_all_types_for_qos(
|
|
context, db_qos_spec['id'])
|
|
qos_spec.volume_types = volume_types
|
|
|
|
qos_spec._context = context
|
|
qos_spec.obj_reset_changes()
|
|
return qos_spec
|
|
|
|
def create(self):
|
|
if self.obj_attr_is_set('id'):
|
|
raise exception.ObjectActionError(action='create',
|
|
reason='already created')
|
|
updates = self.cinder_obj_get_changes()
|
|
|
|
try:
|
|
create_ret = db.qos_specs_create(self._context, updates)
|
|
except db_exc.DBDataError:
|
|
msg = _('Error writing field to database')
|
|
LOG.exception(msg)
|
|
raise exception.Invalid(msg)
|
|
except db_exc.DBError:
|
|
LOG.exception('DB error occurred when creating QoS specs.')
|
|
raise exception.QoSSpecsCreateFailed(name=self.name,
|
|
qos_specs=self.specs)
|
|
# Save ID with the object
|
|
updates['id'] = create_ret['id']
|
|
self._from_db_object(self._context, self, updates)
|
|
|
|
def save(self):
|
|
updates = self.cinder_obj_get_changes()
|
|
if updates:
|
|
if 'specs_keys_removed' in updates.keys():
|
|
for specs_key_to_remove in updates['specs_keys_removed']:
|
|
db.qos_specs_item_delete(
|
|
self._context, self.id, specs_key_to_remove)
|
|
del updates['specs_keys_removed']
|
|
db.qos_specs_update(self._context, self.id, updates)
|
|
|
|
self.obj_reset_changes()
|
|
|
|
def destroy(self, force=False):
|
|
"""Deletes the QoS spec.
|
|
|
|
:param force: when force is True, all volume_type mappings for this QoS
|
|
are deleted. When force is False and volume_type
|
|
mappings still exist, a QoSSpecsInUse exception is thrown
|
|
"""
|
|
if self.volume_types:
|
|
if not force:
|
|
raise exception.QoSSpecsInUse(specs_id=self.id)
|
|
# remove all association
|
|
db.qos_specs_disassociate_all(self._context, self.id)
|
|
updated_values = db.qos_specs_delete(self._context, self.id)
|
|
self.update(updated_values)
|
|
self.obj_reset_changes(updated_values.keys())
|
|
|
|
|
|
@base.CinderObjectRegistry.register
|
|
class QualityOfServiceSpecsList(base.ObjectListBase, base.CinderObject):
|
|
VERSION = '1.0'
|
|
|
|
fields = {
|
|
'objects': fields.ListOfObjectsField('QualityOfServiceSpecs'),
|
|
}
|
|
child_versions = {
|
|
'1.0': '1.0',
|
|
}
|
|
|
|
@classmethod
|
|
def get_all(cls, context, *args, **kwargs):
|
|
specs = db.qos_specs_get_all(context, *args, **kwargs)
|
|
return base.obj_make_list(context, cls(context),
|
|
objects.QualityOfServiceSpecs, specs)
|