Added support of per-tenant-user quotas

Added new keys '--user' in v1.shell to quota-show, quota-update.
Added command quota-delete, that provides ability for admins
to be able to delete a non-default quota (absolute limit) for a tenant
or user, so that tenant's quota will revert back to the configured default.
Added 'responce_key' parameter to Manager._update function
Added '--force' parameter to quota-update to support new functionality
on server
Fixes help output, when in 'manila help' was displayed only first
string of command documentation

Implement bp: user-quota-support

Change-Id: Ibfe9ccf83ea43d5af6652342f1fe6a88c78a0364
This commit is contained in:
Andrei V. Ostapenko 2013-10-11 18:09:52 +03:00
parent c6fa2fcf4b
commit e5562354e4
6 changed files with 112 additions and 12 deletions

View File

@ -157,10 +157,14 @@ class Manager(utils.HookableMixin):
def _delete(self, url):
resp, body = self.api.client.delete(url)
def _update(self, url, body, **kwargs):
def _update(self, url, body, response_key=None, **kwargs):
self.run_hooks('modify_body_for_update', body, **kwargs)
resp, body = self.api.client.put(url, body=body)
return body
if body:
if response_key:
return self.resource_class(self, body[response_key])
else:
return self.resource_class(self, body)
class ManagerWithFind(Manager):

View File

@ -274,7 +274,7 @@ class OpenStackManilaShell(object):
command = attr[3:].replace('_', '-')
callback = getattr(actions_module, attr)
desc = callback.__doc__ or ''
help = desc.strip().split('\n')[0]
help = desc.strip()
arguments = getattr(callback, 'arguments', [])
subparser = subparsers.add_parser(

View File

@ -30,25 +30,42 @@ class QuotaSet(base.Resource):
class QuotaSetManager(base.ManagerWithFind):
resource_class = QuotaSet
def get(self, tenant_id):
def get(self, tenant_id, user_id=None):
if hasattr(tenant_id, 'tenant_id'):
tenant_id = tenant_id.tenant_id
return self._get("/os-quota-sets/%s" % (tenant_id), "quota_set")
if user_id:
url = "/os-quota-sets/%s?user_id=%s" % (tenant_id, user_id)
else:
url = "/os-quota-sets/%s" % tenant_id
return self._get(url, "quota_set")
def update(self, tenant_id, shares=None, snapshots=None, gigabytes=None):
def update(self, tenant_id, shares=None, snapshots=None, gigabytes=None,
force=None, user_id=None):
body = {'quota_set': {
'tenant_id': tenant_id,
'shares': shares,
'snapshots': snapshots,
'gigabytes': gigabytes}}
'gigabytes': gigabytes,
'force': force}}
for key in body['quota_set'].keys():
if body['quota_set'][key] is None:
body['quota_set'].pop(key)
if user_id:
url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id)
else:
url = '/os-quota-sets/%s' % tenant_id
self._update('/os-quota-sets/%s' % (tenant_id), body)
return self._update(url, body, 'quota_set')
def defaults(self, tenant_id):
return self._get('/os-quota-sets/%s/defaults' % tenant_id,
'quota_set')
def delete(self, tenant_id, user_id=None):
if user_id:
url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id)
else:
url = '/os-quota-sets/%s' % tenant_id
self._delete(url)

View File

@ -19,6 +19,7 @@ import sys
import time
from manilaclient import exceptions
from manilaclient.v1 import quotas
from manilaclient import utils
@ -126,20 +127,32 @@ def _quota_update(manager, identifier, args):
updates[resource] = val
if updates:
manager.update(identifier, **updates)
# default value of force is None to make sure this client
# will be compatibile with old nova server
force_update = getattr(args, 'force', None)
user_id = getattr(args, 'user', None)
if isinstance(manager, quotas.QuotaSetManager):
manager.update(identifier, force=force_update, user_id=user_id,
**updates)
else:
manager.update(identifier, **updates)
@utils.arg('--tenant',
metavar='<tenant-id>',
default=None,
help='ID of tenant to list the quotas for.')
@utils.arg('--user',
metavar='<user-id>',
default=None,
help='ID of user to list the quotas for.')
def do_quota_show(cs, args):
"""List the quotas for a tenant/user."""
if not args.tenant:
_quota_show(cs.quotas.get(cs.client.tenant_id))
_quota_show(cs.quotas.get(cs.client.tenant_id, user_id=args.user))
else:
_quota_show(cs.quotas.get(args.tenant))
_quota_show(cs.quotas.get(args.tenant, user_id=args.user))
@utils.arg('--tenant',
@ -158,6 +171,10 @@ def do_quota_defaults(cs, args):
@utils.arg('tenant',
metavar='<tenant_id>',
help='UUID of tenant to set the quotas for.')
@utils.arg('--user',
metavar='<user-id>',
default=None,
help='ID of user to set the quotas for.')
@utils.arg('--shares',
metavar='<shares>',
type=int, default=None,
@ -170,13 +187,36 @@ def do_quota_defaults(cs, args):
metavar='<gigabytes>',
type=int, default=None,
help='New value for the "gigabytes" quota.')
@utils.arg('--force',
dest='force',
action="store_true",
default=None,
help='Whether force update the quota even if the already used'
' and reserved exceeds the new quota')
@utils.service_type('share')
def do_quota_update(cs, args):
"""Update the quotas for a tenant."""
"""Update the quotas for a tenant/user."""
_quota_update(cs.quotas, args.tenant, args)
@utils.arg('--tenant',
metavar='<tenant-id>',
help='ID of tenant to delete quota for.')
@utils.arg('--user',
metavar='<user-id>',
help='ID of user to delete quota for.')
def do_quota_delete(cs, args):
"""Delete quota for a tenant/user so their quota will Revert
back to default.
"""
if not args.tenant:
cs.quotas.delete(cs.client.tenant_id, user_id=args.user)
else:
cs.quotas.delete(args.tenant, user_id=args.user)
@utils.arg('class_name',
metavar='<class>',
help='Name of quota class to list the quotas for.')

View File

@ -127,6 +127,8 @@ class FakeHTTPClient(base_client.HTTPClient):
'snapshots': 2,
'gigabytes': 1}})
def delete_os_quota_sets_test(self, **kw):
return (202, {}, {})
#
# List all extensions
#

View File

@ -27,6 +27,13 @@ class QuotaSetsTest(utils.TestCase):
cs.quotas.get(tenant_id)
cs.assert_called('GET', '/os-quota-sets/%s' % tenant_id)
def test_user_quotas_get(self):
tenant_id = 'test'
user_id = 'fake_user'
cs.quotas.get(tenant_id, user_id=user_id)
url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id)
cs.assert_called('GET', url)
def test_tenant_quotas_defaults(self):
tenant_id = 'test'
cs.quotas.defaults(tenant_id)
@ -38,6 +45,36 @@ class QuotaSetsTest(utils.TestCase):
q.update(snapshots=2)
cs.assert_called('PUT', '/os-quota-sets/test')
def test_update_user_quota(self):
tenant_id = 'test'
user_id = 'fake_user'
q = cs.quotas.get(tenant_id)
q.update(shares=2, user_id=user_id)
q.update(snapshots=2, user_id=user_id)
url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id)
cs.assert_called('PUT', url)
def test_force_update_quota(self):
q = cs.quotas.get('test')
q.update(shares=2, force=True)
cs.assert_called(
'PUT', '/os-quota-sets/test',
{'quota_set': {'force': True,
'shares': 2,
'tenant_id': 'test'}})
def test_quotas_delete(self):
tenant_id = 'test'
cs.quotas.delete(tenant_id)
cs.assert_called('DELETE', '/os-quota-sets/%s' % tenant_id)
def test_user_quotas_delete(self):
tenant_id = 'test'
user_id = 'fake_user'
cs.quotas.delete(tenant_id, user_id=user_id)
url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id)
cs.assert_called('DELETE', url)
def test_refresh_quota(self):
q = cs.quotas.get('test')
q2 = cs.quotas.get('test')