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
This commit is contained in:
committed by
melanie witt
parent
4fbe94a98c
commit
6acefc6b10
@@ -181,6 +181,8 @@ Possible values:
|
||||
'on-demand.'),
|
||||
('nova.quota.NoopQuotaDriver', 'Ignores quota and treats all '
|
||||
'resources as unlimited.'),
|
||||
('nova.quota.UnifiedLimitsDriver', 'Do not use. Still being '
|
||||
'developed.')
|
||||
],
|
||||
help="""
|
||||
Provides abstraction for quota checks. Users can configure a specific
|
||||
|
||||
+14
-4
@@ -882,13 +882,23 @@ class QuotaEngine(object):
|
||||
}
|
||||
# NOTE(mriedem): quota_driver is ever only supplied in tests with a
|
||||
# fake driver.
|
||||
self.__driver = quota_driver
|
||||
self.__driver_override = quota_driver
|
||||
self.__driver = None
|
||||
self.__driver_name = None
|
||||
|
||||
@property
|
||||
def _driver(self):
|
||||
if self.__driver:
|
||||
return self.__driver
|
||||
self.__driver = importutils.import_object(CONF.quota.driver)
|
||||
if self.__driver_override:
|
||||
return self.__driver_override
|
||||
|
||||
# NOTE(johngarbutt) to allow unit tests to change the driver by
|
||||
# simply overriding config, double check if we have the correct
|
||||
# driver cached before we return the currently cached driver
|
||||
driver_name_in_config = CONF.quota.driver
|
||||
if self.__driver_name != driver_name_in_config:
|
||||
self.__driver = importutils.import_object(driver_name_in_config)
|
||||
self.__driver_name = driver_name_in_config
|
||||
|
||||
return self.__driver
|
||||
|
||||
def get_defaults(self, context):
|
||||
|
||||
@@ -477,3 +477,75 @@ class LimitsControllerTestV275(BaseLimitTestSuite):
|
||||
self.assertRaises(
|
||||
exception.ValidationError,
|
||||
self.controller.index, req=req)
|
||||
|
||||
|
||||
class NoopLimitsControllerTest(test.NoDBTestCase):
|
||||
quota_driver = "nova.quota.NoopQuotaDriver"
|
||||
|
||||
def setUp(self):
|
||||
super(NoopLimitsControllerTest, self).setUp()
|
||||
self.flags(driver=self.quota_driver, group="quota")
|
||||
self.controller = limits_v21.LimitsController()
|
||||
# remove policy checks
|
||||
patcher = self.mock_can = mock.patch('nova.context.RequestContext.can')
|
||||
self.mock_can = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_index_v21(self):
|
||||
req = fakes.HTTPRequest.blank("/")
|
||||
response = self.controller.index(req)
|
||||
expected_response = {
|
||||
"limits": {
|
||||
"rate": [],
|
||||
"absolute": {
|
||||
'maxImageMeta': -1,
|
||||
'maxPersonality': -1,
|
||||
'maxPersonalitySize': -1,
|
||||
'maxSecurityGroupRules': -1,
|
||||
'maxSecurityGroups': -1,
|
||||
'maxServerGroupMembers': -1,
|
||||
'maxServerGroups': -1,
|
||||
'maxServerMeta': -1,
|
||||
'maxTotalCores': -1,
|
||||
'maxTotalFloatingIps': -1,
|
||||
'maxTotalInstances': -1,
|
||||
'maxTotalKeypairs': -1,
|
||||
'maxTotalRAMSize': -1,
|
||||
'totalCoresUsed': -1,
|
||||
'totalFloatingIpsUsed': -1,
|
||||
'totalInstancesUsed': -1,
|
||||
'totalRAMUsed': -1,
|
||||
'totalSecurityGroupsUsed': -1,
|
||||
'totalServerGroupsUsed': -1,
|
||||
},
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
def test_index_v275(self):
|
||||
req = fakes.HTTPRequest.blank("/?tenant_id=faketenant",
|
||||
version='2.75')
|
||||
response = self.controller.index(req)
|
||||
expected_response = {
|
||||
"limits": {
|
||||
"rate": [],
|
||||
"absolute": {
|
||||
'maxServerGroupMembers': -1,
|
||||
'maxServerGroups': -1,
|
||||
'maxServerMeta': -1,
|
||||
'maxTotalCores': -1,
|
||||
'maxTotalInstances': -1,
|
||||
'maxTotalKeypairs': -1,
|
||||
'maxTotalRAMSize': -1,
|
||||
'totalCoresUsed': -1,
|
||||
'totalInstancesUsed': -1,
|
||||
'totalRAMUsed': -1,
|
||||
'totalServerGroupsUsed': -1,
|
||||
},
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
|
||||
class UnifiedLimitsControllerTest(NoopLimitsControllerTest):
|
||||
quota_driver = "nova.quota.UnifiedLimitsDriver"
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import copy
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute import quota_classes \
|
||||
as quota_classes_v21
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
|
||||
@@ -156,3 +158,107 @@ class QuotaClassSetsTestV257(QuotaClassSetsTestV250):
|
||||
for resource in quota_classes_v21.FILTERED_QUOTAS_2_57:
|
||||
self.quota_resources.pop(resource, None)
|
||||
self.filtered_quotas.extend(quota_classes_v21.FILTERED_QUOTAS_2_57)
|
||||
|
||||
|
||||
class NoopQuotaClassesTest(test.NoDBTestCase):
|
||||
quota_driver = "nova.quota.NoopQuotaDriver"
|
||||
|
||||
def setUp(self):
|
||||
super(NoopQuotaClassesTest, self).setUp()
|
||||
self.flags(driver=self.quota_driver, group="quota")
|
||||
self.controller = quota_classes_v21.QuotaClassSetsController()
|
||||
|
||||
def test_show_v21(self):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
response = self.controller.show(req, "test_class")
|
||||
expected_response = {
|
||||
'quota_class_set': {
|
||||
'id': 'test_class',
|
||||
'cores': -1,
|
||||
'fixed_ips': -1,
|
||||
'floating_ips': -1,
|
||||
'injected_file_content_bytes': -1,
|
||||
'injected_file_path_bytes': -1,
|
||||
'injected_files': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'security_group_rules': -1,
|
||||
'security_groups': -1
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
def test_show_v257(self):
|
||||
req = fakes.HTTPRequest.blank("", version='2.57')
|
||||
response = self.controller.show(req, "default")
|
||||
expected_response = {
|
||||
'quota_class_set': {
|
||||
'id': 'default',
|
||||
'cores': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'server_group_members': -1,
|
||||
'server_groups': -1,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
def test_update_v21_still_rejects_badrequests(self):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
body = {'quota_class_set': {'instances': 50, 'cores': 50,
|
||||
'ram': 51200, 'unsupported': 12}}
|
||||
self.assertRaises(exception.ValidationError, self.controller.update,
|
||||
req, 'test_class', body=body)
|
||||
|
||||
@mock.patch.object(objects.Quotas, "update_class")
|
||||
def test_update_v21(self, mock_update):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
body = {'quota_class_set': {'ram': 51200}}
|
||||
response = self.controller.update(req, 'default', body=body)
|
||||
expected_response = {
|
||||
'quota_class_set': {
|
||||
'cores': -1,
|
||||
'fixed_ips': -1,
|
||||
'floating_ips': -1,
|
||||
'injected_file_content_bytes': -1,
|
||||
'injected_file_path_bytes': -1,
|
||||
'injected_files': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'security_group_rules': -1,
|
||||
'security_groups': -1
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
mock_update.assert_called_once_with(req.environ['nova.context'],
|
||||
"default", "ram", 51200)
|
||||
|
||||
@mock.patch.object(objects.Quotas, "update_class")
|
||||
def test_update_v257(self, mock_update):
|
||||
req = fakes.HTTPRequest.blank("", version='2.57')
|
||||
body = {'quota_class_set': {'ram': 51200}}
|
||||
response = self.controller.update(req, 'default', body=body)
|
||||
expected_response = {
|
||||
'quota_class_set': {
|
||||
'cores': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'server_group_members': -1,
|
||||
'server_groups': -1,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
mock_update.assert_called_once_with(req.environ['nova.context'],
|
||||
"default", "ram", 51200)
|
||||
|
||||
|
||||
class UnifiedLimitsQuotaClassesTest(NoopQuotaClassesTest):
|
||||
quota_driver = "nova.quota.UnifiedLimitsDriver"
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from oslo_utils.fixture import uuidsentinel as uuids
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute import quota_sets as quotas_v21
|
||||
from nova.db import constants as db_const
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova import quota
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
@@ -660,3 +662,208 @@ class QuotaSetsTestV275(QuotaSetsTestV257):
|
||||
query_string=query_string)
|
||||
self.assertRaises(exception.ValidationError, self.controller.delete,
|
||||
req, 1234)
|
||||
|
||||
|
||||
class NoopQuotaSetsTest(test.NoDBTestCase):
|
||||
quota_driver = "nova.quota.NoopQuotaDriver"
|
||||
|
||||
def setUp(self):
|
||||
super(NoopQuotaSetsTest, self).setUp()
|
||||
self.flags(driver=self.quota_driver, group="quota")
|
||||
self.controller = quotas_v21.QuotaSetsController()
|
||||
self.stub_out('nova.api.openstack.identity.verify_project_id',
|
||||
lambda ctx, project_id: True)
|
||||
|
||||
def test_show_v21(self):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
response = self.controller.show(req, uuids.project_id)
|
||||
expected_response = {
|
||||
'quota_set': {
|
||||
'id': uuids.project_id,
|
||||
'cores': -1,
|
||||
'fixed_ips': -1,
|
||||
'floating_ips': -1,
|
||||
'injected_file_content_bytes': -1,
|
||||
'injected_file_path_bytes': -1,
|
||||
'injected_files': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'security_group_rules': -1,
|
||||
'security_groups': -1,
|
||||
'server_group_members': -1,
|
||||
'server_groups': -1,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
def test_show_v257(self):
|
||||
req = fakes.HTTPRequest.blank("", version='2.57')
|
||||
response = self.controller.show(req, uuids.project_id)
|
||||
expected_response = {
|
||||
'quota_set': {
|
||||
'id': uuids.project_id,
|
||||
'cores': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'server_group_members': -1,
|
||||
'server_groups': -1}}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
def test_detail_v21(self):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
response = self.controller.detail(req, uuids.project_id)
|
||||
expected_detail = {'in_use': -1, 'limit': -1, 'reserved': -1}
|
||||
expected_response = {
|
||||
'quota_set': {
|
||||
'id': uuids.project_id,
|
||||
'cores': expected_detail,
|
||||
'fixed_ips': expected_detail,
|
||||
'floating_ips': expected_detail,
|
||||
'injected_file_content_bytes': expected_detail,
|
||||
'injected_file_path_bytes': expected_detail,
|
||||
'injected_files': expected_detail,
|
||||
'instances': expected_detail,
|
||||
'key_pairs': expected_detail,
|
||||
'metadata_items': expected_detail,
|
||||
'ram': expected_detail,
|
||||
'security_group_rules': expected_detail,
|
||||
'security_groups': expected_detail,
|
||||
'server_group_members': expected_detail,
|
||||
'server_groups': expected_detail,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
def test_detail_v21_user(self):
|
||||
req = fakes.HTTPRequest.blank("?user_id=42")
|
||||
response = self.controller.detail(req, uuids.project_id)
|
||||
expected_detail = {'in_use': -1, 'limit': -1, 'reserved': -1}
|
||||
expected_response = {
|
||||
'quota_set': {
|
||||
'id': uuids.project_id,
|
||||
'cores': expected_detail,
|
||||
'fixed_ips': expected_detail,
|
||||
'floating_ips': expected_detail,
|
||||
'injected_file_content_bytes': expected_detail,
|
||||
'injected_file_path_bytes': expected_detail,
|
||||
'injected_files': expected_detail,
|
||||
'instances': expected_detail,
|
||||
'key_pairs': expected_detail,
|
||||
'metadata_items': expected_detail,
|
||||
'ram': expected_detail,
|
||||
'security_group_rules': expected_detail,
|
||||
'security_groups': expected_detail,
|
||||
'server_group_members': expected_detail,
|
||||
'server_groups': expected_detail,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
def test_update_still_rejects_badrequests(self):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
body = {'quota_set': {'instances': 50, 'cores': 50,
|
||||
'ram': 51200, 'unsupported': 12}}
|
||||
self.assertRaises(exception.ValidationError, self.controller.update,
|
||||
req, uuids.project_id, body=body)
|
||||
|
||||
@mock.patch.object(objects.Quotas, "create_limit")
|
||||
def test_update_v21(self, mock_create):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
body = {'quota_set': {'server_groups': 2}}
|
||||
response = self.controller.update(req, uuids.project_id, body=body)
|
||||
expected_response = {
|
||||
'quota_set': {
|
||||
'cores': -1,
|
||||
'fixed_ips': -1,
|
||||
'floating_ips': -1,
|
||||
'injected_file_content_bytes': -1,
|
||||
'injected_file_path_bytes': -1,
|
||||
'injected_files': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'security_group_rules': -1,
|
||||
'security_groups': -1,
|
||||
'server_group_members': -1,
|
||||
'server_groups': -1,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
mock_create.assert_called_once_with(req.environ['nova.context'],
|
||||
uuids.project_id, "server_groups",
|
||||
2, user_id=None)
|
||||
|
||||
@mock.patch.object(objects.Quotas, "create_limit")
|
||||
def test_update_v21_user(self, mock_create):
|
||||
req = fakes.HTTPRequest.blank("?user_id=42")
|
||||
body = {'quota_set': {'key_pairs': 52}}
|
||||
response = self.controller.update(req, uuids.project_id, body=body)
|
||||
expected_response = {
|
||||
'quota_set': {
|
||||
'cores': -1,
|
||||
'fixed_ips': -1,
|
||||
'floating_ips': -1,
|
||||
'injected_file_content_bytes': -1,
|
||||
'injected_file_path_bytes': -1,
|
||||
'injected_files': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'security_group_rules': -1,
|
||||
'security_groups': -1,
|
||||
'server_group_members': -1,
|
||||
'server_groups': -1,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
mock_create.assert_called_once_with(req.environ['nova.context'],
|
||||
uuids.project_id, "key_pairs", 52,
|
||||
user_id="42")
|
||||
|
||||
def test_defaults_v21(self):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
response = self.controller.defaults(req, uuids.project_id)
|
||||
expected_response = {
|
||||
'quota_set': {
|
||||
'id': uuids.project_id,
|
||||
'cores': -1,
|
||||
'fixed_ips': -1,
|
||||
'floating_ips': -1,
|
||||
'injected_file_content_bytes': -1,
|
||||
'injected_file_path_bytes': -1,
|
||||
'injected_files': -1,
|
||||
'instances': -1,
|
||||
'key_pairs': -1,
|
||||
'metadata_items': -1,
|
||||
'ram': -1,
|
||||
'security_group_rules': -1,
|
||||
'security_groups': -1,
|
||||
'server_group_members': -1,
|
||||
'server_groups': -1,
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected_response, response)
|
||||
|
||||
@mock.patch('nova.objects.Quotas.destroy_all_by_project')
|
||||
def test_quotas_delete(self, mock_destroy_all_by_project):
|
||||
req = fakes.HTTPRequest.blank("")
|
||||
self.controller.delete(req, "1234")
|
||||
mock_destroy_all_by_project.assert_called_once_with(
|
||||
req.environ['nova.context'], "1234")
|
||||
|
||||
@mock.patch('nova.objects.Quotas.destroy_all_by_project_and_user')
|
||||
def test_user_quotas_delete(self, mock_destroy_all_by_user):
|
||||
req = fakes.HTTPRequest.blank("?user_id=42")
|
||||
self.controller.delete(req, "1234")
|
||||
mock_destroy_all_by_user.assert_called_once_with(
|
||||
req.environ['nova.context'], "1234", "42")
|
||||
|
||||
|
||||
class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest):
|
||||
quota_driver = "nova.quota.UnifiedLimitsDriver"
|
||||
|
||||
@@ -340,6 +340,19 @@ class QuotaEngineTestCase(test.TestCase):
|
||||
quota_obj = quota.QuotaEngine(quota_driver=FakeDriver)
|
||||
self.assertEqual(quota_obj._driver, FakeDriver)
|
||||
|
||||
def test_init_with_flag_set(self):
|
||||
quota_obj = quota.QuotaEngine()
|
||||
self.assertIsInstance(quota_obj._driver, quota.DbQuotaDriver)
|
||||
|
||||
self.flags(group="quota", driver="nova.quota.NoopQuotaDriver")
|
||||
self.assertIsInstance(quota_obj._driver, quota.NoopQuotaDriver)
|
||||
|
||||
self.flags(group="quota", driver="nova.quota.UnifiedLimitsDriver")
|
||||
self.assertIsInstance(quota_obj._driver, quota.UnifiedLimitsDriver)
|
||||
|
||||
self.flags(group="quota", driver="nova.quota.DbQuotaDriver")
|
||||
self.assertIsInstance(quota_obj._driver, quota.DbQuotaDriver)
|
||||
|
||||
def _get_quota_engine(self, driver, resources=None):
|
||||
resources = resources or [
|
||||
quota.AbsoluteResource('test_resource4'),
|
||||
|
||||
Reference in New Issue
Block a user