Add the ability to check tenant quota detail

For other component such like cinder, the administrator could check
the quota usage of tenant. Currently Manila retrieves the quota set
information only with 'limit' property.

This patch intends to enhance the API by return more properties
('in_use', 'limit', 'reserved') with new option  '--detail'.

Change-Id: Ibc0f9f65891b8e2a308358420d2c4dc3ea4697ce
Implements: blueprint admin-check-tenant-quota-usage
This commit is contained in:
zhongjun 2016-09-12 14:25:11 +08:00 committed by zhongjun2
parent c91deb8498
commit 64f38da1ff
7 changed files with 161 additions and 22 deletions

View File

@ -79,3 +79,8 @@ class ManilaClientTestQuotasReadOnly(base.BaseTestCase):
@utils.skip_if_microversion_not_supported("2.7")
def test_quota_show_api_2_7(self, role):
self._get_quotas(role, "show", "2.7")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.25")
def test_quota_show_api_2_25(self, role):
self._get_quotas(role, "show --detail", "2.25")

View File

@ -285,6 +285,42 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
}
return (200, {}, instances)
def get_quota_sets_1234(self, *args, **kwargs):
quota_set = {
'quota_set': {
'id': '1234',
'shares': 50,
'gigabytes': 1000,
'snapshots': 50,
'snapshot_gigabytes': 1000,
'share_networks': 10,
}
}
return (200, {}, quota_set)
def get_quota_sets_1234_detail(self, *args, **kwargs):
quota_set = {
'quota_set': {
'id': '1234',
'shares': {'in_use': 0,
'limit': 50,
'reserved': 0},
'gigabytes': {'in_use': 0,
'limit': 10000,
'reserved': 0},
'snapshots': {'in_use': 0,
'limit': 50,
'reserved': 0},
'snapshot_gigabytes': {'in_use': 0,
'limit': 1000,
'reserved': 0},
'share_networks': {'in_use': 0,
'limit': 10,
'reserved': 0},
}
}
return (200, {}, quota_set)
def get_share_instances(self, **kw):
return self._share_instances()
@ -921,3 +957,12 @@ def fake_create(url, body, response_key):
def fake_update(url, body, response_key):
return {'url': url, 'body': body, 'resp_key': response_key}
class FakeQuotaSet(object):
def __init__(self, dictionary):
self.dictionary = dictionary
def to_dict(self):
return self.dictionary

View File

@ -35,28 +35,41 @@ class QuotaSetsTest(utils.TestCase):
return quotas.RESOURCE_PATH
return quotas.RESOURCE_PATH_LEGACY
@ddt.data("2.6", "2.7")
@ddt.data("2.6", "2.7", "2.25")
def test_tenant_quotas_get(self, microversion):
tenant_id = 'test'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test" % resource_path
version = api_versions.APIVersion(microversion)
if version >= api_versions.APIVersion('2.25'):
expected_url = "%s/test/detail" % resource_path
else:
expected_url = ("%s/test"
% resource_path)
with mock.patch.object(manager, '_get',
mock.Mock(return_value='fake_get')):
manager.get(tenant_id)
manager.get(tenant_id, detail=True)
manager._get.assert_called_once_with(expected_url, "quota_set")
@ddt.data("2.6", "2.7")
@ddt.data("2.6", "2.7", "2.25")
def test_user_quotas_get(self, microversion):
tenant_id = 'test'
user_id = 'fake_user'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test?user_id=fake_user" % resource_path
version = api_versions.APIVersion(microversion)
if version >= api_versions.APIVersion('2.25'):
expected_url = ("%s/test/detail?user_id=fake_user"
% resource_path)
else:
expected_url = ("%s/test?user_id=fake_user"
% resource_path)
with mock.patch.object(manager, '_get',
mock.Mock(return_value='fake_get')):
manager.get(tenant_id, user_id=user_id)
manager.get(tenant_id, user_id=user_id, detail=True)
manager._get.assert_called_once_with(expected_url, "quota_set")

View File

@ -1619,6 +1619,33 @@ class ShellTest(test_utils.TestCase):
mock.ANY,
fields=["Name", "Host", "Backend", "Pool"])
@mock.patch.object(cliutils, 'print_dict', mock.Mock())
def test_quota_show(self):
self.run_command('quota-show --tenant 1234')
self.assert_called(
'GET',
'/quota-sets/1234',
)
cliutils.print_dict.assert_called_once_with(mock.ANY)
@mock.patch.object(cliutils, 'print_dict', mock.Mock())
def test_quota_show_with_detail(self):
self.run_command('quota-show --tenant 1234 --detail')
self.assert_called(
'GET',
'/quota-sets/1234/detail',
)
cliutils.print_dict.assert_called_once_with(mock.ANY)
@mock.patch.object(cliutils, 'print_dict', mock.Mock())
def test_quota_show_with_user_id(self):
self.run_command('quota-show --tenant 1234 --user 1111')
self.assert_called(
'GET',
'/quota-sets/1234?user_id=1111',
)
cliutils.print_dict.assert_called_once_with(mock.ANY)
@mock.patch.object(cliutils, 'print_list', mock.Mock())
def test_pool_list_with_detail(self):
self.run_command('pool-list --detail')
@ -1641,6 +1668,23 @@ class ShellTest(test_utils.TestCase):
mock.ANY,
fields=["Name", "Host"])
@ddt.data(({"key1": "value1",
"key2": "value2"},
{"key1": "value1",
"key2": "value2"}),
({"key1": {"key11": "value11", "key12": "value12"},
"key2": {"key21": "value21"}},
{"key1": "key11 = value11\nkey12 = value12",
"key2": "key21 = value21"}),
({}, {}))
@ddt.unpack
@mock.patch.object(cliutils, 'print_dict', mock.Mock())
def test_quota_set_pretty_show(self, value, expected):
fake_quota_set = fakes.FakeQuotaSet(value)
shell_v2._quota_set_pretty_show(fake_quota_set)
cliutils.print_dict.assert_called_with(expected)
@ddt.data('--share-type test_type', '--share_type test_type',
'--share-type-id 0123456789', '--share_type_id 0123456789')
@mock.patch.object(cliutils, 'print_list', mock.Mock())

View File

@ -35,27 +35,40 @@ class QuotaSet(common_base.Resource):
class QuotaSetManager(base.ManagerWithFind):
resource_class = QuotaSet
def _do_get(self, tenant_id, user_id=None, resource_path=RESOURCE_PATH):
def _do_get(self, tenant_id, user_id=None, detail=False,
resource_path=RESOURCE_PATH):
if hasattr(tenant_id, 'tenant_id'):
tenant_id = tenant_id.tenant_id
if detail:
query = '/detail'
else:
query = ''
if user_id:
query = '%s?user_id=%s' % (query, user_id)
data = {
"resource_path": resource_path,
"tenant_id": tenant_id,
"user_id": user_id,
}
if user_id:
url = "%(resource_path)s/%(tenant_id)s?user_id=%(user_id)s" % data
else:
url = "%(resource_path)s/%(tenant_id)s" % data
url = ("%(resource_path)s/%(tenant_id)s" + query) % data
return self._get(url, "quota_set")
@api_versions.wraps("1.0", "2.6")
def get(self, tenant_id, user_id=None):
return self._do_get(tenant_id, user_id, RESOURCE_PATH_LEGACY)
def get(self, tenant_id, user_id=None, detail=False):
return self._do_get(tenant_id, user_id,
resource_path=RESOURCE_PATH_LEGACY)
@api_versions.wraps("2.7") # noqa
def get(self, tenant_id, user_id=None):
return self._do_get(tenant_id, user_id, RESOURCE_PATH)
@api_versions.wraps("2.7", "2.24") # noqa
def get(self, tenant_id, user_id=None, detail=False):
return self._do_get(tenant_id, user_id,
resource_path=RESOURCE_PATH)
@api_versions.wraps("2.25") # noqa
def get(self, tenant_id, user_id=None, detail=False):
return self._do_get(tenant_id, user_id, detail,
resource_path=RESOURCE_PATH)
def _do_update(self, tenant_id, shares=None, snapshots=None,
gigabytes=None, snapshot_gigabytes=None,

View File

@ -219,6 +219,19 @@ def _print_share_snapshot(cs, snapshot):
cliutils.print_dict(info)
def _quota_set_pretty_show(quotas):
"""convert quotas object to dict and display"""
new_quotas = {}
for quota_k, quota_v in sorted(quotas.to_dict().items()):
if isinstance(quota_v, dict):
quota_v = '\n'.join(
['%s = %s' % (k, v) for k, v in sorted(quota_v.items())])
new_quotas[quota_k] = quota_v
cliutils.print_dict(new_quotas)
def _find_share_snapshot_instance(cs, snapshot_instance):
"""Get a share snapshot instance by ID."""
return apiclient_utils.find_resource(
@ -361,13 +374,16 @@ def _quota_update(manager, identifier, args):
metavar='<user-id>',
default=None,
help='ID of user to list the quotas for.')
@cliutils.arg(
'--detail',
action='store_true',
help='Optional flag to indicate whether to show quota in detail. '
'Default false, available only for microversion >= 2.25.')
def do_quota_show(cs, args):
"""List the quotas for a tenant/user."""
project_id = cs.keystone_client.project_id
if not args.tenant:
_quota_show(cs.quotas.get(project_id, user_id=args.user))
else:
_quota_show(cs.quotas.get(args.tenant, user_id=args.user))
project = args.tenant or cs.keystone_client.project_id
qts = cs.quotas.get(project, user_id=args.user, detail=args.detail)
_quota_set_pretty_show(qts)
@cliutils.arg(

View File

@ -0,0 +1,3 @@
---
features:
- Add support to check quota usage by '--detail' in quota-show