Skip to content

Commit 6acefc6

Browse files
JohnGarbuttmelwitt
authored andcommitted
Assert quota related API behavior when noop
Adding tests so its clear what happens with the noop driver when using the quota APIs. To make the unit tests work, we had to make the caching of the quota driver slightly more dynamic. We verify the current config matches the currently cached driver, and reload the driver if there is a miss-match. It also preserves the ability of some unit tests to pass in a fake quota driver. We also test the current unified limits driver, as it is currently identical in behaviour to the noop driver. As things evolve the tests will diverge, but will show the common approach to what is returned from the API in both cases. blueprint unified-limits-nova Change-Id: If3c58d6cbf0a0aee62766c7142beab165c1fb9a4
1 parent 4fbe94a commit 6acefc6

File tree

6 files changed

+414
-4
lines changed

6 files changed

+414
-4
lines changed

nova/conf/quota.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@
181181
'on-demand.'),
182182
('nova.quota.NoopQuotaDriver', 'Ignores quota and treats all '
183183
'resources as unlimited.'),
184+
('nova.quota.UnifiedLimitsDriver', 'Do not use. Still being '
185+
'developed.')
184186
],
185187
help="""
186188
Provides abstraction for quota checks. Users can configure a specific

nova/quota.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -882,13 +882,23 @@ def __init__(self, quota_driver=None, resources=None):
882882
}
883883
# NOTE(mriedem): quota_driver is ever only supplied in tests with a
884884
# fake driver.
885-
self.__driver = quota_driver
885+
self.__driver_override = quota_driver
886+
self.__driver = None
887+
self.__driver_name = None
886888

887889
@property
888890
def _driver(self):
889-
if self.__driver:
890-
return self.__driver
891-
self.__driver = importutils.import_object(CONF.quota.driver)
891+
if self.__driver_override:
892+
return self.__driver_override
893+
894+
# NOTE(johngarbutt) to allow unit tests to change the driver by
895+
# simply overriding config, double check if we have the correct
896+
# driver cached before we return the currently cached driver
897+
driver_name_in_config = CONF.quota.driver
898+
if self.__driver_name != driver_name_in_config:
899+
self.__driver = importutils.import_object(driver_name_in_config)
900+
self.__driver_name = driver_name_in_config
901+
892902
return self.__driver
893903

894904
def get_defaults(self, context):

nova/tests/unit/api/openstack/compute/test_limits.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,75 @@ def test_index_additional_query_param(self):
477477
self.assertRaises(
478478
exception.ValidationError,
479479
self.controller.index, req=req)
480+
481+
482+
class NoopLimitsControllerTest(test.NoDBTestCase):
483+
quota_driver = "nova.quota.NoopQuotaDriver"
484+
485+
def setUp(self):
486+
super(NoopLimitsControllerTest, self).setUp()
487+
self.flags(driver=self.quota_driver, group="quota")
488+
self.controller = limits_v21.LimitsController()
489+
# remove policy checks
490+
patcher = self.mock_can = mock.patch('nova.context.RequestContext.can')
491+
self.mock_can = patcher.start()
492+
self.addCleanup(patcher.stop)
493+
494+
def test_index_v21(self):
495+
req = fakes.HTTPRequest.blank("/")
496+
response = self.controller.index(req)
497+
expected_response = {
498+
"limits": {
499+
"rate": [],
500+
"absolute": {
501+
'maxImageMeta': -1,
502+
'maxPersonality': -1,
503+
'maxPersonalitySize': -1,
504+
'maxSecurityGroupRules': -1,
505+
'maxSecurityGroups': -1,
506+
'maxServerGroupMembers': -1,
507+
'maxServerGroups': -1,
508+
'maxServerMeta': -1,
509+
'maxTotalCores': -1,
510+
'maxTotalFloatingIps': -1,
511+
'maxTotalInstances': -1,
512+
'maxTotalKeypairs': -1,
513+
'maxTotalRAMSize': -1,
514+
'totalCoresUsed': -1,
515+
'totalFloatingIpsUsed': -1,
516+
'totalInstancesUsed': -1,
517+
'totalRAMUsed': -1,
518+
'totalSecurityGroupsUsed': -1,
519+
'totalServerGroupsUsed': -1,
520+
},
521+
},
522+
}
523+
self.assertEqual(expected_response, response)
524+
525+
def test_index_v275(self):
526+
req = fakes.HTTPRequest.blank("/?tenant_id=faketenant",
527+
version='2.75')
528+
response = self.controller.index(req)
529+
expected_response = {
530+
"limits": {
531+
"rate": [],
532+
"absolute": {
533+
'maxServerGroupMembers': -1,
534+
'maxServerGroups': -1,
535+
'maxServerMeta': -1,
536+
'maxTotalCores': -1,
537+
'maxTotalInstances': -1,
538+
'maxTotalKeypairs': -1,
539+
'maxTotalRAMSize': -1,
540+
'totalCoresUsed': -1,
541+
'totalInstancesUsed': -1,
542+
'totalRAMUsed': -1,
543+
'totalServerGroupsUsed': -1,
544+
},
545+
},
546+
}
547+
self.assertEqual(expected_response, response)
548+
549+
550+
class UnifiedLimitsControllerTest(NoopLimitsControllerTest):
551+
quota_driver = "nova.quota.UnifiedLimitsDriver"

nova/tests/unit/api/openstack/compute/test_quota_classes.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515
import copy
16+
import mock
1617
import webob
1718

1819
from nova.api.openstack.compute import quota_classes \
1920
as quota_classes_v21
2021
from nova import exception
22+
from nova import objects
2123
from nova import test
2224
from nova.tests.unit.api.openstack import fakes
2325

@@ -156,3 +158,107 @@ def setUp(self):
156158
for resource in quota_classes_v21.FILTERED_QUOTAS_2_57:
157159
self.quota_resources.pop(resource, None)
158160
self.filtered_quotas.extend(quota_classes_v21.FILTERED_QUOTAS_2_57)
161+
162+
163+
class NoopQuotaClassesTest(test.NoDBTestCase):
164+
quota_driver = "nova.quota.NoopQuotaDriver"
165+
166+
def setUp(self):
167+
super(NoopQuotaClassesTest, self).setUp()
168+
self.flags(driver=self.quota_driver, group="quota")
169+
self.controller = quota_classes_v21.QuotaClassSetsController()
170+
171+
def test_show_v21(self):
172+
req = fakes.HTTPRequest.blank("")
173+
response = self.controller.show(req, "test_class")
174+
expected_response = {
175+
'quota_class_set': {
176+
'id': 'test_class',
177+
'cores': -1,
178+
'fixed_ips': -1,
179+
'floating_ips': -1,
180+
'injected_file_content_bytes': -1,
181+
'injected_file_path_bytes': -1,
182+
'injected_files': -1,
183+
'instances': -1,
184+
'key_pairs': -1,
185+
'metadata_items': -1,
186+
'ram': -1,
187+
'security_group_rules': -1,
188+
'security_groups': -1
189+
}
190+
}
191+
self.assertEqual(expected_response, response)
192+
193+
def test_show_v257(self):
194+
req = fakes.HTTPRequest.blank("", version='2.57')
195+
response = self.controller.show(req, "default")
196+
expected_response = {
197+
'quota_class_set': {
198+
'id': 'default',
199+
'cores': -1,
200+
'instances': -1,
201+
'key_pairs': -1,
202+
'metadata_items': -1,
203+
'ram': -1,
204+
'server_group_members': -1,
205+
'server_groups': -1,
206+
}
207+
}
208+
self.assertEqual(expected_response, response)
209+
210+
def test_update_v21_still_rejects_badrequests(self):
211+
req = fakes.HTTPRequest.blank("")
212+
body = {'quota_class_set': {'instances': 50, 'cores': 50,
213+
'ram': 51200, 'unsupported': 12}}
214+
self.assertRaises(exception.ValidationError, self.controller.update,
215+
req, 'test_class', body=body)
216+
217+
@mock.patch.object(objects.Quotas, "update_class")
218+
def test_update_v21(self, mock_update):
219+
req = fakes.HTTPRequest.blank("")
220+
body = {'quota_class_set': {'ram': 51200}}
221+
response = self.controller.update(req, 'default', body=body)
222+
expected_response = {
223+
'quota_class_set': {
224+
'cores': -1,
225+
'fixed_ips': -1,
226+
'floating_ips': -1,
227+
'injected_file_content_bytes': -1,
228+
'injected_file_path_bytes': -1,
229+
'injected_files': -1,
230+
'instances': -1,
231+
'key_pairs': -1,
232+
'metadata_items': -1,
233+
'ram': -1,
234+
'security_group_rules': -1,
235+
'security_groups': -1
236+
}
237+
}
238+
self.assertEqual(expected_response, response)
239+
mock_update.assert_called_once_with(req.environ['nova.context'],
240+
"default", "ram", 51200)
241+
242+
@mock.patch.object(objects.Quotas, "update_class")
243+
def test_update_v257(self, mock_update):
244+
req = fakes.HTTPRequest.blank("", version='2.57')
245+
body = {'quota_class_set': {'ram': 51200}}
246+
response = self.controller.update(req, 'default', body=body)
247+
expected_response = {
248+
'quota_class_set': {
249+
'cores': -1,
250+
'instances': -1,
251+
'key_pairs': -1,
252+
'metadata_items': -1,
253+
'ram': -1,
254+
'server_group_members': -1,
255+
'server_groups': -1,
256+
}
257+
}
258+
self.assertEqual(expected_response, response)
259+
mock_update.assert_called_once_with(req.environ['nova.context'],
260+
"default", "ram", 51200)
261+
262+
263+
class UnifiedLimitsQuotaClassesTest(NoopQuotaClassesTest):
264+
quota_driver = "nova.quota.UnifiedLimitsDriver"

0 commit comments

Comments
 (0)