diff --git a/nova/quota.py b/nova/quota.py index b8fa078259..9390e3e2f2 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -29,6 +29,7 @@ from nova.db.api import models as api_models from nova.db.main import api as main_db_api from nova import exception from nova.limit import local as local_limit +from nova.limit import placement as placement_limit from nova import objects from nova.scheduler.client import report from nova import utils @@ -806,17 +807,17 @@ class UnifiedLimitsDriver(NoopQuotaDriver): def get_defaults(self, context, resources): local_limits = local_limit.get_legacy_default_limits() - # TODO(melwitt): This is temporary when we are in a state where cores, - # ram, and instances quota limits are not known/enforced with unified - # limits yet. This will occur in later patches and when it does, we - # will change the default to 0 to signal to operators that they need to - # register a limit for a resource before that resource will be - # allocated. - # Default to unlimited, as per no-op for everything that isn't - # a local limit + # Note we get 0 if there is no registered limit, + # to mirror oslo_limit behaviour when there is no registered limit + placement_limits = placement_limit.get_legacy_default_limits() quotas = {} for resource in resources.values(): - quotas[resource.name] = local_limits.get(resource.name, -1) + if resource.name in placement_limits: + quotas[resource.name] = placement_limits[resource.name] + else: + # return -1 for things like security_group_rules + # that are neither a keystone limit or a local limit + quotas[resource.name] = local_limits.get(resource.name, -1) return quotas @@ -829,21 +830,39 @@ class UnifiedLimitsDriver(NoopQuotaDriver): if remains: raise NotImplementedError("remains") - local_limits = self.get_class_quotas(context, resources, quota_class) - local_in_use = {} + local_limits = local_limit.get_legacy_default_limits() + # keystone limits always returns core, ram and instances + # if nothing set in keystone, we get back 0, i.e. don't allow + placement_limits = placement_limit.get_legacy_project_limits( + project_id) + + project_quotas = {} + for resource in resources.values(): + if resource.name in placement_limits: + limit = placement_limits[resource.name] + else: + # return -1 for things like security_group_rules + # that are neither a keystone limit or a local limit + limit = local_limits.get(resource.name, -1) + project_quotas[resource.name] = {"limit": limit} + if usages: local_in_use = local_limit.get_in_use(context, project_id) + p_in_use = placement_limit.get_legacy_counts(context, project_id) - quotas = {} - # As we only apply limits to resources we know about, - # we return unlimited (-1) for all other resources - for resource in resources.values(): - quota = {"limit": local_limits.get(resource.name, -1)} - if usages: - quota["in_use"] = local_in_use.get(resource.name, -1) - quotas[resource.name] = quota + for resource in resources.values(): + # default to 0 for resources that are deprecated, + # i.e. not in keystone or local limits, such that we + # are API compatible with what was returned with + # the db driver, even though noop driver returned -1 + usage_count = 0 + if resource.name in local_in_use: + usage_count = local_in_use[resource.name] + if resource.name in p_in_use: + usage_count = p_in_use[resource.name] + project_quotas[resource.name]["in_use"] = usage_count - return quotas + return project_quotas def get_user_quotas(self, context, resources, project_id, user_id, quota_class=None, usages=True): diff --git a/nova/tests/unit/api/openstack/compute/test_limits.py b/nova/tests/unit/api/openstack/compute/test_limits.py index 9cbdcd792b..b8b01a09dd 100644 --- a/nova/tests/unit/api/openstack/compute/test_limits.py +++ b/nova/tests/unit/api/openstack/compute/test_limits.py @@ -31,6 +31,7 @@ from nova.api.openstack import wsgi import nova.context from nova import exception from nova.limit import local as local_limit +from nova.limit import placement as placement_limit from nova import objects from nova.policies import limits as l_policies from nova import quota @@ -564,8 +565,12 @@ class UnifiedLimitsControllerTest(NoopLimitsControllerTest): local_limit.SERVER_GROUP_MEMBERS: 10} self.useFixture(limit_fixture.LimitFixture(reglimits, {})) + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_index_v21(self, mock_count): + def test_index_v21(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} mock_count.return_value = {'project': {'server_groups': 9}} req = fakes.HTTPRequest.blank("/") response = self.controller.index(req) @@ -581,24 +586,28 @@ class UnifiedLimitsControllerTest(NoopLimitsControllerTest): 'maxServerGroupMembers': 10, 'maxServerGroups': 12, 'maxServerMeta': 128, - 'maxTotalCores': -1, + 'maxTotalCores': 2, 'maxTotalFloatingIps': -1, - 'maxTotalInstances': -1, + 'maxTotalInstances': 1, 'maxTotalKeypairs': 100, - 'maxTotalRAMSize': -1, - 'totalCoresUsed': -1, - 'totalFloatingIpsUsed': -1, - 'totalInstancesUsed': -1, - 'totalRAMUsed': -1, - 'totalSecurityGroupsUsed': -1, + 'maxTotalRAMSize': 3, + 'totalCoresUsed': 5, + 'totalFloatingIpsUsed': 0, + 'totalInstancesUsed': 4, + 'totalRAMUsed': 6, + 'totalSecurityGroupsUsed': 0, 'totalServerGroupsUsed': 9, }, }, } self.assertEqual(expected_response, response) + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_index_v275(self, mock_count): + def test_index_v275(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} mock_count.return_value = {'project': {'server_groups': 9}} req = fakes.HTTPRequest.blank("/?tenant_id=faketenant", version='2.75') @@ -610,13 +619,13 @@ class UnifiedLimitsControllerTest(NoopLimitsControllerTest): 'maxServerGroupMembers': 10, 'maxServerGroups': 12, 'maxServerMeta': 128, - 'maxTotalCores': -1, - 'maxTotalInstances': -1, + 'maxTotalCores': 2, + 'maxTotalInstances': 1, 'maxTotalKeypairs': 100, - 'maxTotalRAMSize': -1, - 'totalCoresUsed': -1, - 'totalInstancesUsed': -1, - 'totalRAMUsed': -1, + 'maxTotalRAMSize': 3, + 'totalCoresUsed': 5, + 'totalInstancesUsed': 4, + 'totalRAMUsed': 6, 'totalServerGroupsUsed': 9, }, }, diff --git a/nova/tests/unit/api/openstack/compute/test_quota_classes.py b/nova/tests/unit/api/openstack/compute/test_quota_classes.py index 9a334480d6..d501412803 100644 --- a/nova/tests/unit/api/openstack/compute/test_quota_classes.py +++ b/nova/tests/unit/api/openstack/compute/test_quota_classes.py @@ -21,6 +21,7 @@ from nova.api.openstack.compute import quota_classes \ as quota_classes_v21 from nova import exception from nova.limit import local as local_limit +from nova.limit import placement as placement_limit from nova import objects from nova import test from nova.tests.unit.api.openstack import fakes @@ -279,20 +280,22 @@ class UnifiedLimitsQuotaClassesTest(NoopQuotaClassesTest): local_limit.SERVER_GROUP_MEMBERS: 10} self.useFixture(limit_fixture.LimitFixture(reglimits, {})) - def test_show_v21(self): + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_show_v21(self, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} req = fakes.HTTPRequest.blank("") response = self.controller.show(req, "test_class") expected_response = { 'quota_class_set': { 'id': 'test_class', - 'cores': -1, + 'cores': 2, 'fixed_ips': -1, 'floating_ips': -1, - 'ram': -1, + 'ram': 3, 'injected_file_content_bytes': 10240, 'injected_file_path_bytes': 255, 'injected_files': 5, - 'instances': -1, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, 'security_group_rules': -1, @@ -301,15 +304,17 @@ class UnifiedLimitsQuotaClassesTest(NoopQuotaClassesTest): } self.assertEqual(expected_response, response) - def test_show_v257(self): + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_show_v257(self, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} req = fakes.HTTPRequest.blank("", version='2.57') response = self.controller.show(req, "default") expected_response = { 'quota_class_set': { 'id': 'default', - 'cores': -1, - 'instances': -1, - 'ram': -1, + 'cores': 2, + 'instances': 1, + 'ram': 3, 'key_pairs': 100, 'metadata_items': 128, 'server_group_members': 10, @@ -325,23 +330,25 @@ class UnifiedLimitsQuotaClassesTest(NoopQuotaClassesTest): self.assertRaises(exception.ValidationError, self.controller.update, req, 'test_class', body=body) + @mock.patch.object(placement_limit, "get_legacy_default_limits") @mock.patch.object(objects.Quotas, "update_class") - def test_update_v21(self, mock_update): + def test_update_v21(self, mock_update, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} 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, + 'cores': 2, 'fixed_ips': -1, 'floating_ips': -1, 'injected_file_content_bytes': 10240, 'injected_file_path_bytes': 255, 'injected_files': 5, - 'instances': -1, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, - 'ram': -1, + 'ram': 3, 'security_group_rules': -1, 'security_groups': -1 } @@ -350,16 +357,18 @@ class UnifiedLimitsQuotaClassesTest(NoopQuotaClassesTest): # TODO(johngarbutt) we should be proxying to keystone self.assertEqual(0, mock_update.call_count) + @mock.patch.object(placement_limit, "get_legacy_default_limits") @mock.patch.object(objects.Quotas, "update_class") - def test_update_v257(self, mock_update): + def test_update_v257(self, mock_update, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} 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, - 'ram': -1, + 'cores': 2, + 'instances': 1, + 'ram': 3, 'key_pairs': 100, 'metadata_items': 128, 'server_group_members': 10, diff --git a/nova/tests/unit/api/openstack/compute/test_quotas.py b/nova/tests/unit/api/openstack/compute/test_quotas.py index 56d13ea1aa..6cb8d9c7ad 100644 --- a/nova/tests/unit/api/openstack/compute/test_quotas.py +++ b/nova/tests/unit/api/openstack/compute/test_quotas.py @@ -23,6 +23,7 @@ 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.limit import local as local_limit +from nova.limit import placement as placement_limit from nova import objects from nova import quota from nova import test @@ -869,7 +870,8 @@ class NoopQuotaSetsTest(test.NoDBTestCase): class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): quota_driver = "nova.quota.UnifiedLimitsDriver" - expected_detail = {'in_use': -1, 'limit': -1, 'reserved': 0} + # this matches what the db driver returns + expected_detail = {'in_use': 0, 'limit': -1, 'reserved': 0} def setUp(self): super(UnifiedLimitsQuotaSetsTest, self).setUp() @@ -882,22 +884,24 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): local_limit.SERVER_GROUP_MEMBERS: 10} self.useFixture(limit_fixture.LimitFixture(reglimits, {})) - def test_show_v21(self): + @mock.patch.object(placement_limit, "get_legacy_project_limits") + def test_show_v21(self, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} req = fakes.HTTPRequest.blank("") response = self.controller.show(req, uuids.project_id) expected_response = { 'quota_set': { 'id': uuids.project_id, - 'cores': -1, + 'cores': 2, 'fixed_ips': -1, 'floating_ips': -1, 'injected_file_content_bytes': 10240, 'injected_file_path_bytes': 255, 'injected_files': 5, - 'instances': -1, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, - 'ram': -1, + 'ram': 3, 'security_group_rules': -1, 'security_groups': -1, 'server_group_members': 10, @@ -906,30 +910,37 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): } self.assertEqual(expected_response, response) - def test_show_v257(self): + @mock.patch.object(placement_limit, "get_legacy_project_limits") + def test_show_v257(self, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} 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, + 'cores': 2, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, - 'ram': -1, + 'ram': 3, 'server_group_members': 10, 'server_groups': 12}} self.assertEqual(expected_response, response) + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_detail_v21(self, mock_count): + def test_detail_v21(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} mock_count.return_value = {'project': {'server_groups': 9}} req = fakes.HTTPRequest.blank("") response = self.controller.detail(req, uuids.project_id) expected_response = { 'quota_set': { 'id': uuids.project_id, - 'cores': self.expected_detail, + 'cores': { + 'in_use': 5, 'limit': 2, 'reserved': 0}, 'fixed_ips': self.expected_detail, 'floating_ips': self.expected_detail, 'injected_file_content_bytes': { @@ -938,12 +949,14 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): 'in_use': 0, 'limit': 255, 'reserved': 0}, 'injected_files': { 'in_use': 0, 'limit': 5, 'reserved': 0}, - 'instances': self.expected_detail, + 'instances': { + 'in_use': 4, 'limit': 1, 'reserved': 0}, 'key_pairs': { 'in_use': 0, 'limit': 100, 'reserved': 0}, 'metadata_items': { 'in_use': 0, 'limit': 128, 'reserved': 0}, - 'ram': self.expected_detail, + 'ram': { + 'in_use': 6, 'limit': 3, 'reserved': 0}, 'security_group_rules': self.expected_detail, 'security_groups': self.expected_detail, 'server_group_members': { @@ -954,15 +967,20 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): } self.assertEqual(expected_response, response) + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_detail_v21_user(self, mock_count): + def test_detail_v21_user(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} mock_count.return_value = {'project': {'server_groups': 9}} req = fakes.HTTPRequest.blank("?user_id=42") response = self.controller.detail(req, uuids.project_id) expected_response = { 'quota_set': { 'id': uuids.project_id, - 'cores': self.expected_detail, + 'cores': { + 'in_use': 5, 'limit': 2, 'reserved': 0}, 'fixed_ips': self.expected_detail, 'floating_ips': self.expected_detail, 'injected_file_content_bytes': { @@ -971,12 +989,14 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): 'in_use': 0, 'limit': 255, 'reserved': 0}, 'injected_files': { 'in_use': 0, 'limit': 5, 'reserved': 0}, - 'instances': self.expected_detail, + 'instances': { + 'in_use': 4, 'limit': 1, 'reserved': 0}, 'key_pairs': { 'in_use': 0, 'limit': 100, 'reserved': 0}, 'metadata_items': { 'in_use': 0, 'limit': 128, 'reserved': 0}, - 'ram': self.expected_detail, + 'ram': { + 'in_use': 6, 'limit': 3, 'reserved': 0}, 'security_group_rules': self.expected_detail, 'security_groups': self.expected_detail, 'server_group_members': { @@ -987,24 +1007,26 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): } self.assertEqual(expected_response, response) + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.Quotas, "create_limit") - def test_update_v21(self, mock_create): + def test_update_v21(self, mock_create, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} req = fakes.HTTPRequest.blank("") # TODO(johngarbutt) still need to implement get_settable_quotas body = {'quota_set': {'server_groups': 2}} response = self.controller.update(req, uuids.project_id, body=body) expected_response = { 'quota_set': { - 'cores': -1, + 'cores': 2, 'fixed_ips': -1, 'floating_ips': -1, 'injected_file_content_bytes': 10240, 'injected_file_path_bytes': 255, 'injected_files': 5, - 'instances': -1, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, - 'ram': -1, + 'ram': 3, 'security_group_rules': -1, 'security_groups': -1, 'server_group_members': 10, @@ -1014,23 +1036,25 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): self.assertEqual(expected_response, response) self.assertEqual(0, mock_create.call_count) + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.Quotas, "create_limit") - def test_update_v21_user(self, mock_create): + def test_update_v21_user(self, mock_create, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} 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, + 'cores': 2, 'fixed_ips': -1, 'floating_ips': -1, 'injected_file_content_bytes': 10240, 'injected_file_path_bytes': 255, 'injected_files': 5, - 'instances': -1, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, - 'ram': -1, + 'ram': 3, 'security_group_rules': -1, 'security_groups': -1, 'server_group_members': 10, @@ -1040,22 +1064,24 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): self.assertEqual(expected_response, response) self.assertEqual(0, mock_create.call_count) - def test_defaults_v21(self): + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_defaults_v21(self, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 3} req = fakes.HTTPRequest.blank("") response = self.controller.defaults(req, uuids.project_id) expected_response = { 'quota_set': { 'id': uuids.project_id, - 'cores': -1, + 'cores': 2, 'fixed_ips': -1, 'floating_ips': -1, 'injected_file_content_bytes': 10240, 'injected_file_path_bytes': 255, 'injected_files': 5, - 'instances': -1, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, - 'ram': -1, + 'ram': 3, 'security_group_rules': -1, 'security_groups': -1, 'server_group_members': 10, @@ -1079,16 +1105,16 @@ class UnifiedLimitsQuotaSetsTest(NoopQuotaSetsTest): expected_response = { 'quota_set': { 'id': uuids.project_id, - 'cores': -1, + 'cores': 0, 'fixed_ips': -1, 'floating_ips': -1, 'injected_file_content_bytes': 4, 'injected_file_path_bytes': 5, 'injected_files': 6, - 'instances': -1, + 'instances': 0, 'key_pairs': 1, 'metadata_items': 7, - 'ram': -1, + 'ram': 0, 'security_group_rules': -1, 'security_groups': -1, 'server_group_members': 2, diff --git a/nova/tests/unit/test_quota.py b/nova/tests/unit/test_quota.py index 58b6657215..edbb814ba7 100644 --- a/nova/tests/unit/test_quota.py +++ b/nova/tests/unit/test_quota.py @@ -26,6 +26,7 @@ from nova import context from nova.db.main import models from nova import exception from nova.limit import local as local_limit +from nova.limit import placement as placement_limit from nova import objects from nova import quota from nova import test @@ -1990,101 +1991,109 @@ class UnifiedLimitsDriverTestCase(NoopQuotaDriverTestCase): self.useFixture(limit_fixture.LimitFixture(reglimits, {})) self.expected_without_dict = { - 'cores': -1, + 'cores': 2, 'fixed_ips': -1, 'floating_ips': -1, 'injected_file_content_bytes': 10240, 'injected_file_path_bytes': 255, 'injected_files': 5, - 'instances': -1, + 'instances': 1, 'key_pairs': 100, 'metadata_items': 128, - 'ram': -1, + 'ram': 0, 'security_group_rules': -1, 'security_groups': -1, 'server_group_members': 10, 'server_groups': 12, } self.expected_without_usages = { - 'cores': {'limit': -1}, + 'cores': {'limit': 2}, 'fixed_ips': {'limit': -1}, 'floating_ips': {'limit': -1}, 'injected_file_content_bytes': {'limit': 10240}, 'injected_file_path_bytes': {'limit': 255}, 'injected_files': {'limit': 5}, - 'instances': {'limit': -1}, + 'instances': {'limit': 1}, 'key_pairs': {'limit': 100}, 'metadata_items': {'limit': 128}, - 'ram': {'limit': -1}, + 'ram': {'limit': 3}, 'security_group_rules': {'limit': -1}, 'security_groups': {'limit': -1}, 'server_group_members': {'limit': 10}, 'server_groups': {'limit': 12} } self.expected_with_usages = { - 'cores': {'in_use': -1, 'limit': -1}, - 'fixed_ips': {'in_use': -1, 'limit': -1}, - 'floating_ips': {'in_use': -1, 'limit': -1}, + 'cores': {'in_use': 5, 'limit': 2}, + 'fixed_ips': {'in_use': 0, 'limit': -1}, + 'floating_ips': {'in_use': 0, 'limit': -1}, 'injected_file_content_bytes': {'in_use': 0, 'limit': 10240}, 'injected_file_path_bytes': {'in_use': 0, 'limit': 255}, 'injected_files': {'in_use': 0, 'limit': 5}, - 'instances': {'in_use': -1, 'limit': -1}, + 'instances': {'in_use': 4, 'limit': 1}, 'key_pairs': {'in_use': 0, 'limit': 100}, 'metadata_items': {'in_use': 0, 'limit': 128}, - 'ram': {'in_use': -1, 'limit': -1}, - 'security_group_rules': {'in_use': -1, 'limit': -1}, - 'security_groups': {'in_use': -1, 'limit': -1}, + 'ram': {'in_use': 6, 'limit': 3}, + 'security_group_rules': {'in_use': 0, 'limit': -1}, + 'security_groups': {'in_use': 0, 'limit': -1}, 'server_group_members': {'in_use': 0, 'limit': 10}, 'server_groups': {'in_use': 9, 'limit': 12} } - def test_get_class_quotas(self): - result = self.driver.get_class_quotas( - None, quota.QUOTAS._resources, 'default') - expected_limits = { - 'cores': -1, - 'fixed_ips': -1, - 'floating_ips': -1, - 'injected_file_content_bytes': 10240, - 'injected_file_path_bytes': 255, - 'injected_files': 5, - 'instances': -1, - 'key_pairs': 100, - 'metadata_items': 128, - 'ram': -1, - 'security_group_rules': -1, - 'security_groups': -1, - 'server_group_members': 10, - 'server_groups': 12, - } - self.assertEqual(expected_limits, result) + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_get_defaults(self, mock_default): + # zero for ram simulates no registered limit for ram + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 0} + result = self.driver.get_defaults(None, quota.QUOTAS._resources) + self.assertEqual(self.expected_without_dict, result) + mock_default.assert_called_once_with() + @mock.patch.object(placement_limit, "get_legacy_default_limits") + def test_get_class_quotas(self, mock_default): + mock_default.return_value = {"instances": 1, "cores": 2, "ram": 0} + result = self.driver.get_class_quotas( + None, quota.QUOTAS._resources, 'test_class') + self.assertEqual(self.expected_without_dict, result) + mock_default.assert_called_once_with() + + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_get_project_quotas(self, mock_count): + def test_get_project_quotas(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} mock_count.return_value = {'project': {'server_groups': 9}} result = self.driver.get_project_quotas( None, quota.QUOTAS._resources, 'test_project') self.assertEqual(self.expected_with_usages, result) mock_count.assert_called_once_with(None, "test_project") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_get_project_quotas_no_usages(self, mock_count): + def test_get_project_quotas_no_usages(self, mock_count, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} result = self.driver.get_project_quotas( None, quota.QUOTAS._resources, 'test_project', usages=False) self.assertEqual(self.expected_without_usages, result) # ensure usages not fetched when not required self.assertEqual(0, mock_count.call_count) + mock_proj.assert_called_once_with("test_project") + @mock.patch.object(placement_limit, "get_legacy_counts") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_get_user_quotas(self, mock_count): + def test_get_user_quotas(self, mock_count, mock_proj, mock_kcount): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} + mock_kcount.return_value = {"instances": 4, "cores": 5, "ram": 6} mock_count.return_value = {'project': {'server_groups': 9}} result = self.driver.get_user_quotas( None, quota.QUOTAS._resources, 'test_project', 'fake_user') self.assertEqual(self.expected_with_usages, result) mock_count.assert_called_once_with(None, "test_project") + @mock.patch.object(placement_limit, "get_legacy_project_limits") @mock.patch.object(objects.InstanceGroupList, "get_counts") - def test_get_user_quotas_no_usages(self, mock_count): + def test_get_user_quotas_no_usages(self, mock_count, mock_proj): + mock_proj.return_value = {"instances": 1, "cores": 2, "ram": 3} result = self.driver.get_user_quotas( None, quota.QUOTAS._resources, 'test_project', 'fake_user', usages=False)