From 77881659251bdff52163ba1572e13a105eadaf7f Mon Sep 17 00:00:00 2001 From: Surya Seetharaman Date: Thu, 17 May 2018 10:16:56 +0200 Subject: [PATCH] Make _instances_cores_ram_count() be smart about cells This makes the _instances_cores_ram_count() method only query for instances in cells that the tenant actually has instances landed in. We do this by getting a list of cell mappings that have instance mappings owned by the project and limiting the scatter/gather operation to just those cells. Change-Id: I0e2a9b2460145d3aee92f7fddc4f4da16af63ff8 Closes-Bug: #1771810 --- nova/quota.py | 13 +++++++++-- nova/tests/functional/db/test_quota.py | 21 +++++++++++++++++ nova/tests/unit/test_quota.py | 32 ++++++++++++++++---------- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/nova/quota.py b/nova/quota.py index 0a2de6a1787b..212fd3e9f070 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -1168,8 +1168,17 @@ def _instances_cores_ram_count(context, project_id, user_id=None): # counting resources if a cell is down. In the future, we should query # placement for cores/ram and InstanceMappings for instances (once we are # deleting InstanceMappings when we delete instances). - results = nova_context.scatter_gather_all_cells( - context, objects.InstanceList.get_counts, project_id, user_id=user_id) + # NOTE(tssurya): We only go into those cells in which the tenant has + # instances. We could optimize this to avoid the CellMappingList query + # for single-cell deployments by checking the cell cache and only doing + # this filtering if there is more than one non-cell0 cell. + # TODO(tssurya): Consider adding a scatter_gather_cells_for_project + # variant that makes this native to nova.context. + cell_mappings = objects.CellMappingList.get_by_project_id( + context, project_id) + results = nova_context.scatter_gather_cells( + context, cell_mappings, nova_context.CELL_TIMEOUT, + objects.InstanceList.get_counts, project_id, user_id=user_id) total_counts = {'project': {'instances': 0, 'cores': 0, 'ram': 0}} if user_id: total_counts['user'] = {'instances': 0, 'cores': 0, 'ram': 0} diff --git a/nova/tests/functional/db/test_quota.py b/nova/tests/functional/db/test_quota.py index 0d4fc544e644..1fb2452daedb 100644 --- a/nova/tests/functional/db/test_quota.py +++ b/nova/tests/functional/db/test_quota.py @@ -98,6 +98,13 @@ class QuotaTestCase(test.NoDBTestCase): user_id='fake-user', vcpus=2, memory_mb=512) instance.create() + # create mapping for the instance since we query only those cells + # in which the project has instances based on the instance_mappings + im = objects.InstanceMapping(context=ctxt, + instance_uuid=instance.uuid, + cell_mapping=mapping1, + project_id='fake-project') + im.create() # Create an instance in cell2 with context.target_cell(ctxt, mapping2) as cctxt: @@ -106,6 +113,13 @@ class QuotaTestCase(test.NoDBTestCase): user_id='fake-user', vcpus=4, memory_mb=1024) instance.create() + # create mapping for the instance since we query only those cells + # in which the project has instances based on the instance_mappings + im = objects.InstanceMapping(context=ctxt, + instance_uuid=instance.uuid, + cell_mapping=mapping2, + project_id='fake-project') + im.create() # Create an instance in cell2 for a different user with context.target_cell(ctxt, mapping2) as cctxt: @@ -114,6 +128,13 @@ class QuotaTestCase(test.NoDBTestCase): user_id='other-fake-user', vcpus=4, memory_mb=1024) instance.create() + # create mapping for the instance since we query only those cells + # in which the project has instances based on the instance_mappings + im = objects.InstanceMapping(context=ctxt, + instance_uuid=instance.uuid, + cell_mapping=mapping2, + project_id='fake-project') + im.create() # Count instances, cores, and ram across cells count = quota._instances_cores_ram_count(ctxt, 'fake-project', diff --git a/nova/tests/unit/test_quota.py b/nova/tests/unit/test_quota.py index 460afbeec073..e37d9399d7f1 100644 --- a/nova/tests/unit/test_quota.py +++ b/nova/tests/unit/test_quota.py @@ -86,18 +86,26 @@ class QuotaIntegrationTestCase(test.TestCase): nova.tests.unit.image.fake.FakeImageService_reset() def _create_instance(self, flavor_name='m1.large'): - """Create a test instance.""" - inst = objects.Instance(context=self.context) - inst.image_id = 'cedef40a-ed67-4d10-800e-17455edce175' - inst.reservation_id = 'r-fakeres' - inst.user_id = self.user_id - inst.project_id = self.project_id - inst.flavor = flavors.get_flavor_by_name(flavor_name) - # This is needed for instance quota counting until we have the - # ability to count allocations in placement. - inst.vcpus = inst.flavor.vcpus - inst.memory_mb = inst.flavor.memory_mb - inst.create() + """Create a test instance in cell1 with an instance mapping.""" + cell1 = self.cell_mappings[test.CELL1_NAME] + with context.target_cell(self.context, cell1) as cctxt: + inst = objects.Instance(context=cctxt) + inst.image_id = 'cedef40a-ed67-4d10-800e-17455edce175' + inst.reservation_id = 'r-fakeres' + inst.user_id = self.user_id + inst.project_id = self.project_id + inst.flavor = flavors.get_flavor_by_name(flavor_name) + # This is needed for instance quota counting until we have the + # ability to count allocations in placement. + inst.vcpus = inst.flavor.vcpus + inst.memory_mb = inst.flavor.memory_mb + inst.create() + # Create the related instance mapping which will be used in + # _instances_cores_ram_count(). + inst_map = objects.InstanceMapping( + self.context, instance_uuid=inst.uuid, project_id=inst.project_id, + cell_mapping=cell1) + inst_map.create() return inst def test_too_many_instances(self):