Implement OSC quota set default and project mandatory

Added openstack command for updating the default quota
class using:

openstack share quota set default --class

This patch also changes the share quota commands to be
project parameter mandatory.

Partially-implements: bp openstack-client-support
Change-Id: I48c190e7e02f38695cdf319d5f80826d467d8b6e
This commit is contained in:
Felipe Rodrigues 2021-08-20 19:06:46 -03:00 committed by Victoria Martinez de la Cruz
parent 1ebaf0d025
commit f9aee522ef
3 changed files with 151 additions and 134 deletions

View File

@ -18,30 +18,46 @@ from manilaclient import api_versions
from manilaclient.common._i18n import _
def _check_user_id_and_share_type_args(user_id, share_type):
if user_id and share_type:
raise exceptions.CommandError(_(
"'user_id' and 'share_type' values are mutually exclusive. "
"one or both should be unset."))
class QuotaSet(command.Command):
"""Set quotas for a project or project/user or project/share-type."""
_description = _("Set Quota")
"""Set quotas for a project or project/user or project/share-type.
It can be used to set the default class for all projects.
"""
_description = _("Set Quota for a project, or project/user or "
"project/share-type or a class.")
def get_parser(self, prog_name):
parser = super(QuotaSet, self).get_parser(prog_name)
quota_type = parser.add_mutually_exclusive_group()
parser.add_argument(
'--project',
metavar='<project>',
help=_('Name or ID of the project to set the quotas for.')
'project',
metavar='<project/class>',
help=_("A project (name/ID) or a class (e.g.: default).")
)
parser.add_argument(
quota_type.add_argument(
'--class',
dest='quota_class',
action='store_true',
default=False,
help=_("Update class quota to all projects. "
"Mutually exclusive with '--user' and '--share-type'.")
)
quota_type.add_argument(
'--user',
metavar='<user>',
default=None,
help=_("Name or ID of a user to set the quotas for. Optional. "
"Mutually exclusive with '--share-type'.")
help=_("Name or ID of a user to set the quotas for. "
"Mutually exclusive with '--share-type' and '--class'.")
)
quota_type.add_argument(
'--share-type',
metavar='<share-type>',
type=str,
default=None,
help=_("Name or ID of a share type to set the quotas for. "
"Mutually exclusive with '--user' and '--class'. "
"Available only for microversion >= 2.39")
)
parser.add_argument(
'--shares',
@ -83,7 +99,7 @@ class QuotaSet(command.Command):
metavar='<share-groups>',
type=int,
default=None,
help=_('New value for the "share-groups" quota.'
help=_('New value for the "share-groups" quota. '
'Available only for microversion >= 2.40')
)
parser.add_argument(
@ -91,7 +107,8 @@ class QuotaSet(command.Command):
metavar='<share-group-snapshots>',
type=int,
default=None,
help=_('New value for the "share-group-snapshots" quota.')
help=_('New value for the "share-group-snapshots" quota. '
'Available only for microversion >= 2.40')
)
parser.add_argument(
'--share-replicas',
@ -109,23 +126,6 @@ class QuotaSet(command.Command):
help=_("Capacity of share replicas in total. "
"Available only for microversion >= 2.53")
)
parser.add_argument(
'--share-type',
metavar='<share-type>',
type=str,
default=None,
help=_("Name or ID of a share type to set the quotas for. "
"Optional. "
"Mutually exclusive with '--user'. "
"Available only for microversion >= 2.39")
)
parser.add_argument(
'--force',
dest='force',
action="store_true",
default=None,
help=_('Force update the quota.')
)
parser.add_argument(
'--per-share-gigabytes',
metavar='<per-share-gigabytes>',
@ -134,6 +134,14 @@ class QuotaSet(command.Command):
help=_("New value for the 'per-share-gigabytes' quota."
"Available only for microversion >= 2.62")
)
parser.add_argument(
'--force',
dest='force',
action="store_true",
default=None,
help=_('Force update the quota. '
'Not applicable for class update.')
)
return parser
def take_action(self, parsed_args):
@ -146,9 +154,6 @@ class QuotaSet(command.Command):
identity_client.users,
parsed_args.user).id
_check_user_id_and_share_type_args(
user_id, parsed_args.share_type)
kwargs = {
"shares": parsed_args.shares,
"snapshots": parsed_args.snapshots,
@ -206,27 +211,33 @@ class QuotaSet(command.Command):
"'share-groups', 'share-group-snapshots', 'share-replicas', "
"'replica-gigabytes', 'per-share-gigabytes'"))
project_id = None
if parsed_args.project:
if parsed_args.quota_class:
kwargs.update({
"class_name": parsed_args.project,
})
try:
share_client.quota_classes.update(**kwargs)
except Exception as e:
raise exceptions.CommandError(_(
"Failed to set quotas for %s class: '%s'")
% (parsed_args.project, e))
else:
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project
).id
else:
project_id = self.app.client_manager.auth_ref.project_id
parsed_args.project).id
kwargs.update({
"tenant_id": project_id,
"force": parsed_args.force,
"user_id": user_id
})
kwargs.update({
"tenant_id": project_id,
"force": parsed_args.force,
"user_id": user_id
})
try:
share_client.quotas.update(**kwargs)
except Exception as e:
raise exceptions.CommandError(_(
"Failed to set quotas for project '%s' : '%s'")
% (parsed_args.project, e))
try:
share_client.quotas.update(**kwargs)
except Exception as e:
raise exceptions.CommandError(_(
"Failed to set quotas for project '%s' : '%s'")
% (parsed_args.project, e))
class QuotaShow(command.ShowOne):
@ -235,24 +246,25 @@ class QuotaShow(command.ShowOne):
def get_parser(self, prog_name):
parser = super(QuotaShow, self).get_parser(prog_name)
quota_type = parser.add_mutually_exclusive_group()
parser.add_argument(
'--project',
'project',
metavar='<project>',
help=_('Name or ID of hte project to list quotas for.')
help=_('Name or ID of the project to list quotas for.')
)
parser.add_argument(
quota_type.add_argument(
'--user',
metavar='<user>',
default=None,
help=_("Name or ID of user to list the quotas for. Optional. "
"Mutually exclusive with '--share-type'.")
)
parser.add_argument(
quota_type.add_argument(
'--share-type',
metavar='<share-type>',
type=str,
default=None,
help=_("UUID or name of a share type to list the quotas for. "
help=_("Name or ID of a share type to list the quotas for. "
"Optional. "
"Mutually exclusive with '--user'. "
"Available only for microversion >= 2.39")
@ -282,16 +294,9 @@ class QuotaShow(command.ShowOne):
identity_client.users,
parsed_args.user).id
_check_user_id_and_share_type_args(
user_id, parsed_args.share_type)
project_id = None
if parsed_args.project:
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project).id
else:
project_id = self.app.client_manager.auth_ref.project_id
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project).id
quotas = {}
if parsed_args.defaults:
@ -331,19 +336,20 @@ class QuotaDelete(command.Command):
def get_parser(self, prog_name):
parser = super(QuotaDelete, self).get_parser(prog_name)
quota_type = parser.add_mutually_exclusive_group()
parser.add_argument(
'--project',
'project',
metavar='<project>',
help=_('Name or ID of the project to delete quotas for.')
)
parser.add_argument(
quota_type.add_argument(
'--user',
metavar='<user>',
default=None,
help=_("Name or ID of user to delete the quotas for. Optional. "
"Mutually exclusive with '--share-type'.")
)
parser.add_argument(
quota_type.add_argument(
'--share-type',
metavar='<share-type>',
type=str,
@ -365,16 +371,9 @@ class QuotaDelete(command.Command):
identity_client.users,
parsed_args.user).id
_check_user_id_and_share_type_args(
user_id, parsed_args.share_type)
project_id = None
if parsed_args.project:
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project).id
else:
project_id = self.app.client_manager.auth_ref.project_id
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project).id
kwargs = {
"tenant_id": project_id,

View File

@ -35,6 +35,7 @@ class FakeShareClient(object):
self.share_types = mock.Mock()
self.share_type_access = mock.Mock()
self.quotas = mock.Mock()
self.quota_classes = mock.Mock()
self.share_snapshots = mock.Mock()
self.share_snapshot_export_locations = mock.Mock()
self.share_snapshot_instances = mock.Mock()

View File

@ -31,6 +31,9 @@ class TestQuotas(manila_fakes.TestShare):
self.quotas_mock = self.app.client_manager.share.quotas
self.quotas_mock.reset_mock()
self.quota_classes_mock = self.app.client_manager.share.quota_classes
self.quota_classes_mock.reset_mock()
self.app.client_manager.share.api_version = api_versions.APIVersion(
api_versions.MAX_VERSION
)
@ -47,11 +50,44 @@ class TestQuotaSet(TestQuotas):
self.quotas_mock.update = mock.Mock()
self.quotas_mock.update.return_value = None
self.quota_classes_mock.update = mock.Mock()
self.quota_classes_mock.update.return_value = None
self.cmd = osc_quotas.QuotaSet(self.app, None)
def test_quota_set_default_class_shares(self):
arglist = [
'default',
'--class',
'--shares', '40'
]
verifylist = [
('project', 'default'),
('quota_class', True),
('shares', 40)
]
with mock.patch('osc_lib.utils.find_resource') as mock_find_resource:
mock_find_resource.return_value = self.project
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.quota_classes_mock.update.assert_called_with(
class_name='default',
gigabytes=None,
share_networks=None,
shares=40,
snapshot_gigabytes=None,
snapshots=None,
per_share_gigabytes=None)
self.assertIsNone(result)
mock_find_resource.assert_not_called()
self.quotas_mock.asert_not_called()
def test_quota_set_shares(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--shares', '40'
]
verifylist = [
@ -79,7 +115,7 @@ class TestQuotaSet(TestQuotas):
def test_quota_set_gigabytes(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--gigabytes', '1100'
]
verifylist = [
@ -107,7 +143,7 @@ class TestQuotaSet(TestQuotas):
def test_quota_set_share_type(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--share-type', 'default'
]
verifylist = [
@ -136,7 +172,7 @@ class TestQuotaSet(TestQuotas):
def test_quota_set_force(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--force',
'--shares', '40'
]
@ -170,7 +206,7 @@ class TestQuotaSet(TestQuotas):
)
arglist = [
'--project', self.project.id,
self.project.id,
'--share-groups', '40'
]
verifylist = [
@ -182,9 +218,9 @@ class TestQuotaSet(TestQuotas):
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
def test_quota_set_update_exception(self):
def test_quota_set_update_project_exception(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--share-groups', '40',
'--share-group-snapshots', '40'
]
@ -199,9 +235,25 @@ class TestQuotaSet(TestQuotas):
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
def test_quota_set_update_class_exception(self):
arglist = [
'default',
'--class',
'--gigabytes', '40'
]
verifylist = [
('project', 'default'),
('gigabytes', 40)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.quota_classes_mock.update.side_effect = BadRequest()
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
def test_quota_set_nothing_to_set_exception(self):
arglist = [
'--project', self.project.id,
self.project.id,
]
verifylist = [
('project', self.project.id)
@ -217,7 +269,7 @@ class TestQuotaSet(TestQuotas):
)
arglist = [
'--project', self.project.id,
self.project.id,
'--share-replicas', '2',
]
verifylist = [
@ -248,7 +300,7 @@ class TestQuotaSet(TestQuotas):
self.app.client_manager.share.api_version = api_versions.APIVersion(
'2.51')
arglist = [
'--project', self.project.id,
self.project.id,
'--replica-gigabytes', '10',
]
verifylist = [
@ -262,7 +314,7 @@ class TestQuotaSet(TestQuotas):
def test_quota_set_per_share_gigabytes(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--per-share-gigabytes', '10',
]
verifylist = [
@ -302,7 +354,7 @@ class TestQuotaShow(TestQuotas):
def test_quota_show(self):
arglist = [
'--project', self.project.id
self.project.id
]
verifylist = [
('project', self.project.id)
@ -329,7 +381,7 @@ class TestQuotaShow(TestQuotas):
)
arglist = [
'--project', self.project.id,
self.project.id,
'--share-type', 'default'
]
verifylist = [
@ -341,25 +393,9 @@ class TestQuotaShow(TestQuotas):
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
def test_quota_show_user_id_share_type_exception(self):
arglist = [
'--project', self.project.id,
'--share-type', 'default',
'--user', self.user.id
]
verifylist = [
('project', self.project.id),
('share_type', 'default'),
('user', self.user.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
def test_quota_show_defaults(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--defaults'
]
verifylist = [
@ -395,7 +431,7 @@ class TestQuotaDelete(TestQuotas):
def test_quota_delete(self):
arglist = [
'--project', self.project.id
self.project.id
]
verifylist = [
('project', self.project.id)
@ -414,7 +450,7 @@ class TestQuotaDelete(TestQuotas):
def test_quota_delete_share_type(self):
arglist = [
'--project', self.project.id,
self.project.id,
'--share-type', 'default'
]
verifylist = [
@ -440,7 +476,7 @@ class TestQuotaDelete(TestQuotas):
)
arglist = [
'--project', self.project.id,
self.project.id,
'--share-type', 'default'
]
verifylist = [
@ -451,22 +487,3 @@ class TestQuotaDelete(TestQuotas):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
def test_quota_delete_user_share_type_exeption(self):
arglist = [
'--project', self.project.id,
'--share-type', 'default',
'--user', self.user.id
]
verifylist = [
('project', self.project.id),
('share_type', 'default'),
('user', self.user.id)
]
with mock.patch('osc_lib.utils.find_resource') as mock_find_resource:
mock_find_resource.return_value = self.project
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)