Commit a8c939e2 authored by Giorgos Korfiatis's avatar Giorgos Korfiatis

cyclades: Enforce quota per project

Take project into account when checking and enforcing quota violations. Add
option to restrict check to a certain project.
parent c584ec5e
......@@ -155,30 +155,54 @@ def handle_floating_ip(viol_id, resource, ips, diff, actions):
ip_actions[ip.id] = viol_id, state, backend_id, "REMOVE"
def get_vms(users=None):
def get_vms(users=None, projects=None):
vms = VirtualMachine.objects.filter(deleted=False).\
select_related("flavor").order_by('-id')
if users is not None:
vms = vms.filter(userid__in=users)
if projects is not None:
vms = vms.filter(project__in=projects)
return _partition_by(lambda vm: vm.userid, vms)
vmsdict = _partition_by(lambda vm: vm.project, vms)
for project, projectdict in vmsdict.iteritems():
vmsdict[project] = _partition_by(lambda vm: vm.userid, projectdict)
return vmsdict
def get_floating_ips(users=None):
def get_floating_ips(users=None, projects=None):
ips = IPAddress.objects.filter(deleted=False, floating_ip=True).\
select_related("nic__machine")
if users is not None:
ips = ips.filter(userid__in=users)
if projects is not None:
ips = ips.filter(project__in=projects)
return _partition_by(lambda ip: ip.userid, ips)
ipsdict = _partition_by(lambda ip: ip.project, ips)
for project, projectdict in ipsdict.iteritems():
ipsdict[project] = _partition_by(lambda ip: ip.userid, projectdict)
return ipsdict
def get_actual_resources(resource_type, users=None):
def get_actual_resources(resource_type, users=None, projects=None):
ACTUAL_RESOURCES = {
"vm": get_vms,
"floating_ip": get_floating_ips,
}
return ACTUAL_RESOURCES[resource_type](users=users)
return ACTUAL_RESOURCES[resource_type](users=users, projects=projects)
def skip_check(obj, to_check=None, excluded=None):
return (to_check is not None and obj not in to_check or
excluded is not None and obj in excluded)
def pick_project_resources(project_dict, users=None, excluded_users=None):
resources = []
for user, user_resources in project_dict.iteritems():
if skip_check(user, users, excluded_users):
continue
resources += user_resources
return resources
VM_ACTION = {
......
......@@ -60,6 +60,12 @@ class Command(SynnefoCommand):
"of users, e.g uuid1,uuid2")),
make_option("--exclude-users",
help=("Exclude list of users from resource enforcement")),
make_option("--projects",
help=("Enforce resources only for the specified list "
"of projects, e.g uuid1,uuid2")),
make_option("--exclude-projects",
help=("Exclude list of projects from resource enforcement")
),
make_option("--resources",
help="Specify resources to check, default: %s" %
",".join(DEFAULT_RESOURCES)),
......@@ -110,6 +116,7 @@ class Command(SynnefoCommand):
write = self.stderr.write
fix = options["fix"]
force = options["force"]
handlers = self.get_handlers(options["resources"])
maxops = options["max_operations"]
if maxops is not None:
try:
......@@ -126,19 +133,35 @@ class Command(SynnefoCommand):
m = "Expected integer shutdown timeout."
raise CommandError(m)
users = options['users']
if users is not None:
users = users.split(',')
excluded_users = options['exclude_users']
excluded_users = set(excluded_users.split(',')
if excluded_users is not None else [])
excluded = options['exclude_users']
excluded = set(excluded.split(',') if excluded is not None else [])
users_to_check = options['users']
if users_to_check is not None:
users_to_check = set(users_to_check.split(',')) - excluded_users
handlers = self.get_handlers(options["resources"])
try:
qh_holdings = util.get_qh_users_holdings(users)
qh_holdings = util.get_qh_users_holdings(users_to_check)
except errors.AstakosClientException as e:
raise CommandError(e)
excluded_projects = options["exclude_projects"]
excluded_projects = set(excluded_projects.split(',')
if excluded_projects is not None else [])
projects_to_check = options["projects"]
if projects_to_check is not None:
projects_to_check = set(projects_to_check.split(',')) - \
excluded_projects
try:
qh_project_holdings = util.get_qh_project_holdings(
projects_to_check)
except errors.AstakosClientException as e:
raise CommandError(e)
qh_project_holdings = sorted(qh_project_holdings.items())
qh_holdings = sorted(qh_holdings.items())
resources = set(h[0] for h in handlers)
dangerous = bool(resources.difference(DEFAULT_RESOURCES))
......@@ -147,15 +170,52 @@ class Command(SynnefoCommand):
actions = {}
overlimit = []
viol_id = 0
for resource, handle_resource, resource_type in handlers:
if resource_type not in actions:
actions[resource_type] = OrderedDict()
actual_resources = enforce.get_actual_resources(
resource_type, projects=projects_to_check)
for project, project_quota in qh_project_holdings:
if enforce.skip_check(project, projects_to_check,
excluded_projects):
continue
try:
qh = util.transform_project_quotas(project_quota)
qh_value, qh_limit, qh_pending = qh[resource]
except KeyError:
write("Resource '%s' does not exist in Quotaholder"
" for project '%s'!\n" %
(resource, project))
continue
if qh_pending:
write("Pending commission for project '%s', "
"resource '%s'. Skipping\n" %
(project, resource))
continue
diff = qh_value - qh_limit
if diff > 0:
viol_id += 1
overlimit.append((viol_id, "project", project, "",
resource, qh_limit, qh_value))
relevant_resources = enforce.pick_project_resources(
actual_resources[project], users=users_to_check,
excluded_users=excluded_users)
handle_resource(viol_id, resource, relevant_resources,
diff, actions)
for resource, handle_resource, resource_type in handlers:
if resource_type not in actions:
actions[resource_type] = OrderedDict()
actual_resources = enforce.get_actual_resources(resource_type,
users)
users_to_check)
for user, user_quota in qh_holdings:
if user in excluded:
if enforce.skip_check(user, users_to_check, excluded_users):
continue
for source, source_quota in user_quota.iteritems():
if enforce.skip_check(source, projects_to_check,
excluded_projects):
continue
try:
qh = util.transform_quotas(source_quota)
qh_value, qh_limit, qh_pending = qh[resource]
......@@ -172,9 +232,9 @@ class Command(SynnefoCommand):
diff = qh_value - qh_limit
if diff > 0:
viol_id += 1
overlimit.append((viol_id, user, source, resource,
qh_limit, qh_value))
relevant_resources = actual_resources[user]
overlimit.append((viol_id, "user", user, source,
resource, qh_limit, qh_value))
relevant_resources = actual_resources[source][user]
handle_resource(viol_id, resource, relevant_resources,
diff, actions)
......@@ -182,7 +242,8 @@ class Command(SynnefoCommand):
write("No violations.\n")
return
headers = ("#", "User", "Source", "Resource", "Limit", "Usage")
headers = ("#", "Type", "Holder", "Source", "Resource", "Limit",
"Usage")
pprint_table(self.stdout, overlimit, headers,
options["output_format"], title="Violations")
......
......@@ -123,6 +123,26 @@ def get_qh_users_holdings(users=None):
return qs
def get_qh_project_holdings(projects=None):
qh = Quotaholder.get()
if projects is None or len(projects) != 1:
req = None
else:
req = projects[0]
quotas = qh.service_get_project_quotas(req)
if projects is None:
return quotas
qs = {}
for project in projects:
try:
qs[project] = quotas[project]
except KeyError:
pass
return qs
def transform_quotas(quotas):
d = {}
for resource, counters in quotas.iteritems():
......@@ -131,3 +151,13 @@ def transform_quotas(quotas):
pending = counters['pending']
d[resource] = (used, limit, pending)
return d
def transform_project_quotas(quotas):
d = {}
for resource, counters in quotas.iteritems():
used = counters['project_usage']
limit = counters['project_limit']
pending = counters['project_pending']
d[resource] = (used, limit, pending)
return d
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