16
16
# License for the specific language governing permissions and limitations
17
17
# under the License.
22
from oslo.config import cfg
21
24
from cinder import context
22
25
from cinder import db
23
26
from cinder.db.sqlalchemy import api as sqa_api
24
27
from cinder.db.sqlalchemy import models as sqa_models
25
28
from cinder import exception
26
from cinder import flags
27
29
from cinder.openstack.common import rpc
28
30
from cinder.openstack.common import timeutils
29
31
from cinder import quota
32
34
from cinder import volume
38
40
class QuotaIntegrationTestCase(test.TestCase):
41
43
super(QuotaIntegrationTestCase, self).setUp()
44
self.volume_type_name = CONF.default_volume_type
45
self.volume_type = db.volume_type_create(
46
context.get_admin_context(),
47
dict(name=self.volume_type_name))
42
49
self.flags(quota_volumes=2,
44
51
quota_gigabytes=20)
46
# Apparently needed by the RPC tests...
47
#self.network = self.start_service('network')
49
53
self.user_id = 'admin'
50
54
self.project_id = 'admin'
51
55
self.context = context.RequestContext(self.user_id,
59
63
self.stubs.Set(rpc, 'call', rpc_call_wrapper)
61
65
def tearDown(self):
66
db.volume_type_destroy(context.get_admin_context(),
67
self.volume_type['id'])
62
68
super(QuotaIntegrationTestCase, self).tearDown()
63
69
cinder.tests.image.fake.FakeImageService_reset()
65
def _create_volume(self, size=10):
71
def _create_volume(self, size=1):
66
72
"""Create a test volume."""
68
74
vol['user_id'] = self.user_id
69
75
vol['project_id'] = self.project_id
71
77
vol['status'] = 'available'
78
vol['volume_type_id'] = self.volume_type['id']
72
79
return db.volume_create(self.context, vol)
74
81
def _create_snapshot(self, volume):
83
90
def test_too_many_volumes(self):
85
for i in range(FLAGS.quota_volumes):
92
for i in range(CONF.quota_volumes):
86
93
vol_ref = self._create_volume()
87
94
volume_ids.append(vol_ref['id'])
88
self.assertRaises(exception.QuotaError,
95
self.assertRaises(exception.VolumeLimitExceeded,
89
96
volume.API().create,
90
self.context, 10, '', '', None)
97
self.context, 1, '', '',
98
volume_type=self.volume_type)
91
99
for volume_id in volume_ids:
92
100
db.volume_destroy(self.context, volume_id)
102
def test_too_many_volumes_of_type(self):
103
resource = 'volumes_%s' % self.volume_type_name
104
db.quota_class_create(self.context, 'default', resource, 1)
106
'quota_volumes': 2000,
107
'quota_gigabytes': 2000
109
self.flags(**flag_args)
110
vol_ref = self._create_volume()
111
self.assertRaises(exception.VolumeLimitExceeded,
113
self.context, 1, '', '',
114
volume_type=self.volume_type)
115
db.volume_destroy(self.context, vol_ref['id'])
117
def test_too_many_snapshots_of_type(self):
118
resource = 'snapshots_%s' % self.volume_type_name
119
db.quota_class_create(self.context, 'default', resource, 1)
121
'quota_volumes': 2000,
122
'quota_gigabytes': 2000,
124
self.flags(**flag_args)
125
vol_ref = self._create_volume()
126
snap_ref = self._create_snapshot(vol_ref)
127
self.assertRaises(exception.SnapshotLimitExceeded,
128
volume.API().create_snapshot,
129
self.context, vol_ref, '', '')
130
db.snapshot_destroy(self.context, snap_ref['id'])
131
db.volume_destroy(self.context, vol_ref['id'])
94
133
def test_too_many_gigabytes(self):
96
135
vol_ref = self._create_volume(size=20)
97
136
volume_ids.append(vol_ref['id'])
98
self.assertRaises(exception.QuotaError,
137
self.assertRaises(exception.VolumeSizeExceedsAvailableQuota,
99
138
volume.API().create,
100
self.context, 10, '', '', None)
139
self.context, 1, '', '',
140
volume_type=self.volume_type)
101
141
for volume_id in volume_ids:
102
142
db.volume_destroy(self.context, volume_id)
129
169
self.assertEqual(reservations.get('gigabytes'), None)
131
171
# Make sure the snapshot volume_size isn't included in usage.
132
vol_type = db.volume_type_create(self.context,
133
dict(name=FLAGS.default_volume_type))
134
172
vol_ref2 = volume.API().create(self.context, 10, '', '')
135
173
usages = db.quota_usage_get_all_by_project(self.context,
140
178
db.snapshot_destroy(self.context, snap_ref2['id'])
141
179
db.volume_destroy(self.context, vol_ref['id'])
142
180
db.volume_destroy(self.context, vol_ref2['id'])
143
db.volume_type_destroy(self.context, vol_type['id'])
182
def test_too_many_gigabytes_of_type(self):
183
resource = 'gigabytes_%s' % self.volume_type_name
184
db.quota_class_create(self.context, 'default', resource, 10)
186
'quota_volumes': 2000,
187
'quota_gigabytes': 2000,
189
self.flags(**flag_args)
190
vol_ref = self._create_volume(size=10)
191
self.assertRaises(exception.VolumeSizeExceedsAvailableQuota,
193
self.context, 1, '', '',
194
volume_type=self.volume_type)
195
db.volume_destroy(self.context, vol_ref['id'])
146
198
class FakeContext(object):
178
230
raise exception.QuotaClassNotFound(class_name=quota_class)
232
def get_default(self, context, resource):
233
self.called.append(('get_default', context, resource))
234
return resource.default
180
236
def get_defaults(self, context, resources):
181
237
self.called.append(('get_defaults', context, resources))
308
364
self.assertEqual(quota_value, 20)
367
class VolumeTypeResourceTestCase(test.TestCase):
368
def test_name_and_flag(self):
369
volume_type_name = 'foo'
370
volume = {'name': volume_type_name, 'id': 'myid'}
371
resource = quota.VolumeTypeResource('volumes', volume)
373
self.assertEqual(resource.name, 'volumes_%s' % volume_type_name)
374
self.assertEqual(resource.flag, None)
375
self.assertEqual(resource.default, -1)
311
378
class QuotaEngineTestCase(test.TestCase):
312
379
def test_init(self):
313
380
quota_obj = quota.QuotaEngine()
315
self.assertEqual(quota_obj._resources, {})
382
self.assertEqual(quota_obj.resources, {})
316
383
self.assertTrue(isinstance(quota_obj._driver, quota.DbQuotaDriver))
318
385
def test_init_override_string(self):
319
386
quota_obj = quota.QuotaEngine(
320
387
quota_driver_class='cinder.tests.test_quota.FakeDriver')
322
self.assertEqual(quota_obj._resources, {})
389
self.assertEqual(quota_obj.resources, {})
323
390
self.assertTrue(isinstance(quota_obj._driver, FakeDriver))
325
392
def test_init_override_obj(self):
326
393
quota_obj = quota.QuotaEngine(quota_driver_class=FakeDriver)
328
self.assertEqual(quota_obj._resources, {})
395
self.assertEqual(quota_obj.resources, {})
329
396
self.assertEqual(quota_obj._driver, FakeDriver)
331
398
def test_register_resource(self):
333
400
resource = quota.AbsoluteResource('test_resource')
334
401
quota_obj.register_resource(resource)
336
self.assertEqual(quota_obj._resources, dict(test_resource=resource))
403
self.assertEqual(quota_obj.resources, dict(test_resource=resource))
338
405
def test_register_resources(self):
339
406
quota_obj = quota.QuotaEngine()
343
410
quota.AbsoluteResource('test_resource3'), ]
344
411
quota_obj.register_resources(resources)
346
self.assertEqual(quota_obj._resources,
413
self.assertEqual(quota_obj.resources,
347
414
dict(test_resource1=resources[0],
348
415
test_resource2=resources[1],
349
416
test_resource3=resources[2], ))
427
494
self.assertEqual(driver.called, [('get_defaults',
429
quota_obj._resources), ])
430
self.assertEqual(result, quota_obj._resources)
496
quota_obj.resources), ])
497
self.assertEqual(result, quota_obj.resources)
432
499
def test_get_class_quotas(self):
433
500
context = FakeContext(None, None)
439
506
self.assertEqual(driver.called, [
440
507
('get_class_quotas',
442
quota_obj._resources,
443
510
'test_class', True),
444
511
('get_class_quotas',
445
context, quota_obj._resources,
512
context, quota_obj.resources,
446
513
'test_class', False), ])
447
self.assertEqual(result1, quota_obj._resources)
448
self.assertEqual(result2, quota_obj._resources)
514
self.assertEqual(result1, quota_obj.resources)
515
self.assertEqual(result2, quota_obj.resources)
450
517
def test_get_project_quotas(self):
451
518
context = FakeContext(None, None)
460
527
self.assertEqual(driver.called, [
461
528
('get_project_quotas',
463
quota_obj._resources,
468
535
('get_project_quotas',
470
quota_obj._resources,
475
self.assertEqual(result1, quota_obj._resources)
476
self.assertEqual(result2, quota_obj._resources)
542
self.assertEqual(result1, quota_obj.resources)
543
self.assertEqual(result2, quota_obj.resources)
478
545
def test_count_no_resource(self):
479
546
context = FakeContext(None, None)
633
700
self.assertEqual(driver.called, [('expire', context), ])
635
def test_resources(self):
702
def test_resource_names(self):
636
703
quota_obj = self._make_quota_obj(None)
638
self.assertEqual(quota_obj.resources,
705
self.assertEqual(quota_obj.resource_names,
639
706
['test_resource1', 'test_resource2',
640
707
'test_resource3', 'test_resource4'])
710
class VolumeTypeQuotaEngineTestCase(test.TestCase):
711
def test_default_resources(self):
712
engine = quota.VolumeTypeQuotaEngine()
713
self.assertEqual(engine.resource_names,
714
['gigabytes', 'snapshots', 'volumes'])
716
def test_volume_type_resources(self):
717
ctx = context.RequestContext('admin', 'admin', is_admin=True)
718
vtype = db.volume_type_create(ctx, {'name': 'type1'})
719
vtype2 = db.volume_type_create(ctx, {'name': 'type_2'})
720
engine = quota.VolumeTypeQuotaEngine()
721
self.assertEqual(engine.resource_names,
722
['gigabytes', 'gigabytes_type1', 'gigabytes_type_2',
723
'snapshots', 'snapshots_type1', 'snapshots_type_2',
724
'volumes', 'volumes_type1', 'volumes_type_2'])
725
db.volume_type_destroy(ctx, vtype['id'])
726
db.volume_type_destroy(ctx, vtype2['id'])
643
729
class DbQuotaDriverTestCase(test.TestCase):
645
731
super(DbQuotaDriverTestCase, self).setUp()
674
761
gigabytes=1000, ))
763
def _stub_quota_class_get_default(self):
764
# Stub out quota_class_get_default
765
def fake_qcgd(context):
766
self.calls.append('quota_class_get_default')
767
return dict(volumes=10,
770
self.stubs.Set(db, 'quota_class_get_default', fake_qcgd)
676
772
def _stub_quota_class_get_all_by_name(self):
677
773
# Stub out quota_class_get_all_by_name
678
774
def fake_qcgabn(context, quota_class):
684
780
def test_get_class_quotas(self):
685
781
self._stub_quota_class_get_all_by_name()
686
result = self.driver.get_class_quotas(None, quota.QUOTAS._resources,
782
result = self.driver.get_class_quotas(None, quota.QUOTAS.resources,
689
785
self.assertEqual(self.calls, ['quota_class_get_all_by_name'])
694
790
def test_get_class_quotas_no_defaults(self):
695
791
self._stub_quota_class_get_all_by_name()
696
result = self.driver.get_class_quotas(None, quota.QUOTAS._resources,
792
result = self.driver.get_class_quotas(None, quota.QUOTAS.resources,
697
793
'test_class', False)
699
795
self.assertEqual(self.calls, ['quota_class_get_all_by_name'])
718
814
self.stubs.Set(db, 'quota_usage_get_all_by_project', fake_qugabp)
720
816
self._stub_quota_class_get_all_by_name()
817
self._stub_quota_class_get_default()
722
819
def test_get_project_quotas(self):
723
820
self._stub_get_by_project()
724
821
result = self.driver.get_project_quotas(
725
822
FakeContext('test_project', 'test_class'),
726
quota.QUOTAS._resources, 'test_project')
823
quota.QUOTAS.resources, 'test_project')
728
825
self.assertEqual(self.calls, ['quota_get_all_by_project',
729
826
'quota_usage_get_all_by_project',
730
'quota_class_get_all_by_name', ])
827
'quota_class_get_all_by_name',
828
'quota_class_get_default', ])
731
829
self.assertEqual(result, dict(volumes=dict(limit=10,
742
840
self._stub_get_by_project()
743
841
result = self.driver.get_project_quotas(
744
842
FakeContext('other_project', 'other_class'),
745
quota.QUOTAS._resources, 'test_project')
843
quota.QUOTAS.resources, 'test_project')
747
845
self.assertEqual(self.calls, ['quota_get_all_by_project',
748
'quota_usage_get_all_by_project', ])
846
'quota_usage_get_all_by_project',
847
'quota_class_get_default', ])
749
848
self.assertEqual(result, dict(volumes=dict(limit=10,
760
859
self._stub_get_by_project()
761
860
result = self.driver.get_project_quotas(
762
861
FakeContext('other_project', 'other_class'),
763
quota.QUOTAS._resources, 'test_project', quota_class='test_class')
862
quota.QUOTAS.resources, 'test_project', quota_class='test_class')
765
864
self.assertEqual(self.calls, ['quota_get_all_by_project',
766
865
'quota_usage_get_all_by_project',
767
'quota_class_get_all_by_name', ])
866
'quota_class_get_all_by_name',
867
'quota_class_get_default', ])
768
868
self.assertEqual(result, dict(volumes=dict(limit=10,
779
879
self._stub_get_by_project()
780
880
result = self.driver.get_project_quotas(
781
881
FakeContext('test_project', 'test_class'),
782
quota.QUOTAS._resources, 'test_project', defaults=False)
882
quota.QUOTAS.resources, 'test_project', defaults=False)
784
884
self.assertEqual(self.calls, ['quota_get_all_by_project',
785
885
'quota_usage_get_all_by_project',
786
'quota_class_get_all_by_name', ])
886
'quota_class_get_all_by_name',
887
'quota_class_get_default', ])
787
888
self.assertEqual(result,
788
889
dict(gigabytes=dict(limit=50,
799
900
self._stub_get_by_project()
800
901
result = self.driver.get_project_quotas(
801
902
FakeContext('test_project', 'test_class'),
802
quota.QUOTAS._resources, 'test_project', usages=False)
903
quota.QUOTAS.resources, 'test_project', usages=False)
804
905
self.assertEqual(self.calls, ['quota_get_all_by_project',
805
'quota_class_get_all_by_name', ])
906
'quota_class_get_all_by_name',
907
'quota_class_get_default', ])
806
908
self.assertEqual(result, dict(volumes=dict(limit=10, ),
807
909
snapshots=dict(limit=10, ),
808
910
gigabytes=dict(limit=50, ), ))
822
924
self._stub_get_project_quotas()
823
925
self.assertRaises(exception.QuotaResourceUnknown,
824
926
self.driver._get_quotas,
825
None, quota.QUOTAS._resources,
927
None, quota.QUOTAS.resources,
826
928
['unknown'], True)
827
929
self.assertEqual(self.calls, [])
830
932
self._stub_get_project_quotas()
831
933
self.assertRaises(exception.QuotaResourceUnknown,
832
934
self.driver._get_quotas,
833
None, quota.QUOTAS._resources,
935
None, quota.QUOTAS.resources,
834
936
['unknown'], False)
835
937
self.assertEqual(self.calls, [])
838
940
self._stub_get_project_quotas()
839
941
self.assertRaises(exception.QuotaResourceUnknown,
840
942
self.driver._get_quotas,
841
None, quota.QUOTAS._resources,
943
None, quota.QUOTAS.resources,
842
944
['metadata_items'], True)
843
945
self.assertEqual(self.calls, [])
846
948
self._stub_get_project_quotas()
847
949
self.assertRaises(exception.QuotaResourceUnknown,
848
950
self.driver._get_quotas,
849
None, quota.QUOTAS._resources,
951
None, quota.QUOTAS.resources,
850
952
['volumes'], False)
851
953
self.assertEqual(self.calls, [])
921
1023
self._stub_quota_reserve()
922
1024
expire = timeutils.utcnow() + datetime.timedelta(seconds=120)
923
1025
result = self.driver.reserve(FakeContext('test_project', 'test_class'),
924
quota.QUOTAS._resources,
1026
quota.QUOTAS.resources,
925
1027
dict(volumes=2), expire=expire)
927
1029
self.assertEqual(self.calls, ['get_project_quotas',
934
1036
self.flags(until_refresh=500)
935
1037
expire = timeutils.utcnow() + datetime.timedelta(seconds=120)
936
1038
result = self.driver.reserve(FakeContext('test_project', 'test_class'),
937
quota.QUOTAS._resources,
1039
quota.QUOTAS.resources,
938
1040
dict(volumes=2), expire=expire)
940
1042
self.assertEqual(self.calls, ['get_project_quotas',
947
1049
self.flags(max_age=86400)
948
1050
expire = timeutils.utcnow() + datetime.timedelta(seconds=120)
949
1051
result = self.driver.reserve(FakeContext('test_project', 'test_class'),
950
quota.QUOTAS._resources,
1052
quota.QUOTAS.resources,
951
1053
dict(volumes=2), expire=expire)
953
1055
self.assertEqual(self.calls, ['get_project_quotas',
1048
1150
self.stubs.Set(sqa_api, 'get_session', fake_get_session)
1049
1151
self.stubs.Set(sqa_api, '_get_quota_usages', fake_get_quota_usages)
1050
self.stubs.Set(sqa_api, 'quota_usage_create', fake_quota_usage_create)
1051
self.stubs.Set(sqa_api, 'reservation_create', fake_reservation_create)
1152
self.stubs.Set(sqa_api, '_quota_usage_create', fake_quota_usage_create)
1153
self.stubs.Set(sqa_api, '_reservation_create', fake_reservation_create)
1053
1155
timeutils.set_time_override()