Implement quota-management commands for KB.

"quota detail" List the Detail limit for a tenant.
"quota sync"   On Demand quota sync for a tenant.
"quota update" Update the quotas for a tenant.
"quota delete" Delete quota for a tenant.
Add test-cases for the same.

Change-Id: Icc018b7bb2e8f68c503c27184fa6390632d81ac6
This commit is contained in:
Goutham Pratapa 2017-01-10 19:34:47 +05:30
parent 3333a3c6be
commit af6959c68f
7 changed files with 380 additions and 9 deletions

View File

@ -21,11 +21,6 @@ class Resource(object):
# This will be overridden by the actual resource
resource_name = 'Something'
def __init__(self, manager, data, values):
self.manager = manager
self._data = data
self._values = values
class ResourceManager(object):
resource_class = None
@ -59,8 +54,28 @@ class ResourceManager(object):
result = self._generate_resource(json_response_key)
return result
def _delete(self, url):
resp = self.http_client.delete(url)
def _sync(self, url, data=None):
resp = self.http_client.put(url, data)
if resp.status_code != 200:
self._raise_api_exception(resp)
def _detail(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_response_key = get_json(resp)
json_objects = [json_response_key[item] for item in json_response_key]
resource = []
for json_object in json_objects:
data = json_object.get('usage').keys()
for values in data:
resource.append(self.resource_class(self, values,
json_object['limits'][values],
json_object['usage'][values]))
return resource
def _delete(self, url, data=None):
resp = self.http_client.delete(url, data)
if resp.status_code != 200:
self._raise_api_exception(resp)

View File

@ -18,6 +18,11 @@ from kingbirdclient.api import base
class QuotaClass(base.Resource):
resource_name = 'os-quota-class-sets'
def __init__(self, manager, data, Limit):
self.manager = manager
self._data = data
self._Limit = Limit
class quota_class_manager(base.ResourceManager):
resource_class = QuotaClass

View File

@ -19,6 +19,12 @@ from kingbirdclient.api import base
class Quota(base.Resource):
resource_name = 'os-quota-sets'
def __init__(self, manager, data, Limit, Usage=None):
self.manager = manager
self._data = data
self._Limit = Limit
self._Usage = Usage
class quota_manager(base.ResourceManager):
resource_class = Quota
@ -34,3 +40,33 @@ class quota_manager(base.ResourceManager):
target_tenant_id = tenant
url = '/%s/os-quota-sets/%s' % (tenant, target_tenant_id)
return self._list(url)
def update_global_limits(self, target_tenant_id, **kwargs):
if kwargs:
data = dict()
data['quota_set'] = {
k: int(v) for k, v in kwargs.items() if v is not None}
tenant = self.http_client.project_id
url = '/%s/os-quota-sets/%s' % (tenant, target_tenant_id)
return self._update(url, data)
def delete_quota(self, target_tenant_id, resources=None):
data = dict()
if resources:
resources = resources.split(',')
data["quota_set"] = str(resources)
tenant = self.http_client.project_id
url = '/%s/os-quota-sets/%s' % (tenant, target_tenant_id)
return self._delete(url, data)
def sync_quota(self, target_tenant_id):
tenant = self.http_client.project_id
url = '/%s/os-quota-sets/%s/sync' % (tenant, target_tenant_id)
return self._sync(url)
def quota_detail(self, target_tenant_id=None):
tenant = self.http_client.project_id
if not target_tenant_id:
target_tenant_id = tenant
url = '/%s/os-quota-sets/%s/detail' % (tenant, target_tenant_id)
return self._detail(url)

View File

@ -27,7 +27,7 @@ def format(quotas=None):
if quotas:
data = (
quotas._data,
quotas._values,
quotas._Limit,
)
else:

View File

@ -12,7 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from osc_lib.command import command
from kingbirdclient.commands.v1 import base
from kingbirdclient import exceptions
def format(quotas=None):
@ -24,7 +27,27 @@ def format(quotas=None):
if quotas:
data = (
quotas._data,
quotas._values,
quotas._Limit,
)
else:
data = (tuple('<none>' for _ in range(len(columns))),)
return columns, data
def detailformat(quotas=None):
columns = (
'Quota',
'Usage',
'Limit',
)
if quotas:
data = (
quotas._data,
quotas._Usage,
quotas._Limit,
)
else:
@ -65,3 +88,237 @@ class GlobalLimits(base.KingbirdLister):
kingbird_client = self.app.client_manager.sync_engine
target_tenant_id = parsed_args.tenant
return kingbird_client.quota_manager.global_limits(target_tenant_id)
class UpdateGlobalLimits(base.KingbirdLister):
"""Update the quotas for a tenant."""
def _get_format_function(self):
return format
def get_parser(self, parsed_args):
parser = super(UpdateGlobalLimits, self).get_parser(parsed_args)
parser.add_argument(
'tenant',
help='ID of tenant to set the quotas .'
)
parser.add_argument(
'--metadata_items',
help='New value for the "metadata-items" quota'
)
parser.add_argument(
'--subnet',
help='New value for the "subnet" quota'
)
parser.add_argument(
'--network',
help='New value for the "network" quota'
)
parser.add_argument(
'--floatingip',
help='New value for the "floatingip" quota'
)
parser.add_argument(
'--gigabytes',
help='New value for the "gigabytes" quota'
)
parser.add_argument(
'--backup_gigabytes',
help='New value for the "backup_gigabytes" quota'
)
parser.add_argument(
'--ram',
help='New value for the "ram" quota'
)
parser.add_argument(
'--floating_ips',
help='New value for the "floating_ips" quota'
)
parser.add_argument(
'--snapshots',
help='New value for the "snapshots" quota'
)
parser.add_argument(
'--security_group_rule',
help='New value for the "security_group_rule" quota'
)
parser.add_argument(
'--instances',
help='New value for the "instances" quota'
)
parser.add_argument(
'--key_pairs',
help='New value for the "key_pairs" quota'
)
parser.add_argument(
'--volumes',
help='New value for the "volumes" quota'
)
parser.add_argument(
'--router',
help='New value for the "router" quota'
)
parser.add_argument(
'--security_group',
help='New value for the "security_group" quota'
)
parser.add_argument(
'--cores',
help='New value for the "cores" quota'
)
parser.add_argument(
'--backups',
help='New value for the "backups" quota'
)
parser.add_argument(
'--fixed_ips',
help='New value for the "fixed_ips" quota'
)
parser.add_argument(
'--port',
help='New value for the "port" quota'
)
parser.add_argument(
'--security_groups',
help='New value for the "security_groups" quota'
)
return parser
def _get_resources(self, parsed_args):
target_tenant_id = parsed_args.tenant
kingbird_client = self.app.client_manager.sync_engine
kwargs = {
"metadata_items": parsed_args.metadata_items,
"subnet": parsed_args.subnet,
"network": parsed_args.network,
"floatingip": parsed_args.floatingip,
"gigabytes": parsed_args.gigabytes,
"backup_gigabytes": parsed_args.backup_gigabytes,
"ram": parsed_args.ram,
"floating_ips": parsed_args.floating_ips,
"snapshots": parsed_args.snapshots,
"security_group_rule": parsed_args.security_group_rule,
"instances": parsed_args.instances,
"key_pairs": parsed_args.key_pairs,
"volumes": parsed_args.volumes,
"router": parsed_args.router,
"security_group": parsed_args.security_group,
"cores": parsed_args.cores,
"backups": parsed_args.backups,
"fixed_ips": parsed_args.fixed_ips,
"port": parsed_args.port,
"security_groups": parsed_args.security_groups
}
return kingbird_client.quota_manager.\
update_global_limits(target_tenant_id, **kwargs)
class ShowQuotaDetail(base.KingbirdLister):
"""List the Detail limit for a tenant."""
def _get_format_function(self):
return detailformat
def get_parser(self, parsed_args):
parser = super(ShowQuotaDetail, self).get_parser(parsed_args)
parser.add_argument(
'--tenant',
help='Lists global limit of a specified tenant-id.'
' Admin tenant can perform this operation.'
)
return parser
def _get_resources(self, parsed_args):
kingbird_client = self.app.client_manager.sync_engine
target_tenant_id = parsed_args.tenant
return kingbird_client.quota_manager.quota_detail(target_tenant_id)
class SyncQuota(command.Command):
"""On Demand quota sync for a tenant."""
def get_parser(self, prog_name):
parser = super(SyncQuota, self).get_parser(prog_name)
parser.add_argument(
'tenant',
help='tenant-id to delete quotas.'
)
return parser
def take_action(self, parsed_args):
kingbird_client = self.app.client_manager.sync_engine
target_tenant = parsed_args.tenant
try:
kingbird_client.quota_manager.sync_quota(target_tenant)
print("Request to sync quota for tenant %s has been triggered." %
(parsed_args.tenant))
except Exception as e:
print(e)
error_msg = "Unable to sync quota for tenant %s." \
% (parsed_args.tenant)
raise exceptions.KingbirdClientException(error_msg)
class DeleteQuota(command.Command):
"""Delete quota for a tenant."""
def get_parser(self, prog_name):
parser = super(DeleteQuota, self).get_parser(prog_name)
parser.add_argument(
'tenant',
help='ID of tenant to delete quotas.'
)
parser.add_argument(
'--resources',
help='resources to which quotas have to be deleted.'
'--<cores,ram,etc...> comma seperated values(csv)'
)
return parser
def take_action(self, parsed_args):
kingbird_client = self.app.client_manager.sync_engine
target_tenant = parsed_args.tenant
resources = parsed_args.resources
try:
kingbird_client.quota_manager.\
delete_quota(target_tenant, resources)
if resources:
print("Request to delete %s for tenant %s has been accepted." %
(parsed_args.resources, parsed_args.tenant))
else:
print("Request to delete quotas"
" for tenant %s has been accepted." %
(parsed_args.tenant))
except Exception as e:
print(e)
error_msg = "Unable to delete quota for specified resource."
raise exceptions.KingbirdClientException(error_msg)

View File

@ -396,6 +396,10 @@ class KingbirdShell(app.App):
'bash-completion': BashCompletionCommand,
'quota defaults': qm.ListDefaults,
'quota show': qm.GlobalLimits,
'quota update': qm.UpdateGlobalLimits,
'quota detail': qm.ShowQuotaDetail,
'quota sync': qm.SyncQuota,
'quota delete': qm.DeleteQuota,
'quota-class show': qcm.ListQuotaClass,
'quota-class update': qcm.UpdateQuotaClass,
'quota-class delete': qcm.DeleteQuotaClass,

View File

@ -26,6 +26,18 @@ QUOTAS_DICT = {
QUOTAMANAGER = qm.Quota(mock, QUOTAS_DICT['Quota'],
QUOTAS_DICT['Limit'])
DETAIL_QUOTA_DICT = {
'Quota': 'fake_item',
'Usage': '123',
'Limit': '121'
}
DETAIL_QUOTAMANAGER = qm.Quota(mock, DETAIL_QUOTA_DICT['Quota'],
DETAIL_QUOTA_DICT['Usage'],
DETAIL_QUOTA_DICT['Limit'])
FAKE_TENANT = 'fake_tenant123'
class TestCLIQuotaManagerV1(base.BaseCommandTest):
@ -44,7 +56,49 @@ class TestCLIQuotaManagerV1(base.BaseCommandTest):
actual_quota = self.call(quota_cmd.GlobalLimits)
self.assertEqual([('fake_item', '123')], actual_quota[1])
def test_global_limits_with_tenant_id(self):
self.client.quota_manager.global_limits.return_value = [QUOTAMANAGER]
actual_quota = self.call(quota_cmd.GlobalLimits,
app_args=['--tenant', FAKE_TENANT])
self.assertEqual([('fake_item', '123')], actual_quota[1])
def test_negative_global_limits(self):
self.client.quota_manager.global_limits.return_value = []
actual_quota = self.call(quota_cmd.GlobalLimits)
self.assertEqual((('<none>', '<none>'),), actual_quota[1])
def test_update_global_limits(self):
self.client.quota_manager.\
update_global_limits.return_value = [QUOTAMANAGER]
actual_quota = self.call(quota_cmd.UpdateGlobalLimits,
app_args=[FAKE_TENANT, '--ram', '51200'])
self.assertEqual([('fake_item', '123')], actual_quota[1])
def test_negative_update_global_limits(self):
self.client.quota_manager.update_global_limits.return_value = []
actual_quota = self.call(quota_cmd.UpdateGlobalLimits,
app_args=[FAKE_TENANT, '--ram', '51200'])
self.assertEqual((('<none>', '<none>'),), actual_quota[1])
def test_delete_quota(self):
self.call(quota_cmd.DeleteQuota, app_args=[FAKE_TENANT])
self.client.quota_manager.delete_quota.\
assert_called_once_with(FAKE_TENANT, None)
def test_sync_quota(self):
self.call(quota_cmd.SyncQuota, app_args=[FAKE_TENANT])
self.client.quota_manager.sync_quota.\
assert_called_once_with(FAKE_TENANT)
def test_detail_quota_with_tenant_id(self):
self.client.quota_manager.\
quota_detail.return_value = [DETAIL_QUOTAMANAGER]
actual_quota = self.call(quota_cmd.ShowQuotaDetail,
app_args=['--tenant', FAKE_TENANT])
self.assertEqual([('fake_item', '121', '123')], actual_quota[1])
def test_detail_quota_without_tenant_id(self):
self.client.quota_manager.\
quota_detail.return_value = [DETAIL_QUOTAMANAGER]
actual_quota = self.call(quota_cmd.ShowQuotaDetail)
self.assertEqual([('fake_item', '121', '123')], actual_quota[1])