manila/manila/tests/api/v2/test_quota_sets.py

734 lines
28 KiB
Python

# Copyright 2013 OpenStack Foundation
# Copyright (c) 2015 Mirantis inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Tests for manila.api.v2.quota_sets.py
"""
import ddt
import mock
from oslo_config import cfg
import webob.exc
import webob.response
from manila.api.openstack import api_version_request as api_version
from manila.api.v2 import quota_sets
from manila import context
from manila import exception
from manila import policy
from manila import test
from manila.tests.api import fakes
from manila import utils
CONF = cfg.CONF
def _get_request(is_admin, user_in_url):
req = mock.MagicMock(
api_version_request=api_version.APIVersionRequest("2.40"))
req.environ = {'manila.context': context.get_admin_context()}
req.environ['manila.context'].is_admin = is_admin
req.environ['manila.context'].auth_token = 'foo_auth_token'
req.environ['manila.context'].project_id = 'foo_project_id'
if user_in_url:
req.environ['manila.context'].user_id = 'foo_user_id'
req.environ['QUERY_STRING'] = 'user_id=foo_user_id'
return req
@ddt.ddt
class QuotaSetsControllerTest(test.TestCase):
def setUp(self):
super(QuotaSetsControllerTest, self).setUp()
self.controller = quota_sets.QuotaSetsController()
self.resource_name = self.controller.resource_name
self.project_id = 'foo_project_id'
self.mock_policy_check = self.mock_object(
policy, 'check_policy', mock.Mock(return_value=True))
@ddt.data(
{"shares": 3, "snapshots": 4, "gigabytes": 5,
"snapshot_gigabytes": 6, "share_networks": 7},
{"shares": -1, "snapshots": -1, "gigabytes": -1,
"snapshot_gigabytes": -1, "share_networks": -1},
{"shares": 13},
{"snapshots": 24},
{"gigabytes": 7},
{"snapshot_gigabytes": 10001},
{"share_networks": 12345},
{"share_groups": 123456},
{"share_group_snapshots": 123456},
)
def test_defaults(self, quotas):
req = _get_request(True, False)
for k, v in quotas.items():
CONF.set_default('quota_' + k, v)
expected = {
'quota_set': {
'id': self.project_id,
'shares': quotas.get('shares', 50),
'gigabytes': quotas.get('gigabytes', 1000),
'snapshots': quotas.get('snapshots', 50),
'snapshot_gigabytes': quotas.get('snapshot_gigabytes', 1000),
'share_networks': quotas.get('share_networks', 10),
'share_groups': quotas.get('share_groups', 50),
'share_group_snapshots': quotas.get(
'share_group_snapshots', 50),
}
}
result = self.controller.defaults(req, self.project_id)
self.assertEqual(expected, result)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'show')
@ddt.data(
('os-', '1.0', quota_sets.QuotaSetsControllerLegacy, 'defaults'),
('os-', '2.6', quota_sets.QuotaSetsControllerLegacy, 'defaults'),
('', '2.7', quota_sets.QuotaSetsController, 'defaults'),
('os-', '1.0', quota_sets.QuotaSetsControllerLegacy, 'show'),
('os-', '2.6', quota_sets.QuotaSetsControllerLegacy, 'show'),
('', '2.7', quota_sets.QuotaSetsController, 'show'),
)
@ddt.unpack
def test_get_quotas_with_different_api_versions(self, url, version,
controller, method_name):
expected = {
'quota_set': {
'id': self.project_id,
'shares': 50,
'gigabytes': 1000,
'snapshots': 50,
'snapshot_gigabytes': 1000,
'share_networks': 10,
}
}
req = fakes.HTTPRequest.blank(
'/fooproject/%squota-sets' % url,
version=version, use_admin_context=True)
result = getattr(controller(), method_name)(req, self.project_id)
self.assertEqual(expected, result)
@staticmethod
def _get_share_type_request_object(microversion=None):
req = _get_request(True, False)
req.environ['QUERY_STRING'] = 'share_type=fake_share_type_name_or_id'
req.api_version_request = api_version.APIVersionRequest(
microversion or '2.39')
return req
@ddt.data('2.39', '2.40')
def test_share_type_quota_detail(self, microversion):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(return_value={'id': 'fake_st_id'}))
req = self._get_share_type_request_object(microversion)
quotas = {
"shares": 23,
"snapshots": 34,
"gigabytes": 45,
"snapshot_gigabytes": 56,
}
expected = {'quota_set': {
'id': self.project_id,
'shares': {
'in_use': 0,
'limit': quotas['shares'],
'reserved': 0,
},
'gigabytes': {
'in_use': 0,
'limit': quotas['gigabytes'],
'reserved': 0,
},
'snapshots': {
'in_use': 0,
'limit': quotas['snapshots'],
'reserved': 0,
},
'snapshot_gigabytes': {
'in_use': 0,
'limit': quotas['snapshot_gigabytes'],
'reserved': 0,
},
}}
for k, v in quotas.items():
CONF.set_default('quota_' + k, v)
result = self.controller.detail(req, self.project_id)
self.assertEqual(expected, result)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'show')
quota_sets.db.share_type_get_by_name_or_id.assert_called_once_with(
req.environ['manila.context'], 'fake_share_type_name_or_id')
@ddt.data('2.39', '2.40')
def test_show_share_type_quota(self, microversion):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(return_value={'id': 'fake_st_id'}))
req = self._get_share_type_request_object(microversion)
quotas = {
"shares": 23,
"snapshots": 34,
"gigabytes": 45,
"snapshot_gigabytes": 56,
}
expected = {
'quota_set': {
'id': self.project_id,
'shares': quotas.get('shares', 50),
'gigabytes': quotas.get('gigabytes', 1000),
'snapshots': quotas.get('snapshots', 50),
'snapshot_gigabytes': quotas.get('snapshot_gigabytes', 1000),
}
}
for k, v in quotas.items():
CONF.set_default('quota_' + k, v)
result = self.controller.show(req, self.project_id)
self.assertEqual(expected, result)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'show')
quota_sets.db.share_type_get_by_name_or_id.assert_called_once_with(
req.environ['manila.context'], 'fake_share_type_name_or_id')
@ddt.data('show', 'detail')
def test_get_share_type_quota_with_old_microversion(self, method):
req = self._get_share_type_request_object('2.38')
self.assertRaises(
webob.exc.HTTPBadRequest,
getattr(self.controller, method),
req, self.project_id)
@ddt.data((None, None), (None, 'foo'), ('bar', None))
@ddt.unpack
def test__validate_user_id_and_share_type_args(self, user_id, st_id):
result = self.controller._validate_user_id_and_share_type_args(
user_id, st_id)
self.assertIsNone(result)
def test__validate_user_id_and_share_type_args_exception(self):
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller._validate_user_id_and_share_type_args,
'foo', 'bar')
def test__get_share_type_id_found(self):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(return_value={'id': 'fake_st_id'}))
ctxt = 'fake_context'
share_type = 'fake_share_type_name_or_id'
result = self.controller._get_share_type_id(ctxt, share_type)
self.assertEqual('fake_st_id', result)
def test__get_share_type_id_not_found(self):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(return_value=None))
ctxt = 'fake_context'
share_type = 'fake_share_type_name_or_id'
self.assertRaises(
webob.exc.HTTPNotFound,
self.controller._get_share_type_id,
ctxt, share_type)
def test__get_share_type_id_is_not_provided(self):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(return_value={'id': 'fake_st_id'}))
ctxt = 'fake_context'
result = self.controller._get_share_type_id(ctxt, None)
self.assertIsNone(result)
@ddt.data(
{},
{"quota_set": {}},
{"quota_set": {"foo": "bar"}},
{"foo": "bar"},
)
def test__ensure_share_group_related_args_are_absent_success(self, body):
result = self.controller._ensure_share_group_related_args_are_absent(
body)
self.assertIsNone(result)
@ddt.data(
{"share_groups": 5},
{"share_group_snapshots": 6},
{"quota_set": {"share_groups": 7}},
{"quota_set": {"share_group_snapshots": 8}},
)
def test__ensure_share_group_related_args_are_absent_error(self, body):
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller._ensure_share_group_related_args_are_absent, body)
@ddt.data(_get_request(True, True), _get_request(True, False))
def test__ensure_share_type_arg_is_absent(self, req):
result = self.controller._ensure_share_type_arg_is_absent(req)
self.assertIsNone(result)
def test__ensure_share_type_arg_is_absent_exception(self):
req = self._get_share_type_request_object('2.39')
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller._ensure_share_type_arg_is_absent,
req)
@ddt.data(_get_request(True, True), _get_request(True, False))
def test_quota_detail(self, request):
request.api_version_request = api_version.APIVersionRequest('2.25')
quotas = {
"shares": 23,
"snapshots": 34,
"gigabytes": 45,
"snapshot_gigabytes": 56,
"share_networks": 67,
}
expected = {
'quota_set': {
'id': self.project_id,
'shares': {'in_use': 0,
'limit': quotas['shares'],
'reserved': 0},
'gigabytes': {'in_use': 0,
'limit': quotas['gigabytes'], 'reserved': 0},
'snapshots': {'in_use': 0,
'limit': quotas['snapshots'], 'reserved': 0},
'snapshot_gigabytes': {
'in_use': 0,
'limit': quotas['snapshot_gigabytes'],
'reserved': 0,
},
'share_networks': {
'in_use': 0,
'limit': quotas['share_networks'],
'reserved': 0
},
}
}
for k, v in quotas.items():
CONF.set_default('quota_' + k, v)
result = self.controller.detail(request, self.project_id)
self.assertEqual(expected, result)
self.mock_policy_check.assert_called_once_with(
request.environ['manila.context'], self.resource_name, 'show')
@ddt.data(_get_request(True, True), _get_request(True, False))
def test_show_quota(self, request):
quotas = {
"shares": 23,
"snapshots": 34,
"gigabytes": 45,
"snapshot_gigabytes": 56,
"share_networks": 67,
"share_groups": 53,
"share_group_snapshots": 57,
}
expected = {
'quota_set': {
'id': self.project_id,
'shares': quotas.get('shares', 50),
'gigabytes': quotas.get('gigabytes', 1000),
'snapshots': quotas.get('snapshots', 50),
'snapshot_gigabytes': quotas.get('snapshot_gigabytes', 1000),
'share_networks': quotas.get('share_networks', 10),
'share_groups': quotas.get('share_groups', 50),
'share_group_snapshots': quotas.get(
'share_group_snapshots', 50),
}
}
for k, v in quotas.items():
CONF.set_default('quota_' + k, v)
result = self.controller.show(request, self.project_id)
self.assertEqual(expected, result)
self.mock_policy_check.assert_called_once_with(
request.environ['manila.context'], self.resource_name, 'show')
def test_show_quota_not_authorized(self):
req = _get_request(True, False)
self.mock_object(
quota_sets.db,
'authorize_project_context',
mock.Mock(side_effect=exception.NotAuthorized))
self.assertRaises(
webob.exc.HTTPForbidden,
self.controller.show,
req, self.project_id)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'show')
@ddt.data(_get_request(True, True), _get_request(True, False))
def test_update_quota(self, request):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(
return_value={'id': 'fake_st_id', 'name': 'fake_st_name'}))
CONF.set_default('quota_shares', 789)
body = {'quota_set': {'tenant_id': self.project_id, 'shares': 788}}
expected = {
'quota_set': {
'shares': body['quota_set']['shares'],
'gigabytes': 1000,
'snapshots': 50,
'snapshot_gigabytes': 1000,
'share_networks': 10,
'share_groups': 50,
'share_group_snapshots': 50,
}
}
mock_policy_update_check_call = mock.call(
request.environ['manila.context'], self.resource_name, 'update')
mock_policy_show_check_call = mock.call(
request.environ['manila.context'], self.resource_name, 'show')
update_result = self.controller.update(
request, self.project_id, body=body)
self.assertEqual(expected, update_result)
show_result = self.controller.show(request, self.project_id)
expected['quota_set']['id'] = self.project_id
self.assertEqual(expected, show_result)
self.mock_policy_check.assert_has_calls([
mock_policy_update_check_call, mock_policy_show_check_call])
quota_sets.db.share_type_get_by_name_or_id.assert_not_called()
@ddt.data('2.39', '2.40')
def test_update_share_type_quota(self, microversion):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(
return_value={'id': 'fake_st_id', 'name': 'fake_st_name'}))
req = self._get_share_type_request_object(microversion)
CONF.set_default('quota_shares', 789)
body = {'quota_set': {'tenant_id': self.project_id, 'shares': 788}}
expected = {
'quota_set': {
'shares': body['quota_set']['shares'],
'gigabytes': 1000,
'snapshots': 50,
'snapshot_gigabytes': 1000,
}
}
update_result = self.controller.update(req, self.project_id, body=body)
self.assertEqual(expected, update_result)
quota_sets.db.share_type_get_by_name_or_id.assert_called_once_with(
req.environ['manila.context'],
req.environ['QUERY_STRING'].split('=')[-1])
quota_sets.db.share_type_get_by_name_or_id.reset_mock()
show_result = self.controller.show(req, self.project_id)
expected['quota_set']['id'] = self.project_id
self.assertEqual(expected, show_result)
self.mock_policy_check.assert_has_calls([
mock.call(req.environ['manila.context'], self.resource_name, key)
for key in ('update', 'show')
])
quota_sets.db.share_type_get_by_name_or_id.assert_called_once_with(
req.environ['manila.context'],
req.environ['QUERY_STRING'].split('=')[-1])
def test_update_share_type_quota_using_too_old_microversion(self):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(
return_value={'id': 'fake_st_id', 'name': 'fake_st_name'}))
req = self._get_share_type_request_object('2.38')
body = {'quota_set': {'tenant_id': self.project_id, 'shares': 788}}
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.update,
req, self.project_id, body=body)
quota_sets.db.share_type_get_by_name_or_id.assert_not_called()
def test_update_share_type_quota_for_share_networks(self):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(
return_value={'id': 'fake_st_id', 'name': 'fake_st_name'}))
req = self._get_share_type_request_object('2.39')
body = {'quota_set': {
'tenant_id': self.project_id, 'share_networks': 788,
}}
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.update,
req, self.project_id, body=body)
quota_sets.db.share_type_get_by_name_or_id.assert_called_once_with(
req.environ['manila.context'],
req.environ['QUERY_STRING'].split('=')[-1])
@ddt.data(-2, 'foo', {1: 2}, [1])
def test_update_quota_with_invalid_value(self, value):
req = _get_request(True, False)
body = {'quota_set': {'tenant_id': self.project_id, 'shares': value}}
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.update,
req, self.project_id, body=body)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'update')
def test_user_quota_can_not_be_bigger_than_tenant_quota(self):
value = 777
CONF.set_default('quota_shares', value)
body = {
'quota_set': {
'tenant_id': self.project_id,
'shares': value + 1,
}
}
req = _get_request(True, True)
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.update,
req, self.project_id, body=body)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'update')
def test_update_inexistent_quota(self):
body = {
'quota_set': {
'tenant_id': self.project_id,
'fake_quota': 13,
}
}
req = _get_request(True, False)
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.update,
req, self.project_id, body=body)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'update')
def test_update_quota_not_authorized(self):
body = {'quota_set': {'tenant_id': self.project_id, 'shares': 13}}
req = _get_request(False, False)
self.assertRaises(
webob.exc.HTTPForbidden,
self.controller.update,
req, self.project_id, body=body)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'update')
@ddt.data(
('os-quota-sets', '1.0', quota_sets.QuotaSetsControllerLegacy),
('os-quota-sets', '2.6', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.7', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_update_all_quotas_with_force(self, url, version, controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%s' % url, version=version, use_admin_context=True)
quotas = (
('quota_shares', 13),
('quota_gigabytes', 14),
('quota_snapshots', 15),
('quota_snapshot_gigabytes', 16),
('quota_share_networks', 17),
)
for quota, value in quotas:
CONF.set_default(quota, value)
expected = {
'quota_set': {
'tenant_id': self.project_id,
'shares': quotas[0][1],
'gigabytes': quotas[1][1],
'snapshots': quotas[2][1],
'snapshot_gigabytes': quotas[3][1],
'share_networks': quotas[4][1],
'force': True,
}
}
update_result = controller().update(
req, self.project_id, body=expected)
expected['quota_set'].pop('force')
expected['quota_set'].pop('tenant_id')
self.assertEqual(expected, update_result)
show_result = controller().show(req, self.project_id)
expected['quota_set']['id'] = self.project_id
self.assertEqual(expected, show_result)
self.mock_policy_check.assert_has_calls([
mock.call(req.environ['manila.context'],
self.resource_name, action)
for action in ('update', 'show')
])
@ddt.data(
('os-quota-sets', '1.0', quota_sets.QuotaSetsControllerLegacy),
('os-quota-sets', '2.6', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.7', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_delete_tenant_quota(self, url, version, controller):
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project_and_user')
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project')
req = fakes.HTTPRequest.blank(
'/fooproject/%s' % url, version=version, use_admin_context=True)
result = controller().delete(req, self.project_id)
self.assertTrue(
utils.IsAMatcher(webob.response.Response) == result
)
self.assertTrue(hasattr(result, 'status_code'))
self.assertEqual(202, result.status_code)
self.assertFalse(
quota_sets.QUOTAS.destroy_all_by_project_and_user.called)
quota_sets.QUOTAS.destroy_all_by_project.assert_called_once_with(
req.environ['manila.context'], self.project_id)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'delete')
def test_delete_user_quota(self):
project_id = 'foo_project_id'
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project_and_user')
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project')
req = _get_request(True, True)
result = self.controller.delete(req, project_id)
self.assertTrue(
utils.IsAMatcher(webob.response.Response) == result
)
self.assertTrue(hasattr(result, 'status_code'))
self.assertEqual(202, result.status_code)
(quota_sets.QUOTAS.destroy_all_by_project_and_user.
assert_called_once_with(
req.environ['manila.context'],
project_id,
req.environ['manila.context'].user_id))
self.assertFalse(quota_sets.QUOTAS.destroy_all_by_project.called)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'delete')
def test_delete_share_type_quota(self):
req = self._get_share_type_request_object('2.39')
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project')
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project_and_user')
mock_delete_st_quotas = self.mock_object(
quota_sets.QUOTAS, 'destroy_all_by_project_and_share_type')
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(
return_value={'id': 'fake_st_id', 'name': 'fake_st_name'}))
result = self.controller.delete(req, self.project_id)
self.assertEqual(utils.IsAMatcher(webob.response.Response), result)
self.assertTrue(hasattr(result, 'status_code'))
self.assertEqual(202, result.status_code)
mock_delete_st_quotas.assert_called_once_with(
req.environ['manila.context'], self.project_id, 'fake_st_id')
quota_sets.QUOTAS.destroy_all_by_project.assert_not_called()
quota_sets.QUOTAS.destroy_all_by_project_and_user.assert_not_called()
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'delete')
quota_sets.db.share_type_get_by_name_or_id.assert_called_once_with(
req.environ['manila.context'],
req.environ['QUERY_STRING'].split('=')[-1])
def test_delete_share_type_quota_using_too_old_microversion(self):
self.mock_object(
quota_sets.db, 'share_type_get_by_name_or_id',
mock.Mock(
return_value={'id': 'fake_st_id', 'name': 'fake_st_name'}))
req = self._get_share_type_request_object('2.38')
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.delete,
req, self.project_id)
quota_sets.db.share_type_get_by_name_or_id.assert_not_called()
def test_delete_not_authorized(self):
req = _get_request(False, False)
self.assertRaises(
webob.exc.HTTPForbidden,
self.controller.delete,
req, self.project_id)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'delete')
@ddt.data(
('os-quota-sets', '2.7', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.6', quota_sets.QuotaSetsController),
('quota-sets', '2.0', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_api_not_found(self, url, version, controller):
req = fakes.HTTPRequest.blank('/fooproject/%s' % url, version=version)
for method_name in ('show', 'defaults', 'delete'):
self.assertRaises(
exception.VersionNotFoundForAPIMethod,
getattr(controller(), method_name),
req, self.project_id)
@ddt.data(
('os-quota-sets', '2.7', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.6', quota_sets.QuotaSetsController),
('quota-sets', '2.0', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_update_api_not_found(self, url, version, controller):
req = fakes.HTTPRequest.blank('/fooproject/%s' % url, version=version)
self.assertRaises(
exception.VersionNotFoundForAPIMethod,
controller().update,
req, self.project_id)