From 45bec041b206678de36f2f463ac6872b785e592e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 22 Sep 2022 18:09:08 +0100 Subject: [PATCH] quota: Add 'quota delete' command We had previously indicated that people use 'quota set' for unsetting a project's quotas, however, there was nothing that actually allowed us to do this. Correct that oversight. Change-Id: I04057e766b8ccf94bf219972249b68dc2bb796d4 Signed-off-by: Stephen Finucane --- doc/source/cli/data/cinder.csv | 2 +- doc/source/cli/data/neutron.csv | 2 +- doc/source/cli/data/nova.csv | 2 +- openstackclient/common/quota.py | 79 +++++++++++++ .../tests/unit/common/test_quota.py | 107 ++++++++++++++++++ .../notes/quota-delete-947df66ae5341cbf.yaml | 5 + setup.cfg | 1 + tox.ini | 4 +- 8 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/quota-delete-947df66ae5341cbf.yaml diff --git a/doc/source/cli/data/cinder.csv b/doc/source/cli/data/cinder.csv index f3da43eb2e..310ae172b0 100644 --- a/doc/source/cli/data/cinder.csv +++ b/doc/source/cli/data/cinder.csv @@ -91,7 +91,7 @@ qos-show,volume qos show,Shows a specified qos specs. quota-class-show,quota show --class,Lists quotas for a quota class. quota-class-update,quota set --class,Updates quotas for a quota class. quota-defaults,quota show --default,Lists default quotas for a tenant. -quota-delete,,Delete the quotas for a tenant. +quota-delete,quota delete --volume,Delete the quotas for a tenant. quota-show,quota show,Lists quotas for a tenant. quota-update,quota set,Updates quotas for a tenant. quota-usage,,Lists quota usage for a tenant. diff --git a/doc/source/cli/data/neutron.csv b/doc/source/cli/data/neutron.csv index 402f4064ee..f0caa4797b 100644 --- a/doc/source/cli/data/neutron.csv +++ b/doc/source/cli/data/neutron.csv @@ -188,7 +188,7 @@ qos-policy-list,network qos policy list,List QoS policies that belong to a given qos-policy-show,network qos policy show,Show information of a given qos policy. qos-policy-update,network qos policy set,Update a given qos policy. quota-default-show,quota show --default,Show default quotas for a given tenant. -quota-delete,,Delete defined quotas of a given tenant. +quota-delete,quota delete --network,Delete defined quotas of a given tenant. quota-list,quota list,List quotas of all tenants who have non-default quota values. quota-show,quota show,Show quotas for a given tenant. quota-update,quota set,Define tenant's quotas not to use defaults. diff --git a/doc/source/cli/data/nova.csv b/doc/source/cli/data/nova.csv index 4252585f9b..2909bc0c6c 100644 --- a/doc/source/cli/data/nova.csv +++ b/doc/source/cli/data/nova.csv @@ -70,7 +70,7 @@ pause,server pause,Pause a server. quota-class-show,quota show --class,List the quotas for a quota class. quota-class-update,quota set --class,Update the quotas for a quota class. quota-defaults,quota list,List the default quotas for a tenant. -quota-delete,quota set,Delete quota for a tenant/user so their quota will Revert back to default. +quota-delete,quota delete --compute,Delete quota for a tenant/user so their quota will Revert back to default. quota-show,quota show,List the quotas for a tenant/user. quota-update,quota set,Update the quotas for a tenant/user. reboot,server reboot,Reboot a server. diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 44482367e7..0110feb668 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -697,3 +697,82 @@ class ShowQuota(command.ShowOne, BaseQuota): info['project_name'] = project_name return zip(*sorted(info.items())) + + +class DeleteQuota(command.Command): + _description = _( + "Delete configured quota for a project and revert to defaults." + ) + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'project', + metavar='', + help=_('Delete quotas for this project (name or ID)'), + ) + option = parser.add_mutually_exclusive_group() + option.add_argument( + '--all', + action='store_const', + const='all', + dest='service', + default='all', + help=_('Delete project quotas for all services (default)'), + ) + option.add_argument( + '--compute', + action='store_const', + const='compute', + dest='service', + default='all', + help=_( + 'Delete compute quotas for the project ' + '(including network quotas when using nova-network)' + ), + ) + option.add_argument( + '--volume', + action='store_const', + const='volume', + dest='service', + default='all', + help=_('Delete volume quotas for the project'), + ) + option.add_argument( + '--network', + action='store_const', + const='network', + dest='service', + default='all', + help=_('Delete network quotas for the project'), + ) + return parser + + def take_action(self, parsed_args): + identity_client = self.app.client_manager.identity + project = utils.find_resource( + identity_client.projects, + parsed_args.project, + ) + + # compute quotas + if parsed_args.service in {'all', 'compute'}: + compute_client = self.app.client_manager.compute + compute_client.quotas.delete(project) + + # volume quotas + if parsed_args.service in {'all', 'volume'}: + volume_client = self.app.client_manager.volume + volume_client.quotas.delete(project) + + # network quotas (but only if we're not using nova-network, otherwise + # we already deleted the quotas in the compute step) + if ( + parsed_args.service in {'all', 'network'} + and self.app.client_manager.is_network_endpoint_enabled() + ): + network_client = self.app.client_manager.network + network_client.quotas.delete(project) + + return None diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 70fd1436a0..087443c169 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -62,6 +62,9 @@ class TestQuota(compute_fakes.TestComputev2): self.app.client_manager.volume.quota_classes self.volume_quotas_class_mock.reset_mock() + self.app.client_manager.network.quotas = mock.Mock() + self.network_quotas_mock = self.app.client_manager.network.quotas + self.app.client_manager.auth_ref = mock.Mock() self.app.client_manager.auth_ref.service_catalog = mock.Mock() self.service_catalog_mock = \ @@ -1154,3 +1157,107 @@ class TestQuotaShow(TestQuota): self.assertEqual(len(network_fakes.QUOTA) - 1, len(result)) # Go back to default mock self.network.get_quota = orig_get_quota + + +class TestQuotaDelete(TestQuota): + """Test cases for quota delete command""" + + def setUp(self): + super().setUp() + + self.cmd = quota.DeleteQuota(self.app, None) + + def test_delete(self): + """Delete all quotas""" + arglist = [ + self.projects[0].id, + ] + verifylist = [ + ('service', 'all'), + ('project', self.projects[0].id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.projects_mock.get.assert_called_once_with(self.projects[0].id) + self.compute_quotas_mock.delete.assert_called_once_with( + self.projects[0], + ) + self.volume_quotas_mock.delete.assert_called_once_with( + self.projects[0], + ) + self.network_quotas_mock.delete.assert_called_once_with( + self.projects[0], + ) + + def test_delete__compute(self): + """Delete compute quotas only""" + arglist = [ + '--compute', + self.projects[0].id, + ] + verifylist = [ + ('service', 'compute'), + ('project', self.projects[0].id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.projects_mock.get.assert_called_once_with(self.projects[0].id) + self.compute_quotas_mock.delete.assert_called_once_with( + self.projects[0], + ) + self.volume_quotas_mock.delete.assert_not_called() + self.network_quotas_mock.delete.assert_not_called() + + def test_delete__volume(self): + """Delete volume quotas only""" + arglist = [ + '--volume', + self.projects[0].id, + ] + verifylist = [ + ('service', 'volume'), + ('project', self.projects[0].id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.projects_mock.get.assert_called_once_with(self.projects[0].id) + self.compute_quotas_mock.delete.assert_not_called() + self.volume_quotas_mock.delete.assert_called_once_with( + self.projects[0], + ) + self.network_quotas_mock.delete.assert_not_called() + + def test_delete__network(self): + """Delete network quotas only""" + arglist = [ + '--network', + self.projects[0].id, + ] + verifylist = [ + ('service', 'network'), + ('project', self.projects[0].id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.projects_mock.get.assert_called_once_with(self.projects[0].id) + self.compute_quotas_mock.delete.assert_not_called() + self.volume_quotas_mock.delete.assert_not_called() + self.network_quotas_mock.delete.assert_called_once_with( + self.projects[0], + ) diff --git a/releasenotes/notes/quota-delete-947df66ae5341cbf.yaml b/releasenotes/notes/quota-delete-947df66ae5341cbf.yaml new file mode 100644 index 0000000000..0d992451c3 --- /dev/null +++ b/releasenotes/notes/quota-delete-947df66ae5341cbf.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added a new command, ``quota delete``, that will allow admins delete quotas + set for projects. Supported by the compute, volume, and network services. diff --git a/setup.cfg b/setup.cfg index fe5db3f2c3..c8935c5860 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,6 +49,7 @@ openstack.common = quota_list = openstackclient.common.quota:ListQuota quota_set = openstackclient.common.quota:SetQuota quota_show = openstackclient.common.quota:ShowQuota + quota_delete = openstackclient.common.quota:DeleteQuota versions_show = openstackclient.common.versions:ShowVersions openstack.compute.v2 = diff --git a/tox.ini b/tox.ini index f631380df5..5f02e7c21c 100644 --- a/tox.ini +++ b/tox.ini @@ -134,7 +134,7 @@ show-source = True # H203: Use assertIs(Not)None to check for None enable-extensions = H203 exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes -# W504 is disabled since you must choose between this or W503 -ignore = W504 +# W503 and W504 are disabled since they're not very useful +ignore = W503,W504 import-order-style = pep8 application_import_names = openstackclient