diff options
author | Kylin CG <kylin7.sg@gmail.com> | 2012-06-04 22:21:41 +0800 |
---|---|---|
committer | Kylin CG <kylin7.sg@gmail.com> | 2012-08-06 23:55:22 +0800 |
commit | 391f345dfff174108e330de1ed4c7ed18f8ff923 (patch) | |
tree | f80dd112f40d94cdbb76b578a4b3b84c8915edef /nova/quota.py | |
parent | 51002f0d0f85f077bbbfed1d151df59240775ff8 (diff) | |
download | nova-391f345dfff174108e330de1ed4c7ed18f8ff923.tar.gz nova-391f345dfff174108e330de1ed4c7ed18f8ff923.tar.xz nova-391f345dfff174108e330de1ed4c7ed18f8ff923.zip |
Adds per-user-quotas support for more detailed quotas management
Implements blueprint per-user-quotas.
Based on the original quotas structure.
NOTE:
quota_instances, quota_cores, quota_ram, quota_volumes,
quota_gigabytes, quota_key_pairs and quota_security_groups
are supported per user.
Allow 'projectadmin' role to access the user quota setting
methods.
Add commands 'nova-manage quota project/user' for quotas
management.
Change-Id: I07a39499432571fedd819c53ae414240cefc3354
Diffstat (limited to 'nova/quota.py')
-rw-r--r-- | nova/quota.py | 219 |
1 files changed, 184 insertions, 35 deletions
diff --git a/nova/quota.py b/nova/quota.py index d3ba0aa02..44e3c593d 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -101,6 +101,11 @@ class DbQuotaDriver(object): return db.quota_get(context, project_id, resource) + def get_by_user(self, context, user_id, project_id, resource): + """Get a specific quota by user.""" + + return db.quota_get_for_user(context, user_id, project_id, resource) + def get_by_class(self, context, quota_class, resource): """Get a specific quota by quota class.""" @@ -143,16 +148,16 @@ class DbQuotaDriver(object): return quotas - def get_project_quotas(self, context, resources, project_id, - quota_class=None, defaults=True, - usages=True): + def _process_quotas(self, context, resources, project_id, quotas, + quota_class=None, defaults=True, usages=None): """ - Given a list of resources, retrieve the quotas for the given - project. + Given a list of resources, process the quotas for the given + quotas and usages. :param context: The request context, for access checks. :param resources: A dictionary of the registered resources. :param project_id: The ID of the project to return quotas for. + :param quotas: The quotas dictionary need to be processed. :param quota_class: If project_id != context.project_id, the quota class cannot be determined. This parameter allows it to be specified. It @@ -162,16 +167,11 @@ class DbQuotaDriver(object): default value, if there is no value from the quota class) will be reported if there is no specific value for the resource. - :param usages: If True, the current in_use and reserved counts + :param usages: If not None, the current in_use and reserved counts will also be returned. """ - quotas = {} - project_quotas = db.quota_get_all_by_project(context, project_id) - if usages: - project_usages = db.quota_usage_get_all_by_project(context, - project_id) - + modified_quotas = {} # Get the quotas for the appropriate class. If the project ID # matches the one in the context, we use the quota_class from # the context, otherwise, we use the provided quota_class (if @@ -185,11 +185,11 @@ class DbQuotaDriver(object): for resource in resources.values(): # Omit default/quota class values - if not defaults and resource.name not in project_quotas: + if not defaults and resource.name not in quotas: continue - quotas[resource.name] = dict( - limit=project_quotas.get(resource.name, class_quotas.get( + modified_quotas[resource.name] = dict( + limit=quotas.get(resource.name, class_quotas.get( resource.name, resource.default)), ) @@ -197,13 +197,96 @@ class DbQuotaDriver(object): # internal consumer of this interface wants to access the # usages directly from inside a transaction. if usages: - usage = project_usages.get(resource.name, {}) - quotas[resource.name].update( + usage = usages.get(resource.name, {}) + modified_quotas[resource.name].update( in_use=usage.get('in_use', 0), reserved=usage.get('reserved', 0), ) - return quotas + return modified_quotas + + def get_project_quotas(self, context, resources, project_id, + quota_class=None, defaults=True, + usages=True): + """ + Given a list of resources, retrieve the quotas for the given + project. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resources. + :param project_id: The ID of the project to return quotas for. + :param quota_class: If project_id != context.project_id, the + quota class cannot be determined. This + parameter allows it to be specified. It + will be ignored if project_id == + context.project_id. + :param defaults: If True, the quota class value (or the + default value, if there is no value from the + quota class) will be reported if there is no + specific value for the resource. + :param usages: If True, the current in_use and reserved counts + will also be returned. + """ + + project_quotas = db.quota_get_all_by_project(context, project_id) + + project_usages = None + if usages: + project_usages = db.quota_usage_get_all_by_project(context, + project_id) + + return self._process_quotas(context, resources, + project_id, project_quotas, + quota_class=quota_class, + defaults=defaults, + usages=project_usages) + + def get_user_quotas(self, context, resources, user_id, project_id, + quota_class=None, defaults=True, + usages=True): + """ + Given a list of resources, retrieve the quotas for the given + user. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resources. + :param project_id: The ID of the project to return quotas for. + :param user_id: The ID of the user to return quotas for. + :param quota_class: If project_id != context.project_id, the + quota class cannot be determined. This + parameter allows it to be specified. It + will be ignored if project_id == + context.project_id. + :param defaults: If True, the quota class value (or the + default value, if there is no value from the + quota class) will be reported if there is no + specific value for the resource. + :param usages: If True, the current in_use and reserved counts + will also be returned. + """ + + user_quotas = db.quota_get_all_by_user(context, user_id, project_id) + + user_usages = None + if usages: + user_usages = db.quota_usage_get_all_by_user(context, + user_id, + project_id) + + return self._process_quotas(context, resources, + project_id, user_quotas, + quota_class=quota_class, + defaults=defaults, + usages=user_usages) + + def get_remaining_quotas(self, context, project_id, resources): + """Get the remaining quotas for the given project.""" + defaults = self.get_defaults(context, resources) + quotas = db.quota_get_remaining(context, project_id) + for key in defaults.keys(): + if key in quotas: + defaults[key] = quotas[key] + return defaults def _get_quotas(self, context, resources, keys, has_sync): """ @@ -235,9 +318,10 @@ class DbQuotaDriver(object): raise exception.QuotaResourceUnknown(unknown=sorted(unknown)) # Grab and return the quotas (without usages) - quotas = self.get_project_quotas(context, sub_resources, - context.project_id, - context.quota_class, usages=False) + quotas = self.get_user_quotas(context, sub_resources, + context.user_id, + context.project_id, + context.quota_class, usages=False) return dict((k, v['limit']) for k, v in quotas.items()) @@ -368,6 +452,18 @@ class DbQuotaDriver(object): db.quota_destroy_all_by_project(context, project_id) + def destroy_all_by_user(self, context, user_id, project_id): + """ + Destroy all quotas, usages, and reservations associated with a + user. + + :param context: The request context, for access checks. + :param user_id: The ID of the user being deleted. + :param project_id: The ID of the project being deleted. + """ + + db.quota_destroy_all_by_user(context, user_id, project_id) + def expire(self, context): """Expire reservations. @@ -566,6 +662,11 @@ class QuotaEngine(object): return self._driver.get_by_project(context, project_id, resource) + def get_by_user(self, context, user_id, project_id, resource): + """Get a specific quota by user.""" + + return self._driver.get_by_user(context, user_id, project_id, resource) + def get_by_class(self, context, quota_class, resource): """Get a specific quota by quota class.""" @@ -611,10 +712,46 @@ class QuotaEngine(object): """ return self._driver.get_project_quotas(context, self._resources, - project_id, - quota_class=quota_class, - defaults=defaults, - usages=usages) + project_id, + quota_class=quota_class, + defaults=defaults, + usages=usages) + + def get_user_quotas(self, context, user_id, project_id, + quota_class=None, defaults=True, + usages=True): + """Retrieve the quotas for the given user. + + :param context: The request context, for access checks. + :param user_id: The ID of the user to return quotas for. + :param project_id: The ID of the project to return quotas for. + :param quota_class: If project_id != context.project_id, the + quota class cannot be determined. This + parameter allows it to be specified. + :param defaults: If True, the quota class value (or the + default value, if there is no value from the + quota class) will be reported if there is no + specific value for the resource. + :param usages: If True, the current in_use and reserved counts + will also be returned. + """ + + return self._driver.get_user_quotas(context, self._resources, + user_id, + project_id, + quota_class=quota_class, + defaults=defaults, + usages=usages) + + def get_remaining_quotas(self, context, project_id): + """Retrieve the remaining quotas for the given project. + + :param context: The request context, for access checks. + :param project_id: The ID of the project to return quotas for. + """ + + return self._driver.get_remaining_quotas(context, project_id, + self._resources) def count(self, context, resource, *args, **kwargs): """Count a resource. @@ -745,6 +882,18 @@ class QuotaEngine(object): self._driver.destroy_all_by_project(context, project_id) + def destroy_all_by_user(self, context, user_id, project_id): + """ + Destroy all quotas, usages, and reservations associated with a + user. + + :param context: The request context, for access checks. + :param user_id: The ID of the user being deleted. + :param project_id: The ID of the project being deleted. + """ + + self._driver.destroy_all_by_user(context, user_id, project_id) + def expire(self, context): """Expire reservations. @@ -761,26 +910,26 @@ class QuotaEngine(object): return sorted(self._resources.keys()) -def _sync_instances(context, project_id, session): +def _sync_instances(context, user_id, project_id, session): return dict(zip(('instances', 'cores', 'ram'), - db.instance_data_get_for_project( - context, project_id, session=session))) + db.instance_data_get_for_user( + context, user_id, project_id, session=session))) -def _sync_volumes(context, project_id, session): +def _sync_volumes(context, user_id, project_id, session): return dict(zip(('volumes', 'gigabytes'), - db.volume_data_get_for_project( - context, project_id, session=session))) + db.volume_data_get_for_user( + context, user_id, project_id, session=session))) -def _sync_floating_ips(context, project_id, session): +def _sync_floating_ips(context, user_id, project_id, session): return dict(floating_ips=db.floating_ip_count_by_project( context, project_id, session=session)) -def _sync_security_groups(context, project_id, session): - return dict(security_groups=db.security_group_count_by_project( - context, project_id, session=session)) +def _sync_security_groups(context, user_id, project_id, session): + return dict(security_groups=db.security_group_count_by_user( + context, user_id, project_id, session=session)) QUOTAS = QuotaEngine() |