Merge "quota: Remove deprecated quota options"

This commit is contained in:
Zuul 2024-07-15 16:17:47 +00:00 committed by Gerrit Code Review
commit 45ac2b62fb
4 changed files with 80 additions and 449 deletions

View File

@ -11,7 +11,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Quota action implementations"""
@ -130,18 +129,12 @@ def get_compute_quotas(
app,
project_id,
*,
quota_class=False,
detail=False,
default=False,
):
try:
client = app.client_manager.compute
if quota_class:
# NOTE(stephenfin): The 'project' argument here could be anything
# as the nova API doesn't care what you pass in. We only pass the
# project in to avoid weirding people out :)
quota = client.quota_classes.get(project_id)
elif default:
if default:
quota = client.quotas.defaults(project_id)
else:
quota = client.quotas.get(project_id, detail=detail)
@ -156,15 +149,12 @@ def get_volume_quotas(
app,
project_id,
*,
quota_class=False,
detail=False,
default=False,
):
try:
client = app.client_manager.volume
if quota_class:
quota = client.quota_classes.get(project_id)
elif default:
if default:
quota = client.quotas.defaults(project_id)
else:
quota = client.quotas.get(project_id, usage=detail)
@ -180,7 +170,6 @@ def get_network_quotas(
app,
project_id,
*,
quota_class=False,
detail=False,
default=False,
):
@ -207,11 +196,6 @@ def get_network_quotas(
return result
# neutron doesn't have the concept of quota classes and if we're using
# nova-network we already fetched this
if quota_class:
return {}
# we have nothing to return if we are not using neutron
if not app.client_manager.is_network_endpoint_enabled():
return {}
@ -227,34 +211,14 @@ def get_network_quotas(
class ListQuota(command.Lister):
_description = _(
"List quotas for all projects with non-default quota values or "
"list detailed quota information for requested project"
)
"""List quotas for all projects with non-default quota values.
Empty output means all projects are using default quotas, which can be
inspected with 'openstack quota show --default'.
"""
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
# TODO(stephenfin): Remove in OSC 8.0
parser.add_argument(
'--project',
metavar='<project>',
help=_(
"**Deprecated** List quotas for this project <project> "
"(name or ID). "
"Use 'quota show' instead."
),
)
# TODO(stephenfin): Remove in OSC 8.0
parser.add_argument(
'--detail',
dest='detail',
action='store_true',
default=False,
help=_(
"**Deprecated** Show details about quotas usage. "
"Use 'quota show --usage' instead."
),
)
option = parser.add_mutually_exclusive_group(required=True)
option.add_argument(
'--compute',
@ -276,102 +240,13 @@ class ListQuota(command.Lister):
)
return parser
def _get_detailed_quotas(self, parsed_args):
project_info = get_project(self.app, parsed_args.project)
project = project_info['id']
quotas = {}
if parsed_args.compute:
quotas.update(
get_compute_quotas(
self.app,
project,
detail=parsed_args.detail,
)
)
if parsed_args.network:
quotas.update(
get_network_quotas(
self.app,
project,
detail=parsed_args.detail,
)
)
if parsed_args.volume:
quotas.update(
get_volume_quotas(
self.app,
parsed_args,
detail=parsed_args.detail,
),
)
result = []
for resource, values in quotas.items():
# NOTE(slaweq): there is no detailed quotas info for some resources
# and it shouldn't be displayed here
if isinstance(values, dict):
result.append(
{
'resource': resource,
'in_use': values.get('in_use'),
'reserved': values.get('reserved'),
'limit': values.get('limit'),
}
)
columns = (
'resource',
'in_use',
'reserved',
'limit',
)
column_headers = (
'Resource',
'In Use',
'Reserved',
'Limit',
)
return (
column_headers,
(utils.get_dict_properties(s, columns) for s in result),
)
def take_action(self, parsed_args):
if parsed_args.detail:
msg = _(
"The --detail option has been deprecated. "
"Use 'openstack quota show --usage' instead."
)
self.log.warning(msg)
elif parsed_args.project: # elif to avoid being too noisy
msg = _(
"The --project option has been deprecated. "
"Use 'openstack quota show' instead."
)
self.log.warning(msg)
result = []
project_ids = []
if parsed_args.project is None:
for p in self.app.client_manager.identity.projects.list():
project_ids.append(getattr(p, 'id', ''))
else:
identity_client = self.app.client_manager.identity
project = utils.find_resource(
identity_client.projects,
parsed_args.project,
)
project_ids.append(getattr(project, 'id', ''))
project_ids = [
p.id for p in self.app.client_manager.identity.projects.list()
]
if parsed_args.compute:
if parsed_args.detail:
return self._get_detailed_quotas(parsed_args)
compute_client = self.app.client_manager.compute
for p in project_ids:
try:
@ -434,9 +309,6 @@ class ListQuota(command.Lister):
)
if parsed_args.volume:
if parsed_args.detail:
return self._get_detailed_quotas(parsed_args)
volume_client = self.app.client_manager.volume
for p in project_ids:
try:
@ -488,9 +360,6 @@ class ListQuota(command.Lister):
)
if parsed_args.network:
if parsed_args.detail:
return self._get_detailed_quotas(parsed_args)
client = self.app.client_manager.network
for p in project_ids:
try:
@ -728,22 +597,24 @@ class SetQuota(common.NetDetectionMixin, command.Command):
"Network quotas are ignored since quota classes are not "
"supported."
)
else:
project = utils.find_resource(
identity_client.projects,
parsed_args.project,
).id
if compute_kwargs:
compute_client.quotas.update(project, **compute_kwargs)
if volume_kwargs:
volume_client.quotas.update(project, **volume_kwargs)
if (
network_kwargs
and self.app.client_manager.is_network_endpoint_enabled()
):
network_client = self.app.client_manager.network
network_client.update_quota(project, **network_kwargs)
return
project = utils.find_resource(
identity_client.projects,
parsed_args.project,
).id
if compute_kwargs:
compute_client.quotas.update(project, **compute_kwargs)
if volume_kwargs:
volume_client.quotas.update(project, **volume_kwargs)
if (
network_kwargs
and self.app.client_manager.is_network_endpoint_enabled()
):
network_client = self.app.client_manager.network
network_client.update_quota(project, **network_kwargs)
class ShowQuota(command.Lister):
@ -758,29 +629,14 @@ and ``server-group-members`` output for a given quota class."""
parser = super().get_parser(prog_name)
parser.add_argument(
'project',
metavar='<project/class>',
metavar='<project>',
nargs='?',
help=_(
'Show quotas for this project or class (name or ID) '
'Show quotas for this project (name or ID) '
'(defaults to current project)'
),
)
type_group = parser.add_mutually_exclusive_group()
# TODO(stephenfin): Remove in OSC 8.0
type_group.add_argument(
'--class',
dest='quota_class',
action='store_true',
default=False,
help=_(
'**Deprecated** Show quotas for <class>. '
'Deprecated as quota classes were never fully implemented '
'and only the default class is supported. '
'Use --default instead which is also supported by the network '
'service. '
'(compute and volume only)'
),
)
type_group.add_argument(
'--default',
dest='default',
@ -832,20 +688,8 @@ and ``server-group-members`` output for a given quota class."""
return parser
def take_action(self, parsed_args):
project = parsed_args.project
if parsed_args.quota_class:
msg = _(
"The '--class' option has been deprecated. Quota classes were "
"never fully implemented and the compute and volume services "
"only support a single 'default' quota class while the "
"network service does not support quota classes at all. "
"Please use 'openstack quota show --default' instead."
)
self.log.warning(msg)
else:
project_info = get_project(self.app, parsed_args.project)
project = project_info['id']
project_info = get_project(self.app, parsed_args.project)
project = project_info['id']
compute_quota_info = {}
volume_quota_info = {}
@ -861,7 +705,6 @@ and ``server-group-members`` output for a given quota class."""
self.app,
project,
detail=parsed_args.usage,
quota_class=parsed_args.quota_class,
default=parsed_args.default,
)
if parsed_args.service in {'all', 'volume'}:
@ -869,7 +712,6 @@ and ``server-group-members`` output for a given quota class."""
self.app,
project,
detail=parsed_args.usage,
quota_class=parsed_args.quota_class,
default=parsed_args.default,
)
if parsed_args.service in {'all', 'network'}:
@ -877,7 +719,6 @@ and ``server-group-members`` output for a given quota class."""
self.app,
project,
detail=parsed_args.usage,
quota_class=parsed_args.quota_class,
default=parsed_args.default,
)

View File

@ -39,40 +39,6 @@ class QuotaTests(base.TestCase):
cls.openstack(f'project delete {cls.PROJECT_NAME}')
super().tearDownClass()
def test_quota_list_details_compute(self):
expected_headers = ["Resource", "In Use", "Reserved", "Limit"]
cmd_output = self.openstack(
'quota list --detail --compute',
parse_output=True,
)
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 = self.openstack(
'quota list --detail --network',
parse_output=True,
)
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):
if not self.haz_network:
self.skipTest("No Network service present")
@ -155,41 +121,23 @@ class QuotaTests(base.TestCase):
if self.haz_network:
self.assertTrue(cmd_output["routers"] >= 0)
def test_quota_set_class(self):
def test_quota_set_default(self):
self.openstack(
'quota set --key-pairs 33 --snapshots 43 ' + '--class default'
'quota set --key-pairs 33 --snapshots 43 --class default'
)
cmd_output = self.openstack(
'quota show --class default',
'quota show --default',
parse_output=True,
)
self.assertIsNotNone(cmd_output)
cmd_output = {x['Resource']: x['Limit'] for x in cmd_output}
self.assertEqual(
33,
cmd_output["key-pairs"],
)
self.assertEqual(
43,
cmd_output["snapshots"],
)
# Check default quota class
cmd_output = self.openstack(
'quota show --class',
parse_output=True,
)
self.assertIsNotNone(cmd_output)
# We don't necessarily know the default quotas, we're checking the
# returned attributes
cmd_output = {x['Resource']: x['Limit'] for x in cmd_output}
self.assertTrue(cmd_output["key-pairs"] >= 0)
self.assertTrue(cmd_output["snapshots"] >= 0)
self.assertEqual(33, cmd_output["key-pairs"])
self.assertEqual(43, cmd_output["snapshots"])
def _restore_quota_limit(self, resource, limit, project):
self.openstack(f'quota set --{resource} {limit} {project}')
def test_quota_network_set_with_no_force(self):
def test_quota_set_network_with_no_force(self):
if not self.haz_network:
self.skipTest('No Network service present')
if not self.is_extension_enabled('quota-check-limit'):
@ -227,7 +175,7 @@ class QuotaTests(base.TestCase):
'quota set --networks 1 --no-force ' + self.PROJECT_NAME,
)
def test_quota_network_set_with_force(self):
def test_quota_set_network_with_force(self):
self.skipTest('story 2010110')
if not self.haz_network:
self.skipTest('No Network service present')
@ -274,3 +222,37 @@ class QuotaTests(base.TestCase):
)
self.assertIsNotNone(cmd_output)
self.assertEqual(1, cmd_output[0]['Networks'])
def test_quota_show(self):
expected_headers = ["Resource", "Limit"]
cmd_output = self.openstack(
'quota show',
parse_output=True,
)
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 has network quota...
self.assertIn("networks", resources)
# ...and compute quota
self.assertIn("instances", resources)
def test_quota_show_usage_option(self):
expected_headers = ["Resource", "Limit", "In Use", "Reserved"]
cmd_output = self.openstack(
'quota show --usage',
parse_output=True,
)
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 has network quota...
self.assertIn("networks", resources)
# ...and compute quota
self.assertIn("instances", resources)

View File

@ -189,115 +189,6 @@ class TestQuotaList(TestQuota):
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.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_client.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_client.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_details_volume(self):
detailed_quota = volume_fakes.create_one_detailed_quota()
detailed_column_header = (
'Resource',
'In Use',
'Reserved',
'Limit',
)
detailed_reference_data = self._get_detailed_reference_data(
detailed_quota
)
self.volume_client.quotas.get = mock.Mock(return_value=detailed_quota)
arglist = [
'--detail',
'--volume',
]
verifylist = [
('detail', True),
('volume', 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):
# Two projects with non-default quotas
self.compute_client.quotas.get = mock.Mock(
@ -414,30 +305,6 @@ class TestQuotaList(TestQuota):
parsed_args,
)
def test_quota_list_compute_by_project(self):
# Two projects with non-default quotas
self.compute_client.quotas.get = mock.Mock(
side_effect=self.compute_quotas,
)
arglist = [
'--compute',
'--project',
self.projects[0].name,
]
verifylist = [
('compute', True),
('project', self.projects[0].name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
ret_quotas = list(data)
self.assertEqual(self.compute_column_header, columns)
self.assertEqual(self.compute_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
def test_quota_list_network(self):
# Two projects with non-default quotas
self.network_client.get_quota = mock.Mock(
@ -507,30 +374,6 @@ class TestQuotaList(TestQuota):
self.assertEqual(self.network_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
def test_quota_list_network_by_project(self):
# Two projects with non-default quotas
self.network_client.get_quota = mock.Mock(
side_effect=self.network_quotas,
)
arglist = [
'--network',
'--project',
self.projects[0].name,
]
verifylist = [
('network', True),
('project', self.projects[0].name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
ret_quotas = list(data)
self.assertEqual(self.network_column_header, columns)
self.assertEqual(self.network_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
def test_quota_list_volume(self):
# Two projects with non-default quotas
self.volume_client.quotas.get = mock.Mock(
@ -600,30 +443,6 @@ class TestQuotaList(TestQuota):
self.assertEqual(self.volume_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
def test_quota_list_volume_by_project(self):
# Two projects with non-default quotas
self.volume_client.quotas.get = mock.Mock(
side_effect=self.volume_quotas,
)
arglist = [
'--volume',
'--project',
self.projects[0].name,
]
verifylist = [
('volume', True),
('project', self.projects[0].name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
ret_quotas = list(data)
self.assertEqual(self.volume_column_header, columns)
self.assertEqual(self.volume_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
class TestQuotaSet(TestQuota):
def setUp(self):
@ -1227,25 +1046,6 @@ class TestQuotaShow(TestQuota):
)
self.assertNotCalled(self.network_client.get_quota)
def test_quota_show__with_class(self):
arglist = [
'--class',
'default',
]
verifylist = [
('quota_class', True),
('project', 'default'), # project is actually a class here
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.compute_quotas_class_mock.get.assert_called_once_with('default')
self.volume_quotas_class_mock.get.assert_called_once_with('default')
# neutron doesn't have the concept of quota classes
self.assertNotCalled(self.network_client.get_quota)
self.assertNotCalled(self.network_client.get_quota_default)
def test_quota_show__with_usage(self):
# update mocks to return detailed quota instead
self.compute_quota = compute_fakes.create_one_comp_detailed_quota()

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
The ``--class`` options of the ``quota show`` command, which was deprecated
in 6.1.0 (Antelope), has now been removed in favour of the ``--default``
option. Quota classes were never fully implemented and the compute and
volume services only support a single ``default`` quota class while the
network service does not support quota classes at all.