summaryrefslogtreecommitdiff
path: root/nova/tests/unit/test_quota.py
diff options
context:
space:
mode:
authormelanie witt <melwittt@gmail.com>2017-03-29 19:47:53 +0000
committermelanie witt <melwittt@gmail.com>2017-07-18 17:31:15 +0000
commit5c90b25e49d47deb7dc6695333d9d5e46efe8665 (patch)
tree277343b9039a798d77fc87ab50a037ea87e350e7 /nova/tests/unit/test_quota.py
parent430ec6504b02c5fb69b3808f7e47cf44e865f104 (diff)
downloadnova-5c90b25e49d47deb7dc6695333d9d5e46efe8665.tar.gz
Count instances to check quota
This changes instances, cores, and ram from ReservableResources to CountableResources and replaces quota reserve/commit/rollback with check_deltas accordingly. All of the reservation and usage related unit tests are removed because: 1. They rely on some global QuotaEngine resources being ReservableResources and every ReservableResource has been removed. 2. Reservations and usages are no longer in use anywhere in the codebase. Part of blueprint cells-count-resources-to-check-quota-in-api Change-Id: I9269ffa2b80e48db96c622d0dc0817738854f602
Diffstat (limited to 'nova/tests/unit/test_quota.py')
-rw-r--r--nova/tests/unit/test_quota.py954
1 files changed, 58 insertions, 896 deletions
diff --git a/nova/tests/unit/test_quota.py b/nova/tests/unit/test_quota.py
index 2a41ca15f9..680c0f4cbc 100644
--- a/nova/tests/unit/test_quota.py
+++ b/nova/tests/unit/test_quota.py
@@ -14,11 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import datetime
-
import mock
from oslo_db.sqlalchemy import enginefacade
-from oslo_utils import timeutils
from six.moves import range
from nova import compute
@@ -26,9 +23,9 @@ from nova.compute import flavors
import nova.conf
from nova import context
from nova import db
-from nova.db.sqlalchemy import api as sqa_api
from nova.db.sqlalchemy import models as sqa_models
from nova import exception
+from nova import objects
from nova import quota
from nova import test
import nova.tests.unit.image.fake
@@ -43,7 +40,10 @@ def _get_fake_get_usages(updates=None):
usages = {'security_group_rules': {'in_use': 1},
'key_pairs': {'in_use': 2},
'server_group_members': {'in_use': 3},
- 'floating_ips': {'in_use': 2}}
+ 'floating_ips': {'in_use': 2},
+ 'instances': {'in_use': 2},
+ 'cores': {'in_use': 4},
+ 'ram': {'in_use': 10 * 1024}}
if updates:
usages.update(updates)
@@ -85,22 +85,24 @@ class QuotaIntegrationTestCase(test.TestCase):
super(QuotaIntegrationTestCase, self).tearDown()
nova.tests.unit.image.fake.FakeImageService_reset()
- def _create_instance(self, cores=2):
+ def _create_instance(self, flavor_name='m1.large'):
"""Create a test instance."""
- inst = {}
- inst['image_id'] = 'cedef40a-ed67-4d10-800e-17455edce175'
- inst['reservation_id'] = 'r-fakeres'
- inst['user_id'] = self.user_id
- inst['project_id'] = self.project_id
- inst['instance_type_id'] = '3' # m1.large
- inst['vcpus'] = cores
- return db.instance_create(self.context, inst)
+ inst = objects.Instance(context=self.context)
+ inst.image_id = 'cedef40a-ed67-4d10-800e-17455edce175'
+ inst.reservation_id = 'r-fakeres'
+ inst.user_id = self.user_id
+ inst.project_id = self.project_id
+ inst.flavor = flavors.get_flavor_by_name(flavor_name)
+ # This is needed for instance quota counting until we have the
+ # ability to count allocations in placement.
+ inst.vcpus = inst.flavor.vcpus
+ inst.memory_mb = inst.flavor.memory_mb
+ inst.create()
+ return inst
def test_too_many_instances(self):
- instance_uuids = []
for i in range(CONF.quota.instances):
- instance = self._create_instance()
- instance_uuids.append(instance['uuid'])
+ self._create_instance()
inst_type = flavors.get_flavor_by_name('m1.small')
image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175'
try:
@@ -110,17 +112,15 @@ class QuotaIntegrationTestCase(test.TestCase):
except exception.QuotaError as e:
expected_kwargs = {'code': 413,
'req': '1, 1',
- 'used': '4, 2',
+ 'used': '8, 2',
'allowed': '4, 2',
'overs': 'cores, instances'}
self.assertEqual(expected_kwargs, e.kwargs)
else:
self.fail('Expected QuotaError exception')
- for instance_uuid in instance_uuids:
- db.instance_destroy(self.context, instance_uuid)
def test_too_many_cores(self):
- instance = self._create_instance(cores=4)
+ self._create_instance()
inst_type = flavors.get_flavor_by_name('m1.small')
image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175'
try:
@@ -136,13 +136,14 @@ class QuotaIntegrationTestCase(test.TestCase):
self.assertEqual(expected_kwargs, e.kwargs)
else:
self.fail('Expected QuotaError exception')
- db.instance_destroy(self.context, instance['uuid'])
def test_many_cores_with_unlimited_quota(self):
# Setting cores quota to unlimited:
self.flags(cores=-1, group='quota')
- instance = self._create_instance(cores=4)
- db.instance_destroy(self.context, instance['uuid'])
+ # Default is 20 cores, so create 3x m1.xlarge with
+ # 8 cores each.
+ for i in range(3):
+ self._create_instance(flavor_name='m1.xlarge')
def test_too_many_addresses(self):
# This test is specifically relying on nova-network.
@@ -253,26 +254,6 @@ class QuotaIntegrationTestCase(test.TestCase):
self.assertRaises(exception.QuotaError,
self._create_with_injected_files, files)
- def test_reservation_expire(self):
- self.useFixture(test.TimeOverride())
-
- def assertInstancesReserved(reserved):
- result = quota.QUOTAS.get_project_quotas(self.context,
- self.context.project_id)
- self.assertEqual(result['instances']['reserved'], reserved)
-
- quota.QUOTAS.reserve(self.context,
- expire=60,
- instances=2)
-
- assertInstancesReserved(2)
-
- timeutils.advance_time_seconds(80)
-
- quota.QUOTAS.expire(self.context)
-
- assertInstancesReserved(0)
-
@enginefacade.transaction_context_provider
class FakeContext(context.RequestContext):
@@ -512,17 +493,23 @@ class BaseResourceTestCase(test.TestCase):
def test_valid_method_call_check_wrong_method_reserve(self):
resources = {'key_pairs': 1}
+ engine_resources = {'key_pairs': quota.CountableResource('key_pairs',
+ None,
+ 'key_pairs')}
self.assertRaises(exception.InvalidQuotaMethodUsage,
quota._valid_method_call_check_resources,
- resources, 'reserve', quota.QUOTAS._resources)
+ resources, 'reserve', engine_resources)
def test_valid_method_call_check_wrong_method_check(self):
resources = {'instances': 1}
+ engine_resources = {'instances': quota.ReservableResource('instances',
+ None,
+ 'instances')}
self.assertRaises(exception.InvalidQuotaMethodUsage,
quota._valid_method_call_check_resources,
- resources, 'check', quota.QUOTAS._resources)
+ resources, 'check', engine_resources)
class QuotaEngineTestCase(test.TestCase):
@@ -1000,25 +987,9 @@ class DbQuotaDriverTestCase(test.TestCase):
'injected_file_path_bytes': 127,
}
- def fake_qugabpau(context, project_id, user_id):
- self.calls.append('quota_usage_get_all_by_project_and_user')
- self.assertEqual(project_id, 'test_project')
- self.assertEqual(user_id, 'fake_user')
- return dict(
- instances=dict(in_use=2, reserved=2),
- cores=dict(in_use=4, reserved=4),
- ram=dict(in_use=10 * 1024, reserved=0),
- metadata_items=dict(in_use=0, reserved=0),
- injected_files=dict(in_use=0, reserved=0),
- injected_file_content_bytes=dict(in_use=0, reserved=0),
- injected_file_path_bytes=dict(in_use=0, reserved=0),
- )
-
self.stub_out('nova.db.quota_get_all_by_project_and_user',
fake_qgabpau)
self.stub_out('nova.db.quota_get_all_by_project', fake_qgabp)
- self.stub_out('nova.db.quota_usage_get_all_by_project_and_user',
- fake_qugabpau)
self._stub_quota_class_get_all_by_name()
@@ -1104,13 +1075,9 @@ class DbQuotaDriverTestCase(test.TestCase):
'security_group_rules': {'in_use': 0}}
self.assertEqual(expected, actual)
- @mock.patch('nova.quota.DbQuotaDriver._get_usages')
+ @mock.patch('nova.quota.DbQuotaDriver._get_usages',
+ side_effect=_get_fake_get_usages())
def test_get_user_quotas(self, mock_get_usages):
- # This will test that the counted usage will not be overwritten by
- # the quota_usages records (in_use=2, reserved=2) from the database.
- usages = {'instances': {'in_use': 5}}
- mock_get_usages.side_effect = _get_fake_get_usages(updates=usages)
-
self.maxDiff = None
self._stub_get_by_project_and_user()
ctxt = FakeContext('test_project', 'test_class')
@@ -1120,7 +1087,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project_and_user',
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project_and_user',
'quota_class_get_all_by_name',
])
mock_get_usages.assert_called_once_with(ctxt, quota.QUOTAS._resources,
@@ -1129,13 +1095,13 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(result, dict(
instances=dict(
limit=5,
- in_use=5,
+ in_use=2,
reserved=0,
),
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
ram=dict(
limit=25 * 1024,
@@ -1231,19 +1197,6 @@ class DbQuotaDriverTestCase(test.TestCase):
injected_file_path_bytes=127,
)
- def fake_qugabp(context, project_id):
- self.calls.append('quota_usage_get_all_by_project')
- self.assertEqual(project_id, 'test_project')
- return dict(
- instances=dict(in_use=2, reserved=2),
- cores=dict(in_use=4, reserved=4),
- ram=dict(in_use=10 * 1024, reserved=0),
- metadata_items=dict(in_use=0, reserved=0),
- injected_files=dict(in_use=0, reserved=0),
- injected_file_content_bytes=dict(in_use=0, reserved=0),
- injected_file_path_bytes=dict(in_use=0, reserved=0),
- )
-
def fake_quota_get_all(context, project_id):
self.calls.append('quota_get_all')
self.assertEqual(project_id, 'test_project')
@@ -1253,19 +1206,14 @@ class DbQuotaDriverTestCase(test.TestCase):
hard_limit=2)]
self.stub_out('nova.db.quota_get_all_by_project', fake_qgabp)
- self.stub_out('nova.db.quota_usage_get_all_by_project', fake_qugabp)
self.stub_out('nova.db.quota_get_all', fake_quota_get_all)
self._stub_quota_class_get_all_by_name()
self._stub_quota_class_get_default()
- @mock.patch('nova.quota.DbQuotaDriver._get_usages')
+ @mock.patch('nova.quota.DbQuotaDriver._get_usages',
+ side_effect=_get_fake_get_usages())
def test_get_project_quotas(self, mock_get_usages):
- # This will test that the counted usage will not be overwritten by
- # the quota_usages records (in_use=2, reserved=2) from the database.
- usages = {'instances': {'in_use': 5}}
- mock_get_usages.side_effect = _get_fake_get_usages(updates=usages)
-
self.maxDiff = None
self._stub_get_by_project()
ctxt = FakeContext('test_project', 'test_class')
@@ -1274,7 +1222,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project',
'quota_class_get_all_by_name',
'quota_class_get_default',
])
@@ -1283,13 +1230,13 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(result, dict(
instances=dict(
limit=5,
- in_use=5,
+ in_use=2,
reserved=0,
),
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
ram=dict(
limit=25 * 1024,
@@ -1364,7 +1311,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project',
'quota_class_get_all_by_name',
'quota_class_get_default',
'quota_get_all',
@@ -1375,13 +1321,13 @@ class DbQuotaDriverTestCase(test.TestCase):
instances=dict(
limit=5,
in_use=2,
- reserved=2,
+ reserved=0,
remains=0,
),
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
remains=8,
),
ram=dict(
@@ -1470,7 +1416,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project_and_user',
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project_and_user',
])
mock_get_usages.assert_called_once_with(ctxt, quota.QUOTAS._resources,
'test_project',
@@ -1479,12 +1424,12 @@ class DbQuotaDriverTestCase(test.TestCase):
instances=dict(
limit=10,
in_use=2,
- reserved=2,
+ reserved=0,
),
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
ram=dict(
limit=50 * 1024,
@@ -1559,7 +1504,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project',
'quota_class_get_default',
])
mock_get_usages.assert_called_once_with(ctxt, quota.QUOTAS._resources,
@@ -1568,12 +1512,12 @@ class DbQuotaDriverTestCase(test.TestCase):
instances=dict(
limit=5,
in_use=2,
- reserved=2,
+ reserved=0,
),
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
ram=dict(
limit=25 * 1024,
@@ -1650,7 +1594,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project_and_user',
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project_and_user',
'quota_class_get_all_by_name',
])
mock_get_usages.assert_called_once_with(ctxt, quota.QUOTAS._resources,
@@ -1660,12 +1603,12 @@ class DbQuotaDriverTestCase(test.TestCase):
instances=dict(
limit=5,
in_use=2,
- reserved=2,
+ reserved=0,
),
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
ram=dict(
limit=25 * 1024,
@@ -1741,7 +1684,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project',
'quota_class_get_all_by_name',
'quota_class_get_default',
])
@@ -1751,12 +1693,12 @@ class DbQuotaDriverTestCase(test.TestCase):
instances=dict(
limit=5,
in_use=2,
- reserved=2,
+ reserved=0,
),
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
ram=dict(
limit=25 * 1024,
@@ -1832,7 +1774,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project_and_user',
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project_and_user',
'quota_class_get_all_by_name',
])
mock_get_usages.assert_called_once_with(ctxt, quota.QUOTAS._resources,
@@ -1842,7 +1783,7 @@ class DbQuotaDriverTestCase(test.TestCase):
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
injected_files=dict(
limit=2,
@@ -1866,7 +1807,6 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, [
'quota_get_all_by_project',
- 'quota_usage_get_all_by_project',
'quota_class_get_all_by_name',
'quota_class_get_default',
])
@@ -1876,7 +1816,7 @@ class DbQuotaDriverTestCase(test.TestCase):
cores=dict(
limit=10,
in_use=4,
- reserved=4,
+ reserved=0,
),
injected_files=dict(
limit=2,
@@ -2016,7 +1956,6 @@ class DbQuotaDriverTestCase(test.TestCase):
result = {}
for k, v in resources.items():
limit = v.default
- reserved = 0
if k == 'instances':
remains = v.default - 5
in_use = 1
@@ -2032,7 +1971,7 @@ class DbQuotaDriverTestCase(test.TestCase):
remains = v.default
in_use = 0
result[k] = {'limit': limit, 'in_use': in_use,
- 'reserved': reserved, 'remains': remains}
+ 'remains': remains}
return result
def fake_process_quotas_in_get_user_quotas(dbdrv, context, resources,
@@ -2043,16 +1982,14 @@ class DbQuotaDriverTestCase(test.TestCase):
self.calls.append('_process_quotas')
result = {}
for k, v in resources.items():
- reserved = 0
if k == 'instances':
in_use = 1
elif k == 'cores':
- in_use = 5
- reserved = 10
+ in_use = 15
else:
in_use = 0
result[k] = {'limit': v.default,
- 'in_use': in_use, 'reserved': reserved}
+ 'in_use': in_use}
return result
def fake_qgabpau(context, project_id, user_id):
@@ -2290,82 +2227,14 @@ class DbQuotaDriverTestCase(test.TestCase):
self.stub_out('nova.quota.DbQuotaDriver.get_project_quotas',
fake_get_project_quotas)
- def test_get_quotas_has_sync_unknown(self):
- self._stub_get_project_quotas()
- self.assertRaises(exception.QuotaResourceUnknown,
- self.driver._get_quotas,
- None, quota.QUOTAS._resources,
- ['unknown'], True)
- self.assertEqual(self.calls, [])
-
- def test_get_quotas_no_sync_unknown(self):
- self._stub_get_project_quotas()
- self.assertRaises(exception.QuotaResourceUnknown,
- self.driver._get_quotas,
- None, quota.QUOTAS._resources,
- ['unknown'], False)
- self.assertEqual(self.calls, [])
-
- def test_get_quotas_has_sync_no_sync_resource(self):
+ def test_get_quotas_unknown(self):
self._stub_get_project_quotas()
self.assertRaises(exception.QuotaResourceUnknown,
self.driver._get_quotas,
None, quota.QUOTAS._resources,
- ['metadata_items'], True)
+ ['unknown'])
self.assertEqual(self.calls, [])
- def test_get_quotas_no_sync_has_sync_resource(self):
- self._stub_get_project_quotas()
- self.assertRaises(exception.QuotaResourceUnknown,
- self.driver._get_quotas,
- None, quota.QUOTAS._resources,
- ['instances'], False)
- self.assertEqual(self.calls, [])
-
- def test_get_quotas_has_sync(self):
- self._stub_get_project_quotas()
- result = self.driver._get_quotas(FakeContext('test_project',
- 'test_class'),
- quota.QUOTAS._resources,
- ['instances', 'cores', 'ram'],
- True,
- project_id='test_project')
-
- self.assertEqual(self.calls, ['get_project_quotas'])
- self.assertEqual(result, dict(
- instances=10,
- cores=20,
- ram=50 * 1024,
- ))
-
- def test_get_quotas_no_sync(self):
- self._stub_get_project_quotas()
- result = self.driver._get_quotas(FakeContext('test_project',
- 'test_class'),
- quota.QUOTAS._resources,
- ['metadata_items', 'injected_files',
- 'injected_file_content_bytes',
- 'injected_file_path_bytes',
- 'security_group_rules',
- 'server_group_members',
- 'server_groups', 'security_groups',
- 'floating_ips'],
- False,
- project_id='test_project')
-
- self.assertEqual(self.calls, ['get_project_quotas'])
- self.assertEqual(result, dict(
- metadata_items=128,
- injected_files=5,
- injected_file_content_bytes=10 * 1024,
- injected_file_path_bytes=255,
- security_group_rules=20,
- server_group_members=10,
- server_groups=10,
- security_groups=10,
- floating_ips=10,
- ))
-
def test_limit_check_under(self):
self._stub_get_project_quotas()
self.assertRaises(exception.InvalidQuotaValue,
@@ -2508,713 +2377,6 @@ class DbQuotaDriverTestCase(test.TestCase):
for kwarg in kwargs:
self.driver.limit_check_project_and_user(ctxt, resources, **kwarg)
- def _stub_quota_reserve(self):
- def fake_quota_reserve(context, resources, quotas, user_quotas, deltas,
- expire, until_refresh, max_age, project_id=None,
- user_id=None):
- self.calls.append(('quota_reserve', expire, until_refresh,
- max_age))
- return ['resv-1', 'resv-2', 'resv-3']
- self.stub_out('nova.db.quota_reserve', fake_quota_reserve)
-
- def test_reserve_bad_expire(self):
- self._stub_get_project_quotas()
- self._stub_quota_reserve()
- self.assertRaises(exception.InvalidReservationExpiration,
- self.driver.reserve,
- FakeContext('test_project', 'test_class'),
- quota.QUOTAS._resources,
- dict(instances=2), expire='invalid')
- self.assertEqual(self.calls, [])
-
- def test_reserve_default_expire(self):
- self._stub_get_project_quotas()
- self._stub_quota_reserve()
- result = self.driver.reserve(FakeContext('test_project', 'test_class'),
- quota.QUOTAS._resources,
- dict(instances=2))
-
- expire = timeutils.utcnow() + datetime.timedelta(seconds=86400)
- self.assertEqual(self.calls, [
- 'get_project_quotas',
- ('quota_reserve', expire, 0, 0),
- ])
- self.assertEqual(result, ['resv-1', 'resv-2', 'resv-3'])
-
- def test_reserve_int_expire(self):
- self._stub_get_project_quotas()
- self._stub_quota_reserve()
- result = self.driver.reserve(FakeContext('test_project', 'test_class'),
- quota.QUOTAS._resources,
- dict(instances=2), expire=3600)
-
- expire = timeutils.utcnow() + datetime.timedelta(seconds=3600)
- self.assertEqual(self.calls, [
- 'get_project_quotas',
- ('quota_reserve', expire, 0, 0),
- ])
- self.assertEqual(result, ['resv-1', 'resv-2', 'resv-3'])
-
- def test_reserve_timedelta_expire(self):
- self._stub_get_project_quotas()
- self._stub_quota_reserve()
- expire_delta = datetime.timedelta(seconds=60)
- result = self.driver.reserve(FakeContext('test_project', 'test_class'),
- quota.QUOTAS._resources,
- dict(instances=2), expire=expire_delta)
-
- expire = timeutils.utcnow() + expire_delta
- self.assertEqual(self.calls, [
- 'get_project_quotas',
- ('quota_reserve', expire, 0, 0),
- ])
- self.assertEqual(result, ['resv-1', 'resv-2', 'resv-3'])
-
- def test_reserve_datetime_expire(self):
- self._stub_get_project_quotas()
- self._stub_quota_reserve()
- expire = timeutils.utcnow() + datetime.timedelta(seconds=120)
- result = self.driver.reserve(FakeContext('test_project', 'test_class'),
- quota.QUOTAS._resources,
- dict(instances=2), expire=expire)
-
- self.assertEqual(self.calls, [
- 'get_project_quotas',
- ('quota_reserve', expire, 0, 0),
- ])
- self.assertEqual(result, ['resv-1', 'resv-2', 'resv-3'])
-
- def test_reserve_until_refresh(self):
- self._stub_get_project_quotas()
- self._stub_quota_reserve()
- self.flags(until_refresh=500, group='quota')
- expire = timeutils.utcnow() + datetime.timedelta(seconds=120)
- result = self.driver.reserve(FakeContext('test_project', 'test_class'),
- quota.QUOTAS._resources,
- dict(instances=2), expire=expire)
-
- self.assertEqual(self.calls, [
- 'get_project_quotas',
- ('quota_reserve', expire, 500, 0),
- ])
- self.assertEqual(result, ['resv-1', 'resv-2', 'resv-3'])
-
- def test_reserve_max_age(self):
- self._stub_get_project_quotas()
- self._stub_quota_reserve()
- self.flags(max_age=86400, group='quota')
- expire = timeutils.utcnow() + datetime.timedelta(seconds=120)
- result = self.driver.reserve(FakeContext('test_project', 'test_class'),
- quota.QUOTAS._resources,
- dict(instances=2), expire=expire)
-
- self.assertEqual(self.calls, [
- 'get_project_quotas',
- ('quota_reserve', expire, 0, 86400),
- ])
- self.assertEqual(result, ['resv-1', 'resv-2', 'resv-3'])
-
- def test_usage_reset(self):
- calls = []
-
- def fake_quota_usage_update(context, project_id, user_id, resource,
- **kwargs):
- calls.append(('quota_usage_update', context, project_id, user_id,
- resource, kwargs))
- if resource == 'nonexist':
- raise exception.QuotaUsageNotFound(project_id=project_id)
- self.stub_out('nova.db.quota_usage_update', fake_quota_usage_update)
-
- ctx = FakeContext('test_project', 'test_class')
- resources = ['res1', 'res2', 'nonexist', 'res4']
- self.driver.usage_reset(ctx, resources)
-
- # Make sure we had some calls
- self.assertEqual(len(calls), len(resources))
-
- # Extract the elevated context that was used and do some
- # sanity checks
- elevated = calls[0][1]
- self.assertEqual(elevated.project_id, ctx.project_id)
- self.assertEqual(elevated.quota_class, ctx.quota_class)
- self.assertTrue(elevated.is_admin)
-
- # Now check that all the expected calls were made
- exemplar = [('quota_usage_update', elevated, 'test_project',
- 'fake_user', res, dict(in_use=-1)) for res in resources]
- self.assertEqual(calls, exemplar)
-
-
-class FakeSession(object):
- def begin(self):
- return self
-
- def add(self, instance):
- pass
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, exc_traceback):
- return False
-
-
-class FakeUsage(sqa_models.QuotaUsage):
- def save(self, *args, **kwargs):
- pass
-
-
-class QuotaSqlAlchemyBase(test.TestCase):
- def setUp(self):
- super(QuotaSqlAlchemyBase, self).setUp()
- self.sync_called = set()
- self.quotas = dict(
- instances=5,
- cores=10,
- ram=10 * 1024,
- )
- self.deltas = dict(
- instances=2,
- cores=4,
- ram=2 * 1024,
- )
-
- def make_sync(res_name):
- def sync(context, project_id, user_id):
- self.sync_called.add(res_name)
- if res_name in self.usages:
- if self.usages[res_name].in_use < 0:
- return {res_name: 2}
- else:
- return {res_name: self.usages[res_name].in_use - 1}
- return {res_name: 0}
- return sync
- self.resources = {}
-
- _existing_quota_sync_func_dict = dict(sqa_api.QUOTA_SYNC_FUNCTIONS)
-
- def restore_sync_functions():
- sqa_api.QUOTA_SYNC_FUNCTIONS.clear()
- sqa_api.QUOTA_SYNC_FUNCTIONS.update(_existing_quota_sync_func_dict)
-
- self.addCleanup(restore_sync_functions)
-
- for res_name in ('instances', 'cores', 'ram'):
- method_name = '_sync_%s' % res_name
- sqa_api.QUOTA_SYNC_FUNCTIONS[method_name] = make_sync(res_name)
- res = quota.ReservableResource(res_name, '_sync_%s' % res_name)
- self.resources[res_name] = res
-
- self.expire = timeutils.utcnow() + datetime.timedelta(seconds=3600)
- self.usages = {}
- self.usages_created = {}
- self.reservations_created = {}
- self.usages_list = [
- dict(resource='instances',
- project_id='test_project',
- user_id='fake_user',
- in_use=2,
- reserved=2,
- until_refresh=None),
- dict(resource='cores',
- project_id='test_project',
- user_id='fake_user',
- in_use=2,
- reserved=4,
- until_refresh=None),
- dict(resource='ram',
- project_id='test_project',
- user_id='fake_user',
- in_use=2,
- reserved=2 * 1024,
- until_refresh=None),
- ]
-
- def fake_get_project_user_quota_usages(context, project_id, user_id):
- return self.usages.copy(), self.usages.copy()
-
- def fake_quota_usage_create(project_id, user_id, resource,
- in_use, reserved, until_refresh,
- session):
- quota_usage_ref = self._make_quota_usage(
- project_id, user_id, resource, in_use, reserved, until_refresh,
- timeutils.utcnow(), timeutils.utcnow())
-
- self.usages_created[resource] = quota_usage_ref
-
- return quota_usage_ref
-
- def fake_reservation_create(uuid, usage_id, project_id,
- user_id, resource, delta, expire,
- session):
- reservation_ref = self._make_reservation(
- uuid, usage_id, project_id, user_id, resource, delta, expire,
- timeutils.utcnow(), timeutils.utcnow())
-
- self.reservations_created[resource] = reservation_ref
-
- return reservation_ref
-
- self.stub_out('nova.db.sqlalchemy.api._get_project_user_quota_usages',
- fake_get_project_user_quota_usages)
- self.stub_out('nova.db.sqlalchemy.api._quota_usage_create',
- fake_quota_usage_create)
- self.stub_out('nova.db.sqlalchemy.api._reservation_create',
- fake_reservation_create)
-
- self.useFixture(test.TimeOverride())
-
- def _make_quota_usage(self, project_id, user_id, resource, in_use,
- reserved, until_refresh, created_at, updated_at):
- quota_usage_ref = FakeUsage()
- quota_usage_ref.id = len(self.usages) + len(self.usages_created)
- quota_usage_ref.project_id = project_id
- quota_usage_ref.user_id = user_id
- quota_usage_ref.resource = resource
- quota_usage_ref.in_use = in_use
- quota_usage_ref.reserved = reserved
- quota_usage_ref.until_refresh = until_refresh
- quota_usage_ref.created_at = created_at
- quota_usage_ref.updated_at = updated_at
- quota_usage_ref.deleted_at = None
- quota_usage_ref.deleted = False
-
- return quota_usage_ref
-
- def init_usage(self, project_id, user_id, resource, in_use, reserved=0,
- until_refresh=None, created_at=None, updated_at=None):
- if created_at is None:
- created_at = timeutils.utcnow()
- if updated_at is None:
- updated_at = timeutils.utcnow()
- if resource == 'fixed_ips':
- user_id = None
-
- quota_usage_ref = self._make_quota_usage(project_id, user_id, resource,
- in_use, reserved,
- until_refresh,
- created_at, updated_at)
-
- self.usages[resource] = quota_usage_ref
-
- def compare_usage(self, usage_dict, expected):
- for usage in expected:
- resource = usage['resource']
- for key, value in usage.items():
- actual = getattr(usage_dict[resource], key)
- self.assertEqual(actual, value,
- "%s != %s on usage for resource %s, key %s" %
- (actual, value, resource, key))
-
- def _make_reservation(self, uuid, usage_id, project_id, user_id, resource,
- delta, expire, created_at, updated_at):
- reservation_ref = sqa_models.Reservation()
- reservation_ref.id = len(self.reservations_created)
- reservation_ref.uuid = uuid
- reservation_ref.usage_id = usage_id
- reservation_ref.project_id = project_id
- reservation_ref.user_id = user_id
- reservation_ref.resource = resource
- reservation_ref.delta = delta
- reservation_ref.expire = expire
- reservation_ref.created_at = created_at
- reservation_ref.updated_at = updated_at
- reservation_ref.deleted_at = None
- reservation_ref.deleted = False
-
- return reservation_ref
-
- def compare_reservation(self, reservations, expected):
- reservations = set(reservations)
- for resv in expected:
- resource = resv['resource']
- resv_obj = self.reservations_created[resource]
-
- self.assertIn(resv_obj.uuid, reservations)
- reservations.discard(resv_obj.uuid)
-
- for key, value in resv.items():
- actual = getattr(resv_obj, key)
- self.assertEqual(actual, value,
- "%s != %s on reservation for resource %s" %
- (actual, value, resource))
-
- self.assertEqual(len(reservations), 0)
-
- def _update_reservations_list(self, usage_id_change=False,
- delta_change=False):
- reservations_list = [
- dict(resource='instances',
- project_id='test_project',
- delta=2),
- dict(resource='cores',
- project_id='test_project',
- delta=4),
- dict(resource='ram',
- delta=2 * 1024),
- ]
- if usage_id_change:
- reservations_list[0]["usage_id"] = self.usages_created['instances']
- reservations_list[1]["usage_id"] = self.usages_created['cores']
- reservations_list[2]["usage_id"] = self.usages_created['ram']
- else:
- reservations_list[0]["usage_id"] = self.usages['instances']
- reservations_list[1]["usage_id"] = self.usages['cores']
- reservations_list[2]["usage_id"] = self.usages['ram']
- if delta_change:
- reservations_list[0]["delta"] = -2
- reservations_list[1]["delta"] = -4
- reservations_list[2]["delta"] = -2 * 1024
- return reservations_list
-
- def _init_usages(self, *in_use, **kwargs):
- for i, option in enumerate(('instances', 'cores', 'ram')):
- self.init_usage('test_project', 'fake_user',
- option, in_use[i], **kwargs)
- return FakeContext('test_project', 'test_class')
-
-
-class QuotaReserveSqlAlchemyTestCase(QuotaSqlAlchemyBase):
- # nova.db.sqlalchemy.api.quota_reserve is so complex it needs its
- # own test case, and since it's a quota manipulator, this is the
- # best place to put it...
-
- def test_quota_reserve_create_usages(self):
- context = FakeContext('test_project', 'test_class')
- result = sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire,
- 0, 0)
-
- self.assertEqual(self.sync_called, set(['instances', 'cores',
- 'ram']))
- self.usages_list[0]["in_use"] = 0
- self.usages_list[1]["in_use"] = 0
- self.usages_list[2]["in_use"] = 0
- self.compare_usage(self.usages_created, self.usages_list)
- reservations_list = self._update_reservations_list(True)
- self.compare_reservation(result, reservations_list)
-
- def test_quota_reserve_negative_in_use(self):
- context = self._init_usages(-1, -1, -1, -1, until_refresh=1)
- result = sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire,
- 5, 0)
-
- self.assertEqual(self.sync_called, set(['instances', 'cores',
- 'ram']))
- self.usages_list[0]["until_refresh"] = 5
- self.usages_list[1]["until_refresh"] = 5
- self.usages_list[2]["until_refresh"] = 5
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- self.compare_reservation(result, self._update_reservations_list())
-
- def test_quota_reserve_until_refresh(self):
- context = self._init_usages(3, 3, 3, 3, until_refresh=1)
- result = sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire,
- 5, 0)
-
- self.assertEqual(self.sync_called, set(['instances', 'cores',
- 'ram']))
- self.usages_list[0]["until_refresh"] = 5
- self.usages_list[1]["until_refresh"] = 5
- self.usages_list[2]["until_refresh"] = 5
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- self.compare_reservation(result, self._update_reservations_list())
-
- def test_quota_reserve_max_age(self):
- max_age = 3600
- record_created = (timeutils.utcnow() -
- datetime.timedelta(seconds=max_age))
- context = self._init_usages(3, 3, 3, 3, created_at=record_created,
- updated_at=record_created)
- result = sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire,
- 0, max_age)
-
- self.assertEqual(self.sync_called, set(['instances', 'cores',
- 'ram']))
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- self.compare_reservation(result, self._update_reservations_list())
-
- def test_quota_reserve_no_refresh(self):
- context = self._init_usages(3, 3, 3, 3)
- result = sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire,
- 0, 0)
-
- self.assertEqual(self.sync_called, set([]))
- self.usages_list[0]["in_use"] = 3
- self.usages_list[1]["in_use"] = 3
- self.usages_list[2]["in_use"] = 3
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- self.compare_reservation(result, self._update_reservations_list())
-
- def test_quota_reserve_unders(self):
- context = self._init_usages(1, 3, 1 * 1024, 1)
- self.deltas["instances"] = -2
- self.deltas["cores"] = -4
- self.deltas["ram"] = -2 * 1024
- result = sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire,
- 0, 0)
-
- self.assertEqual(self.sync_called, set([]))
- self.usages_list[0]["in_use"] = 1
- self.usages_list[0]["reserved"] = 0
- self.usages_list[1]["in_use"] = 3
- self.usages_list[1]["reserved"] = 0
- self.usages_list[2]["in_use"] = 1 * 1024
- self.usages_list[2]["reserved"] = 0
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- reservations_list = self._update_reservations_list(False, True)
- self.compare_reservation(result, reservations_list)
-
- def test_quota_reserve_overs(self):
- context = self._init_usages(4, 8, 10 * 1024, 4)
- try:
- sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire, 0, 0)
- except exception.OverQuota as e:
- expected_kwargs = {'code': 500,
- 'usages': {'instances': {'reserved': 0, 'in_use': 4},
- 'ram': {'reserved': 0, 'in_use': 10240},
- 'cores': {'reserved': 0, 'in_use': 8}},
- 'overs': ['cores', 'instances', 'ram'],
- 'quotas': {'cores': 10, 'ram': 10240,
- 'instances': 5}}
- self.assertEqual(e.kwargs, expected_kwargs)
- else:
- self.fail('Expected OverQuota failure')
- self.assertEqual(self.sync_called, set([]))
- self.usages_list[0]["in_use"] = 4
- self.usages_list[0]["reserved"] = 0
- self.usages_list[1]["in_use"] = 8
- self.usages_list[1]["reserved"] = 0
- self.usages_list[2]["in_use"] = 10 * 1024
- self.usages_list[2]["reserved"] = 0
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- self.assertEqual(self.reservations_created, {})
-
- def test_quota_reserve_cores_unlimited(self):
- # Requesting 8 cores, quota_cores set to unlimited:
- self.flags(cores=-1, group='quota')
- self._init_usages(1, 8, 1 * 1024, 1)
- self.assertEqual(self.sync_called, set([]))
- self.usages_list[0]["in_use"] = 1
- self.usages_list[0]["reserved"] = 0
- self.usages_list[1]["in_use"] = 8
- self.usages_list[1]["reserved"] = 0
- self.usages_list[2]["in_use"] = 1 * 1024
- self.usages_list[2]["reserved"] = 0
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- self.assertEqual(self.reservations_created, {})
-
- def test_quota_reserve_ram_unlimited(self):
- # Requesting 10*1024 ram, quota_ram set to unlimited:
- self.flags(ram=-1, group='quota')
- self._init_usages(1, 1, 10 * 1024, 1)
- self.assertEqual(self.sync_called, set([]))
- self.usages_list[0]["in_use"] = 1
- self.usages_list[0]["reserved"] = 0
- self.usages_list[1]["in_use"] = 1
- self.usages_list[1]["reserved"] = 0
- self.usages_list[2]["in_use"] = 10 * 1024
- self.usages_list[2]["reserved"] = 0
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- self.assertEqual(self.reservations_created, {})
-
- def test_quota_reserve_reduction(self):
- context = self._init_usages(10, 20, 20 * 1024, 10)
- self.deltas["instances"] = -2
- self.deltas["cores"] = -4
- self.deltas["ram"] = -2 * 1024
- result = sqa_api.quota_reserve(context, self.resources, self.quotas,
- self.quotas, self.deltas, self.expire,
- 0, 0)
-
- self.assertEqual(self.sync_called, set([]))
- self.usages_list[0]["in_use"] = 10
- self.usages_list[0]["reserved"] = 0
- self.usages_list[1]["in_use"] = 20
- self.usages_list[1]["reserved"] = 0
- self.usages_list[2]["in_use"] = 20 * 1024
- self.usages_list[2]["reserved"] = 0
- self.compare_usage(self.usages, self.usages_list)
- self.assertEqual(self.usages_created, {})
- reservations_list = self._update_reservations_list(False, True)
- self.compare_reservation(result, reservations_list)
-
-
-class QuotaEngineUsageRefreshTestCase(QuotaSqlAlchemyBase):
- def _init_usages(self, *in_use, **kwargs):
- for i, option in enumerate(('instances', 'cores', 'ram')):
- self.init_usage('test_project', 'fake_user',
- option, in_use[i], **kwargs)
- return FakeContext('test_project', 'test_class')
-
- def setUp(self):
- super(QuotaEngineUsageRefreshTestCase, self).setUp()
-
- # The usages_list are the expected usages (in_use) values after
- # the test has run.
- # The pattern is that the test will initialize the actual in_use
- # to 3 for all the resources, then the refresh will sync
- # the actual in_use to 2 for the resources whose names are in the keys
- # list and are scoped to project or user.
-
- # The usages are indexed as follows:
- # Index Resource name Scope
- # 0 instances user
- # 1 cores user
- # 2 ram user
-
- # None of the usage refresh tests should add a reservation.
- self.usages_list[0]['reserved'] = 0
- self.usages_list[1]['reserved'] = 0
- self.usages_list[2]['reserved'] = 0
-
- def fake_quota_get_all_by_project_and_user(context, project_id,
- user_id):
- return self.quotas
-
- def fake_quota_get_all_by_project(context, project_id):
- return self.quotas
-
- self.stub_out('nova.db.sqlalchemy.api.quota_get_all_by_project',
- fake_quota_get_all_by_project)
- self.stub_out(
- 'nova.db.sqlalchemy.api.quota_get_all_by_project_and_user',
- fake_quota_get_all_by_project_and_user)
-
- # The actual sync function for instances, ram, and cores, is
- # _sync_instances, so override the function here.
- def make_instances_sync():
- def sync(context, project_id, user_id):
- updates = {}
- self.sync_called.add('instances')
-
- for res_name in ('instances', 'cores', 'ram'):
- if res_name not in self.usages:
- # Usage doesn't exist yet, initialize
- # the in_use to 0.
- updates[res_name] = 0
- elif self.usages[res_name].in_use < 0:
- updates[res_name] = 2
- else:
- # Simulate as if the actual usage
- # is one less than the recorded usage.
- updates[res_name] = \
- self.usages[res_name].in_use - 1
- return updates
- return sync
-
- sqa_api.QUOTA_SYNC_FUNCTIONS['_sync_instances'] = make_instances_sync()
-
- def test_usage_refresh_user_all_keys(self):
- self._init_usages(3, 3, 3, 3, 3, 3, 3, until_refresh = 5)
- # Let the parameters determine the project_id and user_id,
- # not the context.
- ctxt = context.get_admin_context()
- quota.QUOTAS.usage_refresh(ctxt, 'test_project', 'fake_user')
-
- self.assertEqual(self.sync_called, set(['instances']))
- self.compare_usage(self.usages, self.usages_list)
-
- # No usages were created.
- self.assertEqual(self.usages_created, {})
-
- def test_usage_refresh_user_one_key(self):
- context = self._init_usages(3, 3, 3, 3, 3, 3, 3,
- until_refresh = 5)
- keys = ['ram']
- # Let the context determine the project_id and user_id
- quota.QUOTAS.usage_refresh(context, None, None, keys)
-
- self.assertEqual(self.sync_called, set(['instances']))
-
- # Compare the expected usages with the actual usages.
- self.compare_usage(self.usages, self.usages_list)
-
- # No usages were created.
- self.assertEqual(self.usages_created, {})
-
- def test_usage_refresh_create_user_usage(self):
- context = FakeContext('test_project', 'test_class')
-
- # Create per-user ram usage
- keys = ['ram']
- quota.QUOTAS.usage_refresh(context, 'test_project', 'fake_user', keys)
-
- self.assertEqual(self.sync_called, set(['instances']))
-
- # Compare the expected usages with the created usages.
- # Expect instances to be created and initialized to 0
- self.usages_list[0]['in_use'] = 0
- # Expect cores to be created and initialized to 0
- self.usages_list[1]['in_use'] = 0
- # Expect ram to be created and initialized to 0
- self.usages_list[2]['in_use'] = 0
- self.compare_usage(self.usages_created, self.usages_list[0:3])
-
- self.assertEqual(len(self.usages_created), 3)
-
- def test_usage_refresh_project_all_keys(self):
- self._init_usages(3, 3, 3, 3, 3, 3, 3, until_refresh = 5)
- # Let the parameter determine the project_id, not the context.
- ctxt = context.get_admin_context()
- quota.QUOTAS.usage_refresh(ctxt, 'test_project')
-
- # No sync functions will be called because there are no more
- # project-scoped ReservableResources
- self.assertEqual(self.sync_called, set([]))
-
- # Compare the expected usages with the actual usages.
- # Expect instances not to change since it is user scoped.
- self.usages_list[0]['in_use'] = 3
- self.usages_list[0]['until_refresh'] = 5
- # Expect cores not to change since it is user scoped.
- self.usages_list[1]['in_use'] = 3
- self.usages_list[1]['until_refresh'] = 5
- # Expect ram not to change since it is user scoped.
- self.usages_list[2]['in_use'] = 3
- self.usages_list[2]['until_refresh'] = 5
- self.compare_usage(self.usages, self.usages_list)
-
- self.assertEqual(self.usages_created, {})
-
- def _test_exception(self, context, project_id, user_id, keys):
- try:
- quota.QUOTAS.usage_refresh(context, project_id, user_id, keys)
- except exception.QuotaUsageRefreshNotAllowed as e:
- self.assertIn(keys[0], e.format_message())
- else:
- self.fail('Expected QuotaUsageRefreshNotAllowed failure')
-
- def test_usage_refresh_non_syncable_user_key(self):
- # security_group_rules is a valid user key, but not syncable
- context = FakeContext('test_project', 'test_class')
- self._test_exception(context, 'test_project', 'fake_user',
- ['security_group_rules'])
-
- def test_usage_refresh_invalid_project_key(self):
- ctxt = context.get_admin_context()
- # ram is a valid syncable user key, but not a valid project key
- self._test_exception(ctxt, "test_project", None, ['ram'])
-
- def test_usage_refresh_non_syncable_project_key(self):
- # injected_files is a valid project key, but not syncable
- ctxt = context.get_admin_context()
- self._test_exception(ctxt, 'test_project', None, ['injected_files'])
-
class NoopQuotaDriverTestCase(test.TestCase):
def setUp(self):