From 91859c5c1a7b17e1f954bae03a7269be3c4348e6 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Mon, 7 Jan 2013 20:58:09 +0000 Subject: Add NoopQuotaDriver Allows turning quota calls into no-ops, pretending that all quotas are unlimited. Useful for child compute cells. Use by setting this in nova.conf: quota_driver=nova.quota.NoopQuotaDriver DocImpact Change-Id: Id80a89df5ba73db3f5edc05bd0ef4627e9c211fa --- nova/quota.py | 188 ++++++++++++++++++++++++++++++++++++++++++++++- nova/tests/test_quota.py | 62 ++++++++++++++++ 2 files changed, 249 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/quota.py b/nova/quota.py index fad125805..c2e34cca5 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -40,7 +40,7 @@ quota_opts = [ cfg.IntOpt('quota_ram', default=50 * 1024, help='megabytes of instance ram allowed per project'), - cfg.IntOpt('quota_floating_ips', + cfg.IntOpt('quota_floating_ips', default=10, help='number of floating ips allowed per project'), cfg.IntOpt('quota_metadata_items', @@ -402,6 +402,192 @@ class DbQuotaDriver(object): db.reservation_expire(context) +class NoopQuotaDriver(object): + """Driver that turns quotas calls into no-ops and pretends that quotas + for all resources are unlimited. This can be used if you do not + wish to have any quota checking. For instance, with nova compute + cells, the parent cell should do quota checking, but the child cell + should not. + """ + + def get_by_project(self, context, project_id, resource): + """Get a specific quota by project.""" + # Unlimited + return -1 + + def get_by_class(self, context, quota_class, resource): + """Get a specific quota by quota class.""" + # Unlimited + return -1 + + def get_defaults(self, context, resources): + """Given a list of resources, retrieve the default quotas. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resources. + """ + quotas = {} + for resource in resources.values(): + quotas[resource.name] = -1 + return quotas + + def get_class_quotas(self, context, resources, quota_class, + defaults=True): + """ + Given a list of resources, retrieve the quotas for the given + quota class. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resources. + :param quota_class: The name of the quota class to return + quotas for. + :param defaults: If True, the default value will be reported + if there is no specific value for the + resource. + """ + quotas = {} + for resource in resources.values(): + quotas[resource.name] = -1 + return 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. + """ + quotas = {} + for resource in resources.values(): + quotas[resource.name] = -1 + return quotas + + def limit_check(self, context, resources, values): + """Check simple quota limits. + + For limits--those quotas for which there is no usage + synchronization function--this method checks that a set of + proposed values are permitted by the limit restriction. + + This method will raise a QuotaResourceUnknown exception if a + given resource is unknown or if it is not a simple limit + resource. + + If any of the proposed values is over the defined quota, an + OverQuota exception will be raised with the sorted list of the + resources which are too high. Otherwise, the method returns + nothing. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resources. + :param values: A dictionary of the values to check against the + quota. + """ + pass + + def reserve(self, context, resources, deltas, expire=None): + """Check quotas and reserve resources. + + For counting quotas--those quotas for which there is a usage + synchronization function--this method checks quotas against + current usage and the desired deltas. + + This method will raise a QuotaResourceUnknown exception if a + given resource is unknown or if it does not have a usage + synchronization function. + + If any of the proposed values is over the defined quota, an + OverQuota exception will be raised with the sorted list of the + resources which are too high. Otherwise, the method returns a + list of reservation UUIDs which were created. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resources. + :param deltas: A dictionary of the proposed delta changes. + :param expire: An optional parameter specifying an expiration + time for the reservations. If it is a simple + number, it is interpreted as a number of + seconds and added to the current time; if it is + a datetime.timedelta object, it will also be + added to the current time. A datetime.datetime + object will be interpreted as the absolute + expiration time. If None is specified, the + default expiration time set by + --default-reservation-expire will be used (this + value will be treated as a number of seconds). + """ + return [] + + def commit(self, context, reservations): + """Commit reservations. + + :param context: The request context, for access checks. + :param reservations: A list of the reservation UUIDs, as + returned by the reserve() method. + """ + pass + + def rollback(self, context, reservations): + """Roll back reservations. + + :param context: The request context, for access checks. + :param reservations: A list of the reservation UUIDs, as + returned by the reserve() method. + """ + pass + + def usage_reset(self, context, resources): + """ + Reset the usage records for a particular user on a list of + resources. This will force that user's usage records to be + refreshed the next time a reservation is made. + + Note: this does not affect the currently outstanding + reservations the user has; those reservations must be + committed or rolled back (or expired). + + :param context: The request context, for access checks. + :param resources: A list of the resource names for which the + usage must be reset. + """ + pass + + def destroy_all_by_project(self, context, project_id): + """ + Destroy all quotas, usages, and reservations associated with a + project. + + :param context: The request context, for access checks. + :param project_id: The ID of the project being deleted. + """ + pass + + def expire(self, context): + """Expire reservations. + + Explores all currently existing reservations and rolls back + any that have expired. + + :param context: The request context, for access checks. + """ + pass + + class BaseResource(object): """Describe a single resource for quota checking.""" diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 50e5d6d8f..28e9ccfda 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -1892,3 +1892,65 @@ class QuotaReserveSqlAlchemyTestCase(test.TestCase): project_id='test_project', delta=-2 * 1024), ]) + + +class NoopQuotaDriverTestCase(test.TestCase): + def setUp(self): + super(NoopQuotaDriverTestCase, self).setUp() + + self.flags(quota_instances=10, + quota_cores=20, + quota_ram=50 * 1024, + quota_floating_ips=10, + quota_metadata_items=128, + quota_injected_files=5, + quota_injected_file_content_bytes=10 * 1024, + quota_injected_file_path_bytes=255, + quota_security_groups=10, + quota_security_group_rules=20, + reservation_expire=86400, + until_refresh=0, + max_age=0, + ) + + self.expected_quotas = dict([(r, -1) + for r in quota.QUOTAS._resources]) + self.driver = quota.NoopQuotaDriver() + + def test_get_defaults(self): + # Use our pre-defined resources + result = self.driver.get_defaults(None, quota.QUOTAS._resources) + self.assertEqual(self.expected_quotas, result) + + def test_get_class_quotas(self): + result = self.driver.get_class_quotas(None, + quota.QUOTAS._resources, + 'test_class') + self.assertEqual(self.expected_quotas, result) + + def test_get_class_quotas_no_defaults(self): + result = self.driver.get_class_quotas(None, + quota.QUOTAS._resources, + 'test_class', + False) + self.assertEqual(self.expected_quotas, result) + + def test_get_project_quotas(self): + result = self.driver.get_project_quotas(None, + quota.QUOTAS._resources, + 'test_project') + self.assertEqual(self.expected_quotas, result) + + def test_get_project_quotas_no_defaults(self): + result = self.driver.get_project_quotas(None, + quota.QUOTAS._resources, + 'test_project', + defaults=False) + self.assertEqual(self.expected_quotas, result) + + def test_get_project_quotas_no_usages(self): + result = self.driver.get_project_quotas(None, + quota.QUOTAS._resources, + 'test_project', + usages=False) + self.assertEqual(self.expected_quotas, result) -- cgit