Add update share-type API to Share Types
Currently, only the name and description and public access of share-type is set when the share-type is created, and not allowed to be edited after the share-type is created. We can only set extra spec for share-type. But not name or description or public access for share-type. Co-Authored-By: Brin Zhang <zhangbailin@inspur.com> APIImpact Implements: blueprint update-share-type-name-or-description Change-Id: I4c7bdd601d48b40c01639b5089d4bff259a7b3af
This commit is contained in:
parent
f1e10ec2ff
commit
01e89ae26b
@ -814,6 +814,13 @@ create_share_from_snapshot_support:
|
||||
required: false
|
||||
type: boolean
|
||||
min_version: 2.24
|
||||
create_share_from_snapshot_support_body:
|
||||
description: |
|
||||
Boolean extra spec used for filtering of back ends by
|
||||
their capability to create shares from snapshots.
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
created_at:
|
||||
description: |
|
||||
The date and time stamp when the resource was created within the service's
|
||||
@ -1195,6 +1202,14 @@ is_default_type:
|
||||
required: true
|
||||
type: boolean
|
||||
min_version: 2.46
|
||||
is_default_type_body:
|
||||
description: |
|
||||
Defines the share type created is default or not. If the returning
|
||||
value is true, then it is the default share type, otherwise, it is
|
||||
not default.
|
||||
in: body
|
||||
required: true
|
||||
type: boolean
|
||||
is_group_type_default:
|
||||
description: |
|
||||
Defines the share group type created is default or not. If the
|
||||
@ -1403,6 +1418,13 @@ mount_snapshot_support:
|
||||
required: false
|
||||
type: boolean
|
||||
min_version: 2.32
|
||||
mount_snapshot_support_body:
|
||||
description: |
|
||||
Boolean extra spec used for filtering of back ends
|
||||
by their capability to mount share snapshots.
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
name:
|
||||
description: |
|
||||
The user defined name of the resource.
|
||||
@ -1775,6 +1797,12 @@ replication_type:
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.11
|
||||
replication_type_body:
|
||||
description: |
|
||||
The share replication type.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
request_id_body:
|
||||
description: |
|
||||
The UUID of the request during which the message was created.
|
||||
@ -1814,6 +1842,13 @@ revert_to_snapshot_support:
|
||||
required: false
|
||||
type: boolean
|
||||
min_version: 2.27
|
||||
revert_to_snapshot_support_body:
|
||||
description: |
|
||||
Boolean extra spec used for filtering of back ends by their
|
||||
capability to revert shares to snapshots.
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
security_service_dns_ip:
|
||||
description: |
|
||||
The DNS IP address that is used inside the project network.
|
||||
@ -2371,6 +2406,21 @@ share_type_access:is_public:
|
||||
required: false
|
||||
type: boolean
|
||||
min_version: 2.7
|
||||
share_type_access:is_public_body:
|
||||
description: |
|
||||
Indicates whether a share type is accessible by all projects (tenants)
|
||||
in the cloud.
|
||||
in: body
|
||||
required: true
|
||||
type: boolean
|
||||
share_type_access:is_public_update_request:
|
||||
description: |
|
||||
Indicates whether the share type should be accessible by all projects
|
||||
(tenants) in the cloud. If not specified, the visibility of the share
|
||||
type is not altered.
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
share_type_description:
|
||||
description: |
|
||||
The description of the share type.
|
||||
@ -2378,6 +2428,12 @@ share_type_description:
|
||||
required: true
|
||||
type: string
|
||||
min_version: 2.41
|
||||
share_type_description_body:
|
||||
description: |
|
||||
The description of the share type.
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
share_type_description_request:
|
||||
description: |
|
||||
The description of the share type. The value of this field is limited to
|
||||
@ -2386,6 +2442,12 @@ share_type_description_request:
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.41
|
||||
share_type_description_update_request:
|
||||
description: |
|
||||
New description for the share type.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
share_type_id_body:
|
||||
description: |
|
||||
The UUID of the share type.
|
||||
|
8
api-ref/source/samples/share-type-update-request.json
Normal file
8
api-ref/source/samples/share-type-update-request.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"share_type":
|
||||
{
|
||||
"share_type_access:is_public": true,
|
||||
"name": "testing",
|
||||
"description": "share type description"
|
||||
}
|
||||
}
|
38
api-ref/source/samples/share-type-update-response.json
Normal file
38
api-ref/source/samples/share-type-update-response.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"share_type": {
|
||||
"required_extra_specs": {
|
||||
"driver_handles_share_servers": true
|
||||
},
|
||||
"share_type_access:is_public": true,
|
||||
"extra_specs": {
|
||||
"replication_type": "readable",
|
||||
"driver_handles_share_servers": "True",
|
||||
"mount_snapshot_support": "False",
|
||||
"revert_to_snapshot_support": "False",
|
||||
"create_share_from_snapshot_support": "True",
|
||||
"snapshot_support": "True"
|
||||
},
|
||||
"id": "7fa1342b-de9d-4d89-bdc8-af67795c0e52",
|
||||
"name": "testing",
|
||||
"is_default": false,
|
||||
"description": "share type description"
|
||||
},
|
||||
"volume_type": {
|
||||
"required_extra_specs": {
|
||||
"driver_handles_share_servers": true
|
||||
},
|
||||
"share_type_access:is_public": true,
|
||||
"extra_specs": {
|
||||
"replication_type": "readable",
|
||||
"driver_handles_share_servers": "True",
|
||||
"mount_snapshot_support": "False",
|
||||
"revert_to_snapshot_support": "False",
|
||||
"create_share_from_snapshot_support": "True",
|
||||
"snapshot_support": "True"
|
||||
},
|
||||
"id": "7fa1342b-de9d-4d89-bdc8-af67795c0e52",
|
||||
"name": "testing",
|
||||
"is_default": false,
|
||||
"description": "share type description"
|
||||
}
|
||||
}
|
@ -602,3 +602,73 @@ Request
|
||||
|
||||
- project_id: project_id_path
|
||||
- share_type_id: share_type_id
|
||||
|
||||
|
||||
Update share type (since API v2.50)
|
||||
===================================
|
||||
|
||||
.. rest_method:: PUT /v2/{project_id}/types/{share_type_id}
|
||||
|
||||
.. versionadded:: 2.50
|
||||
|
||||
Update a share type. Share type extra-specs cannot be updated
|
||||
with this API. Please use the respective APIs to `set extra specs
|
||||
<#set-extra-spec-for-share-type>`_ or `unset extra specs
|
||||
<#unset-an-extra-spec>`_.
|
||||
|
||||
Response codes
|
||||
--------------
|
||||
|
||||
.. rest_status_code:: success status.yaml
|
||||
|
||||
- 200
|
||||
|
||||
.. rest_status_code:: error status.yaml
|
||||
|
||||
- 400
|
||||
- 401
|
||||
- 403
|
||||
- 404
|
||||
- 409
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- project_id: project_id_path
|
||||
- share_type_id: share_type_id
|
||||
- name: share_type_name_request
|
||||
- share_type_access:is_public: share_type_access:is_public_update_request
|
||||
- description: share_type_description_update_request
|
||||
|
||||
Request example
|
||||
---------------
|
||||
|
||||
.. literalinclude:: samples/share-type-update-request.json
|
||||
:language: javascript
|
||||
|
||||
Response parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- id: share_type_id_body
|
||||
- required_extra_specs: required_extra_specs
|
||||
- extra_specs: extra_specs
|
||||
- driver_handles_share_servers: driver_handles_share_servers
|
||||
- snapshot_support: snapshot_support_1
|
||||
- share_type_access:is_public: share_type_access:is_public_body
|
||||
- name: share_type_name
|
||||
- replication_type: replication_type_body
|
||||
- mount_snapshot_support: mount_snapshot_support_body
|
||||
- revert_to_snapshot_support: revert_to_snapshot_support_body
|
||||
- create_share_from_snapshot_support: create_share_from_snapshot_support_body
|
||||
- description: share_type_description_body
|
||||
- is_default: is_default_type_body
|
||||
|
||||
Response example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/share-type-update-response.json
|
||||
:language: javascript
|
||||
|
@ -134,13 +134,16 @@ REST_API_VERSION_HISTORY = """
|
||||
* 2.49 - Added Manage/Unmanage Share Server APIs. Updated Manage/Unmanage
|
||||
Shares and Snapshots APIs to work in
|
||||
``driver_handles_shares_servers`` enabled mode.
|
||||
* 2.50 - Added update share type API to Share Type APIs. Through this API
|
||||
we can update the ``name``, ``description`` and/or
|
||||
``share_type_access:is_public`` fields of the share type.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
# The default api version request is defined to be the
|
||||
# minimum version of the API supported.
|
||||
_MIN_API_VERSION = "2.0"
|
||||
_MAX_API_VERSION = "2.49"
|
||||
_MAX_API_VERSION = "2.50"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
@ -275,3 +275,9 @@ user documentation.
|
||||
-----------------------
|
||||
Added Manage/Unmanage Share Server APIs. Updated Manage/Unmanage Shares and
|
||||
Snapshots APIs to work in ``driver_handles_shares_servers`` enabled mode.
|
||||
|
||||
2.50
|
||||
----
|
||||
Added update share type API to Share Type APIs. We can update the ``name``,
|
||||
``description`` and/or ``share_type_access:is_public`` fields of the share
|
||||
type by the update share type API.
|
||||
|
@ -52,6 +52,10 @@ class ShareTypesController(wsgi.Controller):
|
||||
def _notify_share_type_error(self, context, method, payload):
|
||||
rpc.get_notifier('shareType').error(context, method, payload)
|
||||
|
||||
def _notify_share_type_info(self, context, method, share_type):
|
||||
payload = dict(share_types=share_type)
|
||||
rpc.get_notifier('shareType').info(context, method, payload)
|
||||
|
||||
def _check_body(self, body, action_name):
|
||||
if not self.is_valid_body(body, action_name):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
@ -221,9 +225,8 @@ class ShareTypesController(wsgi.Controller):
|
||||
share_type = share_types.get_share_type_by_name(context, name)
|
||||
share_type['required_extra_specs'] = required_extra_specs
|
||||
req.cache_db_share_type(share_type)
|
||||
notifier_info = dict(share_types=share_type)
|
||||
rpc.get_notifier('shareType').info(
|
||||
context, 'share_type.create', notifier_info)
|
||||
self._notify_share_type_info(
|
||||
context, 'share_type.create', share_type)
|
||||
|
||||
except exception.InvalidExtraSpec as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=six.text_type(e))
|
||||
@ -252,9 +255,8 @@ class ShareTypesController(wsgi.Controller):
|
||||
try:
|
||||
share_type = share_types.get_share_type(context, id)
|
||||
share_types.destroy(context, share_type['id'])
|
||||
notifier_info = dict(share_types=share_type)
|
||||
rpc.get_notifier('shareType').info(
|
||||
context, 'share_type.delete', notifier_info)
|
||||
self._notify_share_type_info(
|
||||
context, 'share_type.delete', share_type)
|
||||
except exception.ShareTypeInUse as err:
|
||||
notifier_err = dict(id=id, error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.delete',
|
||||
@ -270,6 +272,85 @@ class ShareTypesController(wsgi.Controller):
|
||||
|
||||
return webob.Response(status_int=http_client.ACCEPTED)
|
||||
|
||||
@wsgi.Controller.api_version("2.50")
|
||||
@wsgi.action("update")
|
||||
@wsgi.Controller.authorize
|
||||
def update(self, req, id, body):
|
||||
"""Update name description is_public for a given share type."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
if (not self.is_valid_body(body, 'share_type') and
|
||||
not self.is_valid_body(body, 'volume_type')):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
elif self.is_valid_body(body, 'share_type'):
|
||||
sha_type = body['share_type']
|
||||
else:
|
||||
sha_type = body['volume_type']
|
||||
name = sha_type.get('name')
|
||||
description = sha_type.get('description')
|
||||
is_public = sha_type.get('share_type_access:is_public', None)
|
||||
|
||||
if is_public is not None:
|
||||
try:
|
||||
is_public = strutils.bool_from_string(is_public, strict=True)
|
||||
except ValueError:
|
||||
msg = _("share_type_access:is_public has a non-boolean"
|
||||
" value.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
# If name specified, name can not be empty or greater than 255.
|
||||
if name is not None:
|
||||
if len(name.strip()) == 0:
|
||||
msg = _("Share type name cannot be empty.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
if len(name) > 255:
|
||||
msg = _("Share type name cannot be greater than 255 "
|
||||
"characters in length.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
# If description specified, length can not greater than 255.
|
||||
if description and len(description) > 255:
|
||||
msg = _("Share type description cannot be greater than 255 "
|
||||
"characters in length.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
# Name, description and is_public can not be None.
|
||||
# Specify one of them, or a combination thereof.
|
||||
if name is None and description is None and is_public is None:
|
||||
msg = _("Specify share type name, description, "
|
||||
"share_type_access:is_public or a combination thereof.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
share_types.update(context, id, name, description,
|
||||
is_public=is_public)
|
||||
# Get the updated
|
||||
sha_type = self._show_share_type_details(context, id)
|
||||
req.cache_resource(sha_type, name='types')
|
||||
self._notify_share_type_info(
|
||||
context, 'share_type.update', sha_type)
|
||||
|
||||
except exception.ShareTypeNotFound as err:
|
||||
notifier_err = {"id": id, "error_message": err}
|
||||
self._notify_share_type_error(
|
||||
context, 'share_type.update', notifier_err)
|
||||
# Not found exception will be handled at the wsgi level
|
||||
raise
|
||||
except exception.ShareTypeExists as err:
|
||||
notifier_err = {"share_type": sha_type, "error_message": err}
|
||||
self._notify_share_type_error(
|
||||
context, 'share_type.update', notifier_err)
|
||||
raise webob.exc.HTTPConflict(explanation=err.msg)
|
||||
except exception.ShareTypeUpdateFailed as err:
|
||||
notifier_err = {"share_type": sha_type, "error_message": err}
|
||||
self._notify_share_type_error(
|
||||
context, 'share_type.update', notifier_err)
|
||||
raise webob.exc.HTTPInternalServerError(
|
||||
explanation=err.msg)
|
||||
|
||||
return self._view_builder.show(req, sha_type)
|
||||
|
||||
@wsgi.Controller.authorize('list_project_access')
|
||||
def share_type_access(self, req, id):
|
||||
context = req.environ['manila.context']
|
||||
|
@ -949,6 +949,11 @@ def share_type_create(context, values, projects=None):
|
||||
return IMPL.share_type_create(context, values, projects)
|
||||
|
||||
|
||||
def share_type_update(context, share_type_id, values):
|
||||
"""Update an exist share type."""
|
||||
return IMPL.share_type_update(context, share_type_id, values)
|
||||
|
||||
|
||||
def share_type_get_all(context, inactive=False, filters=None):
|
||||
"""Get all share types.
|
||||
|
||||
|
@ -210,6 +210,18 @@ def apply_sorting(model, query, sort_key, sort_dir):
|
||||
return query.order_by(sort_method())
|
||||
|
||||
|
||||
def handle_db_data_error(f):
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except db_exc.DBDataError:
|
||||
msg = _('Error writing field to database.')
|
||||
LOG.exception(msg)
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def model_query(context, model, *args, **kwargs):
|
||||
"""Query helper that accounts for context's `read_deleted` field.
|
||||
|
||||
@ -3944,6 +3956,45 @@ def _share_type_get_query(context, session=None, read_deleted=None,
|
||||
return query
|
||||
|
||||
|
||||
@handle_db_data_error
|
||||
@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
|
||||
def _type_update(context, type_id, values, is_group):
|
||||
|
||||
if values.get('name') is None:
|
||||
values.pop('name', None)
|
||||
|
||||
if is_group:
|
||||
model = models.ShareGroupTypes
|
||||
exists_exc = exception.ShareGroupTypeExists
|
||||
exists_args = {'type_id': values.get('name')}
|
||||
else:
|
||||
model = models.ShareTypes
|
||||
exists_exc = exception.ShareTypeExists
|
||||
exists_args = {'id': values.get('name')}
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(context, model, session=session)
|
||||
|
||||
try:
|
||||
result = query.filter_by(id=type_id).update(values)
|
||||
except db_exception.DBDuplicateEntry:
|
||||
# This exception only occurs if there's a non-deleted
|
||||
# share/group type which has the same name as the name being
|
||||
# updated.
|
||||
raise exists_exc(**exists_args)
|
||||
|
||||
if not result:
|
||||
if is_group:
|
||||
raise exception.ShareGroupTypeNotFound(type_id=type_id)
|
||||
else:
|
||||
raise exception.ShareTypeNotFound(share_type_id=type_id)
|
||||
|
||||
|
||||
def share_type_update(context, share_type_id, values):
|
||||
_type_update(context, share_type_id, values, is_group=False)
|
||||
|
||||
|
||||
@require_context
|
||||
def share_type_get_all(context, inactive=False, filters=None):
|
||||
"""Returns a dict describing all share_types with name as key."""
|
||||
|
@ -681,6 +681,10 @@ class ShareTypeCreateFailed(ManilaException):
|
||||
"name %(name)s and specs %(extra_specs)s.")
|
||||
|
||||
|
||||
class ShareTypeUpdateFailed(ManilaException):
|
||||
message = _("Cannot update share_type %(id)s.")
|
||||
|
||||
|
||||
class ShareGroupTypeCreateFailed(ManilaException):
|
||||
message = _("Cannot create share group type with "
|
||||
"name %(name)s and specs %(group_specs)s.")
|
||||
|
@ -31,6 +31,16 @@ share_type_policies = [
|
||||
'path': '/types',
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=BASE_POLICY_NAME % 'update',
|
||||
check_str=base.RULE_ADMIN_API,
|
||||
description='Update share type.',
|
||||
operations=[
|
||||
{
|
||||
'method': 'PUT',
|
||||
'path': '/types/{share_type_id}',
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=BASE_POLICY_NAME % 'show',
|
||||
check_str=base.RULE_DEFAULT,
|
||||
|
@ -70,6 +70,24 @@ def sanitize_extra_specs(extra_specs):
|
||||
return extra_specs
|
||||
|
||||
|
||||
def update(context, id, name, description, is_public=None):
|
||||
"""Update share type by id."""
|
||||
values = {}
|
||||
if name:
|
||||
values.update({'name': name})
|
||||
if description == "":
|
||||
values.update({'description': None})
|
||||
elif description:
|
||||
values.update({'description': description})
|
||||
if is_public is not None:
|
||||
values.update({'is_public': is_public})
|
||||
try:
|
||||
db.share_type_update(context, id, values)
|
||||
except db_exception.DBError:
|
||||
LOG.exception('DB error.')
|
||||
raise exception.ShareTypeUpdateFailed(id=id)
|
||||
|
||||
|
||||
def destroy(context, id):
|
||||
"""Marks share types as deleted."""
|
||||
if id is None:
|
||||
|
@ -19,6 +19,7 @@ import ddt
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
import random
|
||||
import webob
|
||||
|
||||
from manila.api.v2 import share_types as types
|
||||
@ -45,15 +46,25 @@ def stub_share_type(id):
|
||||
"key5": "value5",
|
||||
constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: "true",
|
||||
}
|
||||
return dict(
|
||||
id=id,
|
||||
name='share_type_%s' % str(id),
|
||||
description='description_%s' % str(id),
|
||||
extra_specs=specs,
|
||||
required_extra_specs={
|
||||
if id == 4:
|
||||
name = 'update_share_type_%s' % str(id)
|
||||
description = 'update_description_%s' % str(id)
|
||||
is_public = False
|
||||
else:
|
||||
name = 'share_type_%s' % str(id)
|
||||
description = 'description_%s' % str(id)
|
||||
is_public = True
|
||||
share_type = {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'is_public': is_public,
|
||||
'extra_specs': specs,
|
||||
'required_extra_specs': {
|
||||
constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: "true",
|
||||
}
|
||||
)
|
||||
}
|
||||
return share_type
|
||||
|
||||
|
||||
def return_share_types_get_all_types(context, search_opts=None):
|
||||
@ -102,6 +113,20 @@ def return_share_types_get_share_type(context, id=1):
|
||||
return stub_share_type(int(id))
|
||||
|
||||
|
||||
def return_share_type_update(context, id=4, name=None, description=None,
|
||||
is_public=None):
|
||||
if id == 888:
|
||||
raise exception.ShareTypeUpdateFailed(id=id)
|
||||
if id == 999:
|
||||
raise exception.ShareTypeNotFound(share_type_id=id)
|
||||
pre_share_type = stub_share_type(int(id))
|
||||
new_name = name
|
||||
new_description = description
|
||||
return pre_share_type.update({"name": new_name,
|
||||
"description": new_description,
|
||||
"is_public": is_public})
|
||||
|
||||
|
||||
def return_share_types_get_by_name(context, name):
|
||||
if name == "777":
|
||||
raise exception.ShareTypeNotFoundByName(share_type_name=name)
|
||||
@ -146,6 +171,28 @@ def make_create_body(name="test_share_1", extra_specs=None,
|
||||
return body
|
||||
|
||||
|
||||
def generate_long_description(des_length=256):
|
||||
random_str = ''
|
||||
base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz'
|
||||
length = len(base_str) - 1
|
||||
for i in range(des_length):
|
||||
random_str += base_str[random.randint(0, length)]
|
||||
return random_str
|
||||
|
||||
|
||||
def make_update_body(name=None, description=None, is_public=None):
|
||||
body = {"share_type": {}}
|
||||
if name:
|
||||
body["share_type"].update({"name": name})
|
||||
if description:
|
||||
body["share_type"].update({"description": description})
|
||||
if is_public is not None:
|
||||
body["share_type"].update(
|
||||
{"share_type_access:is_public": is_public})
|
||||
|
||||
return body
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareTypesAPITest(test.TestCase):
|
||||
|
||||
@ -167,6 +214,9 @@ class ShareTypesAPITest(test.TestCase):
|
||||
self.mock_object(
|
||||
share_types, 'get_share_type',
|
||||
mock.Mock(side_effect=return_share_types_get_share_type))
|
||||
self.mock_object(
|
||||
share_types, 'update',
|
||||
mock.Mock(side_effect=return_share_type_update))
|
||||
self.mock_object(
|
||||
share_types, 'destroy',
|
||||
mock.Mock(side_effect=return_share_types_destroy))
|
||||
@ -390,6 +440,92 @@ class ShareTypesAPITest(test.TestCase):
|
||||
self.controller._parse_is_public,
|
||||
'fakefakefake')
|
||||
|
||||
@ddt.data(
|
||||
("new_name", "new_description", "wrong_bool"),
|
||||
(" ", "new_description", "true"),
|
||||
(" ", generate_long_description(256), "true"),
|
||||
(None, None, None),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_share_types_update_with_invalid_parameter(
|
||||
self, name, description, is_public):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/4',
|
||||
version='2.50')
|
||||
body = make_update_body(name, description, is_public)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
req, 4, body)
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_update_with_invalid_body(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/4',
|
||||
version='2.50')
|
||||
body = {'share_type': 'i_am_invalid_body'}
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
req, 4, body)
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_update(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/4',
|
||||
version='2.50')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
body = make_update_body("update_share_type_4",
|
||||
"update_description_4",
|
||||
is_public=False)
|
||||
res_dict = self.controller.update(req, 4, body)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
self.assertEqual(2, len(res_dict))
|
||||
|
||||
self.assertEqual('update_share_type_4', res_dict['share_type']['name'])
|
||||
self.assertEqual('update_share_type_4',
|
||||
res_dict['volume_type']['name'])
|
||||
self.assertIs(False,
|
||||
res_dict['share_type']['share_type_access:is_public'])
|
||||
|
||||
self.assertEqual('update_description_4',
|
||||
res_dict['share_type']['description'])
|
||||
self.assertEqual('update_description_4',
|
||||
res_dict['volume_type']['description'])
|
||||
|
||||
def test_share_types_update_pre_v250(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/4',
|
||||
version='2.49')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
body = make_update_body("update_share_type_4",
|
||||
"update_description_4",
|
||||
is_public=False)
|
||||
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
||||
self.controller.update,
|
||||
req, 4, body)
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_update_failed(self):
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/888',
|
||||
version='2.50')
|
||||
body = make_update_body("update_share_type_888",
|
||||
"update_description_888",
|
||||
is_public=False)
|
||||
self.assertRaises(webob.exc.HTTPInternalServerError,
|
||||
self.controller.update,
|
||||
req, 888, body)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_update_not_found(self):
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/999',
|
||||
version='2.50')
|
||||
|
||||
body = make_update_body("update_share_type_999",
|
||||
"update_description_999",
|
||||
is_public=False)
|
||||
|
||||
self.assertRaises(exception.ShareTypeNotFound,
|
||||
self.controller.update,
|
||||
req, 999, body)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_delete(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
|
@ -3275,6 +3275,40 @@ class ShareTypeAPITestCase(test.TestCase):
|
||||
self.assertRaises(exception.DefaultShareTypeNotConfigured,
|
||||
db_api.share_type_get, self.ctxt, None)
|
||||
|
||||
@ddt.data(
|
||||
{'name': 'st_1', 'description': 'des_1', 'is_public': True},
|
||||
{'name': 'st_2', 'description': 'des_2', 'is_public': None},
|
||||
{'name': 'st_3', 'description': None, 'is_public': False},
|
||||
{'name': None, 'description': 'des_4', 'is_public': True},
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_share_type_update(self, name, description, is_public):
|
||||
values = {}
|
||||
if name:
|
||||
values.update({'name': name})
|
||||
if description:
|
||||
values.update({'description': description})
|
||||
if is_public is not None:
|
||||
values.update({'is_public': is_public})
|
||||
share_type = db_utils.create_share_type(name='st_name')
|
||||
db_api.share_type_update(self.ctxt, share_type['id'], values)
|
||||
updated_st = db_api.share_type_get_by_name_or_id(self.ctxt,
|
||||
share_type['id'])
|
||||
if name:
|
||||
self.assertEqual(name, updated_st['name'])
|
||||
if description:
|
||||
self.assertEqual(description, updated_st['description'])
|
||||
if is_public is not None:
|
||||
self.assertEqual(is_public, updated_st['is_public'])
|
||||
|
||||
def test_share_type_update_not_found(self):
|
||||
share_type = db_utils.create_share_type(name='st_update_test')
|
||||
db_api.share_type_destroy(self.ctxt, share_type['id'])
|
||||
values = {"name": "not_exist"}
|
||||
self.assertRaises(exception.ShareTypeNotFound,
|
||||
db_api.share_type_update,
|
||||
self.ctxt, share_type['id'], values)
|
||||
|
||||
|
||||
class MessagesDatabaseAPITestCase(test.TestCase):
|
||||
|
||||
|
@ -41,6 +41,28 @@ def create_share_type_dict(extra_specs=None):
|
||||
}
|
||||
|
||||
|
||||
def return_share_type_update(context, id, values):
|
||||
name = values.get('name')
|
||||
description = values.get('description')
|
||||
is_public = values.get('is_public')
|
||||
if id == '444':
|
||||
raise exception.ShareTypeUpdateFailed(id=id)
|
||||
else:
|
||||
st_update = {
|
||||
'created_at': datetime.datetime(2019, 9, 9, 14, 40, 31),
|
||||
'deleted': '0',
|
||||
'deleted_at': None,
|
||||
'extra_specs': {u'gold': u'True'},
|
||||
'required_extra_specs': {},
|
||||
'id': id,
|
||||
'name': name,
|
||||
'is_public': is_public,
|
||||
'description': description,
|
||||
'updated_at': None
|
||||
}
|
||||
return st_update
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareTypesTestCase(test.TestCase):
|
||||
|
||||
@ -71,6 +93,21 @@ class ShareTypesTestCase(test.TestCase):
|
||||
}
|
||||
}
|
||||
|
||||
fake_type_update = {
|
||||
'test_type_update': {
|
||||
'created_at': datetime.datetime(2019, 9, 9, 14, 40, 31),
|
||||
'deleted': '0',
|
||||
'deleted_at': None,
|
||||
'extra_specs': {u'gold': u'True'},
|
||||
'required_extra_specs': {},
|
||||
'id': '888',
|
||||
'name': 'new_name',
|
||||
'is_public': True,
|
||||
'description': 'new_description',
|
||||
'updated_at': None
|
||||
}
|
||||
}
|
||||
|
||||
fake_r_extra_specs = {
|
||||
u'gold': u'True',
|
||||
u'driver_handles_share_servers': u'True'
|
||||
@ -254,6 +291,27 @@ class ShareTypesTestCase(test.TestCase):
|
||||
share_types.get_share_type_extra_specs.assert_called_once_with(
|
||||
self.fake_share_type_id)
|
||||
|
||||
def test_update_share_type(self):
|
||||
expected = self.fake_type_update['test_type_update']
|
||||
self.mock_object(db,
|
||||
'share_type_update',
|
||||
mock.Mock(side_effect=return_share_type_update))
|
||||
self.mock_object(db,
|
||||
'share_type_get',
|
||||
mock.Mock(return_value=expected))
|
||||
new_name = "new_name"
|
||||
new_description = "new_description"
|
||||
is_public = True
|
||||
self.assertRaises(exception.ShareTypeUpdateFailed, share_types.update,
|
||||
self.context, id='444', name=new_name,
|
||||
description=new_description, is_public=is_public)
|
||||
share_types.update(self.context, '888', new_name,
|
||||
new_description, is_public)
|
||||
st_update = share_types.get_share_type(self.context, '888')
|
||||
self.assertEqual(new_name, st_update['name'])
|
||||
self.assertEqual(new_description, st_update['description'])
|
||||
self.assertEqual(is_public, st_update['is_public'])
|
||||
|
||||
@ddt.data({}, {"fake": "fake"})
|
||||
def test_create_without_required_extra_spec(self, optional_specs):
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- The ``name``, ``description`` and/or ``share_type_access:is_public``
|
||||
attributes of share types can be updated with API version ``2.50``
|
||||
and beyond.
|
Loading…
Reference in New Issue
Block a user