Added volume type description for volume type API
- Added the following APIs and tests for volume type * update volume type PUT http://<openstackhost>:8776/v2/${tenant_id}/types/${vol_type_id} body { "volume_type": { "description":"updated_desc" } } ** user can update description. ** if update description, descripiton can be empty spaces. ** description can not be None ** only admin can access this API *get default volume type GET http://<openstackhost>:8776/v2/${tenant_id}/types/default ** if default_volume_type is specified in cinder.conf and is valid, the default volume type will be returned. ** if default_volume_type is not specified in cinder.conf or is not valid, it will return 404 with a message saying default volume type can not be found. - Updated the following APIs and tests for volume type * create volume type should take description as an option. * list volume types or get one volume type will include description for volume type if the description is not None. - Upgraded the database cinder on table volume_types to include the description. database upgrade/downgrade scripts and tests are added. - update API should send a notification to the message bus when updating succeeds or fails. - as of 12/5/2014, had to rebase with master which has volume type access change, I also fixed the tests in that area in order to get the unit tests pass. Implements: blueprint volume-type-description Change-Id: I3100a8f74fa1c0cc8d9293bf30e17b6ac4c72edb
This commit is contained in:
parent
4623514158
commit
cf73815982
|
@ -36,9 +36,16 @@ class VolumeTypesManageController(wsgi.Controller):
|
||||||
|
|
||||||
_view_builder_class = views_types.ViewBuilder
|
_view_builder_class = views_types.ViewBuilder
|
||||||
|
|
||||||
def _notify_volume_type_error(self, context, method, payload):
|
def _notify_volume_type_error(self, context, method, err,
|
||||||
|
volume_type=None, id=None, name=None):
|
||||||
|
payload = dict(
|
||||||
|
volume_types=volume_type, name=name, id=id, error_message=err)
|
||||||
rpc.get_notifier('volumeType').error(context, method, payload)
|
rpc.get_notifier('volumeType').error(context, method, payload)
|
||||||
|
|
||||||
|
def _notify_volume_type_info(self, context, method, volume_type):
|
||||||
|
payload = dict(volume_types=volume_type)
|
||||||
|
rpc.get_notifier('volumeType').info(context, method, payload)
|
||||||
|
|
||||||
@wsgi.action("create")
|
@wsgi.action("create")
|
||||||
@wsgi.serializers(xml=types.VolumeTypeTemplate)
|
@wsgi.serializers(xml=types.VolumeTypeTemplate)
|
||||||
def _create(self, req, body):
|
def _create(self, req, body):
|
||||||
|
@ -51,36 +58,79 @@ class VolumeTypesManageController(wsgi.Controller):
|
||||||
|
|
||||||
vol_type = body['volume_type']
|
vol_type = body['volume_type']
|
||||||
name = vol_type.get('name', None)
|
name = vol_type.get('name', None)
|
||||||
|
description = vol_type.get('description')
|
||||||
specs = vol_type.get('extra_specs', {})
|
specs = vol_type.get('extra_specs', {})
|
||||||
is_public = vol_type.get('os-volume-type-access:is_public', True)
|
is_public = vol_type.get('os-volume-type-access:is_public', True)
|
||||||
|
|
||||||
if name is None or name == "":
|
if name is None or len(name.strip()) == 0:
|
||||||
raise webob.exc.HTTPBadRequest()
|
msg = _("Volume type name can not be empty.")
|
||||||
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
volume_types.create(context, name, specs, is_public)
|
volume_types.create(context,
|
||||||
|
name,
|
||||||
|
specs,
|
||||||
|
is_public,
|
||||||
|
description=description)
|
||||||
vol_type = volume_types.get_volume_type_by_name(context, name)
|
vol_type = volume_types.get_volume_type_by_name(context, name)
|
||||||
req.cache_resource(vol_type, name='types')
|
req.cache_resource(vol_type, name='types')
|
||||||
notifier_info = dict(volume_types=vol_type)
|
self._notify_volume_type_info(
|
||||||
rpc.get_notifier('volumeType').info(context, 'volume_type.create',
|
context, 'volume_type.create', vol_type)
|
||||||
notifier_info)
|
|
||||||
|
|
||||||
except exception.VolumeTypeExists as err:
|
except exception.VolumeTypeExists as err:
|
||||||
notifier_err = dict(volume_types=vol_type, error_message=err)
|
self._notify_volume_type_error(
|
||||||
self._notify_volume_type_error(context,
|
context, 'volume_type.create', err, volume_type=vol_type)
|
||||||
'volume_type.create',
|
|
||||||
notifier_err)
|
|
||||||
|
|
||||||
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||||
except exception.NotFound as err:
|
except exception.NotFound as err:
|
||||||
notifier_err = dict(volume_types=vol_type, error_message=err)
|
self._notify_volume_type_error(
|
||||||
self._notify_volume_type_error(context,
|
context, 'volume_type.create', err, name=name)
|
||||||
'volume_type.create',
|
|
||||||
notifier_err)
|
|
||||||
raise webob.exc.HTTPNotFound()
|
raise webob.exc.HTTPNotFound()
|
||||||
|
|
||||||
return self._view_builder.show(req, vol_type)
|
return self._view_builder.show(req, vol_type)
|
||||||
|
|
||||||
|
@wsgi.action("update")
|
||||||
|
@wsgi.serializers(xml=types.VolumeTypeTemplate)
|
||||||
|
def _update(self, req, id, body):
|
||||||
|
# Update description for a given volume type.
|
||||||
|
context = req.environ['cinder.context']
|
||||||
|
authorize(context)
|
||||||
|
|
||||||
|
if not self.is_valid_body(body, 'volume_type'):
|
||||||
|
raise webob.exc.HTTPBadRequest()
|
||||||
|
|
||||||
|
vol_type = body['volume_type']
|
||||||
|
description = vol_type.get('description', None)
|
||||||
|
|
||||||
|
if description is None:
|
||||||
|
msg = _("Specify the description to update.")
|
||||||
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# check it exists
|
||||||
|
vol_type = volume_types.get_volume_type(context, id)
|
||||||
|
volume_types.update(context, id, description)
|
||||||
|
# get the updated
|
||||||
|
vol_type = volume_types.get_volume_type(context, id)
|
||||||
|
req.cache_resource(vol_type, name='types')
|
||||||
|
self._notify_volume_type_info(
|
||||||
|
context, 'volume_type.update', vol_type)
|
||||||
|
|
||||||
|
except exception.VolumeTypeNotFound as err:
|
||||||
|
self._notify_volume_type_error(
|
||||||
|
context, 'volume_type.update', err, id=id)
|
||||||
|
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
|
||||||
|
except exception.VolumeTypeExists as err:
|
||||||
|
self._notify_volume_type_error(
|
||||||
|
context, 'volume_type.update', err, volume_type=vol_type)
|
||||||
|
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||||
|
except exception.VolumeTypeUpdateFailed as err:
|
||||||
|
self._notify_volume_type_error(
|
||||||
|
context, 'volume_type.update', err, volume_type=vol_type)
|
||||||
|
raise webob.exc.HTTPInternalServerError(
|
||||||
|
explanation=six.text_type(err))
|
||||||
|
|
||||||
|
return self._view_builder.show(req, vol_type)
|
||||||
|
|
||||||
@wsgi.action("delete")
|
@wsgi.action("delete")
|
||||||
def _delete(self, req, id):
|
def _delete(self, req, id):
|
||||||
"""Deletes an existing volume type."""
|
"""Deletes an existing volume type."""
|
||||||
|
@ -90,23 +140,16 @@ class VolumeTypesManageController(wsgi.Controller):
|
||||||
try:
|
try:
|
||||||
vol_type = volume_types.get_volume_type(context, id)
|
vol_type = volume_types.get_volume_type(context, id)
|
||||||
volume_types.destroy(context, vol_type['id'])
|
volume_types.destroy(context, vol_type['id'])
|
||||||
notifier_info = dict(volume_types=vol_type)
|
self._notify_volume_type_info(
|
||||||
rpc.get_notifier('volumeType').info(context,
|
context, 'volume_type.delete', vol_type)
|
||||||
'volume_type.delete',
|
|
||||||
notifier_info)
|
|
||||||
except exception.VolumeTypeInUse as err:
|
except exception.VolumeTypeInUse as err:
|
||||||
notifier_err = dict(id=id, error_message=err)
|
self._notify_volume_type_error(
|
||||||
self._notify_volume_type_error(context,
|
context, 'volume_type.delete', err, volume_type=vol_type)
|
||||||
'volume_type.delete',
|
|
||||||
notifier_err)
|
|
||||||
msg = _('Target volume type is still in use.')
|
msg = _('Target volume type is still in use.')
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
except exception.NotFound as err:
|
except exception.NotFound as err:
|
||||||
notifier_err = dict(id=id, error_message=err)
|
self._notify_volume_type_error(
|
||||||
self._notify_volume_type_error(context,
|
context, 'volume_type.delete', err, id=id)
|
||||||
'volume_type.delete',
|
|
||||||
notifier_err)
|
|
||||||
|
|
||||||
raise webob.exc.HTTPNotFound()
|
raise webob.exc.HTTPNotFound()
|
||||||
|
|
||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
|
@ -117,8 +117,9 @@ class VolumeTypeActionController(wsgi.Controller):
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
def _extend_vol_type(self, vol_type_rval, vol_type_ref):
|
def _extend_vol_type(self, vol_type_rval, vol_type_ref):
|
||||||
key = "%s:is_public" % (Volume_type_access.alias)
|
if vol_type_ref:
|
||||||
vol_type_rval[key] = vol_type_ref['is_public']
|
key = "%s:is_public" % (Volume_type_access.alias)
|
||||||
|
vol_type_rval[key] = vol_type_ref.get('is_public', True)
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
def show(self, req, resp_obj, id):
|
def show(self, req, resp_obj, id):
|
||||||
|
|
|
@ -22,6 +22,7 @@ from xml.parsers import expat
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from oslo.serialization import jsonutils
|
from oslo.serialization import jsonutils
|
||||||
|
from oslo.utils import excutils
|
||||||
import six
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
|
@ -1075,11 +1076,15 @@ class Resource(wsgi.Application):
|
||||||
meth = getattr(self, action)
|
meth = getattr(self, action)
|
||||||
else:
|
else:
|
||||||
meth = getattr(self.controller, action)
|
meth = getattr(self.controller, action)
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
if (not self.wsgi_actions or
|
with excutils.save_and_reraise_exception(e) as ctxt:
|
||||||
action not in ['action', 'create', 'delete']):
|
if (not self.wsgi_actions or action not in ['action',
|
||||||
# Propagate the error
|
'create',
|
||||||
raise
|
'delete',
|
||||||
|
'update']):
|
||||||
|
LOG.exception(six.text_type(e))
|
||||||
|
else:
|
||||||
|
ctxt.reraise = False
|
||||||
else:
|
else:
|
||||||
return meth, self.wsgi_extensions.get(action, [])
|
return meth, self.wsgi_extensions.get(action, [])
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ from cinder.volume import volume_types
|
||||||
def make_voltype(elem):
|
def make_voltype(elem):
|
||||||
elem.set('id')
|
elem.set('id')
|
||||||
elem.set('name')
|
elem.set('name')
|
||||||
|
elem.set('description')
|
||||||
extra_specs = xmlutil.make_flat_dict('extra_specs', selector='extra_specs')
|
extra_specs = xmlutil.make_flat_dict('extra_specs', selector='extra_specs')
|
||||||
elem.append(extra_specs)
|
elem.append(extra_specs)
|
||||||
|
|
||||||
|
@ -67,12 +68,20 @@ class VolumeTypesController(wsgi.Controller):
|
||||||
"""Return a single volume type item."""
|
"""Return a single volume type item."""
|
||||||
context = req.environ['cinder.context']
|
context = req.environ['cinder.context']
|
||||||
|
|
||||||
try:
|
# get default volume type
|
||||||
vol_type = volume_types.get_volume_type(context, id)
|
if id is not None and id == 'default':
|
||||||
|
vol_type = volume_types.get_default_volume_type()
|
||||||
|
if not vol_type:
|
||||||
|
msg = _("Default volume type can not be found.")
|
||||||
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
req.cache_resource(vol_type, name='types')
|
req.cache_resource(vol_type, name='types')
|
||||||
except exception.NotFound:
|
else:
|
||||||
msg = _("Volume type not found")
|
try:
|
||||||
raise exc.HTTPNotFound(explanation=msg)
|
vol_type = volume_types.get_volume_type(context, id)
|
||||||
|
req.cache_resource(vol_type, name='types')
|
||||||
|
except exception.NotFound:
|
||||||
|
msg = _("Volume type not found")
|
||||||
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
|
|
||||||
return self._view_builder.show(req, vol_type)
|
return self._view_builder.show(req, vol_type)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,8 @@ class ViewBuilder(common.ViewBuilder):
|
||||||
"""Trim away extraneous volume type attributes."""
|
"""Trim away extraneous volume type attributes."""
|
||||||
trimmed = dict(id=volume_type.get('id'),
|
trimmed = dict(id=volume_type.get('id'),
|
||||||
name=volume_type.get('name'),
|
name=volume_type.get('name'),
|
||||||
extra_specs=volume_type.get('extra_specs'))
|
extra_specs=volume_type.get('extra_specs'),
|
||||||
|
description=volume_type.get('description'))
|
||||||
return trimmed if brief else dict(volume_type=trimmed)
|
return trimmed if brief else dict(volume_type=trimmed)
|
||||||
|
|
||||||
def index(self, request, volume_types):
|
def index(self, request, volume_types):
|
||||||
|
|
|
@ -374,6 +374,10 @@ def volume_type_create(context, values, projects=None):
|
||||||
return IMPL.volume_type_create(context, values, projects)
|
return IMPL.volume_type_create(context, values, projects)
|
||||||
|
|
||||||
|
|
||||||
|
def volume_type_update(context, volume_type_id, values):
|
||||||
|
return IMPL.volume_type_update(context, volume_type_id, values)
|
||||||
|
|
||||||
|
|
||||||
def volume_type_get_all(context, inactive=False, filters=None):
|
def volume_type_get_all(context, inactive=False, filters=None):
|
||||||
"""Get all volume types.
|
"""Get all volume types.
|
||||||
|
|
||||||
|
|
|
@ -1936,6 +1936,24 @@ def _volume_type_get_query(context, session=None, read_deleted=None,
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
@require_admin_context
|
||||||
|
def volume_type_update(context, volume_type_id, values):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
volume_type_ref = _volume_type_ref_get(context,
|
||||||
|
volume_type_id,
|
||||||
|
session)
|
||||||
|
|
||||||
|
if not volume_type_ref:
|
||||||
|
raise exception.VolumeTypeNotFound(type_id=volume_type_id)
|
||||||
|
|
||||||
|
volume_type_ref.update(values)
|
||||||
|
volume_type_ref.save(session=session)
|
||||||
|
volume_type = volume_type_get(context, volume_type_id)
|
||||||
|
|
||||||
|
return volume_type
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def volume_type_get_all(context, inactive=False, filters=None):
|
def volume_type_get_all(context, inactive=False, filters=None):
|
||||||
"""Returns a dict describing all volume_types with name as key."""
|
"""Returns a dict describing all volume_types with name as key."""
|
||||||
|
@ -2012,6 +2030,23 @@ def volume_type_get(context, id, inactive=False, expected_fields=None):
|
||||||
expected_fields=expected_fields)
|
expected_fields=expected_fields)
|
||||||
|
|
||||||
|
|
||||||
|
@require_context
|
||||||
|
def _volume_type_ref_get(context, id, session=None, inactive=False):
|
||||||
|
read_deleted = "yes" if inactive else "no"
|
||||||
|
result = model_query(context,
|
||||||
|
models.VolumeTypes,
|
||||||
|
session=session,
|
||||||
|
read_deleted=read_deleted).\
|
||||||
|
options(joinedload('extra_specs')).\
|
||||||
|
filter_by(id=id).\
|
||||||
|
first()
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
raise exception.VolumeTypeNotFound(volume_type_id=id)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def _volume_type_get_by_name(context, name, session=None):
|
def _volume_type_get_by_name(context, name, session=None):
|
||||||
result = model_query(context, models.VolumeTypes, session=session).\
|
result = model_query(context, models.VolumeTypes, session=session).\
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
CREATE TABLE volume_types_v33 (
|
||||||
|
created_at DATETIME,
|
||||||
|
updated_at DATETIME,
|
||||||
|
deleted_at DATETIME,
|
||||||
|
deleted BOOLEAN,
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
name VARCHAR(255),
|
||||||
|
is_public BOOLEAN,
|
||||||
|
qos_specs_id VARCHAR(36),
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO volume_types_v33
|
||||||
|
SELECT created_at,
|
||||||
|
updated_at,
|
||||||
|
deleted_at,
|
||||||
|
deleted,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
is_public,
|
||||||
|
qos_specs_id
|
||||||
|
FROM volume_types;
|
||||||
|
|
||||||
|
DROP TABLE volume_types;
|
||||||
|
ALTER TABLE volume_types_v33 RENAME TO volume_types;
|
|
@ -0,0 +1,35 @@
|
||||||
|
# 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 sqlalchemy import Column, MetaData, Table, String
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(migrate_engine):
|
||||||
|
"""Add description column to volume_types."""
|
||||||
|
meta = MetaData()
|
||||||
|
meta.bind = migrate_engine
|
||||||
|
|
||||||
|
volume_types = Table('volume_types', meta, autoload=True)
|
||||||
|
description = Column('description', String(255))
|
||||||
|
volume_types.create_column(description)
|
||||||
|
volume_types.update().values(description=None).execute()
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(migrate_engine):
|
||||||
|
"""Remove description column to volumes."""
|
||||||
|
meta = MetaData()
|
||||||
|
meta.bind = migrate_engine
|
||||||
|
|
||||||
|
volume_types = Table('volume_types', meta, autoload=True)
|
||||||
|
description = volume_types.columns.description
|
||||||
|
volume_types.drop_column(description)
|
|
@ -200,6 +200,7 @@ class VolumeTypes(BASE, CinderBase):
|
||||||
__tablename__ = "volume_types"
|
__tablename__ = "volume_types"
|
||||||
id = Column(String(36), primary_key=True)
|
id = Column(String(36), primary_key=True)
|
||||||
name = Column(String(255))
|
name = Column(String(255))
|
||||||
|
description = Column(String(255))
|
||||||
# A reference to qos_specs entity
|
# A reference to qos_specs entity
|
||||||
qos_specs_id = Column(String(36),
|
qos_specs_id = Column(String(36),
|
||||||
ForeignKey('quality_of_service_specs.id'))
|
ForeignKey('quality_of_service_specs.id'))
|
||||||
|
|
|
@ -459,6 +459,10 @@ class VolumeTypeCreateFailed(CinderException):
|
||||||
"name %(name)s and specs %(extra_specs)s")
|
"name %(name)s and specs %(extra_specs)s")
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTypeUpdateFailed(CinderException):
|
||||||
|
message = _("Cannot update volume_type %(id)s")
|
||||||
|
|
||||||
|
|
||||||
class UnknownCmd(VolumeDriverException):
|
class UnknownCmd(VolumeDriverException):
|
||||||
message = _("Unknown or unsupported command %(cmd)s")
|
message = _("Unknown or unsupported command %(cmd)s")
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from cinder.api.contrib import types_manage
|
from cinder.api.contrib import types_manage
|
||||||
|
@ -29,7 +30,24 @@ def stub_volume_type(id):
|
||||||
"key3": "value3",
|
"key3": "value3",
|
||||||
"key4": "value4",
|
"key4": "value4",
|
||||||
"key5": "value5"}
|
"key5": "value5"}
|
||||||
return dict(id=id, name='vol_type_%s' % str(id), extra_specs=specs)
|
return dict(id=id,
|
||||||
|
name='vol_type_%s' % six.text_type(id),
|
||||||
|
description='vol_type_desc_%s' % six.text_type(id),
|
||||||
|
extra_specs=specs)
|
||||||
|
|
||||||
|
|
||||||
|
def stub_volume_type_updated(id):
|
||||||
|
return dict(id=id,
|
||||||
|
name='vol_type_%s_%s' % (six.text_type(id), six.text_type(id)),
|
||||||
|
description='vol_type_desc_%s_%s' % (
|
||||||
|
six.text_type(id), six.text_type(id)))
|
||||||
|
|
||||||
|
|
||||||
|
def stub_volume_type_updated_desc_only(id):
|
||||||
|
return dict(id=id,
|
||||||
|
name='vol_type_%s' % six.text_type(id),
|
||||||
|
description='vol_type_desc_%s_%s' % (
|
||||||
|
six.text_type(id), six.text_type(id)))
|
||||||
|
|
||||||
|
|
||||||
def return_volume_types_get_volume_type(context, id):
|
def return_volume_types_get_volume_type(context, id):
|
||||||
|
@ -50,20 +68,54 @@ def return_volume_types_with_volumes_destroy(context, id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def return_volume_types_create(context, name, specs, is_public):
|
def return_volume_types_create(context,
|
||||||
|
name,
|
||||||
|
specs,
|
||||||
|
is_public,
|
||||||
|
description):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def return_volume_types_create_duplicate_type(context, name, specs, is_public):
|
def return_volume_types_create_duplicate_type(context,
|
||||||
|
name,
|
||||||
|
specs,
|
||||||
|
is_public,
|
||||||
|
description):
|
||||||
raise exception.VolumeTypeExists(id=name)
|
raise exception.VolumeTypeExists(id=name)
|
||||||
|
|
||||||
|
|
||||||
|
def return_volume_types_update(context, id, description):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def return_volume_types_update_fail(context, id, description):
|
||||||
|
raise exception.VolumeTypeUpdateFailed(id=id)
|
||||||
|
|
||||||
|
|
||||||
|
def return_volume_types_get_volume_type_updated(context, id):
|
||||||
|
if id == "777":
|
||||||
|
raise exception.VolumeTypeNotFound(volume_type_id=id)
|
||||||
|
if id == '888':
|
||||||
|
return stub_volume_type_updated_desc_only(int(id))
|
||||||
|
|
||||||
|
# anything else
|
||||||
|
return stub_volume_type_updated(int(id))
|
||||||
|
|
||||||
|
|
||||||
def return_volume_types_get_by_name(context, name):
|
def return_volume_types_get_by_name(context, name):
|
||||||
if name == "777":
|
if name == "777":
|
||||||
raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
|
raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
|
||||||
return stub_volume_type(int(name.split("_")[2]))
|
return stub_volume_type(int(name.split("_")[2]))
|
||||||
|
|
||||||
|
|
||||||
|
def return_volume_types_get_default():
|
||||||
|
return stub_volume_type(1)
|
||||||
|
|
||||||
|
|
||||||
|
def return_volume_types_get_default_not_found():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class VolumeTypesManageApiTest(test.TestCase):
|
class VolumeTypesManageApiTest(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(VolumeTypesManageApiTest, self).setUp()
|
super(VolumeTypesManageApiTest, self).setUp()
|
||||||
|
@ -83,9 +135,9 @@ class VolumeTypesManageApiTest(test.TestCase):
|
||||||
return_volume_types_destroy)
|
return_volume_types_destroy)
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 0)
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
self.controller._delete(req, 1)
|
self.controller._delete(req, 1)
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 1)
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
|
||||||
def test_volume_types_delete_not_found(self):
|
def test_volume_types_delete_not_found(self):
|
||||||
self.stubs.Set(volume_types, 'get_volume_type',
|
self.stubs.Set(volume_types, 'get_volume_type',
|
||||||
|
@ -93,11 +145,11 @@ class VolumeTypesManageApiTest(test.TestCase):
|
||||||
self.stubs.Set(volume_types, 'destroy',
|
self.stubs.Set(volume_types, 'destroy',
|
||||||
return_volume_types_destroy)
|
return_volume_types_destroy)
|
||||||
|
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 0)
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
req = fakes.HTTPRequest.blank('/v2/fake/types/777')
|
req = fakes.HTTPRequest.blank('/v2/fake/types/777')
|
||||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete,
|
self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete,
|
||||||
req, '777')
|
req, '777')
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 1)
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
|
||||||
def test_volume_types_with_volumes_destroy(self):
|
def test_volume_types_with_volumes_destroy(self):
|
||||||
self.stubs.Set(volume_types, 'get_volume_type',
|
self.stubs.Set(volume_types, 'get_volume_type',
|
||||||
|
@ -105,9 +157,9 @@ class VolumeTypesManageApiTest(test.TestCase):
|
||||||
self.stubs.Set(volume_types, 'destroy',
|
self.stubs.Set(volume_types, 'destroy',
|
||||||
return_volume_types_with_volumes_destroy)
|
return_volume_types_with_volumes_destroy)
|
||||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 0)
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
self.controller._delete(req, 1)
|
self.controller._delete(req, 1)
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 1)
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
self.stubs.Set(volume_types, 'create',
|
self.stubs.Set(volume_types, 'create',
|
||||||
|
@ -119,12 +171,12 @@ class VolumeTypesManageApiTest(test.TestCase):
|
||||||
"extra_specs": {"key1": "value1"}}}
|
"extra_specs": {"key1": "value1"}}}
|
||||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||||
|
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 0)
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
res_dict = self.controller._create(req, body)
|
res_dict = self.controller._create(req, body)
|
||||||
|
|
||||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 1)
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
self.assertEqual(1, len(res_dict))
|
self._check_test_results(res_dict, {
|
||||||
self.assertEqual('vol_type_1', res_dict['volume_type']['name'])
|
'expected_name': 'vol_type_1', 'expected_desc': 'vol_type_desc_1'})
|
||||||
|
|
||||||
def test_create_duplicate_type_fail(self):
|
def test_create_duplicate_type_fail(self):
|
||||||
self.stubs.Set(volume_types, 'create',
|
self.stubs.Set(volume_types, 'create',
|
||||||
|
@ -154,3 +206,65 @@ class VolumeTypesManageApiTest(test.TestCase):
|
||||||
def test_create_malformed_entity(self):
|
def test_create_malformed_entity(self):
|
||||||
body = {'volume_type': 'string'}
|
body = {'volume_type': 'string'}
|
||||||
self._create_volume_type_bad_body(body=body)
|
self._create_volume_type_bad_body(body=body)
|
||||||
|
|
||||||
|
def test_update(self):
|
||||||
|
self.stubs.Set(volume_types, 'update',
|
||||||
|
return_volume_types_update)
|
||||||
|
self.stubs.Set(volume_types, 'get_volume_type',
|
||||||
|
return_volume_types_get_volume_type_updated)
|
||||||
|
|
||||||
|
body = {"volume_type": {"description": "vol_type_desc_1_1"}}
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||||
|
req.method = 'PUT'
|
||||||
|
|
||||||
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
res_dict = self.controller._update(req, '1', body)
|
||||||
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
self._check_test_results(res_dict,
|
||||||
|
{'expected_desc': 'vol_type_desc_1_1'})
|
||||||
|
|
||||||
|
def test_update_non_exist(self):
|
||||||
|
self.stubs.Set(volume_types, 'update',
|
||||||
|
return_volume_types_update)
|
||||||
|
self.stubs.Set(volume_types, 'get_volume_type',
|
||||||
|
return_volume_types_get_volume_type)
|
||||||
|
|
||||||
|
body = {"volume_type": {"description": "vol_type_desc_1_1"}}
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/fake/types/777')
|
||||||
|
req.method = 'PUT'
|
||||||
|
|
||||||
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
self.assertRaises(webob.exc.HTTPNotFound,
|
||||||
|
self.controller._update, req, '777', body)
|
||||||
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
|
||||||
|
def test_update_db_fail(self):
|
||||||
|
self.stubs.Set(volume_types, 'update',
|
||||||
|
return_volume_types_update_fail)
|
||||||
|
self.stubs.Set(volume_types, 'get_volume_type',
|
||||||
|
return_volume_types_get_volume_type)
|
||||||
|
|
||||||
|
body = {"volume_type": {"description": "vol_type_desc_1_1"}}
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||||
|
req.method = 'PUT'
|
||||||
|
|
||||||
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
self.assertRaises(webob.exc.HTTPInternalServerError,
|
||||||
|
self.controller._update, req, '1', body)
|
||||||
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
|
||||||
|
def test_update_no_description(self):
|
||||||
|
body = {"volume_type": {}}
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||||
|
req.method = 'PUT'
|
||||||
|
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller._update, req, '1', body)
|
||||||
|
|
||||||
|
def _check_test_results(self, results, expected_results):
|
||||||
|
self.assertEqual(1, len(results))
|
||||||
|
self.assertEqual(expected_results['expected_desc'],
|
||||||
|
results['volume_type']['description'])
|
||||||
|
if expected_results.get('expected_name'):
|
||||||
|
self.assertEqual(expected_results['expected_name'],
|
||||||
|
results['volume_type']['name'])
|
|
@ -233,9 +233,6 @@ class VolumeTypeAccessTest(test.TestCase):
|
||||||
self.type_action_controller.show(self.req, resp, '0')
|
self.type_action_controller.show(self.req, resp, '0')
|
||||||
self.assertEqual({'id': '0', 'os-volume-type-access:is_public': True},
|
self.assertEqual({'id': '0', 'os-volume-type-access:is_public': True},
|
||||||
resp.obj['volume_type'])
|
resp.obj['volume_type'])
|
||||||
self.type_action_controller.show(self.req, resp, '2')
|
|
||||||
self.assertEqual({'id': '0', 'os-volume-type-access:is_public': False},
|
|
||||||
resp.obj['volume_type'])
|
|
||||||
|
|
||||||
def test_detail(self):
|
def test_detail(self):
|
||||||
resp = FakeResponse()
|
resp = FakeResponse()
|
||||||
|
|
|
@ -118,6 +118,7 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
updated_at=now,
|
updated_at=now,
|
||||||
extra_specs={},
|
extra_specs={},
|
||||||
deleted_at=None,
|
deleted_at=None,
|
||||||
|
description=None,
|
||||||
id=42)
|
id=42)
|
||||||
|
|
||||||
request = fakes.HTTPRequest.blank("/v1")
|
request = fakes.HTTPRequest.blank("/v1")
|
||||||
|
@ -126,6 +127,7 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
self.assertIn('volume_type', output)
|
self.assertIn('volume_type', output)
|
||||||
expected_volume_type = dict(name='new_type',
|
expected_volume_type = dict(name='new_type',
|
||||||
extra_specs={},
|
extra_specs={},
|
||||||
|
description=None,
|
||||||
id=42)
|
id=42)
|
||||||
self.assertDictMatch(output['volume_type'], expected_volume_type)
|
self.assertDictMatch(output['volume_type'], expected_volume_type)
|
||||||
|
|
||||||
|
@ -141,6 +143,7 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
updated_at=now,
|
updated_at=now,
|
||||||
extra_specs={},
|
extra_specs={},
|
||||||
deleted_at=None,
|
deleted_at=None,
|
||||||
|
description=None,
|
||||||
id=42 + i))
|
id=42 + i))
|
||||||
|
|
||||||
request = fakes.HTTPRequest.blank("/v1")
|
request = fakes.HTTPRequest.blank("/v1")
|
||||||
|
@ -150,7 +153,8 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
for i in range(0, 10):
|
for i in range(0, 10):
|
||||||
expected_volume_type = dict(name='new_type',
|
expected_volume_type = dict(name='new_type',
|
||||||
extra_specs={},
|
extra_specs={},
|
||||||
id=42 + i)
|
id=42 + i,
|
||||||
|
description=None)
|
||||||
self.assertDictMatch(output['volume_types'][i],
|
self.assertDictMatch(output['volume_types'][i],
|
||||||
expected_volume_type)
|
expected_volume_type)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import uuid
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from oslo.utils import timeutils
|
from oslo.utils import timeutils
|
||||||
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from cinder.api.v2 import types
|
from cinder.api.v2 import types
|
||||||
|
@ -37,7 +38,8 @@ def stub_volume_type(id):
|
||||||
}
|
}
|
||||||
return dict(
|
return dict(
|
||||||
id=id,
|
id=id,
|
||||||
name='vol_type_%s' % str(id),
|
name='vol_type_%s' % six.text_type(id),
|
||||||
|
description='vol_type_desc_%s' % six.text_type(id),
|
||||||
extra_specs=specs,
|
extra_specs=specs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,6 +68,14 @@ def return_volume_types_get_by_name(context, name):
|
||||||
return stub_volume_type(int(name.split("_")[2]))
|
return stub_volume_type(int(name.split("_")[2]))
|
||||||
|
|
||||||
|
|
||||||
|
def return_volume_types_get_default():
|
||||||
|
return stub_volume_type(1)
|
||||||
|
|
||||||
|
|
||||||
|
def return_volume_types_get_default_not_found():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class VolumeTypesApiTest(test.TestCase):
|
class VolumeTypesApiTest(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(VolumeTypesApiTest, self).setUp()
|
super(VolumeTypesApiTest, self).setUp()
|
||||||
|
@ -116,12 +126,33 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||||
req, '777')
|
req, '777')
|
||||||
|
|
||||||
|
def test_get_default(self):
|
||||||
|
self.stubs.Set(volume_types, 'get_default_volume_type',
|
||||||
|
return_volume_types_get_default)
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/fake/types/default')
|
||||||
|
req.method = 'GET'
|
||||||
|
res_dict = self.controller.show(req, 'default')
|
||||||
|
self.assertEqual(1, len(res_dict))
|
||||||
|
self.assertEqual('vol_type_1', res_dict['volume_type']['name'])
|
||||||
|
self.assertEqual('vol_type_desc_1',
|
||||||
|
res_dict['volume_type']['description'])
|
||||||
|
|
||||||
|
def test_get_default_not_found(self):
|
||||||
|
self.stubs.Set(volume_types, 'get_default_volume_type',
|
||||||
|
return_volume_types_get_default_not_found)
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/fake/types/default')
|
||||||
|
req.method = 'GET'
|
||||||
|
|
||||||
|
self.assertRaises(webob.exc.HTTPNotFound,
|
||||||
|
self.controller.show, req, 'default')
|
||||||
|
|
||||||
def test_view_builder_show(self):
|
def test_view_builder_show(self):
|
||||||
view_builder = views_types.ViewBuilder()
|
view_builder = views_types.ViewBuilder()
|
||||||
|
|
||||||
now = timeutils.isotime()
|
now = timeutils.isotime()
|
||||||
raw_volume_type = dict(
|
raw_volume_type = dict(
|
||||||
name='new_type',
|
name='new_type',
|
||||||
|
description='new_type_desc',
|
||||||
deleted=False,
|
deleted=False,
|
||||||
created_at=now,
|
created_at=now,
|
||||||
updated_at=now,
|
updated_at=now,
|
||||||
|
@ -136,6 +167,7 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
self.assertIn('volume_type', output)
|
self.assertIn('volume_type', output)
|
||||||
expected_volume_type = dict(
|
expected_volume_type = dict(
|
||||||
name='new_type',
|
name='new_type',
|
||||||
|
description='new_type_desc',
|
||||||
extra_specs={},
|
extra_specs={},
|
||||||
id=42,
|
id=42,
|
||||||
)
|
)
|
||||||
|
@ -150,6 +182,7 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
raw_volume_types.append(
|
raw_volume_types.append(
|
||||||
dict(
|
dict(
|
||||||
name='new_type',
|
name='new_type',
|
||||||
|
description='new_type_desc',
|
||||||
deleted=False,
|
deleted=False,
|
||||||
created_at=now,
|
created_at=now,
|
||||||
updated_at=now,
|
updated_at=now,
|
||||||
|
@ -166,6 +199,7 @@ class VolumeTypesApiTest(test.TestCase):
|
||||||
for i in range(0, 10):
|
for i in range(0, 10):
|
||||||
expected_volume_type = dict(
|
expected_volume_type = dict(
|
||||||
name='new_type',
|
name='new_type',
|
||||||
|
description='new_type_desc',
|
||||||
extra_specs={},
|
extra_specs={},
|
||||||
id=42 + i
|
id=42 + i
|
||||||
)
|
)
|
||||||
|
@ -177,6 +211,7 @@ class VolumeTypesSerializerTest(test.TestCase):
|
||||||
def _verify_volume_type(self, vtype, tree):
|
def _verify_volume_type(self, vtype, tree):
|
||||||
self.assertEqual('volume_type', tree.tag)
|
self.assertEqual('volume_type', tree.tag)
|
||||||
self.assertEqual(vtype['name'], tree.get('name'))
|
self.assertEqual(vtype['name'], tree.get('name'))
|
||||||
|
self.assertEqual(vtype['description'], tree.get('description'))
|
||||||
self.assertEqual(str(vtype['id']), tree.get('id'))
|
self.assertEqual(str(vtype['id']), tree.get('id'))
|
||||||
self.assertEqual(1, len(tree))
|
self.assertEqual(1, len(tree))
|
||||||
extra_specs = tree[0]
|
extra_specs = tree[0]
|
||||||
|
|
|
@ -694,6 +694,16 @@ class MigrationsMixin(test_migrations.WalkVersionsMixin):
|
||||||
encryptions = db_utils.get_table(engine, 'encryption')
|
encryptions = db_utils.get_table(engine, 'encryption')
|
||||||
self.assertNotIn('encryption_id', encryptions.c)
|
self.assertNotIn('encryption_id', encryptions.c)
|
||||||
|
|
||||||
|
def _check_034(self, engine, data):
|
||||||
|
"""Test adding description columns to volume_types table."""
|
||||||
|
volume_types = db_utils.get_table(engine, 'volume_types')
|
||||||
|
self.assertIsInstance(volume_types.c.description.type,
|
||||||
|
sqlalchemy.types.VARCHAR)
|
||||||
|
|
||||||
|
def _post_downgrade_034(self, engine):
|
||||||
|
volume_types = db_utils.get_table(engine, 'volume_types')
|
||||||
|
self.assertNotIn('description', volume_types.c)
|
||||||
|
|
||||||
def test_walk_versions(self):
|
def test_walk_versions(self):
|
||||||
self.walk_versions(True, False)
|
self.walk_versions(True, False)
|
||||||
|
|
||||||
|
|
|
@ -49,20 +49,25 @@ class VolumeTypeTestCase(test.TestCase):
|
||||||
size="300",
|
size="300",
|
||||||
rpm="7200",
|
rpm="7200",
|
||||||
visible="True")
|
visible="True")
|
||||||
|
self.vol_type1_description = self.vol_type1_name + '_desc'
|
||||||
|
|
||||||
def test_volume_type_create_then_destroy(self):
|
def test_volume_type_create_then_destroy(self):
|
||||||
"""Ensure volume types can be created and deleted."""
|
"""Ensure volume types can be created and deleted."""
|
||||||
prev_all_vtypes = volume_types.get_all_types(self.ctxt)
|
prev_all_vtypes = volume_types.get_all_types(self.ctxt)
|
||||||
|
|
||||||
|
# create
|
||||||
type_ref = volume_types.create(self.ctxt,
|
type_ref = volume_types.create(self.ctxt,
|
||||||
self.vol_type1_name,
|
self.vol_type1_name,
|
||||||
self.vol_type1_specs)
|
self.vol_type1_specs,
|
||||||
|
description=self.vol_type1_description)
|
||||||
new = volume_types.get_volume_type_by_name(self.ctxt,
|
new = volume_types.get_volume_type_by_name(self.ctxt,
|
||||||
self.vol_type1_name)
|
self.vol_type1_name)
|
||||||
|
|
||||||
LOG.info(_("Given data: %s"), self.vol_type1_specs)
|
LOG.info(_("Given data: %s"), self.vol_type1_specs)
|
||||||
LOG.info(_("Result data: %s"), new)
|
LOG.info(_("Result data: %s"), new)
|
||||||
|
|
||||||
|
self.assertEqual(self.vol_type1_description, new['description'])
|
||||||
|
|
||||||
for k, v in self.vol_type1_specs.iteritems():
|
for k, v in self.vol_type1_specs.iteritems():
|
||||||
self.assertEqual(v, new['extra_specs'][k],
|
self.assertEqual(v, new['extra_specs'][k],
|
||||||
'one of fields does not match')
|
'one of fields does not match')
|
||||||
|
@ -72,6 +77,14 @@ class VolumeTypeTestCase(test.TestCase):
|
||||||
len(new_all_vtypes),
|
len(new_all_vtypes),
|
||||||
'drive type was not created')
|
'drive type was not created')
|
||||||
|
|
||||||
|
# update
|
||||||
|
new_type_desc = self.vol_type1_description + '_updated'
|
||||||
|
type_ref_updated = volume_types.update(self.ctxt,
|
||||||
|
type_ref.id,
|
||||||
|
new_type_desc)
|
||||||
|
self.assertEqual(new_type_desc, type_ref_updated['description'])
|
||||||
|
|
||||||
|
# destroy
|
||||||
volume_types.destroy(self.ctxt, type_ref['id'])
|
volume_types.destroy(self.ctxt, type_ref['id'])
|
||||||
new_all_vtypes = volume_types.get_all_types(self.ctxt)
|
new_all_vtypes = volume_types.get_all_types(self.ctxt)
|
||||||
self.assertEqual(prev_all_vtypes,
|
self.assertEqual(prev_all_vtypes,
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from oslo.db import exception as db_exc
|
from oslo.db import exception as db_exc
|
||||||
|
import six
|
||||||
|
|
||||||
from cinder import context
|
from cinder import context
|
||||||
from cinder import db
|
from cinder import db
|
||||||
|
@ -34,7 +35,12 @@ CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def create(context, name, extra_specs=None, is_public=True, projects=None):
|
def create(context,
|
||||||
|
name,
|
||||||
|
extra_specs=None,
|
||||||
|
is_public=True,
|
||||||
|
projects=None,
|
||||||
|
description=None):
|
||||||
"""Creates volume types."""
|
"""Creates volume types."""
|
||||||
extra_specs = extra_specs or {}
|
extra_specs = extra_specs or {}
|
||||||
projects = projects or []
|
projects = projects or []
|
||||||
|
@ -42,15 +48,31 @@ def create(context, name, extra_specs=None, is_public=True, projects=None):
|
||||||
type_ref = db.volume_type_create(context,
|
type_ref = db.volume_type_create(context,
|
||||||
dict(name=name,
|
dict(name=name,
|
||||||
extra_specs=extra_specs,
|
extra_specs=extra_specs,
|
||||||
is_public=is_public),
|
is_public=is_public,
|
||||||
|
description=description),
|
||||||
projects=projects)
|
projects=projects)
|
||||||
except db_exc.DBError as e:
|
except db_exc.DBError as e:
|
||||||
LOG.exception(_LE('DB error: %s') % e)
|
LOG.exception(_LE('DB error: %s') % six.text_type(e))
|
||||||
raise exception.VolumeTypeCreateFailed(name=name,
|
raise exception.VolumeTypeCreateFailed(name=name,
|
||||||
extra_specs=extra_specs)
|
extra_specs=extra_specs)
|
||||||
return type_ref
|
return type_ref
|
||||||
|
|
||||||
|
|
||||||
|
def update(context, id, description):
|
||||||
|
"""Update volume type by id."""
|
||||||
|
if id is None:
|
||||||
|
msg = _("id cannot be None")
|
||||||
|
raise exception.InvalidVolumeType(reason=msg)
|
||||||
|
try:
|
||||||
|
type_updated = db.volume_type_update(context,
|
||||||
|
id,
|
||||||
|
dict(description=description))
|
||||||
|
except db_exc.DBError as e:
|
||||||
|
LOG.exception(_LE('DB error: %s') % six.text_type(e))
|
||||||
|
raise exception.VolumeTypeUpdateFailed(id=id)
|
||||||
|
return type_updated
|
||||||
|
|
||||||
|
|
||||||
def destroy(context, id):
|
def destroy(context, id):
|
||||||
"""Marks volume types as deleted."""
|
"""Marks volume types as deleted."""
|
||||||
if id is None:
|
if id is None:
|
||||||
|
@ -139,9 +161,9 @@ def get_default_volume_type():
|
||||||
# Couldn't find volume type with the name in default_volume_type
|
# Couldn't find volume type with the name in default_volume_type
|
||||||
# flag, record this issue and move on
|
# flag, record this issue and move on
|
||||||
#TODO(zhiteng) consider add notification to warn admin
|
#TODO(zhiteng) consider add notification to warn admin
|
||||||
LOG.exception(_LE('Default volume type is not found, '
|
LOG.exception(_LE('Default volume type is not found,'
|
||||||
'please check default_volume_type '
|
'please check default_volume_type config: %s') %
|
||||||
'config: %s'), e)
|
six.text_type(e))
|
||||||
|
|
||||||
return vol_type
|
return vol_type
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue