Fix creation of share group types using share type names

Before it was possible to create share group types mapping them to
share types using only share type IDs and when we were providing its
names we were getting DB error and HTTP 500 as a response.

Fix it by properly looking for share type by both its unique values -
ID and name. Also, raise proper 404 error when nothing is found.

Add functional tests covering this case.

Change-Id: I216f935383a87f6d679c431bc46cfa8977a6d8ab
Depends-On: Ic555d241f98d0fa027897c69a7115d1be88f6c96
Closes-Bug: #1659625
This commit is contained in:
Valeriy Ponomaryov 2017-01-27 19:58:30 +02:00
parent c4da03274c
commit ba5384f865
9 changed files with 105 additions and 8 deletions

View File

@ -138,6 +138,8 @@ class ShareGroupTypesController(wsgi.Controller):
context, name) context, name)
except exception.ShareGroupTypeExists as err: except exception.ShareGroupTypeExists as err:
raise webob.exc.HTTPConflict(explanation=six.text_type(err)) raise webob.exc.HTTPConflict(explanation=six.text_type(err))
except exception.ShareTypeDoesNotExist as err:
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
except exception.NotFound: except exception.NotFound:
raise webob.exc.HTTPNotFound() raise webob.exc.HTTPNotFound()
return self._view_builder.show(req, share_group_type) return self._view_builder.show(req, share_group_type)

View File

@ -894,6 +894,11 @@ def share_type_get_by_name(context, name):
return IMPL.share_type_get_by_name(context, name) return IMPL.share_type_get_by_name(context, name)
def share_type_get_by_name_or_id(context, name_or_id):
"""Get share type by name or ID and return None if not found."""
return IMPL.share_type_get_by_name_or_id(context, name_or_id)
def share_type_access_get_all(context, type_id): def share_type_access_get_all(context, type_id):
"""Get all share type access of a share type.""" """Get all share type access of a share type."""
return IMPL.share_type_access_get_all(context, type_id) return IMPL.share_type_access_get_all(context, type_id)

View File

@ -3537,10 +3537,24 @@ def _share_type_get_by_name(context, name, session=None):
@require_context @require_context
def share_type_get_by_name(context, name): def share_type_get_by_name(context, name):
"""Return a dict describing specific share_type.""" """Return a dict describing specific share_type."""
return _share_type_get_by_name(context, name) return _share_type_get_by_name(context, name)
@require_context
def share_type_get_by_name_or_id(context, name_or_id):
"""Return a dict describing specific share_type using its name or ID.
:returns: ShareType object or None if not found
"""
try:
return _share_type_get(context, name_or_id)
except exception.ShareTypeNotFound:
try:
return _share_type_get_by_name(context, name_or_id)
except exception.ShareTypeNotFoundByName:
return None
@require_admin_context @require_admin_context
def share_type_destroy(context, id): def share_type_destroy(context, id):
session = get_session() session = get_session()
@ -4201,10 +4215,13 @@ def share_group_type_create(context, values, projects=None):
values['group_specs'] = _metadata_refs( values['group_specs'] = _metadata_refs(
values.get('group_specs'), models.ShareGroupTypeSpecs) values.get('group_specs'), models.ShareGroupTypeSpecs)
mappings = [] mappings = []
for item in values.get('share_types') or []: for item in values.get('share_types', []):
share_type = share_type_get_by_name_or_id(context, item)
if not share_type:
raise exception.ShareTypeDoesNotExist(share_type=item)
mapping = models.ShareGroupTypeShareTypeMapping() mapping = models.ShareGroupTypeShareTypeMapping()
mapping['id'] = six.text_type(uuidutils.generate_uuid()) mapping['id'] = six.text_type(uuidutils.generate_uuid())
mapping['share_type_id'] = item mapping['share_type_id'] = share_type['id']
mapping['share_group_type_id'] = values['id'] mapping['share_group_type_id'] = values['id']
mappings.append(mapping) mappings.append(mapping)
@ -4214,6 +4231,8 @@ def share_group_type_create(context, values, projects=None):
share_group_type_ref.save(session=session) share_group_type_ref.save(session=session)
except db_exception.DBDuplicateEntry: except db_exception.DBDuplicateEntry:
raise exception.ShareGroupTypeExists(type_id=values['name']) raise exception.ShareGroupTypeExists(type_id=values['name'])
except exception.ShareTypeDoesNotExist:
raise
except Exception as e: except Exception as e:
raise db_exception.DBError(e) raise db_exception.DBError(e)

View File

@ -619,6 +619,10 @@ class ShareTypeExists(ManilaException):
message = _("Share Type %(id)s already exists.") message = _("Share Type %(id)s already exists.")
class ShareTypeDoesNotExist(NotFound):
message = _("Share Type %(share_type)s does not exist.")
class ShareGroupTypeExists(ManilaException): class ShareGroupTypeExists(ManilaException):
message = _("Share group type %(type_id)s already exists.") message = _("Share group type %(type_id)s already exists.")

View File

@ -390,6 +390,19 @@ class ShareGroupTypesAPITest(test.TestCase):
webob.exc.HTTPBadRequest, webob.exc.HTTPBadRequest,
self.controller._create, req, fake_body) self.controller._create, req, fake_body)
def test_create_provided_share_type_does_not_exist(self):
req = fake_request('/v2/fake/share-group-types', admin=True)
fake_body = {
'share_group_type': {
'name': GROUP_TYPE_1['name'],
'share_types': SHARE_TYPE_ID + '_does_not_exist',
}
}
self.assertRaises(
webob.exc.HTTPNotFound,
self.controller._create, req, fake_body)
class ShareGroupTypeAccessTest(test.TestCase): class ShareGroupTypeAccessTest(test.TestCase):

View File

@ -2576,3 +2576,37 @@ class PurgeDeletedTest(test.TestCase):
type_row = db_api.model_query(self.context, type_row = db_api.model_query(self.context,
models.ShareTypes).count() models.ShareTypes).count()
self.assertEqual(0, s_row + type_row) self.assertEqual(0, s_row + type_row)
class ShareTypeAPITestCase(test.TestCase):
def setUp(self):
super(self.__class__, self).setUp()
self.ctxt = context.RequestContext(
user_id='user_id', project_id='project_id', is_admin=True)
def test_share_type_get_by_name_or_id_found_by_id(self):
share_type = db_utils.create_share_type()
result = db_api.share_type_get_by_name_or_id(
self.ctxt, share_type['id'])
self.assertIsNotNone(result)
self.assertEqual(share_type['id'], result['id'])
def test_share_type_get_by_name_or_id_found_by_name(self):
name = uuidutils.generate_uuid()
db_utils.create_share_type(name=name)
result = db_api.share_type_get_by_name_or_id(self.ctxt, name)
self.assertIsNotNone(result)
self.assertEqual(name, result['name'])
self.assertNotEqual(name, result['id'])
def test_share_type_get_by_name_or_id_when_does_not_exist(self):
fake_id = uuidutils.generate_uuid()
result = db_api.share_type_get_by_name_or_id(self.ctxt, fake_id)
self.assertIsNone(result)

View File

@ -469,6 +469,13 @@ class ManilaExceptionResponseCode404(test.TestCase):
self.assertEqual(404, e.code) self.assertEqual(404, e.code)
self.assertIn(share_type_name, e.msg) self.assertIn(share_type_name, e.msg)
def test_share_type_does_not_exist(self):
# verify response code for exception.ShareTypeDoesNotExist
share_type = "fake_share_type_1234"
e = exception.ShareTypeDoesNotExist(share_type=share_type)
self.assertEqual(404, e.code)
self.assertIn(share_type, e.msg)
def test_share_type_extra_specs_not_found(self): def test_share_type_extra_specs_not_found(self):
# verify response code for exception.ShareTypeExtraSpecsNotFound # verify response code for exception.ShareTypeExtraSpecsNotFound
share_type_id = "fake_share_type_id" share_type_id = "fake_share_type_id"

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ddt
from tempest import config from tempest import config
from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import data_utils
import testtools import testtools
@ -27,6 +28,7 @@ CONF = config.CONF
@testtools.skipUnless( @testtools.skipUnless(
CONF.share.run_share_group_tests, 'Share Group tests disabled.') CONF.share.run_share_group_tests, 'Share Group tests disabled.')
@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION) @base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
@ddt.ddt
class ShareGroupTypesTest(base.BaseSharesAdminTest): class ShareGroupTypesTest(base.BaseSharesAdminTest):
@classmethod @classmethod
@ -44,13 +46,14 @@ class ShareGroupTypesTest(base.BaseSharesAdminTest):
cls.share_type2 = share_type['share_type'] cls.share_type2 = share_type['share_type']
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_create_get_delete_share_group_type_min(self): @ddt.data('id', 'name')
def test_create_get_delete_share_group_type_min(self, st_key):
name = data_utils.rand_name("tempest-manila") name = data_utils.rand_name("tempest-manila")
# Create share group type # Create share group type
sg_type_c = self.create_share_group_type( sg_type_c = self.create_share_group_type(
name=name, name=name,
share_types=self.share_type['id'], share_types=self.share_type[st_key],
cleanup_in_class=False, cleanup_in_class=False,
version=constants.MIN_SHARE_GROUP_MICROVERSION) version=constants.MIN_SHARE_GROUP_MICROVERSION)
@ -76,12 +79,13 @@ class ShareGroupTypesTest(base.BaseSharesAdminTest):
share_group_type_id=sg_type_r['id']) share_group_type_id=sg_type_r['id'])
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_create_share_group_type_multiple_share_types_min(self): @ddt.data('id', 'name')
def test_create_share_group_type_multiple_share_types_min(self, st_key):
name = data_utils.rand_name("tempest-manila") name = data_utils.rand_name("tempest-manila")
sg_type = self.create_share_group_type( sg_type = self.create_share_group_type(
name=name, name=name,
share_types=[self.share_type['id'], self.share_type2['id']], share_types=[self.share_type[st_key], self.share_type2[st_key]],
cleanup_in_class=False, cleanup_in_class=False,
version=constants.MIN_SHARE_GROUP_MICROVERSION) version=constants.MIN_SHARE_GROUP_MICROVERSION)

View File

@ -40,10 +40,19 @@ class ShareGroupTypesAdminNegativeTest(base.BaseSharesMixedTest):
client=cls.admin_shares_v2_client) client=cls.admin_shares_v2_client)
@tc.attr(base.TAG_NEGATIVE, base.TAG_API) @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_create_share_ggroup_with_nonexistent_share_type(self): def test_create_share_group_type_without_name(self):
self.assertRaises( self.assertRaises(
lib_exc.BadRequest, lib_exc.BadRequest,
self.admin_shares_v2_client.create_share_group_type, self.admin_shares_v2_client.create_share_group_type,
name=None,
share_types=data_utils.rand_name("fake"))
@tc.attr(base.TAG_NEGATIVE, base.TAG_API)
def test_create_share_group_type_with_nonexistent_share_type(self):
self.assertRaises(
lib_exc.NotFound,
self.admin_shares_v2_client.create_share_group_type,
name=data_utils.rand_name("sgt_name_should_have_not_been_created"),
share_types=data_utils.rand_name("fake")) share_types=data_utils.rand_name("fake"))
@tc.attr(base.TAG_NEGATIVE, base.TAG_API) @tc.attr(base.TAG_NEGATIVE, base.TAG_API)