Add share group quotas

Add support for share group quotas implemented on server side [1].

[1] I397a8e886226cb22fa50abdf2a4a938bb04c655d

Change-Id: Ib2b12d906c54c05faf8a72ac851d100e25023d50
Implements Blueprint add-share-groups-quota
This commit is contained in:
Valeriy Ponomaryov 2017-06-09 20:32:02 +03:00
parent 8045f4d24e
commit 6964524f4c
8 changed files with 223 additions and 26 deletions

View File

@ -27,7 +27,7 @@ from manilaclient import utils
LOG = logging.getLogger(__name__)
MAX_VERSION = '2.39'
MAX_VERSION = '2.40'
MIN_VERSION = '2.0'
DEPRECATED_VERSION = '1.0'
_VERSIONED_METHOD_MAP = {}

View File

@ -20,6 +20,7 @@ from tempest.lib.cli import output_parser
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from manilaclient import api_versions
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
@ -54,12 +55,12 @@ class QuotasReadWriteTest(base.BaseTestCase):
)
self.st_id = self.share_type["ID"]
def _verify_current_st_quotas_equal_to(self, quotas):
def _verify_current_st_quotas_equal_to(self, quotas, microversion):
# Read share type quotas
cmd = 'quota-show --tenant-id %s --share-type %s' % (
self.project_id, self.st_id)
st_quotas_raw = self.admin_client.manila(
cmd, microversion=self.microversion)
cmd, microversion=microversion)
st_quotas = output_parser.details(st_quotas_raw)
# Verify that quotas
@ -71,11 +72,109 @@ class QuotasReadWriteTest(base.BaseTestCase):
self.assertIn(key, quotas)
self.assertEqual(int(quotas[key]), int(value))
def test_update_share_type_quotas_positive(self):
def _verify_current_quotas_equal_to(self, quotas, microversion):
# Read quotas
cmd = 'quota-show --tenant-id %s' % self.project_id
quotas_raw = self.admin_client.manila(
cmd, microversion=microversion)
quotas = output_parser.details(quotas_raw)
# Verify that quotas
self.assertGreater(len(quotas), 3)
for key, value in quotas.items():
if key not in ('shares', 'gigabytes', 'snapshots',
'snapshot_gigabytes',
'share_groups', 'share_group_snapshots'):
continue
self.assertIn(key, quotas)
self.assertEqual(int(quotas[key]), int(value))
@ddt.data(*set([
"2.40", api_versions.MAX_VERSION,
]))
def test_update_quotas_for_share_groups(self, microversion):
if not utils.is_microversion_supported(microversion):
msg = "Microversion '%s' not supported." % microversion
raise self.skipException(msg)
# Get default quotas
cmd = 'quota-defaults'
quotas_raw = self.admin_client.manila(cmd, microversion=microversion)
default_quotas = output_parser.details(quotas_raw)
# Get project quotas
cmd = 'quota-show --tenant-id %s ' % self.project_id
quotas_raw = self.admin_client.manila(
cmd, microversion=self.microversion)
quotas_raw = self.admin_client.manila(cmd, microversion=microversion)
p_quotas = output_parser.details(quotas_raw)
# Define custom share group quotas for project
p_custom_quotas = {
'share_groups': -1 if int(p_quotas['share_groups']) != -1 else 999,
'share_group_snapshots': -1 if int(
p_quotas['share_group_snapshots']) != -1 else 999,
}
# Update share group quotas for project
cmd = ('quota-update %s --share-groups %s '
'--share-group-snapshots %s') % (
self.project_id,
p_custom_quotas['share_groups'],
p_custom_quotas['share_group_snapshots'],
)
self.admin_client.manila(cmd, microversion=microversion)
# Verify quotas
self._verify_current_quotas_equal_to(p_custom_quotas, microversion)
# Reset quotas
cmd = 'quota-delete --tenant-id %s --share-type %s' % (
self.project_id, self.st_id)
self.admin_client.manila(cmd, microversion=microversion)
# Verify quotas after reset
self._verify_current_quotas_equal_to(default_quotas, microversion)
# Return project quotas back
cmd = ('quota-update %s --share-groups %s '
'--share-group-snapshots %s') % (
self.project_id,
p_quotas['share_groups'], p_quotas['share_group_snapshots'])
self.admin_client.manila(cmd, microversion=microversion)
# Verify quotas after reset
self._verify_current_quotas_equal_to(p_quotas, microversion)
@ddt.data('--share-groups', '--share-group-snapshots')
@utils.skip_if_microversion_not_supported("2.39")
def test_update_quotas_for_share_groups_using_too_old_microversion(self,
arg):
cmd = 'quota-update %s %s 13' % (self.project_id, arg)
self.assertRaises(
exceptions.CommandFailed,
self.admin_client.manila,
cmd, microversion='2.39')
@ddt.data('--share-groups', '--share-group-snapshots')
@utils.skip_if_microversion_not_supported("2.40")
def test_update_share_type_quotas_for_share_groups(self, arg):
cmd = 'quota-update %s --share-type %s %s 13' % (
self.project_id, self.st_id, arg)
self.assertRaises(
exceptions.CommandFailed,
self.admin_client.manila,
cmd, microversion='2.40')
@ddt.data(*set([
"2.39", "2.40", api_versions.MAX_VERSION,
]))
def test_update_share_type_quotas_positive(self, microversion):
if not utils.is_microversion_supported(microversion):
msg = "Microversion '%s' not supported." % microversion
raise self.skipException(msg)
# Get project quotas
cmd = 'quota-show --tenant-id %s ' % self.project_id
quotas_raw = self.admin_client.manila(cmd, microversion=microversion)
p_quotas = output_parser.details(quotas_raw)
# Define share type quotas
@ -96,18 +195,18 @@ class QuotasReadWriteTest(base.BaseTestCase):
st_custom_quotas['gigabytes'],
st_custom_quotas['snapshots'],
st_custom_quotas['snapshot_gigabytes'])
self.admin_client.manila(cmd, microversion=self.microversion)
self.admin_client.manila(cmd, microversion=microversion)
# Verify share type quotas
self._verify_current_st_quotas_equal_to(st_custom_quotas)
self._verify_current_st_quotas_equal_to(st_custom_quotas, microversion)
# Reset share type quotas
cmd = 'quota-delete --tenant-id %s --share-type %s' % (
self.project_id, self.st_id)
self.admin_client.manila(cmd, microversion=self.microversion)
self.admin_client.manila(cmd, microversion=microversion)
# Verify share type quotas after reset
self._verify_current_st_quotas_equal_to(p_quotas)
self._verify_current_st_quotas_equal_to(p_quotas, microversion)
@utils.skip_if_microversion_not_supported("2.38")
def test_read_share_type_quotas_with_too_old_microversion(self):

View File

@ -302,6 +302,9 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
}
return (200, {}, instances)
def put_quota_sets_1234(self, *args, **kwargs):
return (200, {}, {})
def get_quota_sets_1234(self, *args, **kwargs):
quota_set = {
'quota_set': {

View File

@ -131,7 +131,7 @@ class QuotaSetsTest(utils.TestCase):
manager._update.assert_called_once_with(
expected_url, expected_body, "quota_set")
@ddt.data("2.6", "2.7", "2.38", "2.39")
@ddt.data("2.6", "2.7", "2.38", "2.39", "2.40")
def test_update_user_quota(self, microversion):
tenant_id = 'test'
user_id = 'fake_user'
@ -148,11 +148,26 @@ class QuotaSetsTest(utils.TestCase):
'share_networks': 5,
},
}
kwargs = {
'shares': expected_body['quota_set']['shares'],
'snapshots': expected_body['quota_set']['snapshots'],
'gigabytes': expected_body['quota_set']['gigabytes'],
'snapshot_gigabytes': expected_body['quota_set'][
'snapshot_gigabytes'],
'share_networks': expected_body['quota_set']['share_networks'],
'user_id': user_id,
}
if microversion == '2.40':
expected_body['quota_set']['share_groups'] = 6
expected_body['quota_set']['share_group_snapshots'] = 7
kwargs['share_groups'] = expected_body['quota_set'][
'share_groups']
kwargs['share_group_snapshots'] = expected_body['quota_set'][
'share_group_snapshots']
with mock.patch.object(manager, '_update',
mock.Mock(return_value='fake_update')):
manager.update(
tenant_id, shares=1, snapshots=2, gigabytes=3,
snapshot_gigabytes=4, share_networks=5, user_id=user_id)
manager.update(tenant_id, **kwargs)
manager._update.assert_called_once_with(
expected_url, expected_body, "quota_set")

View File

@ -1804,6 +1804,38 @@ class ShellTest(test_utils.TestCase):
)
mock_print_dict.assert_called_once_with(mock.ANY)
@ddt.data(
('--shares 13', {'shares': 13}),
('--gigabytes 14', {'gigabytes': 14}),
('--snapshots 15', {'snapshots': 15}),
('--snapshot-gigabytes 13', {'snapshot_gigabytes': 13}),
('--share-networks 13', {'share_networks': 13}),
('--share-groups 13', {'share_groups': 13}),
('--share-groups 0', {'share_groups': 0}),
('--share-group-snapshots 13', {'share_group_snapshots': 13}),
('--share-group-snapshots 0', {'share_group_snapshots': 0}),
)
@ddt.unpack
def test_quota_update(self, cmd, expected_body):
self.run_command('quota-update 1234 %s' % cmd)
expected = {'quota_set': expected_body}
self.assert_called('PUT', '/quota-sets/1234', body=expected)
@ddt.data(
"quota-update 1234 --share-groups 13 --share-type foo",
"quota-update 1234 --share-group-snapshots 14 --share-type bar",
("quota-update 1234 --share-groups 13 --share-type foo "
"--share-group-snapshots 14"),
"--os-share-api-version 2.39 quota-update 1234 --share-groups 13",
("--os-share-api-version 2.39 quota-update 1234 "
"--share-group-snapshots 13"),
("--os-share-api-version 2.38 quota-update 1234 --shares 5 "
"--share-type foo"),
)
def test_quota_update_with_wrong_combinations(self, cmd):
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
@mock.patch.object(cliutils, 'print_list', mock.Mock())
def test_pool_list_with_detail(self):
self.run_command('pool-list --detail')

View File

@ -90,8 +90,10 @@ class QuotaSetManager(base.ManagerWithFind):
def _do_update(self, tenant_id, shares=None, snapshots=None,
gigabytes=None, snapshot_gigabytes=None,
share_networks=None, force=None, user_id=None,
share_type=None, resource_path=RESOURCE_PATH):
share_networks=None,
force=None, user_id=None, share_type=None,
share_groups=None, share_group_snapshots=None,
resource_path=RESOURCE_PATH):
self._check_user_id_and_share_type_args(user_id, share_type)
body = {
'quota_set': {
@ -101,6 +103,8 @@ class QuotaSetManager(base.ManagerWithFind):
'gigabytes': gigabytes,
'snapshot_gigabytes': snapshot_gigabytes,
'share_networks': share_networks,
'share_groups': share_groups,
'share_group_snapshots': share_group_snapshots,
'force': force,
},
}
@ -141,10 +145,26 @@ class QuotaSetManager(base.ManagerWithFind):
share_networks, force, user_id, resource_path=RESOURCE_PATH,
)
@api_versions.wraps("2.39") # noqa
@api_versions.wraps("2.39", "2.39") # noqa
def update(self, tenant_id, user_id=None, share_type=None,
shares=None, snapshots=None, gigabytes=None,
snapshot_gigabytes=None, share_networks=None, force=None):
if share_type and share_networks:
raise ValueError(
"'share_networks' quota can be set only for project or user, "
"not share type.")
return self._do_update(
tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes,
share_networks, force, user_id,
share_type=share_type,
resource_path=RESOURCE_PATH,
)
@api_versions.wraps("2.40") # noqa
def update(self, tenant_id, user_id=None, share_type=None,
shares=None, snapshots=None, gigabytes=None,
snapshot_gigabytes=None, share_networks=None,
share_groups=None, share_group_snapshots=None,
force=None):
if share_type and share_networks:
raise ValueError(
@ -154,6 +174,8 @@ class QuotaSetManager(base.ManagerWithFind):
tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes,
share_networks, force, user_id,
share_type=share_type,
share_groups=share_groups,
share_group_snapshots=share_group_snapshots,
resource_path=RESOURCE_PATH,
)

View File

@ -372,13 +372,6 @@ _quota_resources = [
]
def _quota_show(quotas):
quota_dict = {}
for resource in _quota_resources:
quota_dict[resource] = getattr(quotas, resource, None)
cliutils.print_dict(quota_dict)
def _quota_update(manager, identifier, args):
updates = {}
for resource in _quota_resources:
@ -450,7 +443,7 @@ def do_quota_show(cs, args):
def do_quota_defaults(cs, args):
"""List the default quotas for a tenant."""
project = args.tenant_id or cs.keystone_client.project_id
_quota_show(cs.quotas.defaults(project))
_quota_set_pretty_show(cs.quotas.defaults(project))
@cliutils.arg(
@ -497,6 +490,21 @@ def do_quota_defaults(cs, args):
default=None,
action='single_alias',
help='New value for the "share_networks" quota.')
@cliutils.arg(
'--share-groups', '--share_groups', '--groups',
metavar='<share_groups>',
type=int,
default=None,
action='single_alias',
help='New value for the "share_groups" quota.')
@cliutils.arg(
'--share-group-snapshots', '--share_group_snapshots',
'--group-snapshots', '--group_snapshots',
metavar='<share_group_snapshots>',
type=int,
default=None,
action='single_alias',
help='New value for the "share_group_snapshots" quota.')
@cliutils.arg(
'--share-type',
'--share_type',
@ -533,6 +541,17 @@ def do_quota_update(cs, args):
"'share type' quotas are available only starting with "
"'2.39' API microversion.")
kwargs["share_type"] = args.share_type
if args.share_groups is not None or args.share_group_snapshots is not None:
if cs.api_version < api_versions.APIVersion("2.40"):
raise exceptions.CommandError(
"'share group' quotas are available only starting with "
"'2.40' API microversion.")
elif args.share_type is not None:
raise exceptions.CommandError(
"Share type quotas handle only 'shares', 'gigabytes', "
"'snapshots' and 'snapshot_gigabytes' resources.")
kwargs["share_groups"] = args.share_groups
kwargs["share_group_snapshots"] = args.share_group_snapshots
cs.quotas.update(**kwargs)
@ -583,7 +602,7 @@ def do_quota_delete(cs, args):
def do_quota_class_show(cs, args):
"""List the quotas for a quota class."""
_quota_show(cs.quota_classes.get(args.class_name))
_quota_set_pretty_show(cs.quota_classes.get(args.class_name))
@cliutils.arg(

View File

@ -0,0 +1,7 @@
---
features:
- Added support for share group and share group snapshot quotas.
upgrade:
- After addition of share group and share group snapshot quotas, it is now
possible to get 'over limit' error creating share groups and share group
snapshots.