Browse Source

Merge "Add update share-type API to Share Types"

tags/9.0.0.0rc1
Zuul 5 months ago
parent
commit
2a97de623f
16 changed files with 603 additions and 14 deletions
  1. +62
    -0
      api-ref/source/parameters.yaml
  2. +8
    -0
      api-ref/source/samples/share-type-update-request.json
  3. +38
    -0
      api-ref/source/samples/share-type-update-response.json
  4. +70
    -0
      api-ref/source/share-types.inc
  5. +4
    -1
      manila/api/openstack/api_version_request.py
  6. +6
    -0
      manila/api/openstack/rest_api_version_history.rst
  7. +87
    -6
      manila/api/v2/share_types.py
  8. +5
    -0
      manila/db/api.py
  9. +51
    -0
      manila/db/sqlalchemy/api.py
  10. +4
    -0
      manila/exception.py
  11. +10
    -0
      manila/policies/share_type.py
  12. +18
    -0
      manila/share/share_types.py
  13. +143
    -7
      manila/tests/api/v2/test_share_types.py
  14. +34
    -0
      manila/tests/db/sqlalchemy/test_api.py
  15. +58
    -0
      manila/tests/share/test_share_types.py
  16. +5
    -0
      releasenotes/notes/bp-update-share-type-name-or-description-a39c5991b930932f.yaml

+ 62
- 0
api-ref/source/parameters.yaml View File

@@ -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
- 0
api-ref/source/samples/share-type-update-request.json View File

@@ -0,0 +1,8 @@
{
"share_type":
{
"share_type_access:is_public": true,
"name": "testing",
"description": "share type description"
}
}

+ 38
- 0
api-ref/source/samples/share-type-update-response.json View 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"
}
}

+ 70
- 0
api-ref/source/share-types.inc View File

@@ -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

+ 4
- 1
manila/api/openstack/api_version_request.py View File

@@ -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



+ 6
- 0
manila/api/openstack/rest_api_version_history.rst View File

@@ -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.

+ 87
- 6
manila/api/v2/share_types.py View File

@@ -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']

+ 5
- 0
manila/db/api.py View File

@@ -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.


+ 51
- 0
manila/db/sqlalchemy/api.py View File

@@ -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."""

+ 4
- 0
manila/exception.py View File

@@ -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.")

+ 10
- 0
manila/policies/share_type.py View File

@@ -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,

+ 18
- 0
manila/share/share_types.py View File

@@ -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:

+ 143
- 7
manila/tests/api/v2/test_share_types.py View File

@@ -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))

+ 34
- 0
manila/tests/db/sqlalchemy/test_api.py View File

@@ -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):


+ 58
- 0
manila/tests/share/test_share_types.py View File

@@ -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):


+ 5
- 0
releasenotes/notes/bp-update-share-type-name-or-description-a39c5991b930932f.yaml View File

@@ -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…
Cancel
Save