OSC Quota List

Implement Neutron feature of Quota List into
OpenStack Client.

Change-Id: Idf941acf8d00b136776b7381b877c56d82622f57
Partially-Implements: blueprint neutron-client-quota
This commit is contained in:
Sindhu Devale 2016-09-29 15:32:51 -05:00
parent 35c308ebad
commit 58591d3c37
9 changed files with 547 additions and 0 deletions

View File

@ -7,6 +7,29 @@ single object with multiple properties.
Block Storage v1, v2, Compute v2, Network v2
quota list
----------
List quotas for all projects with non-default quota values
.. program:: quota list
.. code:: bash
openstack quota list
--compute | --network | --volume
.. option:: --network
List network quotas
.. option:: --compute
List compute quotas
.. option:: --volume
List volume quotas
quota set
---------

View File

@ -16,6 +16,7 @@
"""Quota action implementations"""
import itertools
import logging
import sys
from osc_lib.command import command
@ -25,6 +26,8 @@ import six
from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
# List the quota items, map the internal argument name to the option
# name that the user sees.
@ -78,6 +81,176 @@ NETWORK_QUOTAS = {
'l7policy': 'l7policies',
}
NETWORK_KEYS = ['floating_ips', 'networks', 'rbac_policies', 'routers',
'ports', 'security_group_rules', 'security_groups',
'subnet_pools', 'subnets']
def _xform_get_quota(data, value, keys):
res = []
res_info = {}
for key in keys:
res_info[key] = getattr(data, key, '')
res_info['id'] = value
res.append(res_info)
return res
class ListQuota(command.Lister):
_description = _("List quotas for all projects "
"with non-default quota values")
def get_parser(self, prog_name):
parser = super(ListQuota, self).get_parser(prog_name)
option = parser.add_mutually_exclusive_group(required=True)
option.add_argument(
'--compute',
action='store_true',
default=False,
help=_('List compute quota'),
)
option.add_argument(
'--volume',
action='store_true',
default=False,
help=_('List volume quota'),
)
option.add_argument(
'--network',
action='store_true',
default=False,
help=_('List network quota'),
)
return parser
def take_action(self, parsed_args):
projects = self.app.client_manager.identity.projects.list()
result = []
project_ids = [getattr(p, 'id', '') for p in projects]
if parsed_args.compute:
compute_client = self.app.client_manager.compute
for p in project_ids:
data = compute_client.quotas.get(p)
result_data = _xform_get_quota(data, p,
COMPUTE_QUOTAS.keys())
default_data = compute_client.quotas.defaults(p)
result_default = _xform_get_quota(default_data,
p,
COMPUTE_QUOTAS.keys())
if result_default != result_data:
result += result_data
columns = (
'id',
'cores',
'fixed_ips',
'injected_files',
'injected_file_content_bytes',
'injected_file_path_bytes',
'instances',
'key_pairs',
'metadata_items',
'ram',
'server_groups',
'server_group_members',
)
column_headers = (
'Project ID',
'Cores',
'Fixed IPs',
'Injected Files',
'Injected File Content Bytes',
'Injected File Path Bytes',
'Instances',
'Key Pairs',
'Metadata Items',
'Ram',
'Server Groups',
'Server Group Members',
)
return (column_headers,
(utils.get_dict_properties(
s, columns,
) for s in result))
if parsed_args.volume:
volume_client = self.app.client_manager.volume
for p in project_ids:
data = volume_client.quotas.get(p)
result_data = _xform_get_quota(data, p,
VOLUME_QUOTAS.keys())
default_data = volume_client.quotas.defaults(p)
result_default = _xform_get_quota(default_data,
p,
VOLUME_QUOTAS.keys())
if result_default != result_data:
result += result_data
columns = (
'id',
'backups',
'backup_gigabytes',
'gigabytes',
'per_volume_gigabytes',
'snapshots',
'volumes',
)
column_headers = (
'Project ID',
'Backups',
'Backup Gigabytes',
'Gigabytes',
'Per Volume Gigabytes',
'Snapshots',
'Volumes',
)
return (column_headers,
(utils.get_dict_properties(
s, columns,
) for s in result))
if parsed_args.network:
client = self.app.client_manager.network
for p in project_ids:
data = client.get_quota(p)
result_data = _xform_get_quota(data, p, NETWORK_KEYS)
default_data = client.get_quota_default(p)
result_default = _xform_get_quota(default_data,
p, NETWORK_KEYS)
if result_default != result_data:
result += result_data
columns = (
'id',
'floating_ips',
'networks',
'ports',
'rbac_policies',
'routers',
'security_groups',
'security_group_rules',
'subnets',
'subnet_pools',
)
column_headers = (
'Project ID',
'Floating IPs',
'Networks',
'Ports',
'RBAC Policies',
'Routers',
'Security Groups',
'Security Group Rules',
'Subnets',
'Subnet Pools'
)
return (column_headers,
(utils.get_dict_properties(
s, columns,
) for s in result))
return ((), ())
class SetQuota(command.Command):
_description = _("Set quotas for project or class")

View File

@ -25,6 +25,27 @@ class QuotaTests(base.TestCase):
cls.PROJECT_NAME =\
cls.get_openstack_configuration_value('auth.project_name')
def test_quota_list_network_option(self):
self.openstack('quota set --networks 40 ' +
self.PROJECT_NAME)
raw_output = self.openstack('quota list --network')
self.assertIsNotNone(raw_output)
self.assertIn("40", raw_output)
def test_quota_list_compute_option(self):
self.openstack('quota set --instances 40 ' +
self.PROJECT_NAME)
raw_output = self.openstack('quota list --compute')
self.assertIsNotNone(raw_output)
self.assertIn("40", raw_output)
def test_quota_list_volume_option(self):
self.openstack('quota set --backups 40 ' +
self.PROJECT_NAME)
raw_output = self.openstack('quota list --volume')
self.assertIsNotNone(raw_output)
self.assertIn("40", raw_output)
def test_quota_set(self):
self.openstack('quota set --instances 11 --volumes 11 --networks 11 ' +
self.PROJECT_NAME)

View File

@ -19,6 +19,7 @@ from openstackclient.common import quota
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
@ -530,3 +531,161 @@ class TestQuotaShow(TestQuota):
self.network.get_quota.assert_called_once_with(
identity_fakes.project_id)
self.assertNotCalled(self.network.get_quota_default)
class TestQuotaList(TestQuota):
"""Test cases for quota list command"""
project = identity_fakes_v3.FakeProject.create_one_project()
quota_list = network_fakes.FakeQuota.create_one_net_quota()
quota_list1 = compute_fakes.FakeQuota.create_one_comp_quota()
quota_list2 = volume_fakes.FakeQuota.create_one_vol_quota()
default_quota = network_fakes.FakeQuota.create_one_default_net_quota()
default_quota1 = compute_fakes.FakeQuota.create_one_default_comp_quota()
default_quota2 = volume_fakes.FakeQuota.create_one_default_vol_quota()
reference_data = (project.id,
quota_list.floating_ips,
quota_list.networks,
quota_list.ports,
quota_list.rbac_policies,
quota_list.routers,
quota_list.security_groups,
quota_list.security_group_rules,
quota_list.subnets,
quota_list.subnet_pools)
comp_reference_data = (project.id,
quota_list1.cores,
quota_list1.fixed_ips,
quota_list1.injected_files,
quota_list1.injected_file_content_bytes,
quota_list1.injected_file_path_bytes,
quota_list1.instances,
quota_list1.key_pairs,
quota_list1.metadata_items,
quota_list1.ram,
quota_list1.server_groups,
quota_list1.server_group_members)
vol_reference_data = (project.id,
quota_list2.backups,
quota_list2.backup_gigabytes,
quota_list2.gigabytes,
quota_list2.per_volume_gigabytes,
quota_list2.snapshots,
quota_list2.volumes)
net_column_header = (
'Project ID',
'Floating IPs',
'Networks',
'Ports',
'RBAC Policies',
'Routers',
'Security Groups',
'Security Group Rules',
'Subnets',
'Subnet Pools'
)
comp_column_header = (
'Project ID',
'Cores',
'Fixed IPs',
'Injected Files',
'Injected File Content Bytes',
'Injected File Path Bytes',
'Instances',
'Key Pairs',
'Metadata Items',
'Ram',
'Server Groups',
'Server Group Members',
)
vol_column_header = (
'Project ID',
'Backups',
'Backup Gigabytes',
'Gigabytes',
'Per Volume Gigabytes',
'Snapshots',
'Volumes',
)
def setUp(self):
super(TestQuotaList, self).setUp()
self.projects_mock.get.return_value = fakes.FakeResource(
None,
copy.deepcopy(identity_fakes.PROJECT),
loaded=True,
)
self.identity = self.app.client_manager.identity
self.identity.tenants.list = mock.Mock(return_value=[self.project])
self.network = self.app.client_manager.network
self.compute = self.app.client_manager.compute
self.volume = self.app.client_manager.volume
self.network.get_quota = mock.Mock(return_value=self.quota_list)
self.compute.quotas.get = mock.Mock(return_value=self.quota_list1)
self.volume.quotas.get = mock.Mock(return_value=self.quota_list2)
self.network.get_quota_default = mock.Mock(
return_value=self.default_quota)
self.compute.quotas.defaults = mock.Mock(
return_value=self.default_quota1)
self.volume.quotas.defaults = mock.Mock(
return_value=self.default_quota2)
self.cmd = quota.ListQuota(self.app, None)
def test_quota_list_network(self):
arglist = [
'--network'
]
verifylist = [
('network', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.net_column_header, columns)
self.assertEqual(self.reference_data, list(data)[0])
def test_quota_list_compute(self):
arglist = [
'--compute'
]
verifylist = [
('compute', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.comp_column_header, columns)
self.assertEqual(self.comp_reference_data, list(data)[0])
def test_quota_list_volume(self):
arglist = [
'--volume'
]
verifylist = [
('volume', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.vol_column_header, columns)
self.assertEqual(self.vol_reference_data, list(data)[0])

View File

@ -1313,3 +1313,67 @@ class FakeUsage(object):
usages.append(FakeUsage.create_one_usage(attrs))
return usages
class FakeQuota(object):
"""Fake quota"""
@staticmethod
def create_one_comp_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'cores': 20,
'fixed_ips': 30,
'injected_files': 100,
'injected_file_content_bytes': 10240,
'injected_file_path_bytes': 255,
'instances': 50,
'key_pairs': 20,
'metadata_items': 10,
'ram': 51200,
'server_groups': 10,
'server_group_members': 10
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs),
loaded=True)
quota.project_id = quota_attrs['id']
return quota
@staticmethod
def create_one_default_comp_quota(attrs=None):
"""Crate one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'cores': 10,
'fixed_ips': 10,
'injected_files': 100,
'injected_file_content_bytes': 10240,
'injected_file_path_bytes': 255,
'instances': 20,
'key_pairs': 20,
'metadata_items': 10,
'ram': 51200,
'server_groups': 10,
'server_group_members': 10
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs),
loaded=True)
quota.project_id = quota_attrs['id']
return quota

View File

@ -1494,3 +1494,53 @@ class FakeNetworkServiceProvider(object):
create_one_network_service_provider(
attrs))
return service_providers
class FakeQuota(object):
"""Fake quota"""
@staticmethod
def create_one_net_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'floating_ips': 20,
'networks': 25,
'ports': 11,
'rbac_policies': 15,
'routers': 40,
'security_groups': 10,
'security_group_rules': 100,
'subnets': 20,
'subnet_pools': 30}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs),
loaded=True)
return quota
@staticmethod
def create_one_default_net_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'floatingip': 30,
'network': 20,
'port': 10,
'rbac_policy': 25,
'router': 30,
'security_group': 30,
'security_group_rule': 200,
'subnet': 10,
'subnetpool': 20}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs),
loaded=True)
return quota

View File

@ -954,3 +954,53 @@ class FakeType(object):
info=copy.deepcopy(encryption_info),
loaded=True)
return encryption_type
class FakeQuota(object):
"""Fake quota"""
@staticmethod
def create_one_vol_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'backups': 100,
'backup_gigabytes': 100,
'gigabytes': 10,
'per_volume_gigabytes': 10,
'snapshots': 0,
'volumes': 10}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs),
loaded=True)
quota.project_id = quota_attrs['id']
return quota
@staticmethod
def create_one_default_vol_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'backups': 100,
'backup_gigabytes': 100,
'gigabytes': 100,
'per_volume_gigabytes': 100,
'snapshots': 100,
'volumes': 100}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs),
loaded=True)
quota.project_id = quota_attrs['id']
return quota

View File

@ -0,0 +1,6 @@
---
features:
- |
Add ``quota list`` command with ``--compute``, ``--volume``
and ``--network`` options.
[Blueprint `quota-list <https://blueprints.launchpad.net/python-openstackclient/+spec/neutron-client-quota>`_]

View File

@ -47,6 +47,7 @@ openstack.common =
configuration_show = openstackclient.common.configuration:ShowConfiguration
extension_list = openstackclient.common.extension:ListExtension
limits_show = openstackclient.common.limits:ShowLimits
quota_list = openstackclient.common.quota:ListQuota
quota_set = openstackclient.common.quota:SetQuota
quota_show = openstackclient.common.quota:ShowQuota