Commit c3cc2f3f authored by Giorgos Korfiatis's avatar Giorgos Korfiatis Committed by Christos Stavrakakis
Browse files

astakos: Modify base quota in bulk

Add option `--all' in command user-modify to support setting base quota
for all users. Exceptions can be specified with `--exclude'.

Also select user for update in user-modify.
parent 545ef538
......@@ -317,7 +317,11 @@ you can set it for each resource like this::
# use this to display quota / uuid
# snf-manage user-show 'uuid or email' --quota
# snf-manage user-modify 'user-uuid' --set-base-quota 'cyclades.vm' 10
# snf-manage user-modify <user-uuid> --base-quota 'cyclades.vm' 10
You can set base quota for all existing users, with possible exceptions, using::
# snf-manage user-modify --all --base-quota cyclades.vm 10 --exclude uuid1,uuid2
All quota for which values different from the default have been set,
can be listed with::
......@@ -342,7 +346,7 @@ per user with::
You can also set a user-specific limit with::
# snf-manage user-modify 'user-uuid' --set-base-quota 'astakos.pending_app' 5
# snf-manage user-modify <user-uuid> --base-quota 'astakos.pending_app' 5
When users apply for projects they are not automatically granted
the resources. They must first be approved by the administrator.
......
......@@ -69,11 +69,11 @@ For clarity, option ``--limit`` will be renamed ``--default-quota``.
We can currently change a user's base quota with::
snf-manage user-modify <id> --set-base-quota <resource_name> <value>
snf-manage user-modify <id> --set-base-quota <resource_name> <value>
This command will be extended with option ``--all`` to allow changing base
quota for multiple users; option ``--exclude`` will allow introducing
exceptions.
exceptions. ``--set-base-quota`` will be renamed ``--base-quota``.
Inspecting base quota
=====================
......
......@@ -402,7 +402,7 @@ applying/approving applications in order to modify some project settings,
such as the quota limits.
Currently, the administrator can change the user base quota with:
``snf-manage user-modify <id> --set-base-quota <resource> <capacity>``.
``snf-manage user-modify <id> --base-quota <resource> <capacity>``.
This will be removed in favor of the ``project-modify`` command, so that all
quota are handled in a uniform way. Similar to ``user-modify --all``,
``project-modify`` will get options ``--all-base`` and ``--all-non-base`` to
......
......@@ -43,7 +43,7 @@ from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from synnefo.util import units
from astakos.im.models import AstakosUser, AstakosUserQuota
from astakos.im.models import AstakosUser, Resource
from astakos.im import quotas
from astakos.im import activation_backends
from ._common import (remove_user_permission, add_user_permission, is_uuid,
......@@ -53,10 +53,19 @@ activation_backend = activation_backends.get_backend()
class Command(BaseCommand):
args = "<user ID>"
args = "<user ID> (or --all)"
help = "Modify a user's attributes"
option_list = BaseCommand.option_list + (
make_option('--all',
action='store_true',
default=False,
help=("Operate on all users. Currently only setting "
"base quota is supported in this mode. Can be "
"combined with `--exclude'.")),
make_option('--exclude',
help=("If `--all' is given, exclude users given as a "
"list of uuids: uuid1,uuid2,uuid3")),
make_option('--invitations',
dest='invitations',
metavar='NUM',
......@@ -129,7 +138,7 @@ class Command(BaseCommand):
make_option('--reject-reason',
dest='reject_reason',
help="Reason user got rejected"),
make_option('--set-base-quota',
make_option('--base-quota',
dest='set_base_quota',
metavar='<resource> <capacity>',
nargs=2,
......@@ -153,12 +162,23 @@ class Command(BaseCommand):
@transaction.commit_on_success
def handle(self, *args, **options):
if options['all']:
if not args:
return self.handle_all_users(*args, **options)
else:
raise CommandError("Please provide a user ID or --all")
if len(args) != 1:
raise CommandError("Please provide a user ID")
raise CommandError("Please provide a user ID or --all")
if options["exclude"] is not None:
m = "Option --exclude is meaningful only combined with --all."
raise CommandError(m)
if args[0].isdigit():
try:
user = AstakosUser.objects.get(id=int(args[0]))
user = AstakosUser.objects.select_for_update().\
get(id=int(args[0]))
except AstakosUser.DoesNotExist:
raise CommandError("Invalid user ID")
elif is_uuid(args[0]):
......@@ -304,7 +324,7 @@ class Command(BaseCommand):
set_base_quota = options.get('set_base_quota')
if set_base_quota is not None:
resource, capacity = set_base_quota
self.set_limit(user, resource, capacity, force)
self.set_limits([user], resource, capacity, force)
delete = options.get('delete')
if delete:
......@@ -335,36 +355,73 @@ class Command(BaseCommand):
user.email = newemail
user.save()
def set_limit(self, user, resource, capacity, force):
def confirm(self):
self.stdout.write("Confirm? [y/N] ")
response = raw_input()
if string.lower(response) not in ['y', 'yes']:
self.stdout.write("Aborted.\n")
exit()
def handle_limits_user(self, user, res, capacity, style):
default_capacity = res.uplimit
resource = res.name
quota = user.get_resource_policy(resource)
s_default = show_resource_value(default_capacity, resource, style)
s_current = show_resource_value(quota.capacity, resource, style)
s_capacity = (show_resource_value(capacity, resource, style)
if capacity != 'default' else capacity)
self.stdout.write("user: %s (%s)\n" % (user.uuid, user.username))
self.stdout.write("default capacity: %s\n" % s_default)
self.stdout.write("current capacity: %s\n" % s_current)
self.stdout.write("new capacity: %s\n" % s_capacity)
self.confirm()
def handle_limits_all(self, res, capacity, exclude, style):
m = "This will set base quota for all users"
app = (" except %s" % ", ".join(exclude)) if exclude else ""
self.stdout.write(m+app+".\n")
resource = res.name
self.stdout.write("resource: %s\n" % resource)
s_capacity = (show_resource_value(capacity, resource, style)
if capacity != 'default' else capacity)
self.stdout.write("capacity: %s\n" % s_capacity)
self.confirm()
def set_limits(self, users, resource, capacity, force=False, exclude=None):
try:
r = Resource.objects.get(name=resource)
except Resource.DoesNotExist:
raise CommandError("No such resource '%s'." % resource)
style = None
if capacity != 'default':
if capacity != "default":
try:
capacity, style = units.parse_with_style(capacity)
except:
m = "Please specify capacity as a decimal integer or 'default'"
m = ("Please specify capacity as a decimal integer or "
"'default'")
raise CommandError(m)
try:
quota = user.get_resource_policy(resource)
except AstakosUserQuota.DoesNotExist:
raise CommandError("No such resource: %s" % resource)
default_capacity = quota.resource.uplimit
if not force:
s_default = show_resource_value(default_capacity, resource, style)
s_current = show_resource_value(quota.capacity, resource, style)
s_capacity = (show_resource_value(capacity, resource, style)
if capacity != 'default' else capacity)
self.stdout.write("user: %s (%s)\n" % (user.uuid, user.username))
self.stdout.write("default capacity: %s\n" % s_default)
self.stdout.write("current capacity: %s\n" % s_current)
self.stdout.write("new capacity: %s\n" % s_capacity)
self.stdout.write("Confirm? (y/n) ")
response = raw_input()
if string.lower(response) not in ['y', 'yes']:
self.stdout.write("Aborted.\n")
return
if capacity == 'default':
capacity = default_capacity
quotas.update_base_quota(quota, capacity)
if len(users) == 1:
self.handle_limits_user(users[0], r, capacity, style)
else:
self.handle_limits_all(r, capacity, exclude, style)
if capacity == "default":
capacity = r.uplimit
quotas.update_base_quota(users, resource, capacity)
def handle_all_users(self, *args, **options):
force = options["force"]
exclude = options["exclude"]
if exclude is not None:
exclude = exclude.split(',')
set_base_quota = options.get('set_base_quota')
if set_base_quota is not None:
users = AstakosUser.objects.accepted().select_for_update()
if exclude:
users = users.exclude(uuid__in=exclude)
resource, capacity = set_base_quota
self.set_limits(users, resource, capacity, force, exclude)
......@@ -143,11 +143,12 @@ def get_pending_app_quota(user):
return quota[SYSTEM][PENDING_APP_RESOURCE]
def update_base_quota(quota, capacity):
quota.capacity = capacity
quota.save()
qh_sync_locked_user(quota.user)
def update_base_quota(users, resource, value):
userids = [user.pk for user in users]
AstakosUserQuota.objects.\
filter(resource__name=resource, user__pk__in=userids).\
update(capacity=value)
qh_sync_locked_users(users)
def initial_quotas(users, flt=None):
......
......@@ -78,6 +78,8 @@ class ProjectAPITest(TestCase):
"api_visible": False}
r, _ = register.add_resource(pending_app)
register.update_resources([(r, 3)])
accepted = AstakosUser.objects.accepted()
quotas.update_base_quota(accepted, r.name, 3)
def create(self, app, headers):
dump = json.dumps(app)
......@@ -673,8 +675,7 @@ class TestProjects(TestCase):
@im_settings(PROJECT_ADMINS=['uuid1'])
def test_applications(self):
# let user have 2 pending applications
q = self.user.get_resource_policy('astakos.pending_app')
quotas.update_base_quota(q, 2)
quotas.update_base_quota([self.user], 'astakos.pending_app', 2)
r = self.user_client.get(reverse('project_add'), follow=True)
self.assertRedirects(r, reverse('project_add'))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment