Merge "Add support for get details of Quota"
This commit is contained in:
commit
f992617f79
doc/source/cli/command-objects
openstackclient
common
tests
releasenotes/notes
@ -17,6 +17,8 @@ List quotas for all projects with non-default quota values
|
|||||||
|
|
||||||
openstack quota list
|
openstack quota list
|
||||||
--compute | --network | --volume
|
--compute | --network | --volume
|
||||||
|
[--project <project>]
|
||||||
|
[--detail]
|
||||||
|
|
||||||
.. option:: --network
|
.. option:: --network
|
||||||
|
|
||||||
@ -30,6 +32,14 @@ List quotas for all projects with non-default quota values
|
|||||||
|
|
||||||
List volume quotas
|
List volume quotas
|
||||||
|
|
||||||
|
.. option:: --project <project>
|
||||||
|
|
||||||
|
List quotas for this project <project> (name or ID)
|
||||||
|
|
||||||
|
.. option:: --detail
|
||||||
|
|
||||||
|
Show details about quotas usage
|
||||||
|
|
||||||
quota set
|
quota set
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -97,12 +97,164 @@ def _xform_get_quota(data, value, keys):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class ListQuota(command.Lister):
|
class BaseQuota(object):
|
||||||
_description = _("List quotas for all projects "
|
def _get_project(self, parsed_args):
|
||||||
"with non-default quota values")
|
if parsed_args.project is not None:
|
||||||
|
identity_client = self.app.client_manager.identity
|
||||||
|
project = utils.find_resource(
|
||||||
|
identity_client.projects,
|
||||||
|
parsed_args.project,
|
||||||
|
)
|
||||||
|
project_id = project.id
|
||||||
|
project_name = project.name
|
||||||
|
elif self.app.client_manager.auth_ref:
|
||||||
|
# Get the project from the current auth
|
||||||
|
project = self.app.client_manager.auth_ref
|
||||||
|
project_id = project.project_id
|
||||||
|
project_name = project.project_name
|
||||||
|
else:
|
||||||
|
project = None
|
||||||
|
project_id = None
|
||||||
|
project_name = None
|
||||||
|
project_info = {}
|
||||||
|
project_info['id'] = project_id
|
||||||
|
project_info['name'] = project_name
|
||||||
|
return project_info
|
||||||
|
|
||||||
|
def get_compute_quota(self, client, parsed_args):
|
||||||
|
quota_class = (
|
||||||
|
parsed_args.quota_class if 'quota_class' in parsed_args else False)
|
||||||
|
detail = parsed_args.detail if 'detail' in parsed_args else False
|
||||||
|
default = parsed_args.default if 'default' in parsed_args else False
|
||||||
|
try:
|
||||||
|
if quota_class:
|
||||||
|
quota = client.quota_classes.get(parsed_args.project)
|
||||||
|
else:
|
||||||
|
project_info = self._get_project(parsed_args)
|
||||||
|
project = project_info['id']
|
||||||
|
if default:
|
||||||
|
quota = client.quotas.defaults(project)
|
||||||
|
else:
|
||||||
|
quota = client.quotas.get(project, detail=detail)
|
||||||
|
except Exception as e:
|
||||||
|
if type(e).__name__ == 'EndpointNotFound':
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return quota._info
|
||||||
|
|
||||||
|
def get_volume_quota(self, client, parsed_args):
|
||||||
|
quota_class = (
|
||||||
|
parsed_args.quota_class if 'quota_class' in parsed_args else False)
|
||||||
|
default = parsed_args.default if 'default' in parsed_args else False
|
||||||
|
try:
|
||||||
|
if quota_class:
|
||||||
|
quota = client.quota_classes.get(parsed_args.project)
|
||||||
|
else:
|
||||||
|
project_info = self._get_project(parsed_args)
|
||||||
|
project = project_info['id']
|
||||||
|
if default:
|
||||||
|
quota = client.quotas.defaults(project)
|
||||||
|
else:
|
||||||
|
quota = client.quotas.get(project)
|
||||||
|
except Exception as e:
|
||||||
|
if type(e).__name__ == 'EndpointNotFound':
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return quota._info
|
||||||
|
|
||||||
|
def get_network_quota(self, parsed_args):
|
||||||
|
quota_class = (
|
||||||
|
parsed_args.quota_class if 'quota_class' in parsed_args else False)
|
||||||
|
detail = parsed_args.detail if 'detail' in parsed_args else False
|
||||||
|
default = parsed_args.default if 'default' in parsed_args else False
|
||||||
|
if quota_class:
|
||||||
|
return {}
|
||||||
|
if self.app.client_manager.is_network_endpoint_enabled():
|
||||||
|
project_info = self._get_project(parsed_args)
|
||||||
|
project = project_info['id']
|
||||||
|
client = self.app.client_manager.network
|
||||||
|
if default:
|
||||||
|
network_quota = client.get_quota_default(project)
|
||||||
|
if type(network_quota) is not dict:
|
||||||
|
network_quota = network_quota.to_dict()
|
||||||
|
else:
|
||||||
|
network_quota = client.get_quota(project,
|
||||||
|
details=detail)
|
||||||
|
if type(network_quota) is not dict:
|
||||||
|
network_quota = network_quota.to_dict()
|
||||||
|
if detail:
|
||||||
|
# NOTE(slaweq): Neutron returns values with key "used" but
|
||||||
|
# Nova for example returns same data with key "in_use"
|
||||||
|
# instead.
|
||||||
|
# Because of that we need to convert Neutron key to
|
||||||
|
# the same as is returned from Nova to make result
|
||||||
|
# more consistent
|
||||||
|
for key, values in network_quota.items():
|
||||||
|
if type(values) is dict and "used" in values:
|
||||||
|
values[u'in_use'] = values.pop("used")
|
||||||
|
network_quota[key] = values
|
||||||
|
return network_quota
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class ListQuota(command.Lister, BaseQuota):
|
||||||
|
_description = _(
|
||||||
|
"List quotas for all projects with non-default quota values or "
|
||||||
|
"list detailed quota informations for requested project")
|
||||||
|
|
||||||
|
def _get_detailed_quotas(self, parsed_args):
|
||||||
|
columns = (
|
||||||
|
'resource',
|
||||||
|
'in_use',
|
||||||
|
'reserved',
|
||||||
|
'limit'
|
||||||
|
)
|
||||||
|
column_headers = (
|
||||||
|
'Resource',
|
||||||
|
'In Use',
|
||||||
|
'Reserved',
|
||||||
|
'Limit'
|
||||||
|
)
|
||||||
|
quotas = {}
|
||||||
|
if parsed_args.compute:
|
||||||
|
quotas.update(self.get_compute_quota(
|
||||||
|
self.app.client_manager.compute, parsed_args))
|
||||||
|
if parsed_args.network:
|
||||||
|
quotas.update(self.get_network_quota(parsed_args))
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for resource, values in quotas.items():
|
||||||
|
# NOTE(slaweq): there is no detailed quotas info for some resources
|
||||||
|
# and it should't be displayed here
|
||||||
|
if type(values) is dict:
|
||||||
|
result.append({
|
||||||
|
'resource': resource,
|
||||||
|
'in_use': values.get('in_use'),
|
||||||
|
'reserved': values.get('reserved'),
|
||||||
|
'limit': values.get('limit')
|
||||||
|
})
|
||||||
|
return (column_headers,
|
||||||
|
(utils.get_dict_properties(
|
||||||
|
s, columns,
|
||||||
|
) for s in result))
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super(ListQuota, self).get_parser(prog_name)
|
parser = super(ListQuota, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'--project',
|
||||||
|
metavar='<project>',
|
||||||
|
help=_('List quotas for this project <project> (name or ID)'),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--detail',
|
||||||
|
dest='detail',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Show details about quotas usage')
|
||||||
|
)
|
||||||
option = parser.add_mutually_exclusive_group(required=True)
|
option = parser.add_mutually_exclusive_group(required=True)
|
||||||
option.add_argument(
|
option.add_argument(
|
||||||
'--compute',
|
'--compute',
|
||||||
@ -130,6 +282,8 @@ class ListQuota(command.Lister):
|
|||||||
project_ids = [getattr(p, 'id', '') for p in projects]
|
project_ids = [getattr(p, 'id', '') for p in projects]
|
||||||
|
|
||||||
if parsed_args.compute:
|
if parsed_args.compute:
|
||||||
|
if parsed_args.detail:
|
||||||
|
return self._get_detailed_quotas(parsed_args)
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for p in project_ids:
|
for p in project_ids:
|
||||||
try:
|
try:
|
||||||
@ -193,6 +347,9 @@ class ListQuota(command.Lister):
|
|||||||
) for s in result))
|
) for s in result))
|
||||||
|
|
||||||
if parsed_args.volume:
|
if parsed_args.volume:
|
||||||
|
if parsed_args.detail:
|
||||||
|
LOG.warning("Volume service doesn't provide detailed quota"
|
||||||
|
" information")
|
||||||
volume_client = self.app.client_manager.volume
|
volume_client = self.app.client_manager.volume
|
||||||
for p in project_ids:
|
for p in project_ids:
|
||||||
try:
|
try:
|
||||||
@ -243,6 +400,8 @@ class ListQuota(command.Lister):
|
|||||||
) for s in result))
|
) for s in result))
|
||||||
|
|
||||||
if parsed_args.network:
|
if parsed_args.network:
|
||||||
|
if parsed_args.detail:
|
||||||
|
return self._get_detailed_quotas(parsed_args)
|
||||||
client = self.app.client_manager.network
|
client = self.app.client_manager.network
|
||||||
for p in project_ids:
|
for p in project_ids:
|
||||||
try:
|
try:
|
||||||
@ -410,7 +569,7 @@ class SetQuota(command.Command):
|
|||||||
**network_kwargs)
|
**network_kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ShowQuota(command.ShowOne):
|
class ShowQuota(command.ShowOne, BaseQuota):
|
||||||
_description = _("Show quotas for project or class")
|
_description = _("Show quotas for project or class")
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
@ -438,62 +597,6 @@ class ShowQuota(command.ShowOne):
|
|||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def _get_project(self, parsed_args):
|
|
||||||
if parsed_args.project is not None:
|
|
||||||
identity_client = self.app.client_manager.identity
|
|
||||||
project = utils.find_resource(
|
|
||||||
identity_client.projects,
|
|
||||||
parsed_args.project,
|
|
||||||
)
|
|
||||||
project_id = project.id
|
|
||||||
project_name = project.name
|
|
||||||
elif self.app.client_manager.auth_ref:
|
|
||||||
# Get the project from the current auth
|
|
||||||
project = self.app.client_manager.auth_ref
|
|
||||||
project_id = project.project_id
|
|
||||||
project_name = project.project_name
|
|
||||||
else:
|
|
||||||
project = None
|
|
||||||
project_id = None
|
|
||||||
project_name = None
|
|
||||||
project_info = {}
|
|
||||||
project_info['id'] = project_id
|
|
||||||
project_info['name'] = project_name
|
|
||||||
return project_info
|
|
||||||
|
|
||||||
def get_compute_volume_quota(self, client, parsed_args):
|
|
||||||
try:
|
|
||||||
if parsed_args.quota_class:
|
|
||||||
quota = client.quota_classes.get(parsed_args.project)
|
|
||||||
else:
|
|
||||||
project_info = self._get_project(parsed_args)
|
|
||||||
project = project_info['id']
|
|
||||||
if parsed_args.default:
|
|
||||||
quota = client.quotas.defaults(project)
|
|
||||||
else:
|
|
||||||
quota = client.quotas.get(project)
|
|
||||||
except Exception as e:
|
|
||||||
if type(e).__name__ == 'EndpointNotFound':
|
|
||||||
return {}
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
return quota._info
|
|
||||||
|
|
||||||
def get_network_quota(self, parsed_args):
|
|
||||||
if parsed_args.quota_class:
|
|
||||||
return {}
|
|
||||||
if self.app.client_manager.is_network_endpoint_enabled():
|
|
||||||
project_info = self._get_project(parsed_args)
|
|
||||||
project = project_info['id']
|
|
||||||
client = self.app.client_manager.network
|
|
||||||
if parsed_args.default:
|
|
||||||
network_quota = client.get_quota_default(project)
|
|
||||||
else:
|
|
||||||
network_quota = client.get_quota(project)
|
|
||||||
return network_quota
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.compute
|
||||||
@ -504,9 +607,9 @@ class ShowQuota(command.ShowOne):
|
|||||||
# does not exist. If this is determined to be the
|
# does not exist. If this is determined to be the
|
||||||
# intended behaviour of the API we will validate
|
# intended behaviour of the API we will validate
|
||||||
# the argument with Identity ourselves later.
|
# the argument with Identity ourselves later.
|
||||||
compute_quota_info = self.get_compute_volume_quota(compute_client,
|
compute_quota_info = self.get_compute_quota(compute_client,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
volume_quota_info = self.get_compute_volume_quota(volume_client,
|
volume_quota_info = self.get_volume_quota(volume_client,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
network_quota_info = self.get_network_quota(parsed_args)
|
network_quota_info = self.get_network_quota(parsed_args)
|
||||||
# NOTE(reedip): Remove the below check once requirement for
|
# NOTE(reedip): Remove the below check once requirement for
|
||||||
|
@ -31,6 +31,38 @@ class QuotaTests(base.TestCase):
|
|||||||
cls.PROJECT_NAME =\
|
cls.PROJECT_NAME =\
|
||||||
cls.get_openstack_configuration_value('auth.project_name')
|
cls.get_openstack_configuration_value('auth.project_name')
|
||||||
|
|
||||||
|
def test_quota_list_details_compute(self):
|
||||||
|
expected_headers = ["Resource", "In Use", "Reserved", "Limit"]
|
||||||
|
cmd_output = json.loads(self.openstack(
|
||||||
|
'quota list -f json --detail --compute'
|
||||||
|
))
|
||||||
|
self.assertIsNotNone(cmd_output)
|
||||||
|
resources = []
|
||||||
|
for row in cmd_output:
|
||||||
|
row_headers = [str(r) for r in row.keys()]
|
||||||
|
self.assertEqual(sorted(expected_headers), sorted(row_headers))
|
||||||
|
resources.append(row['Resource'])
|
||||||
|
# Ensure that returned quota is compute quota
|
||||||
|
self.assertIn("instances", resources)
|
||||||
|
# and that there is no network quota here
|
||||||
|
self.assertNotIn("networks", resources)
|
||||||
|
|
||||||
|
def test_quota_list_details_network(self):
|
||||||
|
expected_headers = ["Resource", "In Use", "Reserved", "Limit"]
|
||||||
|
cmd_output = json.loads(self.openstack(
|
||||||
|
'quota list -f json --detail --network'
|
||||||
|
))
|
||||||
|
self.assertIsNotNone(cmd_output)
|
||||||
|
resources = []
|
||||||
|
for row in cmd_output:
|
||||||
|
row_headers = [str(r) for r in row.keys()]
|
||||||
|
self.assertEqual(sorted(expected_headers), sorted(row_headers))
|
||||||
|
resources.append(row['Resource'])
|
||||||
|
# Ensure that returned quota is network quota
|
||||||
|
self.assertIn("networks", resources)
|
||||||
|
# and that there is no compute quota here
|
||||||
|
self.assertNotIn("instances", resources)
|
||||||
|
|
||||||
def test_quota_list_network_option(self):
|
def test_quota_list_network_option(self):
|
||||||
if not self.haz_network:
|
if not self.haz_network:
|
||||||
self.skipTest("No Network service present")
|
self.skipTest("No Network service present")
|
||||||
|
@ -197,6 +197,85 @@ class TestQuotaList(TestQuota):
|
|||||||
|
|
||||||
self.cmd = quota.ListQuota(self.app, None)
|
self.cmd = quota.ListQuota(self.app, None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_detailed_reference_data(quota):
|
||||||
|
reference_data = []
|
||||||
|
for name, values in quota.to_dict().items():
|
||||||
|
if type(values) is dict:
|
||||||
|
if 'used' in values:
|
||||||
|
# For network quota it's "used" key instead of "in_use"
|
||||||
|
in_use = values['used']
|
||||||
|
else:
|
||||||
|
in_use = values['in_use']
|
||||||
|
resource_values = [
|
||||||
|
in_use,
|
||||||
|
values['reserved'],
|
||||||
|
values['limit']]
|
||||||
|
reference_data.append(tuple([name] + resource_values))
|
||||||
|
return reference_data
|
||||||
|
|
||||||
|
def test_quota_list_details_compute(self):
|
||||||
|
detailed_quota = (
|
||||||
|
compute_fakes.FakeQuota.create_one_comp_detailed_quota())
|
||||||
|
|
||||||
|
detailed_column_header = (
|
||||||
|
'Resource',
|
||||||
|
'In Use',
|
||||||
|
'Reserved',
|
||||||
|
'Limit',
|
||||||
|
)
|
||||||
|
detailed_reference_data = (
|
||||||
|
self._get_detailed_reference_data(detailed_quota))
|
||||||
|
|
||||||
|
self.compute.quotas.get = mock.Mock(return_value=detailed_quota)
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--detail', '--compute',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('detail', True),
|
||||||
|
('compute', True),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
ret_quotas = list(data)
|
||||||
|
|
||||||
|
self.assertEqual(detailed_column_header, columns)
|
||||||
|
self.assertEqual(
|
||||||
|
sorted(detailed_reference_data), sorted(ret_quotas))
|
||||||
|
|
||||||
|
def test_quota_list_details_network(self):
|
||||||
|
detailed_quota = (
|
||||||
|
network_fakes.FakeQuota.create_one_net_detailed_quota())
|
||||||
|
|
||||||
|
detailed_column_header = (
|
||||||
|
'Resource',
|
||||||
|
'In Use',
|
||||||
|
'Reserved',
|
||||||
|
'Limit',
|
||||||
|
)
|
||||||
|
detailed_reference_data = (
|
||||||
|
self._get_detailed_reference_data(detailed_quota))
|
||||||
|
|
||||||
|
self.network.get_quota = mock.Mock(return_value=detailed_quota)
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--detail', '--network',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('detail', True),
|
||||||
|
('network', True),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
ret_quotas = list(data)
|
||||||
|
|
||||||
|
self.assertEqual(detailed_column_header, columns)
|
||||||
|
self.assertEqual(
|
||||||
|
sorted(detailed_reference_data), sorted(ret_quotas))
|
||||||
|
|
||||||
def test_quota_list_compute(self):
|
def test_quota_list_compute(self):
|
||||||
# Two projects with non-default quotas
|
# Two projects with non-default quotas
|
||||||
self.compute.quotas.get = mock.Mock(
|
self.compute.quotas.get = mock.Mock(
|
||||||
@ -827,13 +906,13 @@ class TestQuotaShow(TestQuota):
|
|||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.compute_quotas_mock.get.assert_called_once_with(
|
self.compute_quotas_mock.get.assert_called_once_with(
|
||||||
self.projects[0].id,
|
self.projects[0].id, detail=False
|
||||||
)
|
)
|
||||||
self.volume_quotas_mock.get.assert_called_once_with(
|
self.volume_quotas_mock.get.assert_called_once_with(
|
||||||
self.projects[0].id,
|
self.projects[0].id,
|
||||||
)
|
)
|
||||||
self.network.get_quota.assert_called_once_with(
|
self.network.get_quota.assert_called_once_with(
|
||||||
self.projects[0].id,
|
self.projects[0].id, details=False
|
||||||
)
|
)
|
||||||
self.assertNotCalled(self.network.get_quota_default)
|
self.assertNotCalled(self.network.get_quota_default)
|
||||||
|
|
||||||
@ -889,12 +968,12 @@ class TestQuotaShow(TestQuota):
|
|||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.compute_quotas_mock.get.assert_called_once_with(
|
self.compute_quotas_mock.get.assert_called_once_with(
|
||||||
identity_fakes.project_id,
|
identity_fakes.project_id, detail=False
|
||||||
)
|
)
|
||||||
self.volume_quotas_mock.get.assert_called_once_with(
|
self.volume_quotas_mock.get.assert_called_once_with(
|
||||||
identity_fakes.project_id,
|
identity_fakes.project_id,
|
||||||
)
|
)
|
||||||
self.network.get_quota.assert_called_once_with(
|
self.network.get_quota.assert_called_once_with(
|
||||||
identity_fakes.project_id,
|
identity_fakes.project_id, details=False
|
||||||
)
|
)
|
||||||
self.assertNotCalled(self.network.get_quota_default)
|
self.assertNotCalled(self.network.get_quota_default)
|
||||||
|
@ -1401,6 +1401,38 @@ class FakeQuota(object):
|
|||||||
|
|
||||||
return quota
|
return quota
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_one_comp_detailed_quota(attrs=None):
|
||||||
|
"""Create one quota"""
|
||||||
|
|
||||||
|
attrs = attrs or {}
|
||||||
|
|
||||||
|
quota_attrs = {
|
||||||
|
'id': 'project-id-' + uuid.uuid4().hex,
|
||||||
|
'cores': {'reserved': 0, 'in_use': 0, 'limit': 20},
|
||||||
|
'fixed_ips': {'reserved': 0, 'in_use': 0, 'limit': 30},
|
||||||
|
'injected_files': {'reserved': 0, 'in_use': 0, 'limit': 100},
|
||||||
|
'injected_file_content_bytes': {
|
||||||
|
'reserved': 0, 'in_use': 0, 'limit': 10240},
|
||||||
|
'injected_file_path_bytes': {
|
||||||
|
'reserved': 0, 'in_use': 0, 'limit': 255},
|
||||||
|
'instances': {'reserved': 0, 'in_use': 0, 'limit': 50},
|
||||||
|
'key_pairs': {'reserved': 0, 'in_use': 0, 'limit': 20},
|
||||||
|
'metadata_items': {'reserved': 0, 'in_use': 0, 'limit': 10},
|
||||||
|
'ram': {'reserved': 0, 'in_use': 0, 'limit': 51200},
|
||||||
|
'server_groups': {'reserved': 0, 'in_use': 0, 'limit': 10},
|
||||||
|
'server_group_members': {'reserved': 0, 'in_use': 0, 'limit': 10}
|
||||||
|
}
|
||||||
|
|
||||||
|
quota_attrs.update(attrs)
|
||||||
|
quota = fakes.FakeResource(
|
||||||
|
info=copy.deepcopy(quota_attrs),
|
||||||
|
loaded=True)
|
||||||
|
|
||||||
|
quota.project_id = quota_attrs['id']
|
||||||
|
|
||||||
|
return quota
|
||||||
|
|
||||||
|
|
||||||
class FakeLimits(object):
|
class FakeLimits(object):
|
||||||
"""Fake limits"""
|
"""Fake limits"""
|
||||||
|
@ -1700,3 +1700,26 @@ class FakeQuota(object):
|
|||||||
info=copy.deepcopy(quota_attrs),
|
info=copy.deepcopy(quota_attrs),
|
||||||
loaded=True)
|
loaded=True)
|
||||||
return quota
|
return quota
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_one_net_detailed_quota(attrs=None):
|
||||||
|
"""Create one quota"""
|
||||||
|
attrs = attrs or {}
|
||||||
|
|
||||||
|
quota_attrs = {
|
||||||
|
'floating_ips': {'used': 0, 'reserved': 0, 'limit': 20},
|
||||||
|
'networks': {'used': 0, 'reserved': 0, 'limit': 25},
|
||||||
|
'ports': {'used': 0, 'reserved': 0, 'limit': 11},
|
||||||
|
'rbac_policies': {'used': 0, 'reserved': 0, 'limit': 15},
|
||||||
|
'routers': {'used': 0, 'reserved': 0, 'limit': 40},
|
||||||
|
'security_groups': {'used': 0, 'reserved': 0, 'limit': 10},
|
||||||
|
'security_group_rules': {'used': 0, 'reserved': 0, 'limit': 100},
|
||||||
|
'subnets': {'used': 0, 'reserved': 0, 'limit': 20},
|
||||||
|
'subnet_pools': {'used': 0, 'reserved': 0, 'limit': 30}}
|
||||||
|
|
||||||
|
quota_attrs.update(attrs)
|
||||||
|
|
||||||
|
quota = fakes.FakeResource(
|
||||||
|
info=copy.deepcopy(quota_attrs),
|
||||||
|
loaded=True)
|
||||||
|
return quota
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add support for list detailed ``quota`` usage for project.
|
||||||
|
This can be done by passing ``--detail`` parameter to `quota list` command.
|
||||||
|
[Bug `1716043 <https://bugs.launchpad.net/neutron/+bug/1716043>`_]
|
Loading…
x
Reference in New Issue
Block a user