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
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
min_version: 2.24
|
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:
|
created_at:
|
||||||
description: |
|
description: |
|
||||||
The date and time stamp when the resource was created within the service's
|
The date and time stamp when the resource was created within the service's
|
||||||
@ -1195,6 +1202,14 @@ is_default_type:
|
|||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
min_version: 2.46
|
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:
|
is_group_type_default:
|
||||||
description: |
|
description: |
|
||||||
Defines the share group type created is default or not. If the
|
Defines the share group type created is default or not. If the
|
||||||
@ -1403,6 +1418,13 @@ mount_snapshot_support:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
min_version: 2.32
|
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:
|
name:
|
||||||
description: |
|
description: |
|
||||||
The user defined name of the resource.
|
The user defined name of the resource.
|
||||||
@ -1775,6 +1797,12 @@ replication_type:
|
|||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
min_version: 2.11
|
min_version: 2.11
|
||||||
|
replication_type_body:
|
||||||
|
description: |
|
||||||
|
The share replication type.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
request_id_body:
|
request_id_body:
|
||||||
description: |
|
description: |
|
||||||
The UUID of the request during which the message was created.
|
The UUID of the request during which the message was created.
|
||||||
@ -1814,6 +1842,13 @@ revert_to_snapshot_support:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
min_version: 2.27
|
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:
|
security_service_dns_ip:
|
||||||
description: |
|
description: |
|
||||||
The DNS IP address that is used inside the project network.
|
The DNS IP address that is used inside the project network.
|
||||||
@ -2371,6 +2406,21 @@ share_type_access:is_public:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
min_version: 2.7
|
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:
|
share_type_description:
|
||||||
description: |
|
description: |
|
||||||
The description of the share type.
|
The description of the share type.
|
||||||
@ -2378,6 +2428,12 @@ share_type_description:
|
|||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
min_version: 2.41
|
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:
|
share_type_description_request:
|
||||||
description: |
|
description: |
|
||||||
The description of the share type. The value of this field is limited to
|
The description of the share type. The value of this field is limited to
|
||||||
@ -2386,6 +2442,12 @@ share_type_description_request:
|
|||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
min_version: 2.41
|
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:
|
share_type_id_body:
|
||||||
description: |
|
description: |
|
||||||
The UUID of the share type.
|
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
|
- project_id: project_id_path
|
||||||
- share_type_id: share_type_id
|
- 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
|
* 2.49 - Added Manage/Unmanage Share Server APIs. Updated Manage/Unmanage
|
||||||
Shares and Snapshots APIs to work in
|
Shares and Snapshots APIs to work in
|
||||||
``driver_handles_shares_servers`` enabled mode.
|
``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 minimum and maximum versions of the API supported
|
||||||
# The default api version request is defined to be the
|
# The default api version request is defined to be the
|
||||||
# minimum version of the API supported.
|
# minimum version of the API supported.
|
||||||
_MIN_API_VERSION = "2.0"
|
_MIN_API_VERSION = "2.0"
|
||||||
_MAX_API_VERSION = "2.49"
|
_MAX_API_VERSION = "2.50"
|
||||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
@ -275,3 +275,9 @@ user documentation.
|
|||||||
-----------------------
|
-----------------------
|
||||||
Added Manage/Unmanage Share Server APIs. Updated Manage/Unmanage Shares and
|
Added Manage/Unmanage Share Server APIs. Updated Manage/Unmanage Shares and
|
||||||
Snapshots APIs to work in ``driver_handles_shares_servers`` enabled mode.
|
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):
|
def _notify_share_type_error(self, context, method, payload):
|
||||||
rpc.get_notifier('shareType').error(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):
|
def _check_body(self, body, action_name):
|
||||||
if not self.is_valid_body(body, action_name):
|
if not self.is_valid_body(body, action_name):
|
||||||
raise webob.exc.HTTPBadRequest()
|
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 = share_types.get_share_type_by_name(context, name)
|
||||||
share_type['required_extra_specs'] = required_extra_specs
|
share_type['required_extra_specs'] = required_extra_specs
|
||||||
req.cache_db_share_type(share_type)
|
req.cache_db_share_type(share_type)
|
||||||
notifier_info = dict(share_types=share_type)
|
self._notify_share_type_info(
|
||||||
rpc.get_notifier('shareType').info(
|
context, 'share_type.create', share_type)
|
||||||
context, 'share_type.create', notifier_info)
|
|
||||||
|
|
||||||
except exception.InvalidExtraSpec as e:
|
except exception.InvalidExtraSpec as e:
|
||||||
raise webob.exc.HTTPBadRequest(explanation=six.text_type(e))
|
raise webob.exc.HTTPBadRequest(explanation=six.text_type(e))
|
||||||
@ -252,9 +255,8 @@ class ShareTypesController(wsgi.Controller):
|
|||||||
try:
|
try:
|
||||||
share_type = share_types.get_share_type(context, id)
|
share_type = share_types.get_share_type(context, id)
|
||||||
share_types.destroy(context, share_type['id'])
|
share_types.destroy(context, share_type['id'])
|
||||||
notifier_info = dict(share_types=share_type)
|
self._notify_share_type_info(
|
||||||
rpc.get_notifier('shareType').info(
|
context, 'share_type.delete', share_type)
|
||||||
context, 'share_type.delete', notifier_info)
|
|
||||||
except exception.ShareTypeInUse as err:
|
except exception.ShareTypeInUse as err:
|
||||||
notifier_err = dict(id=id, error_message=six.text_type(err))
|
notifier_err = dict(id=id, error_message=six.text_type(err))
|
||||||
self._notify_share_type_error(context, 'share_type.delete',
|
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)
|
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')
|
@wsgi.Controller.authorize('list_project_access')
|
||||||
def share_type_access(self, req, id):
|
def share_type_access(self, req, id):
|
||||||
context = req.environ['manila.context']
|
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)
|
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):
|
def share_type_get_all(context, inactive=False, filters=None):
|
||||||
"""Get all share types.
|
"""Get all share types.
|
||||||
|
|
||||||
|
@ -210,6 +210,18 @@ def apply_sorting(model, query, sort_key, sort_dir):
|
|||||||
return query.order_by(sort_method())
|
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):
|
def model_query(context, model, *args, **kwargs):
|
||||||
"""Query helper that accounts for context's `read_deleted` field.
|
"""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
|
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
|
@require_context
|
||||||
def share_type_get_all(context, inactive=False, filters=None):
|
def share_type_get_all(context, inactive=False, filters=None):
|
||||||
"""Returns a dict describing all share_types with name as key."""
|
"""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.")
|
"name %(name)s and specs %(extra_specs)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class ShareTypeUpdateFailed(ManilaException):
|
||||||
|
message = _("Cannot update share_type %(id)s.")
|
||||||
|
|
||||||
|
|
||||||
class ShareGroupTypeCreateFailed(ManilaException):
|
class ShareGroupTypeCreateFailed(ManilaException):
|
||||||
message = _("Cannot create share group type with "
|
message = _("Cannot create share group type with "
|
||||||
"name %(name)s and specs %(group_specs)s.")
|
"name %(name)s and specs %(group_specs)s.")
|
||||||
|
@ -31,6 +31,16 @@ share_type_policies = [
|
|||||||
'path': '/types',
|
'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(
|
policy.DocumentedRuleDefault(
|
||||||
name=BASE_POLICY_NAME % 'show',
|
name=BASE_POLICY_NAME % 'show',
|
||||||
check_str=base.RULE_DEFAULT,
|
check_str=base.RULE_DEFAULT,
|
||||||
|
@ -70,6 +70,24 @@ def sanitize_extra_specs(extra_specs):
|
|||||||
return 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):
|
def destroy(context, id):
|
||||||
"""Marks share types as deleted."""
|
"""Marks share types as deleted."""
|
||||||
if id is None:
|
if id is None:
|
||||||
|
@ -19,6 +19,7 @@ import ddt
|
|||||||
import mock
|
import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
|
import random
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from manila.api.v2 import share_types as types
|
from manila.api.v2 import share_types as types
|
||||||
@ -45,15 +46,25 @@ def stub_share_type(id):
|
|||||||
"key5": "value5",
|
"key5": "value5",
|
||||||
constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: "true",
|
constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: "true",
|
||||||
}
|
}
|
||||||
return dict(
|
if id == 4:
|
||||||
id=id,
|
name = 'update_share_type_%s' % str(id)
|
||||||
name='share_type_%s' % str(id),
|
description = 'update_description_%s' % str(id)
|
||||||
description='description_%s' % str(id),
|
is_public = False
|
||||||
extra_specs=specs,
|
else:
|
||||||
required_extra_specs={
|
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",
|
constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: "true",
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
return share_type
|
||||||
|
|
||||||
|
|
||||||
def return_share_types_get_all_types(context, search_opts=None):
|
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))
|
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):
|
def return_share_types_get_by_name(context, name):
|
||||||
if name == "777":
|
if name == "777":
|
||||||
raise exception.ShareTypeNotFoundByName(share_type_name=name)
|
raise exception.ShareTypeNotFoundByName(share_type_name=name)
|
||||||
@ -146,6 +171,28 @@ def make_create_body(name="test_share_1", extra_specs=None,
|
|||||||
return body
|
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
|
@ddt.ddt
|
||||||
class ShareTypesAPITest(test.TestCase):
|
class ShareTypesAPITest(test.TestCase):
|
||||||
|
|
||||||
@ -167,6 +214,9 @@ class ShareTypesAPITest(test.TestCase):
|
|||||||
self.mock_object(
|
self.mock_object(
|
||||||
share_types, 'get_share_type',
|
share_types, 'get_share_type',
|
||||||
mock.Mock(side_effect=return_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(
|
self.mock_object(
|
||||||
share_types, 'destroy',
|
share_types, 'destroy',
|
||||||
mock.Mock(side_effect=return_share_types_destroy))
|
mock.Mock(side_effect=return_share_types_destroy))
|
||||||
@ -390,6 +440,92 @@ class ShareTypesAPITest(test.TestCase):
|
|||||||
self.controller._parse_is_public,
|
self.controller._parse_is_public,
|
||||||
'fakefakefake')
|
'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):
|
def test_share_types_delete(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
@ -3275,6 +3275,40 @@ class ShareTypeAPITestCase(test.TestCase):
|
|||||||
self.assertRaises(exception.DefaultShareTypeNotConfigured,
|
self.assertRaises(exception.DefaultShareTypeNotConfigured,
|
||||||
db_api.share_type_get, self.ctxt, None)
|
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):
|
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
|
@ddt.ddt
|
||||||
class ShareTypesTestCase(test.TestCase):
|
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 = {
|
fake_r_extra_specs = {
|
||||||
u'gold': u'True',
|
u'gold': u'True',
|
||||||
u'driver_handles_share_servers': 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(
|
share_types.get_share_type_extra_specs.assert_called_once_with(
|
||||||
self.fake_share_type_id)
|
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"})
|
@ddt.data({}, {"fake": "fake"})
|
||||||
def test_create_without_required_extra_spec(self, optional_specs):
|
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