From 1bf5e884eb8ac33cd1073c601b1279c1af0c7d3b Mon Sep 17 00:00:00 2001 From: "anthony.bellino" Date: Tue, 28 Mar 2017 16:51:56 +0000 Subject: [PATCH] Add policy granularity to the qos-specs API Add granularity to the volume_extension:qos_specs_manage policy with the addition of actions for Create/Get/Update/Delete and add unit tests to cover authorization accordingly. Change-Id: I1ca996e968a273b989bea0bf3c54b47349ca47fe Closes-bug: #1623575 --- cinder/api/contrib/qos_specs_manage.py | 31 +++-- .../unit/api/contrib/test_qos_specs_manage.py | 126 +++++++++++++----- cinder/tests/unit/policy.json | 6 +- etc/cinder/policy.json | 6 + 4 files changed, 122 insertions(+), 47 deletions(-) diff --git a/cinder/api/contrib/qos_specs_manage.py b/cinder/api/contrib/qos_specs_manage.py index 4f85bfaf208..8f97ed794c8 100644 --- a/cinder/api/contrib/qos_specs_manage.py +++ b/cinder/api/contrib/qos_specs_manage.py @@ -33,7 +33,16 @@ from cinder.volume import qos_specs LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('volume', 'qos_specs_manage') +authorize_create = extensions.extension_authorizer('volume', + 'qos_specs_manage:create') +authorize_get = extensions.extension_authorizer('volume', + 'qos_specs_manage:get') +authorize_get_all = extensions.extension_authorizer('volume', + 'qos_specs_manage:get_all') +authorize_update = extensions.extension_authorizer('volume', + 'qos_specs_manage:update') +authorize_delete = extensions.extension_authorizer('volume', + 'qos_specs_manage:delete') def _check_specs(context, specs_id): @@ -56,7 +65,7 @@ class QoSSpecsController(wsgi.Controller): def index(self, req): """Returns the list of qos_specs.""" context = req.environ['cinder.context'] - authorize(context) + authorize_get_all(context) params = req.params.copy() @@ -75,7 +84,7 @@ class QoSSpecsController(wsgi.Controller): def create(self, req, body=None): context = req.environ['cinder.context'] - authorize(context) + authorize_create(context) self.assert_valid_body(body, 'qos_specs') @@ -122,7 +131,7 @@ class QoSSpecsController(wsgi.Controller): def update(self, req, id, body=None): context = req.environ['cinder.context'] - authorize(context) + authorize_update(context) self.assert_valid_body(body, 'qos_specs') specs = body['qos_specs'] @@ -152,7 +161,7 @@ class QoSSpecsController(wsgi.Controller): def show(self, req, id): """Return a single qos spec item.""" context = req.environ['cinder.context'] - authorize(context) + authorize_get(context) # Not found exception will be handled at the wsgi level spec = qos_specs.get_qos_specs(context, id) @@ -162,7 +171,7 @@ class QoSSpecsController(wsgi.Controller): def delete(self, req, id): """Deletes an existing qos specs.""" context = req.environ['cinder.context'] - authorize(context) + authorize_delete(context) # Convert string to bool type in strict manner force = utils.get_bool_param('force', req.params) @@ -198,7 +207,7 @@ class QoSSpecsController(wsgi.Controller): def delete_keys(self, req, id, body): """Deletes specified keys in qos specs.""" context = req.environ['cinder.context'] - authorize(context) + authorize_delete(context) if not (body and 'keys' in body and isinstance(body.get('keys'), list)): @@ -226,7 +235,7 @@ class QoSSpecsController(wsgi.Controller): def associations(self, req, id): """List all associations of given qos specs.""" context = req.environ['cinder.context'] - authorize(context) + authorize_get_all(context) LOG.debug("Get associations for qos_spec id: %s", id) @@ -256,7 +265,7 @@ class QoSSpecsController(wsgi.Controller): def associate(self, req, id): """Associate a qos specs with a volume type.""" context = req.environ['cinder.context'] - authorize(context) + authorize_update(context) type_id = req.params.get('vol_type_id', None) @@ -305,7 +314,7 @@ class QoSSpecsController(wsgi.Controller): def disassociate(self, req, id): """Disassociate a qos specs from a volume type.""" context = req.environ['cinder.context'] - authorize(context) + authorize_update(context) type_id = req.params.get('vol_type_id', None) @@ -345,7 +354,7 @@ class QoSSpecsController(wsgi.Controller): def disassociate_all(self, req, id): """Disassociate a qos specs from all volume types.""" context = req.environ['cinder.context'] - authorize(context) + authorize_update(context) LOG.debug("Disassociate qos_spec: %s from all.", id) diff --git a/cinder/tests/unit/api/contrib/test_qos_specs_manage.py b/cinder/tests/unit/api/contrib/test_qos_specs_manage.py index 0a51f1ff9a2..b4b63e35f14 100644 --- a/cinder/tests/unit/api/contrib/test_qos_specs_manage.py +++ b/cinder/tests/unit/api/contrib/test_qos_specs_manage.py @@ -173,7 +173,8 @@ class QoSSpecManageApiTest(test.TestCase): @mock.patch('cinder.volume.qos_specs.get_all_specs', side_effect=return_qos_specs_get_all) def test_index(self, mock_get_all_specs): - req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID) + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID, + use_admin_context=True) res = self.controller.index(req) self.assertEqual(3, len(res['qos_specs'])) @@ -272,7 +273,7 @@ class QoSSpecManageApiTest(test.TestCase): side_effect=return_qos_specs_delete) def test_qos_specs_delete(self, mock_qos_delete, mock_qos_get_specs): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % ( - fake.PROJECT_ID, fake.QOS_SPEC_ID)) + fake.PROJECT_ID, fake.QOS_SPEC_ID), use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): self.controller.delete(req, fake.QOS_SPEC_ID) @@ -288,7 +289,8 @@ class QoSSpecManageApiTest(test.TestCase): with mock.patch('cinder.rpc.get_notifier', return_value=notifier): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % (fake.PROJECT_ID, - fake.WILL_NOT_BE_FOUND_ID)) + fake.WILL_NOT_BE_FOUND_ID), + use_admin_context=True) self.assertRaises(exception.QoSSpecsNotFound, self.controller.delete, req, fake.WILL_NOT_BE_FOUND_ID) @@ -301,7 +303,7 @@ class QoSSpecManageApiTest(test.TestCase): def test_qos_specs_delete_inuse(self, mock_qos_delete, mock_qos_get_specs): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % ( - fake.PROJECT_ID, fake.IN_USE_ID)) + fake.PROJECT_ID, fake.IN_USE_ID), use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -316,7 +318,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_qos_specs_delete_inuse_force(self, mock_qos_delete, mock_qos_get_specs): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s?force=True' % - (fake.PROJECT_ID, fake.IN_USE_ID)) + (fake.PROJECT_ID, fake.IN_USE_ID), + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -329,7 +332,8 @@ class QoSSpecManageApiTest(test.TestCase): invalid_force = "invalid_bool" req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/delete_keys?force=%s' % - (fake.PROJECT_ID, fake.QOS_SPEC_ID, invalid_force)) + (fake.PROJECT_ID, fake.QOS_SPEC_ID, invalid_force), + use_admin_context=True) self.assertRaises(exception.InvalidParameterValue, self.controller.delete, @@ -340,7 +344,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_qos_specs_delete_keys(self, mock_qos_delete_keys): body = {"keys": ['bar', 'zoo']} req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s/delete_keys' % - (fake.PROJECT_ID, fake.IN_USE_ID)) + (fake.PROJECT_ID, fake.IN_USE_ID), + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -353,7 +358,8 @@ class QoSSpecManageApiTest(test.TestCase): body = {"keys": ['bar', 'zoo']} req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s/delete_keys' % (fake.PROJECT_ID, - fake.WILL_NOT_BE_FOUND_ID)) + fake.WILL_NOT_BE_FOUND_ID), + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -366,7 +372,8 @@ class QoSSpecManageApiTest(test.TestCase): side_effect=return_qos_specs_delete_keys) def test_qos_specs_delete_keys_badkey(self, mock_qos_specs_delete): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s/delete_keys' % - (fake.PROJECT_ID, fake.IN_USE_ID)) + (fake.PROJECT_ID, fake.IN_USE_ID), + use_admin_context=True) body = {"keys": ['foo', 'zoo']} notifier = fake_notifier.get_fake_notifier() @@ -381,7 +388,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_qos_specs_delete_keys_get_notifier(self, mock_qos_delete_keys): body = {"keys": ['bar', 'zoo']} req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s/delete_keys' % - (fake.PROJECT_ID, fake.IN_USE_ID)) + (fake.PROJECT_ID, fake.IN_USE_ID), + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier, @@ -396,7 +404,9 @@ class QoSSpecManageApiTest(test.TestCase): body = {"qos_specs": {"name": "qos_specs_%s" % fake.QOS_SPEC_ID, "key1": "value1"}} - req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID) + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % + fake.PROJECT_ID, + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -412,7 +422,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_create_invalid_input(self, mock_qos_get_specs): body = {"qos_specs": {"name": 'qos_spec_%s' % fake.INVALID_ID, "consumer": "invalid_consumer"}} - req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID) + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID, + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -425,7 +436,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_create_conflict(self, mock_qos_spec_create): body = {"qos_specs": {"name": 'qos_spec_%s' % fake.ALREADY_EXISTS_ID, "key1": "value1"}} - req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID) + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID, + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -438,7 +450,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_create_failed(self, mock_qos_spec_create): body = {"qos_specs": {"name": 'qos_spec_%s' % fake.ACTION_FAILED_ID, "key1": "value1"}} - req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID) + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % fake.PROJECT_ID, + use_admin_context=True) notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): @@ -486,7 +499,8 @@ class QoSSpecManageApiTest(test.TestCase): notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % - (fake.PROJECT_ID, fake.QOS_SPEC_ID)) + (fake.PROJECT_ID, fake.QOS_SPEC_ID), + use_admin_context=True) body = {'qos_specs': {'key1': 'value1', 'key2': 'value2'}} res = self.controller.update(req, fake.QOS_SPEC_ID, body) @@ -500,7 +514,8 @@ class QoSSpecManageApiTest(test.TestCase): with mock.patch('cinder.rpc.get_notifier', return_value=notifier): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % (fake.PROJECT_ID, - fake.WILL_NOT_BE_FOUND_ID)) + fake.WILL_NOT_BE_FOUND_ID), + use_admin_context=True) body = {'qos_specs': {'key1': 'value1', 'key2': 'value2'}} self.assertRaises(exception.QoSSpecsNotFound, @@ -514,7 +529,8 @@ class QoSSpecManageApiTest(test.TestCase): notifier = fake_notifier.get_fake_notifier() with mock.patch('cinder.rpc.get_notifier', return_value=notifier): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % - (fake.PROJECT_ID, fake.INVALID_ID)) + (fake.PROJECT_ID, fake.INVALID_ID), + use_admin_context=True) body = {'qos_specs': {'key1': 'value1', 'key2': 'value2'}} self.assertRaises(exception.InvalidQoSSpecs, @@ -529,7 +545,8 @@ class QoSSpecManageApiTest(test.TestCase): with mock.patch('cinder.rpc.get_notifier', return_value=notifier): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % (fake.PROJECT_ID, - fake.UPDATE_FAILED_ID)) + fake.UPDATE_FAILED_ID), + use_admin_context=True) body = {'qos_specs': {'key1': 'value1', 'key2': 'value2'}} self.assertRaises(webob.exc.HTTPInternalServerError, @@ -541,7 +558,7 @@ class QoSSpecManageApiTest(test.TestCase): side_effect=return_qos_specs_get_qos_specs) def test_show(self, mock_get_qos_specs): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % ( - fake.PROJECT_ID, fake.QOS_SPEC_ID)) + fake.PROJECT_ID, fake.QOS_SPEC_ID), use_admin_context=True) res_dict = self.controller.show(req, fake.QOS_SPEC_ID) self.assertEqual(fake.QOS_SPEC_ID, res_dict['qos_specs']['id']) @@ -553,7 +570,7 @@ class QoSSpecManageApiTest(test.TestCase): def test_get_associations(self, mock_get_assciations): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/associations' % ( - fake.PROJECT_ID, fake.QOS_SPEC_ID)) + fake.PROJECT_ID, fake.QOS_SPEC_ID), use_admin_context=True) res = self.controller.associations(req, fake.QOS_SPEC_ID) self.assertEqual('FakeVolTypeName', @@ -566,7 +583,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_get_associations_not_found(self, mock_get_assciations): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/associations' % - (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID)) + (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID), + use_admin_context=True) self.assertRaises(exception.QoSSpecsNotFound, self.controller.associations, req, fake.WILL_NOT_BE_FOUND_ID) @@ -576,7 +594,7 @@ class QoSSpecManageApiTest(test.TestCase): def test_get_associations_failed(self, mock_get_associations): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/associations' % ( - fake.PROJECT_ID, fake.RAISE_ID)) + fake.PROJECT_ID, fake.RAISE_ID), use_admin_context=True) self.assertRaises(webob.exc.HTTPInternalServerError, self.controller.associations, req, fake.RAISE_ID) @@ -588,7 +606,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_associate(self, mock_associate, mock_get_qos): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/associate?vol_type_id=%s' % - (fake.PROJECT_ID, fake.QOS_SPEC_ID, fake.VOLUME_TYPE_ID)) + (fake.PROJECT_ID, fake.QOS_SPEC_ID, fake.VOLUME_TYPE_ID), + use_admin_context=True) res = self.controller.associate(req, fake.QOS_SPEC_ID) self.assertEqual(http_client.ACCEPTED, res.status_int) @@ -599,7 +618,8 @@ class QoSSpecManageApiTest(test.TestCase): side_effect=return_associate_qos_specs) def test_associate_no_type(self, mock_associate, mock_get_qos): req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s/associate' % - (fake.PROJECT_ID, fake.QOS_SPEC_ID)) + (fake.PROJECT_ID, fake.QOS_SPEC_ID), + use_admin_context=True) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.associate, req, fake.QOS_SPEC_ID) @@ -611,14 +631,15 @@ class QoSSpecManageApiTest(test.TestCase): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/associate?vol_type_id=%s' % ( fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID, - fake.VOLUME_TYPE_ID)) + fake.VOLUME_TYPE_ID), use_admin_context=True) self.assertRaises(exception.QoSSpecsNotFound, self.controller.associate, req, fake.WILL_NOT_BE_FOUND_ID) req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/associate?vol_type_id=%s' % - (fake.PROJECT_ID, fake.QOS_SPEC_ID, fake.WILL_NOT_BE_FOUND_ID)) + (fake.PROJECT_ID, fake.QOS_SPEC_ID, fake.WILL_NOT_BE_FOUND_ID), + use_admin_context=True) self.assertRaises(exception.VolumeTypeNotFound, self.controller.associate, req, fake.QOS_SPEC_ID) @@ -630,7 +651,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_associate_fail(self, mock_associate, mock_get_qos): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/associate?vol_type_id=%s' % - (fake.PROJECT_ID, fake.ACTION_FAILED_ID, fake.VOLUME_TYPE_ID)) + (fake.PROJECT_ID, fake.ACTION_FAILED_ID, fake.VOLUME_TYPE_ID), + use_admin_context=True) self.assertRaises(webob.exc.HTTPInternalServerError, self.controller.associate, req, fake.ACTION_FAILED_ID) @@ -642,7 +664,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_disassociate(self, mock_disassociate, mock_get_qos): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate?vol_type_id=%s' % ( - fake.PROJECT_ID, fake.QOS_SPEC_ID, fake.VOLUME_TYPE_ID)) + fake.PROJECT_ID, fake.QOS_SPEC_ID, fake.VOLUME_TYPE_ID), + use_admin_context=True) res = self.controller.disassociate(req, fake.QOS_SPEC_ID) self.assertEqual(http_client.ACCEPTED, res.status_int) @@ -653,7 +676,7 @@ class QoSSpecManageApiTest(test.TestCase): def test_disassociate_no_type(self, mock_disassociate, mock_get_qos): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate' % ( - fake.PROJECT_ID, fake.QOS_SPEC_ID)) + fake.PROJECT_ID, fake.QOS_SPEC_ID), use_admin_context=True) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.disassociate, req, fake.QOS_SPEC_ID) @@ -666,14 +689,15 @@ class QoSSpecManageApiTest(test.TestCase): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate?vol_type_id=%s' % ( fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID, - fake.VOLUME_TYPE_ID)) + fake.VOLUME_TYPE_ID), use_admin_context=True) self.assertRaises(exception.QoSSpecsNotFound, self.controller.disassociate, req, fake.WILL_NOT_BE_FOUND_ID) req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate?vol_type_id=%s' % - (fake.PROJECT_ID, fake.VOLUME_TYPE_ID, fake.WILL_NOT_BE_FOUND_ID)) + (fake.PROJECT_ID, fake.VOLUME_TYPE_ID, fake.WILL_NOT_BE_FOUND_ID), + use_admin_context=True) self.assertRaises(exception.VolumeTypeNotFound, self.controller.disassociate, req, fake.VOLUME_TYPE_ID) @@ -685,7 +709,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_disassociate_failed(self, mock_disassociate, mock_get_qos): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate?vol_type_id=%s' % ( - fake.PROJECT_ID, fake.ACTION2_FAILED_ID, fake.VOLUME_TYPE_ID)) + fake.PROJECT_ID, fake.ACTION2_FAILED_ID, fake.VOLUME_TYPE_ID), + use_admin_context=True) self.assertRaises(webob.exc.HTTPInternalServerError, self.controller.disassociate, req, fake.ACTION2_FAILED_ID) @@ -697,7 +722,7 @@ class QoSSpecManageApiTest(test.TestCase): def test_disassociate_all(self, mock_disassociate, mock_get_qos): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate_all' % ( - fake.PROJECT_ID, fake.QOS_SPEC_ID)) + fake.PROJECT_ID, fake.QOS_SPEC_ID), use_admin_context=True) res = self.controller.disassociate_all(req, fake.QOS_SPEC_ID) self.assertEqual(http_client.ACCEPTED, res.status_int) @@ -708,7 +733,8 @@ class QoSSpecManageApiTest(test.TestCase): def test_disassociate_all_not_found(self, mock_disassociate, mock_get): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate_all' % ( - fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID)) + fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID), + use_admin_context=True) self.assertRaises(exception.QoSSpecsNotFound, self.controller.disassociate_all, req, fake.WILL_NOT_BE_FOUND_ID) @@ -720,7 +746,37 @@ class QoSSpecManageApiTest(test.TestCase): def test_disassociate_all_failed(self, mock_disassociate, mock_get): req = fakes.HTTPRequest.blank( '/v2/%s/qos-specs/%s/disassociate_all' % ( - fake.PROJECT_ID, fake.ACTION2_FAILED_ID)) + fake.PROJECT_ID, fake.ACTION2_FAILED_ID), + use_admin_context=True) self.assertRaises(webob.exc.HTTPInternalServerError, self.controller.disassociate_all, req, fake.ACTION2_FAILED_ID) + + def test_index_no_admin_user(self): + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % + fake.PROJECT_ID, use_admin_context=False) + self.assertRaises(exception.PolicyNotAuthorized, + self.controller.index, req) + + def test_create_no_admin_user(self): + body = {"qos_specs": {"name": "qos_specs_%s" % fake.QOS_SPEC_ID, + "key1": "value1"}} + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs' % + fake.PROJECT_ID, use_admin_context=False) + self.assertRaises(exception.PolicyNotAuthorized, + self.controller.create, req, body) + + def test_update_no_admin_user(self): + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % + (fake.PROJECT_ID, fake.QOS_SPEC_ID), + use_admin_context=False) + body = {'qos_specs': {'key1': 'value1', + 'key2': 'value2'}} + self.assertRaises(exception.PolicyNotAuthorized, + self.controller.update, req, fake.QOS_SPEC_ID, body) + + def test_qos_specs_delete_no_admin_user(self): + req = fakes.HTTPRequest.blank('/v2/%s/qos-specs/%s' % ( + fake.PROJECT_ID, fake.QOS_SPEC_ID), use_admin_context=False) + self.assertRaises(exception.PolicyNotAuthorized, + self.controller.delete, req, fake.QOS_SPEC_ID) diff --git a/cinder/tests/unit/policy.json b/cinder/tests/unit/policy.json index 3635f808421..a361778a943 100644 --- a/cinder/tests/unit/policy.json +++ b/cinder/tests/unit/policy.json @@ -63,7 +63,11 @@ "volume_extension:volume_type_access:removeProjectAccess": "rule:admin_api", "volume_extension:volume_type_encryption": "rule:admin_api", "volume_extension:volume_encryption_metadata": "rule:admin_or_owner", - "volume_extension:qos_specs_manage": "", + "volume_extension:qos_specs_manage:create": "rule:admin_api", + "volume_extension:qos_specs_manage:get": "rule:admin_api", + "volume_extension:qos_specs_manage:get_all": "rule:admin_api", + "volume_extension:qos_specs_manage:update": "rule:admin_api", + "volume_extension:qos_specs_manage:delete": "rule:admin_api", "volume_extension:extended_snapshot_attributes": "", "volume_extension:volume_image_metadata": "", "volume_extension:volume_host_attribute": "rule:admin_api", diff --git a/etc/cinder/policy.json b/etc/cinder/policy.json index 86590497bb9..5c3015915ec 100644 --- a/etc/cinder/policy.json +++ b/etc/cinder/policy.json @@ -42,6 +42,12 @@ "volume_extension:extended_snapshot_attributes": "rule:admin_or_owner", "volume_extension:volume_image_metadata": "rule:admin_or_owner", + "volume_extension:qos_specs_manage:create": "rule:admin_api", + "volume_extension:qos_specs_manage:get": "rule:admin_api", + "volume_extension:qos_specs_manage:get_all": "rule:admin_api", + "volume_extension:qos_specs_manage:update": "rule:admin_api", + "volume_extension:qos_specs_manage:delete": "rule:admin_api", + "volume_extension:quotas:show": "", "volume_extension:quotas:update": "rule:admin_api", "volume_extension:quotas:delete": "rule:admin_api",