diff --git a/trove/common/exception.py b/trove/common/exception.py index 58f81f9764..509f524352 100644 --- a/trove/common/exception.py +++ b/trove/common/exception.py @@ -206,6 +206,12 @@ class OverLimit(TroveError): "rate.") +class QuotaLimitTooSmall(TroveError): + + message = _("Quota limit '%(limit)s' for '%(resource)s' is too small" + " - must be at least '-1'.") + + class QuotaExceeded(TroveError): message = _("Quota exceeded for resources: %(overs)s.") diff --git a/trove/extensions/mgmt/quota/service.py b/trove/extensions/mgmt/quota/service.py index 1786160c7a..b61f4fb295 100644 --- a/trove/extensions/mgmt/quota/service.py +++ b/trove/extensions/mgmt/quota/service.py @@ -55,6 +55,9 @@ class QuotaController(wsgi.Controller): for resource, limit in body['quotas'].items(): if limit is None: continue + elif limit < -1: + raise exception.QuotaLimitTooSmall(limit=limit, + resource=resource) if resource == "xmlns": continue if resource not in registered_resources: diff --git a/trove/quota/quota.py b/trove/quota/quota.py index 24931b7215..ee33f88675 100644 --- a/trove/quota/quota.py +++ b/trove/quota/quota.py @@ -147,6 +147,7 @@ class DbQuotaDriver(object): overs = [resource for resource in deltas if (int(deltas[resource]) > 0 and + quotas[resource].hard_limit >= 0 and (quota_usages[resource].in_use + quota_usages[resource].reserved + int(deltas[resource])) > quotas[resource].hard_limit)] diff --git a/trove/tests/unittests/quota/test_quota.py b/trove/tests/unittests/quota/test_quota.py index 37c8d46eae..9d33e31d19 100644 --- a/trove/tests/unittests/quota/test_quota.py +++ b/trove/tests/unittests/quota/test_quota.py @@ -144,6 +144,15 @@ class QuotaControllerTest(trove_testtools.TestCase): self.assertEqual(200, result.status) self.assertEqual(10, result._data['quotas']['volumes']) + def test_update_resource_with_invalid_negative_number(self): + quota = MagicMock(spec=Quota) + with patch.object(DatabaseModelBase, 'find_by', return_value=quota): + body = {'quotas': {'instances': -2}} + self.assertRaises(exception.QuotaLimitTooSmall, + self.controller.update, + self.req, body, FAKE_TENANT1, + FAKE_TENANT2) + class DbQuotaDriverTest(trove_testtools.TestCase): @@ -386,6 +395,34 @@ class DbQuotaDriverTest(trove_testtools.TestCase): self.assertEqual(0, usages[Resource.VOLUMES].in_use) self.assertEqual(0, usages[Resource.VOLUMES].reserved) + def test_check_quota_with_unlimited_quota(self): + + FAKE_QUOTA_USAGE = [QuotaUsage(id=1, + tenant_id=FAKE_TENANT1, + resource=Resource.INSTANCES, + in_use=1, + reserved=2), + QuotaUsage(id=2, + tenant_id=FAKE_TENANT1, + resource=Resource.VOLUMES, + in_use=1, + reserved=1)] + FAKE_QUOTAS = [Quota(tenant_id=FAKE_TENANT1, + resource=Resource.INSTANCES, + hard_limit=-1), + Quota(tenant_id=FAKE_TENANT1, + resource=Resource.VOLUMES, + hard_limit=-1)] + + self.mock_quota_result.all = Mock(return_value=FAKE_QUOTAS) + self.mock_usage_result.all = Mock(return_value=FAKE_QUOTA_USAGE) + QuotaUsage.save = Mock() + Reservation.create = Mock() + + delta = {'instances': 2, 'volumes': 3} + self.assertIsNone(self.driver.check_quotas(FAKE_TENANT1, resources, + delta)) + def test_reserve(self): FAKE_QUOTAS = [QuotaUsage(id=1,