Skip to content

Commit de682f9

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Default user_id when not specified in check_num_instances_quota"
2 parents 34db1b2 + 4c11d54 commit de682f9

File tree

4 files changed

+66
-9
lines changed

4 files changed

+66
-9
lines changed

nova/compute/utils.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,9 +1105,18 @@ def check_num_instances_quota(context, instance_type, min_count,
11051105
max_count, project_id=None, user_id=None,
11061106
orig_num_req=None):
11071107
"""Enforce quota limits on number of instances created."""
1108-
# project_id is used for the TooManyInstances error message
1108+
# project_id is also used for the TooManyInstances error message
11091109
if project_id is None:
11101110
project_id = context.project_id
1111+
if user_id is None:
1112+
user_id = context.user_id
1113+
# Check whether we need to count resources per-user and check a per-user
1114+
# quota limit. If we have no per-user quota limit defined for a
1115+
# project/user, we can avoid wasteful resource counting.
1116+
user_quotas = objects.Quotas.get_all_by_project_and_user(
1117+
context, project_id, user_id)
1118+
if not any(r in user_quotas for r in ['instances', 'cores', 'ram']):
1119+
user_id = None
11111120
# Determine requested cores and ram
11121121
req_cores = max_count * instance_type.vcpus
11131122
req_ram = max_count * instance_type.memory_mb

nova/tests/functional/regressions/test_bug_1893284.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,8 @@ def test_create_server_with_per_user_quota(self):
8484
server_req = self._build_server(
8585
image_uuid=fake_image.AUTO_DISK_CONFIG_ENABLED_IMAGE_UUID,
8686
networks='none')
87-
# FIXME(melwitt): Uncomment this when the bug is fixed. Because of the
88-
# the bug, the first request by the non-admin user will fail.
89-
# server = self.api.post_server({'server': server_req})
90-
# self._wait_for_state_change(server, 'ACTIVE')
87+
server = self.api.post_server({'server': server_req})
88+
self._wait_for_state_change(server, 'ACTIVE')
9189
# A request to boot a second instance should fail because the
9290
# non-admin has already booted 1 allowed instance.
9391
ex = self.assertRaises(

nova/tests/unit/compute/test_api.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ def test_create_with_networks_max_count_none(self, provision_instances,
231231
requested_networks=requested_networks,
232232
max_count=None)
233233

234+
@mock.patch('nova.objects.Quotas.get_all_by_project_and_user',
235+
new=mock.MagicMock())
234236
@mock.patch('nova.objects.Quotas.count_as_dict')
235237
@mock.patch('nova.objects.Quotas.limit_check')
236238
@mock.patch('nova.objects.Quotas.limit_check_project_and_user')
@@ -4170,6 +4172,8 @@ def test_check_injected_file_quota_onset_file_content_limit(self):
41704172
self._test_check_injected_file_quota_onset_file_limit_exceeded,
41714173
side_effect)
41724174

4175+
@mock.patch('nova.objects.Quotas.get_all_by_project_and_user',
4176+
new=mock.MagicMock())
41734177
@mock.patch('nova.objects.Quotas.count_as_dict')
41744178
@mock.patch('nova.objects.Quotas.limit_check_project_and_user')
41754179
@mock.patch('nova.objects.Instance.save')
@@ -4194,9 +4198,11 @@ def test_restore_by_admin(self, update_qfd, action_start, instance_save,
41944198
self.assertEqual(instance.task_state, task_states.RESTORING)
41954199
# mock.ANY might be 'instances', 'cores', or 'ram' depending on how the
41964200
# deltas dict is iterated in check_deltas
4201+
# user_id is expected to be None because no per-user quotas have been
4202+
# defined
41974203
quota_count.assert_called_once_with(admin_context, mock.ANY,
41984204
instance.project_id,
4199-
user_id=instance.user_id)
4205+
user_id=None)
42004206
quota_check.assert_called_once_with(
42014207
admin_context,
42024208
user_values={'instances': 2,
@@ -4205,9 +4211,11 @@ def test_restore_by_admin(self, update_qfd, action_start, instance_save,
42054211
project_values={'instances': 2,
42064212
'cores': 1 + instance.flavor.vcpus,
42074213
'ram': 512 + instance.flavor.memory_mb},
4208-
project_id=instance.project_id, user_id=instance.user_id)
4214+
project_id=instance.project_id)
42094215
update_qfd.assert_called_once_with(admin_context, instance, False)
42104216

4217+
@mock.patch('nova.objects.Quotas.get_all_by_project_and_user',
4218+
new=mock.MagicMock())
42114219
@mock.patch('nova.objects.Quotas.count_as_dict')
42124220
@mock.patch('nova.objects.Quotas.limit_check_project_and_user')
42134221
@mock.patch('nova.objects.Instance.save')
@@ -4231,9 +4239,11 @@ def test_restore_by_instance_owner(self, update_qfd, action_start,
42314239
self.assertEqual(instance.task_state, task_states.RESTORING)
42324240
# mock.ANY might be 'instances', 'cores', or 'ram' depending on how the
42334241
# deltas dict is iterated in check_deltas
4242+
# user_id is expected to be None because no per-user quotas have been
4243+
# defined
42344244
quota_count.assert_called_once_with(self.context, mock.ANY,
42354245
instance.project_id,
4236-
user_id=instance.user_id)
4246+
user_id=None)
42374247
quota_check.assert_called_once_with(
42384248
self.context,
42394249
user_values={'instances': 2,
@@ -4242,7 +4252,7 @@ def test_restore_by_instance_owner(self, update_qfd, action_start,
42424252
project_values={'instances': 2,
42434253
'cores': 1 + instance.flavor.vcpus,
42444254
'ram': 512 + instance.flavor.memory_mb},
4245-
project_id=instance.project_id, user_id=instance.user_id)
4255+
project_id=instance.project_id)
42464256
update_qfd.assert_called_once_with(self.context, instance, False)
42474257

42484258
@mock.patch.object(objects.InstanceAction, 'action_start')

nova/tests/unit/compute/test_compute_utils.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,46 @@ def test_check_instance_quota_exceeds_with_multiple_resources(self,
13961396
else:
13971397
self.fail("Exception not raised")
13981398

1399+
@mock.patch('nova.objects.Quotas.get_all_by_project_and_user')
1400+
@mock.patch('nova.objects.Quotas.check_deltas')
1401+
def test_check_num_instances_omits_user_if_no_user_quota(self, mock_check,
1402+
mock_get):
1403+
# Return no per-user quota.
1404+
mock_get.return_value = {'project_id': self.context.project_id,
1405+
'user_id': self.context.user_id}
1406+
fake_flavor = objects.Flavor(vcpus=1, memory_mb=512)
1407+
compute_utils.check_num_instances_quota(
1408+
self.context, fake_flavor, 1, 1)
1409+
deltas = {'instances': 1, 'cores': 1, 'ram': 512}
1410+
# Verify that user_id has not been passed along to scope the resource
1411+
# counting.
1412+
mock_check.assert_called_once_with(
1413+
self.context, deltas, self.context.project_id, user_id=None,
1414+
check_project_id=self.context.project_id, check_user_id=None)
1415+
1416+
@mock.patch('nova.objects.Quotas.get_all_by_project_and_user')
1417+
@mock.patch('nova.objects.Quotas.check_deltas')
1418+
def test_check_num_instances_passes_user_if_user_quota(self, mock_check,
1419+
mock_get):
1420+
for resource in ['instances', 'cores', 'ram']:
1421+
# Return some per-user quota for each of the instance-related
1422+
# resources.
1423+
mock_get.return_value = {'project_id': self.context.project_id,
1424+
'user_id': self.context.user_id,
1425+
resource: 5}
1426+
fake_flavor = objects.Flavor(vcpus=1, memory_mb=512)
1427+
compute_utils.check_num_instances_quota(
1428+
self.context, fake_flavor, 1, 1)
1429+
deltas = {'instances': 1, 'cores': 1, 'ram': 512}
1430+
# Verify that user_id is passed along to scope the resource
1431+
# counting and limit checking.
1432+
mock_check.assert_called_once_with(
1433+
self.context, deltas, self.context.project_id,
1434+
user_id=self.context.user_id,
1435+
check_project_id=self.context.project_id,
1436+
check_user_id=self.context.user_id)
1437+
mock_check.reset_mock()
1438+
13991439

14001440
class IsVolumeBackedInstanceTestCase(test.TestCase):
14011441
def setUp(self):

0 commit comments

Comments
 (0)