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:
parent
3333a3c6be
commit
af6959c68f
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -27,7 +27,7 @@ def format(quotas=None):
|
|||
if quotas:
|
||||
data = (
|
||||
quotas._data,
|
||||
quotas._values,
|
||||
quotas._Limit,
|
||||
)
|
||||
|
||||
else:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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])
|
||||
|
|
Loading…
Reference in New Issue