From b974e20862d7ba9d7d1e687b122889f85a540e5a Mon Sep 17 00:00:00 2001 From: Eric Fried Date: Mon, 6 Nov 2017 14:41:50 -0600 Subject: [PATCH] Extract allocation candidates functional tests This change set pulls AllocationCandidatesTestCase out of test_resource_provider and into its own test_allocation_candidates module. There is no change to the code. This is just a refactor. We're going to add a bunch more test cases for allocation candidates, and the test_resource_provider module was already getting out of hand. Change-Id: Iedfb712d4668a2d34112449aa6ef0263d02e24a4 --- .../db/test_allocation_candidates.py | 768 ++++++++++++++++++ .../functional/db/test_resource_provider.py | 743 ----------------- 2 files changed, 768 insertions(+), 743 deletions(-) create mode 100644 nova/tests/functional/db/test_allocation_candidates.py diff --git a/nova/tests/functional/db/test_allocation_candidates.py b/nova/tests/functional/db/test_allocation_candidates.py new file mode 100644 index 0000000000..9f45e551e9 --- /dev/null +++ b/nova/tests/functional/db/test_allocation_candidates.py @@ -0,0 +1,768 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from nova import context +from nova.objects import fields +from nova.objects import resource_provider as rp_obj +from nova import test +from nova.tests import fixtures +from nova.tests import uuidsentinel + + +class AllocationCandidatesTestCase(test.NoDBTestCase): + """Tests a variety of scenarios with both shared and non-shared resource + providers that the AllocationCandidates.get_by_filters() method returns a + set of alternative allocation requests and provider summaries that may be + used by the scheduler to sort/weigh the options it has for claiming + resources against providers. + """ + + USES_DB_SELF = True + + def setUp(self): + super(AllocationCandidatesTestCase, self).setUp() + self.useFixture(fixtures.Database()) + self.api_db = self.useFixture(fixtures.Database(database='api')) + self.ctx = context.RequestContext('fake-user', 'fake-project') + + def _requested_resources(self): + # The resources we will request + resources = { + fields.ResourceClass.VCPU: 1, + fields.ResourceClass.MEMORY_MB: 64, + fields.ResourceClass.DISK_GB: 1500, + } + return resources + + def _find_summary_for_provider(self, p_sums, rp_uuid): + for summary in p_sums: + if summary.resource_provider.uuid == rp_uuid: + return summary + + def _find_summary_for_resource(self, p_sum, rc_name): + for resource in p_sum.resources: + if resource.resource_class == rc_name: + return resource + + def _find_requests_for_provider(self, reqs, rp_uuid): + res = [] + for ar in reqs: + for rr in ar.resource_requests: + if rr.resource_provider.uuid == rp_uuid: + res.append(rr) + return res + + def _find_request_for_resource(self, res_reqs, rc_name): + for rr in res_reqs: + if rr.resource_class == rc_name: + return rr + + def test_all_local(self): + """Create some resource providers that can satisfy the request for + resources with local (non-shared) resources and verify that the + allocation requests returned by AllocationCandidates correspond with + each of these resource providers. + """ + # Create two compute node providers with VCPU, RAM and local disk + cn1_uuid = uuidsentinel.cn1 + cn1 = rp_obj.ResourceProvider( + self.ctx, + name='cn1', + uuid=cn1_uuid, + ) + cn1.create() + + cn2_uuid = uuidsentinel.cn2 + cn2 = rp_obj.ResourceProvider( + self.ctx, + name='cn2', + uuid=cn2_uuid, + ) + cn2.create() + + cn3_uuid = uuidsentinel.cn3 + cn3 = rp_obj.ResourceProvider( + self.ctx, + name='cn3', + uuid=cn3_uuid + ) + cn3.create() + + for cn in (cn1, cn2, cn3): + vcpu = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.VCPU, + total=24, + reserved=0, + min_unit=1, + max_unit=24, + step_size=1, + allocation_ratio=16.0, + ) + memory_mb = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.MEMORY_MB, + total=32768, + reserved=0, + min_unit=64, + max_unit=32768, + step_size=64, + allocation_ratio=1.5, + ) + if cn.uuid == cn3_uuid: + disk_gb = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.DISK_GB, + total=1000, + reserved=100, + min_unit=10, + max_unit=1000, + step_size=10, + allocation_ratio=1.0, + ) + else: + disk_gb = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.DISK_GB, + total=2000, + reserved=100, + min_unit=10, + max_unit=2000, + step_size=10, + allocation_ratio=1.0, + ) + disk_gb.obj_set_defaults() + inv_list = rp_obj.InventoryList(objects=[ + vcpu, + memory_mb, + disk_gb, + ]) + cn.set_inventory(inv_list) + + # Ask for the alternative placement possibilities and verify each + # provider is returned + requested_resources = self._requested_resources() + p_alts = rp_obj.AllocationCandidates.get_by_filters( + self.ctx, + filters={ + 'resources': requested_resources, + }, + ) + + # Verify the provider summary information indicates 0 usage and + # capacity calculated from above inventory numbers for both compute + # nodes + p_sums = p_alts.provider_summaries + self.assertEqual(2, len(p_sums)) + + p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) + + self.assertEqual(set([cn1_uuid, cn2_uuid]), p_sum_rps) + + cn1_p_sum = self._find_summary_for_provider(p_sums, cn1_uuid) + self.assertIsNotNone(cn1_p_sum) + self.assertEqual(3, len(cn1_p_sum.resources)) + + cn1_p_sum_vcpu = self._find_summary_for_resource(cn1_p_sum, 'VCPU') + self.assertIsNotNone(cn1_p_sum_vcpu) + + expected_capacity = (24 * 16.0) + self.assertEqual(expected_capacity, cn1_p_sum_vcpu.capacity) + self.assertEqual(0, cn1_p_sum_vcpu.used) + + # Let's verify the disk for the second compute node + cn2_p_sum = self._find_summary_for_provider(p_sums, cn2_uuid) + self.assertIsNotNone(cn2_p_sum) + self.assertEqual(3, len(cn2_p_sum.resources)) + + cn2_p_sum_disk = self._find_summary_for_resource(cn2_p_sum, 'DISK_GB') + self.assertIsNotNone(cn2_p_sum_disk) + + expected_capacity = ((2000 - 100) * 1.0) + self.assertEqual(expected_capacity, cn2_p_sum_disk.capacity) + self.assertEqual(0, cn2_p_sum_disk.used) + + # Verify the allocation requests that are returned. There should be 2 + # allocation requests, one for each compute node, containing 3 + # resources in each allocation request, one each for VCPU, RAM, and + # disk. The amounts of the requests should correspond to the requested + # resource amounts in the filter:resources dict passed to + # AllocationCandidates.get_by_filters(). + a_reqs = p_alts.allocation_requests + self.assertEqual(2, len(a_reqs)) + + a_req_rps = set() + for ar in a_reqs: + for rr in ar.resource_requests: + a_req_rps.add(rr.resource_provider.uuid) + + self.assertEqual(set([cn1_uuid, cn2_uuid]), a_req_rps) + + cn1_reqs = self._find_requests_for_provider(a_reqs, cn1_uuid) + # There should be a req object for each resource we have requested + self.assertEqual(3, len(cn1_reqs)) + + cn1_req_vcpu = self._find_request_for_resource(cn1_reqs, 'VCPU') + self.assertIsNotNone(cn1_req_vcpu) + self.assertEqual(requested_resources['VCPU'], cn1_req_vcpu.amount) + + cn2_req_disk = self._find_request_for_resource(cn1_reqs, 'DISK_GB') + self.assertIsNotNone(cn2_req_disk) + self.assertEqual(requested_resources['DISK_GB'], cn2_req_disk.amount) + + def test_local_with_shared_disk(self): + """Create some resource providers that can satisfy the request for + resources with local VCPU and MEMORY_MB but rely on a shared storage + pool to satisfy DISK_GB and verify that the allocation requests + returned by AllocationCandidates have DISK_GB served up by the shared + storage pool resource provider and VCPU/MEMORY_MB by the compute node + providers + """ + # The aggregate that will be associated to everything... + agg_uuid = uuidsentinel.agg + + # Create two compute node providers with VCPU, RAM and NO local disk + cn1_uuid = uuidsentinel.cn1 + cn1 = rp_obj.ResourceProvider( + self.ctx, + name='cn1', + uuid=cn1_uuid, + ) + cn1.create() + + cn2_uuid = uuidsentinel.cn2 + cn2 = rp_obj.ResourceProvider( + self.ctx, + name='cn2', + uuid=cn2_uuid, + ) + cn2.create() + + # Populate the two compute node providers with inventory, sans DISK_GB + for cn in (cn1, cn2): + vcpu = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.VCPU, + total=24, + reserved=0, + min_unit=1, + max_unit=24, + step_size=1, + allocation_ratio=16.0, + ) + memory_mb = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.MEMORY_MB, + total=1024, + reserved=0, + min_unit=64, + max_unit=1024, + step_size=1, + allocation_ratio=1.5, + ) + inv_list = rp_obj.InventoryList(objects=[vcpu, memory_mb]) + cn.set_inventory(inv_list) + + # Create the shared storage pool + ss_uuid = uuidsentinel.ss + ss = rp_obj.ResourceProvider( + self.ctx, + name='shared storage', + uuid=ss_uuid, + ) + ss.create() + + # Give the shared storage pool some inventory of DISK_GB + disk_gb = rp_obj.Inventory( + resource_provider=ss, + resource_class=fields.ResourceClass.DISK_GB, + total=2000, + reserved=100, + min_unit=10, + max_unit=2000, + step_size=1, + allocation_ratio=1.0, + ) + inv_list = rp_obj.InventoryList(objects=[disk_gb]) + ss.set_inventory(inv_list) + + # Mark the shared storage pool as having inventory shared among any + # provider associated via aggregate + t = rp_obj.Trait.get_by_name(self.ctx, "MISC_SHARES_VIA_AGGREGATE") + ss.set_traits(rp_obj.TraitList(objects=[t])) + + # Now associate the shared storage pool and both compute nodes with the + # same aggregate + cn1.set_aggregates([agg_uuid]) + cn2.set_aggregates([agg_uuid]) + ss.set_aggregates([agg_uuid]) + + # Ask for the alternative placement possibilities and verify each + # compute node provider is listed in the allocation requests as well as + # the shared storage pool provider + requested_resources = self._requested_resources() + p_alts = rp_obj.AllocationCandidates.get_by_filters( + self.ctx, + filters={ + 'resources': requested_resources, + }, + ) + + # Verify the provider summary information indicates 0 usage and + # capacity calculated from above inventory numbers for both compute + # nodes + p_sums = p_alts.provider_summaries + self.assertEqual(3, len(p_sums)) + + p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) + + self.assertEqual(set([cn1_uuid, cn2_uuid, ss_uuid]), p_sum_rps) + + cn1_p_sum = self._find_summary_for_provider(p_sums, cn1_uuid) + self.assertIsNotNone(cn1_p_sum) + self.assertEqual(2, len(cn1_p_sum.resources)) + + cn1_p_sum_vcpu = self._find_summary_for_resource(cn1_p_sum, 'VCPU') + self.assertIsNotNone(cn1_p_sum_vcpu) + + expected_capacity = (24 * 16.0) + self.assertEqual(expected_capacity, cn1_p_sum_vcpu.capacity) + self.assertEqual(0, cn1_p_sum_vcpu.used) + + # Let's verify memory for the second compute node + cn2_p_sum = self._find_summary_for_provider(p_sums, cn2_uuid) + self.assertIsNotNone(cn2_p_sum) + self.assertEqual(2, len(cn2_p_sum.resources)) + + cn2_p_sum_ram = self._find_summary_for_resource(cn2_p_sum, 'MEMORY_MB') + self.assertIsNotNone(cn2_p_sum_ram) + + expected_capacity = (1024 * 1.5) + self.assertEqual(expected_capacity, cn2_p_sum_ram.capacity) + self.assertEqual(0, cn2_p_sum_ram.used) + + # Let's verify only diks for the shared storage pool + ss_p_sum = self._find_summary_for_provider(p_sums, ss_uuid) + self.assertIsNotNone(ss_p_sum) + self.assertEqual(1, len(ss_p_sum.resources)) + + ss_p_sum_disk = self._find_summary_for_resource(ss_p_sum, 'DISK_GB') + self.assertIsNotNone(ss_p_sum_disk) + + expected_capacity = ((2000 - 100) * 1.0) + self.assertEqual(expected_capacity, ss_p_sum_disk.capacity) + self.assertEqual(0, ss_p_sum_disk.used) + + # Verify the allocation requests that are returned. There should be 2 + # allocation requests, one for each compute node, containing 3 + # resources in each allocation request, one each for VCPU, RAM, and + # disk. The amounts of the requests should correspond to the requested + # resource amounts in the filter:resources dict passed to + # AllocationCandidates.get_by_filters(). The providers for VCPU and + # MEMORY_MB should be the compute nodes while the provider for the + # DISK_GB should be the shared storage pool + a_reqs = p_alts.allocation_requests + self.assertEqual(2, len(a_reqs)) + + a_req_rps = set() + for ar in a_reqs: + for rr in ar.resource_requests: + a_req_rps.add(rr.resource_provider.uuid) + + self.assertEqual(set([cn1_uuid, cn2_uuid, ss_uuid]), a_req_rps) + + cn1_reqs = self._find_requests_for_provider(a_reqs, cn1_uuid) + # There should be a req object for only VCPU and MEMORY_MB + self.assertEqual(2, len(cn1_reqs)) + + cn1_req_vcpu = self._find_request_for_resource(cn1_reqs, 'VCPU') + self.assertIsNotNone(cn1_req_vcpu) + self.assertEqual(requested_resources['VCPU'], cn1_req_vcpu.amount) + + cn2_reqs = self._find_requests_for_provider(a_reqs, cn2_uuid) + + # There should NOT be an allocation resource request that lists a + # compute node provider UUID for DISK_GB, since the shared storage pool + # is the thing that is providing the disk + cn1_req_disk = self._find_request_for_resource(cn1_reqs, 'DISK_GB') + self.assertIsNone(cn1_req_disk) + cn2_req_disk = self._find_request_for_resource(cn2_reqs, 'DISK_GB') + self.assertIsNone(cn2_req_disk) + + # Let's check the second compute node for MEMORY_MB + cn2_req_ram = self._find_request_for_resource(cn2_reqs, 'MEMORY_MB') + self.assertIsNotNone(cn2_req_ram) + self.assertEqual(requested_resources['MEMORY_MB'], cn2_req_ram.amount) + + # We should find the shared storage pool providing the DISK_GB for each + # of the allocation requests + ss_reqs = self._find_requests_for_provider(a_reqs, ss_uuid) + self.assertEqual(2, len(ss_reqs)) + + # Shared storage shouldn't be listed as providing anything but disk... + ss_req_ram = self._find_request_for_resource(ss_reqs, 'MEMORY_MB') + self.assertIsNone(ss_req_ram) + + ss_req_disk = self._find_request_for_resource(ss_reqs, 'DISK_GB') + self.assertIsNotNone(ss_req_disk) + self.assertEqual(requested_resources['DISK_GB'], ss_req_disk.amount) + + # Test for bug #1705071. We query for allocation candidates with a + # request for ONLY the DISK_GB (the resource that is shared with + # compute nodes) and no VCPU/MEMORY_MB. Before the fix for bug + # #1705071, this resulted in a KeyError + + p_alts = rp_obj.AllocationCandidates.get_by_filters( + self.ctx, + filters={ + 'resources': { + 'DISK_GB': 10, + } + }, + ) + + # We should only have provider summary information for the sharing + # storage provider, since that's the only provider that can be + # allocated against for this request. In the future, we may look into + # returning the shared-with providers in the provider summaries, but + # that's a distant possibility. + p_sums = p_alts.provider_summaries + self.assertEqual(1, len(p_sums)) + + p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) + + self.assertEqual(set([ss_uuid]), p_sum_rps) + + # The allocation_requests will only include the shared storage + # provider because the only thing we're requesting to allocate is + # against the provider of DISK_GB, which happens to be the shared + # storage provider. + a_reqs = p_alts.allocation_requests + self.assertEqual(1, len(a_reqs)) + + a_req_rps = set() + for ar in a_reqs: + for rr in ar.resource_requests: + a_req_rps.add(rr.resource_provider.uuid) + + self.assertEqual(set([ss_uuid]), a_req_rps) + + def test_local_with_shared_custom_resource(self): + """Create some resource providers that can satisfy the request for + resources with local VCPU and MEMORY_MB but rely on a shared resource + provider to satisfy a custom resource requirement and verify that the + allocation requests returned by AllocationCandidates have the custom + resource served up by the shared custom resource provider and + VCPU/MEMORY_MB by the compute node providers + """ + # The aggregate that will be associated to everything... + agg_uuid = uuidsentinel.agg + + # Create two compute node providers with VCPU, RAM and NO local + # CUSTOM_MAGIC resources + cn1_uuid = uuidsentinel.cn1 + cn1 = rp_obj.ResourceProvider( + self.ctx, + name='cn1', + uuid=cn1_uuid, + ) + cn1.create() + + cn2_uuid = uuidsentinel.cn2 + cn2 = rp_obj.ResourceProvider( + self.ctx, + name='cn2', + uuid=cn2_uuid, + ) + cn2.create() + + # Populate the two compute node providers with inventory + for cn in (cn1, cn2): + vcpu = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.VCPU, + total=24, + reserved=0, + min_unit=1, + max_unit=24, + step_size=1, + allocation_ratio=16.0, + ) + memory_mb = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.MEMORY_MB, + total=1024, + reserved=0, + min_unit=64, + max_unit=1024, + step_size=1, + allocation_ratio=1.5, + ) + inv_list = rp_obj.InventoryList(objects=[vcpu, memory_mb]) + cn.set_inventory(inv_list) + + # Create a custom resource called MAGIC + magic_rc = rp_obj.ResourceClass( + self.ctx, + name='CUSTOM_MAGIC', + ) + magic_rc.create() + + # Create the shared provider that servers MAGIC + magic_p_uuid = uuidsentinel.magic_p + magic_p = rp_obj.ResourceProvider( + self.ctx, + name='shared custom resource provider', + uuid=magic_p_uuid, + ) + magic_p.create() + + # Give the provider some MAGIC + magic = rp_obj.Inventory( + resource_provider=magic_p, + resource_class=magic_rc.name, + total=2048, + reserved=1024, + min_unit=10, + max_unit=2048, + step_size=1, + allocation_ratio=1.0, + ) + inv_list = rp_obj.InventoryList(objects=[magic]) + magic_p.set_inventory(inv_list) + + # Mark the magic provider as having inventory shared among any provider + # associated via aggregate + t = rp_obj.Trait( + self.ctx, + name="MISC_SHARES_VIA_AGGREGATE", + ) + # TODO(jaypipes): Once MISC_SHARES_VIA_AGGREGATE is a standard + # os-traits trait, we won't need to create() here. Instead, we will + # just do: + # t = rp_obj.Trait.get_by_name( + # self.context, + # "MISC_SHARES_VIA_AGGREGATE", + # ) + t.create() + magic_p.set_traits(rp_obj.TraitList(objects=[t])) + + # Now associate the shared custom resource provider and both compute + # nodes with the same aggregate + cn1.set_aggregates([agg_uuid]) + cn2.set_aggregates([agg_uuid]) + magic_p.set_aggregates([agg_uuid]) + + # The resources we will request + requested_resources = { + fields.ResourceClass.VCPU: 1, + fields.ResourceClass.MEMORY_MB: 64, + magic_rc.name: 512, + } + + p_alts = rp_obj.AllocationCandidates.get_by_filters( + self.ctx, + filters={ + 'resources': requested_resources, + }, + ) + + # Verify the allocation requests that are returned. There should be 2 + # allocation requests, one for each compute node, containing 3 + # resources in each allocation request, one each for VCPU, RAM, and + # MAGIC. The amounts of the requests should correspond to the requested + # resource amounts in the filter:resources dict passed to + # AllocationCandidates.get_by_filters(). The providers for VCPU and + # MEMORY_MB should be the compute nodes while the provider for the + # MAGIC should be the shared custom resource provider. + a_reqs = p_alts.allocation_requests + self.assertEqual(2, len(a_reqs)) + + a_req_rps = set() + for ar in a_reqs: + for rr in ar.resource_requests: + a_req_rps.add(rr.resource_provider.uuid) + + self.assertEqual(set([cn1_uuid, cn2_uuid, magic_p_uuid]), a_req_rps) + + cn1_reqs = self._find_requests_for_provider(a_reqs, cn1_uuid) + # There should be a req object for only VCPU and MEMORY_MB + self.assertEqual(2, len(cn1_reqs)) + + cn1_req_vcpu = self._find_request_for_resource(cn1_reqs, 'VCPU') + self.assertIsNotNone(cn1_req_vcpu) + self.assertEqual(requested_resources['VCPU'], cn1_req_vcpu.amount) + + cn2_reqs = self._find_requests_for_provider(a_reqs, cn2_uuid) + + # There should NOT be an allocation resource request that lists a + # compute node provider UUID for MAGIC, since the shared + # custom provider is the thing that is providing the disk + cn1_req_disk = self._find_request_for_resource(cn1_reqs, magic_rc.name) + self.assertIsNone(cn1_req_disk) + cn2_req_disk = self._find_request_for_resource(cn2_reqs, magic_rc.name) + self.assertIsNone(cn2_req_disk) + + # Let's check the second compute node for MEMORY_MB + cn2_req_ram = self._find_request_for_resource(cn2_reqs, 'MEMORY_MB') + self.assertIsNotNone(cn2_req_ram) + self.assertEqual(requested_resources['MEMORY_MB'], cn2_req_ram.amount) + + # We should find the shared custom resource provider providing the + # MAGIC for each of the allocation requests + magic_p_reqs = self._find_requests_for_provider(a_reqs, magic_p_uuid) + self.assertEqual(2, len(magic_p_reqs)) + + # Shared custom resource provider shouldn't be listed as providing + # anything but MAGIC... + magic_p_req_ram = self._find_request_for_resource( + magic_p_reqs, 'MEMORY_MB') + self.assertIsNone(magic_p_req_ram) + + magic_p_req_magic = self._find_request_for_resource( + magic_p_reqs, magic_rc.name) + self.assertIsNotNone(magic_p_req_magic) + self.assertEqual( + requested_resources[magic_rc.name], magic_p_req_magic.amount) + + def test_mix_local_and_shared(self): + # The aggregate that will be associated to shared storage pool + agg_uuid = uuidsentinel.agg + + # Create three compute node providers with VCPU and RAM, but only + # the third compute node has DISK. The first two computes will + # share the storage from the shared storage pool + cn1_uuid = uuidsentinel.cn1 + cn1 = rp_obj.ResourceProvider( + self.ctx, + name='cn1', + uuid=cn1_uuid, + ) + cn1.create() + + cn2_uuid = uuidsentinel.cn2 + cn2 = rp_obj.ResourceProvider( + self.ctx, + name='cn2', + uuid=cn2_uuid, + ) + cn2.create() + + cn3_uuid = uuidsentinel.cn3 + cn3 = rp_obj.ResourceProvider( + self.ctx, + name='cn3', + uuid=cn3_uuid + ) + cn3.create() + + # Populate the two compute node providers with inventory + for cn in (cn1, cn2, cn3): + vcpu = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.VCPU, + total=24, + reserved=0, + min_unit=1, + max_unit=24, + step_size=1, + allocation_ratio=16.0, + ) + memory_mb = rp_obj.Inventory( + resource_provider=cn, + resource_class=fields.ResourceClass.MEMORY_MB, + total=1024, + reserved=0, + min_unit=64, + max_unit=1024, + step_size=1, + allocation_ratio=1.5, + ) + disk_gb = rp_obj.Inventory( + resource_provider=cn3, + resource_class=fields.ResourceClass.DISK_GB, + total=2000, + reserved=100, + min_unit=10, + max_unit=2000, + step_size=1, + allocation_ratio=1.0, + ) + if cn == cn3: + inv_list = rp_obj.InventoryList( + objects=[vcpu, memory_mb, disk_gb]) + else: + inv_list = rp_obj.InventoryList(objects=[vcpu, memory_mb]) + cn.set_inventory(inv_list) + + # Create the shared storage pool + ss_uuid = uuidsentinel.ss + ss = rp_obj.ResourceProvider( + self.ctx, + name='shared storage', + uuid=ss_uuid, + ) + ss.create() + + # Give the shared storage pool some inventory of DISK_GB + disk_gb = rp_obj.Inventory( + resource_provider=ss, + resource_class=fields.ResourceClass.DISK_GB, + total=2000, + reserved=100, + min_unit=10, + max_unit=2000, + step_size=1, + allocation_ratio=1.0, + ) + inv_list = rp_obj.InventoryList(objects=[disk_gb]) + ss.set_inventory(inv_list) + + t = rp_obj.Trait.get_by_name(self.ctx, "MISC_SHARES_VIA_AGGREGATE") + ss.set_traits(rp_obj.TraitList(objects=[t])) + + # Put the cn1, cn2 and ss in the same aggregate + cn1.set_aggregates([agg_uuid]) + cn2.set_aggregates([agg_uuid]) + ss.set_aggregates([agg_uuid]) + + requested_resources = self._requested_resources() + p_alts = rp_obj.AllocationCandidates.get_by_filters( + self.ctx, + filters={ + 'resources': requested_resources, + }, + ) + + # Expect cn1, cn2, cn3 and ss in the summaries + p_sums = p_alts.provider_summaries + self.assertEqual(4, len(p_sums)) + + p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) + + self.assertEqual(set([cn1_uuid, cn2_uuid, + ss_uuid, cn3_uuid]), + p_sum_rps) + + # Expect three allocation requests: (cn1, ss), (cn2, ss), (cn3) + a_reqs = p_alts.allocation_requests + self.assertEqual(3, len(a_reqs)) + + expected_ar = [] + for ar in a_reqs: + rr_set = set() + for rr in ar.resource_requests: + rr_set.add(rr.resource_provider.uuid) + expected_ar.append(rr_set) + + self.assertEqual(sorted(expected_ar), + sorted([set([cn1.uuid, ss.uuid]), + set([cn2.uuid, ss.uuid]), set([cn3.uuid])])) diff --git a/nova/tests/functional/db/test_resource_provider.py b/nova/tests/functional/db/test_resource_provider.py index 7de9df8d02..31d21b02d9 100644 --- a/nova/tests/functional/db/test_resource_provider.py +++ b/nova/tests/functional/db/test_resource_provider.py @@ -2465,746 +2465,3 @@ class SharedProviderTestCase(ResourceProviderBaseCase): ) got_ids = [rp.id for rp in got_rps] self.assertEqual([cn1.id], got_ids) - - -class AllocationCandidatesTestCase(ResourceProviderBaseCase): - """Tests a variety of scenarios with both shared and non-shared resource - providers that the AllocationCandidates.get_by_filters() method returns a - set of alternative allocation requests and provider summaries that may be - used by the scheduler to sort/weigh the options it has for claiming - resources against providers. - """ - - def _requested_resources(self): - # The resources we will request - resources = { - fields.ResourceClass.VCPU: 1, - fields.ResourceClass.MEMORY_MB: 64, - fields.ResourceClass.DISK_GB: 1500, - } - return resources - - def _find_summary_for_provider(self, p_sums, rp_uuid): - for summary in p_sums: - if summary.resource_provider.uuid == rp_uuid: - return summary - - def _find_summary_for_resource(self, p_sum, rc_name): - for resource in p_sum.resources: - if resource.resource_class == rc_name: - return resource - - def _find_requests_for_provider(self, reqs, rp_uuid): - res = [] - for ar in reqs: - for rr in ar.resource_requests: - if rr.resource_provider.uuid == rp_uuid: - res.append(rr) - return res - - def _find_request_for_resource(self, res_reqs, rc_name): - for rr in res_reqs: - if rr.resource_class == rc_name: - return rr - - def test_all_local(self): - """Create some resource providers that can satisfy the request for - resources with local (non-shared) resources and verify that the - allocation requests returned by AllocationCandidates correspond with - each of these resource providers. - """ - # Create two compute node providers with VCPU, RAM and local disk - cn1_uuid = uuidsentinel.cn1 - cn1 = rp_obj.ResourceProvider( - self.ctx, - name='cn1', - uuid=cn1_uuid, - ) - cn1.create() - - cn2_uuid = uuidsentinel.cn2 - cn2 = rp_obj.ResourceProvider( - self.ctx, - name='cn2', - uuid=cn2_uuid, - ) - cn2.create() - - cn3_uuid = uuidsentinel.cn3 - cn3 = rp_obj.ResourceProvider( - self.ctx, - name='cn3', - uuid=cn3_uuid - ) - cn3.create() - - for cn in (cn1, cn2, cn3): - vcpu = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.VCPU, - total=24, - reserved=0, - min_unit=1, - max_unit=24, - step_size=1, - allocation_ratio=16.0, - ) - memory_mb = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.MEMORY_MB, - total=32768, - reserved=0, - min_unit=64, - max_unit=32768, - step_size=64, - allocation_ratio=1.5, - ) - if cn.uuid == cn3_uuid: - disk_gb = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.DISK_GB, - total=1000, - reserved=100, - min_unit=10, - max_unit=1000, - step_size=10, - allocation_ratio=1.0, - ) - else: - disk_gb = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.DISK_GB, - total=2000, - reserved=100, - min_unit=10, - max_unit=2000, - step_size=10, - allocation_ratio=1.0, - ) - disk_gb.obj_set_defaults() - inv_list = rp_obj.InventoryList(objects=[ - vcpu, - memory_mb, - disk_gb, - ]) - cn.set_inventory(inv_list) - - # Ask for the alternative placement possibilities and verify each - # provider is returned - requested_resources = self._requested_resources() - p_alts = rp_obj.AllocationCandidates.get_by_filters( - self.ctx, - filters={ - 'resources': requested_resources, - }, - ) - - # Verify the provider summary information indicates 0 usage and - # capacity calculated from above inventory numbers for both compute - # nodes - p_sums = p_alts.provider_summaries - self.assertEqual(2, len(p_sums)) - - p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) - - self.assertEqual(set([cn1_uuid, cn2_uuid]), p_sum_rps) - - cn1_p_sum = self._find_summary_for_provider(p_sums, cn1_uuid) - self.assertIsNotNone(cn1_p_sum) - self.assertEqual(3, len(cn1_p_sum.resources)) - - cn1_p_sum_vcpu = self._find_summary_for_resource(cn1_p_sum, 'VCPU') - self.assertIsNotNone(cn1_p_sum_vcpu) - - expected_capacity = (24 * 16.0) - self.assertEqual(expected_capacity, cn1_p_sum_vcpu.capacity) - self.assertEqual(0, cn1_p_sum_vcpu.used) - - # Let's verify the disk for the second compute node - cn2_p_sum = self._find_summary_for_provider(p_sums, cn2_uuid) - self.assertIsNotNone(cn2_p_sum) - self.assertEqual(3, len(cn2_p_sum.resources)) - - cn2_p_sum_disk = self._find_summary_for_resource(cn2_p_sum, 'DISK_GB') - self.assertIsNotNone(cn2_p_sum_disk) - - expected_capacity = ((2000 - 100) * 1.0) - self.assertEqual(expected_capacity, cn2_p_sum_disk.capacity) - self.assertEqual(0, cn2_p_sum_disk.used) - - # Verify the allocation requests that are returned. There should be 2 - # allocation requests, one for each compute node, containing 3 - # resources in each allocation request, one each for VCPU, RAM, and - # disk. The amounts of the requests should correspond to the requested - # resource amounts in the filter:resources dict passed to - # AllocationCandidates.get_by_filters(). - a_reqs = p_alts.allocation_requests - self.assertEqual(2, len(a_reqs)) - - a_req_rps = set() - for ar in a_reqs: - for rr in ar.resource_requests: - a_req_rps.add(rr.resource_provider.uuid) - - self.assertEqual(set([cn1_uuid, cn2_uuid]), a_req_rps) - - cn1_reqs = self._find_requests_for_provider(a_reqs, cn1_uuid) - # There should be a req object for each resource we have requested - self.assertEqual(3, len(cn1_reqs)) - - cn1_req_vcpu = self._find_request_for_resource(cn1_reqs, 'VCPU') - self.assertIsNotNone(cn1_req_vcpu) - self.assertEqual(requested_resources['VCPU'], cn1_req_vcpu.amount) - - cn2_req_disk = self._find_request_for_resource(cn1_reqs, 'DISK_GB') - self.assertIsNotNone(cn2_req_disk) - self.assertEqual(requested_resources['DISK_GB'], cn2_req_disk.amount) - - def test_local_with_shared_disk(self): - """Create some resource providers that can satisfy the request for - resources with local VCPU and MEMORY_MB but rely on a shared storage - pool to satisfy DISK_GB and verify that the allocation requests - returned by AllocationCandidates have DISK_GB served up by the shared - storage pool resource provider and VCPU/MEMORY_MB by the compute node - providers - """ - # The aggregate that will be associated to everything... - agg_uuid = uuidsentinel.agg - - # Create two compute node providers with VCPU, RAM and NO local disk - cn1_uuid = uuidsentinel.cn1 - cn1 = rp_obj.ResourceProvider( - self.ctx, - name='cn1', - uuid=cn1_uuid, - ) - cn1.create() - - cn2_uuid = uuidsentinel.cn2 - cn2 = rp_obj.ResourceProvider( - self.ctx, - name='cn2', - uuid=cn2_uuid, - ) - cn2.create() - - # Populate the two compute node providers with inventory, sans DISK_GB - for cn in (cn1, cn2): - vcpu = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.VCPU, - total=24, - reserved=0, - min_unit=1, - max_unit=24, - step_size=1, - allocation_ratio=16.0, - ) - memory_mb = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.MEMORY_MB, - total=1024, - reserved=0, - min_unit=64, - max_unit=1024, - step_size=1, - allocation_ratio=1.5, - ) - inv_list = rp_obj.InventoryList(objects=[vcpu, memory_mb]) - cn.set_inventory(inv_list) - - # Create the shared storage pool - ss_uuid = uuidsentinel.ss - ss = rp_obj.ResourceProvider( - self.ctx, - name='shared storage', - uuid=ss_uuid, - ) - ss.create() - - # Give the shared storage pool some inventory of DISK_GB - disk_gb = rp_obj.Inventory( - resource_provider=ss, - resource_class=fields.ResourceClass.DISK_GB, - total=2000, - reserved=100, - min_unit=10, - max_unit=2000, - step_size=1, - allocation_ratio=1.0, - ) - inv_list = rp_obj.InventoryList(objects=[disk_gb]) - ss.set_inventory(inv_list) - - # Mark the shared storage pool as having inventory shared among any - # provider associated via aggregate - t = rp_obj.Trait.get_by_name(self.ctx, "MISC_SHARES_VIA_AGGREGATE") - ss.set_traits(rp_obj.TraitList(objects=[t])) - - # Now associate the shared storage pool and both compute nodes with the - # same aggregate - cn1.set_aggregates([agg_uuid]) - cn2.set_aggregates([agg_uuid]) - ss.set_aggregates([agg_uuid]) - - # Ask for the alternative placement possibilities and verify each - # compute node provider is listed in the allocation requests as well as - # the shared storage pool provider - requested_resources = self._requested_resources() - p_alts = rp_obj.AllocationCandidates.get_by_filters( - self.ctx, - filters={ - 'resources': requested_resources, - }, - ) - - # Verify the provider summary information indicates 0 usage and - # capacity calculated from above inventory numbers for both compute - # nodes - p_sums = p_alts.provider_summaries - self.assertEqual(3, len(p_sums)) - - p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) - - self.assertEqual(set([cn1_uuid, cn2_uuid, ss_uuid]), p_sum_rps) - - cn1_p_sum = self._find_summary_for_provider(p_sums, cn1_uuid) - self.assertIsNotNone(cn1_p_sum) - self.assertEqual(2, len(cn1_p_sum.resources)) - - cn1_p_sum_vcpu = self._find_summary_for_resource(cn1_p_sum, 'VCPU') - self.assertIsNotNone(cn1_p_sum_vcpu) - - expected_capacity = (24 * 16.0) - self.assertEqual(expected_capacity, cn1_p_sum_vcpu.capacity) - self.assertEqual(0, cn1_p_sum_vcpu.used) - - # Let's verify memory for the second compute node - cn2_p_sum = self._find_summary_for_provider(p_sums, cn2_uuid) - self.assertIsNotNone(cn2_p_sum) - self.assertEqual(2, len(cn2_p_sum.resources)) - - cn2_p_sum_ram = self._find_summary_for_resource(cn2_p_sum, 'MEMORY_MB') - self.assertIsNotNone(cn2_p_sum_ram) - - expected_capacity = (1024 * 1.5) - self.assertEqual(expected_capacity, cn2_p_sum_ram.capacity) - self.assertEqual(0, cn2_p_sum_ram.used) - - # Let's verify only diks for the shared storage pool - ss_p_sum = self._find_summary_for_provider(p_sums, ss_uuid) - self.assertIsNotNone(ss_p_sum) - self.assertEqual(1, len(ss_p_sum.resources)) - - ss_p_sum_disk = self._find_summary_for_resource(ss_p_sum, 'DISK_GB') - self.assertIsNotNone(ss_p_sum_disk) - - expected_capacity = ((2000 - 100) * 1.0) - self.assertEqual(expected_capacity, ss_p_sum_disk.capacity) - self.assertEqual(0, ss_p_sum_disk.used) - - # Verify the allocation requests that are returned. There should be 2 - # allocation requests, one for each compute node, containing 3 - # resources in each allocation request, one each for VCPU, RAM, and - # disk. The amounts of the requests should correspond to the requested - # resource amounts in the filter:resources dict passed to - # AllocationCandidates.get_by_filters(). The providers for VCPU and - # MEMORY_MB should be the compute nodes while the provider for the - # DISK_GB should be the shared storage pool - a_reqs = p_alts.allocation_requests - self.assertEqual(2, len(a_reqs)) - - a_req_rps = set() - for ar in a_reqs: - for rr in ar.resource_requests: - a_req_rps.add(rr.resource_provider.uuid) - - self.assertEqual(set([cn1_uuid, cn2_uuid, ss_uuid]), a_req_rps) - - cn1_reqs = self._find_requests_for_provider(a_reqs, cn1_uuid) - # There should be a req object for only VCPU and MEMORY_MB - self.assertEqual(2, len(cn1_reqs)) - - cn1_req_vcpu = self._find_request_for_resource(cn1_reqs, 'VCPU') - self.assertIsNotNone(cn1_req_vcpu) - self.assertEqual(requested_resources['VCPU'], cn1_req_vcpu.amount) - - cn2_reqs = self._find_requests_for_provider(a_reqs, cn2_uuid) - - # There should NOT be an allocation resource request that lists a - # compute node provider UUID for DISK_GB, since the shared storage pool - # is the thing that is providing the disk - cn1_req_disk = self._find_request_for_resource(cn1_reqs, 'DISK_GB') - self.assertIsNone(cn1_req_disk) - cn2_req_disk = self._find_request_for_resource(cn2_reqs, 'DISK_GB') - self.assertIsNone(cn2_req_disk) - - # Let's check the second compute node for MEMORY_MB - cn2_req_ram = self._find_request_for_resource(cn2_reqs, 'MEMORY_MB') - self.assertIsNotNone(cn2_req_ram) - self.assertEqual(requested_resources['MEMORY_MB'], cn2_req_ram.amount) - - # We should find the shared storage pool providing the DISK_GB for each - # of the allocation requests - ss_reqs = self._find_requests_for_provider(a_reqs, ss_uuid) - self.assertEqual(2, len(ss_reqs)) - - # Shared storage shouldn't be listed as providing anything but disk... - ss_req_ram = self._find_request_for_resource(ss_reqs, 'MEMORY_MB') - self.assertIsNone(ss_req_ram) - - ss_req_disk = self._find_request_for_resource(ss_reqs, 'DISK_GB') - self.assertIsNotNone(ss_req_disk) - self.assertEqual(requested_resources['DISK_GB'], ss_req_disk.amount) - - # Test for bug #1705071. We query for allocation candidates with a - # request for ONLY the DISK_GB (the resource that is shared with - # compute nodes) and no VCPU/MEMORY_MB. Before the fix for bug - # #1705071, this resulted in a KeyError - - p_alts = rp_obj.AllocationCandidates.get_by_filters( - self.ctx, - filters={ - 'resources': { - 'DISK_GB': 10, - } - }, - ) - - # We should only have provider summary information for the sharing - # storage provider, since that's the only provider that can be - # allocated against for this request. In the future, we may look into - # returning the shared-with providers in the provider summaries, but - # that's a distant possibility. - p_sums = p_alts.provider_summaries - self.assertEqual(1, len(p_sums)) - - p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) - - self.assertEqual(set([ss_uuid]), p_sum_rps) - - # The allocation_requests will only include the shared storage - # provider because the only thing we're requesting to allocate is - # against the provider of DISK_GB, which happens to be the shared - # storage provider. - a_reqs = p_alts.allocation_requests - self.assertEqual(1, len(a_reqs)) - - a_req_rps = set() - for ar in a_reqs: - for rr in ar.resource_requests: - a_req_rps.add(rr.resource_provider.uuid) - - self.assertEqual(set([ss_uuid]), a_req_rps) - - def test_local_with_shared_custom_resource(self): - """Create some resource providers that can satisfy the request for - resources with local VCPU and MEMORY_MB but rely on a shared resource - provider to satisfy a custom resource requirement and verify that the - allocation requests returned by AllocationCandidates have the custom - resource served up by the shared custom resource provider and - VCPU/MEMORY_MB by the compute node providers - """ - # The aggregate that will be associated to everything... - agg_uuid = uuidsentinel.agg - - # Create two compute node providers with VCPU, RAM and NO local - # CUSTOM_MAGIC resources - cn1_uuid = uuidsentinel.cn1 - cn1 = rp_obj.ResourceProvider( - self.ctx, - name='cn1', - uuid=cn1_uuid, - ) - cn1.create() - - cn2_uuid = uuidsentinel.cn2 - cn2 = rp_obj.ResourceProvider( - self.ctx, - name='cn2', - uuid=cn2_uuid, - ) - cn2.create() - - # Populate the two compute node providers with inventory - for cn in (cn1, cn2): - vcpu = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.VCPU, - total=24, - reserved=0, - min_unit=1, - max_unit=24, - step_size=1, - allocation_ratio=16.0, - ) - memory_mb = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.MEMORY_MB, - total=1024, - reserved=0, - min_unit=64, - max_unit=1024, - step_size=1, - allocation_ratio=1.5, - ) - inv_list = rp_obj.InventoryList(objects=[vcpu, memory_mb]) - cn.set_inventory(inv_list) - - # Create a custom resource called MAGIC - magic_rc = rp_obj.ResourceClass( - self.ctx, - name='CUSTOM_MAGIC', - ) - magic_rc.create() - - # Create the shared provider that servers MAGIC - magic_p_uuid = uuidsentinel.magic_p - magic_p = rp_obj.ResourceProvider( - self.ctx, - name='shared custom resource provider', - uuid=magic_p_uuid, - ) - magic_p.create() - - # Give the provider some MAGIC - magic = rp_obj.Inventory( - resource_provider=magic_p, - resource_class=magic_rc.name, - total=2048, - reserved=1024, - min_unit=10, - max_unit=2048, - step_size=1, - allocation_ratio=1.0, - ) - inv_list = rp_obj.InventoryList(objects=[magic]) - magic_p.set_inventory(inv_list) - - # Mark the magic provider as having inventory shared among any provider - # associated via aggregate - t = rp_obj.Trait( - self.ctx, - name="MISC_SHARES_VIA_AGGREGATE", - ) - # TODO(jaypipes): Once MISC_SHARES_VIA_AGGREGATE is a standard - # os-traits trait, we won't need to create() here. Instead, we will - # just do: - # t = rp_obj.Trait.get_by_name( - # self.context, - # "MISC_SHARES_VIA_AGGREGATE", - # ) - t.create() - magic_p.set_traits(rp_obj.TraitList(objects=[t])) - - # Now associate the shared custom resource provider and both compute - # nodes with the same aggregate - cn1.set_aggregates([agg_uuid]) - cn2.set_aggregates([agg_uuid]) - magic_p.set_aggregates([agg_uuid]) - - # The resources we will request - requested_resources = { - fields.ResourceClass.VCPU: 1, - fields.ResourceClass.MEMORY_MB: 64, - magic_rc.name: 512, - } - - p_alts = rp_obj.AllocationCandidates.get_by_filters( - self.ctx, - filters={ - 'resources': requested_resources, - }, - ) - - # Verify the allocation requests that are returned. There should be 2 - # allocation requests, one for each compute node, containing 3 - # resources in each allocation request, one each for VCPU, RAM, and - # MAGIC. The amounts of the requests should correspond to the requested - # resource amounts in the filter:resources dict passed to - # AllocationCandidates.get_by_filters(). The providers for VCPU and - # MEMORY_MB should be the compute nodes while the provider for the - # MAGIC should be the shared custom resource provider. - a_reqs = p_alts.allocation_requests - self.assertEqual(2, len(a_reqs)) - - a_req_rps = set() - for ar in a_reqs: - for rr in ar.resource_requests: - a_req_rps.add(rr.resource_provider.uuid) - - self.assertEqual(set([cn1_uuid, cn2_uuid, magic_p_uuid]), a_req_rps) - - cn1_reqs = self._find_requests_for_provider(a_reqs, cn1_uuid) - # There should be a req object for only VCPU and MEMORY_MB - self.assertEqual(2, len(cn1_reqs)) - - cn1_req_vcpu = self._find_request_for_resource(cn1_reqs, 'VCPU') - self.assertIsNotNone(cn1_req_vcpu) - self.assertEqual(requested_resources['VCPU'], cn1_req_vcpu.amount) - - cn2_reqs = self._find_requests_for_provider(a_reqs, cn2_uuid) - - # There should NOT be an allocation resource request that lists a - # compute node provider UUID for MAGIC, since the shared - # custom provider is the thing that is providing the disk - cn1_req_disk = self._find_request_for_resource(cn1_reqs, magic_rc.name) - self.assertIsNone(cn1_req_disk) - cn2_req_disk = self._find_request_for_resource(cn2_reqs, magic_rc.name) - self.assertIsNone(cn2_req_disk) - - # Let's check the second compute node for MEMORY_MB - cn2_req_ram = self._find_request_for_resource(cn2_reqs, 'MEMORY_MB') - self.assertIsNotNone(cn2_req_ram) - self.assertEqual(requested_resources['MEMORY_MB'], cn2_req_ram.amount) - - # We should find the shared custom resource provider providing the - # MAGIC for each of the allocation requests - magic_p_reqs = self._find_requests_for_provider(a_reqs, magic_p_uuid) - self.assertEqual(2, len(magic_p_reqs)) - - # Shared custom resource provider shouldn't be listed as providing - # anything but MAGIC... - magic_p_req_ram = self._find_request_for_resource( - magic_p_reqs, 'MEMORY_MB') - self.assertIsNone(magic_p_req_ram) - - magic_p_req_magic = self._find_request_for_resource( - magic_p_reqs, magic_rc.name) - self.assertIsNotNone(magic_p_req_magic) - self.assertEqual( - requested_resources[magic_rc.name], magic_p_req_magic.amount) - - def test_mix_local_and_shared(self): - # The aggregate that will be associated to shared storage pool - agg_uuid = uuidsentinel.agg - - # Create three compute node providers with VCPU and RAM, but only - # the third compute node has DISK. The first two computes will - # share the storage from the shared storage pool - cn1_uuid = uuidsentinel.cn1 - cn1 = rp_obj.ResourceProvider( - self.ctx, - name='cn1', - uuid=cn1_uuid, - ) - cn1.create() - - cn2_uuid = uuidsentinel.cn2 - cn2 = rp_obj.ResourceProvider( - self.ctx, - name='cn2', - uuid=cn2_uuid, - ) - cn2.create() - - cn3_uuid = uuidsentinel.cn3 - cn3 = rp_obj.ResourceProvider( - self.ctx, - name='cn3', - uuid=cn3_uuid - ) - cn3.create() - - # Populate the two compute node providers with inventory - for cn in (cn1, cn2, cn3): - vcpu = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.VCPU, - total=24, - reserved=0, - min_unit=1, - max_unit=24, - step_size=1, - allocation_ratio=16.0, - ) - memory_mb = rp_obj.Inventory( - resource_provider=cn, - resource_class=fields.ResourceClass.MEMORY_MB, - total=1024, - reserved=0, - min_unit=64, - max_unit=1024, - step_size=1, - allocation_ratio=1.5, - ) - disk_gb = rp_obj.Inventory( - resource_provider=cn3, - resource_class=fields.ResourceClass.DISK_GB, - total=2000, - reserved=100, - min_unit=10, - max_unit=2000, - step_size=1, - allocation_ratio=1.0, - ) - if cn == cn3: - inv_list = rp_obj.InventoryList( - objects=[vcpu, memory_mb, disk_gb]) - else: - inv_list = rp_obj.InventoryList(objects=[vcpu, memory_mb]) - cn.set_inventory(inv_list) - - # Create the shared storage pool - ss_uuid = uuidsentinel.ss - ss = rp_obj.ResourceProvider( - self.ctx, - name='shared storage', - uuid=ss_uuid, - ) - ss.create() - - # Give the shared storage pool some inventory of DISK_GB - disk_gb = rp_obj.Inventory( - resource_provider=ss, - resource_class=fields.ResourceClass.DISK_GB, - total=2000, - reserved=100, - min_unit=10, - max_unit=2000, - step_size=1, - allocation_ratio=1.0, - ) - inv_list = rp_obj.InventoryList(objects=[disk_gb]) - ss.set_inventory(inv_list) - - t = rp_obj.Trait.get_by_name(self.ctx, "MISC_SHARES_VIA_AGGREGATE") - ss.set_traits(rp_obj.TraitList(objects=[t])) - - # Put the cn1, cn2 and ss in the same aggregate - cn1.set_aggregates([agg_uuid]) - cn2.set_aggregates([agg_uuid]) - ss.set_aggregates([agg_uuid]) - - requested_resources = self._requested_resources() - p_alts = rp_obj.AllocationCandidates.get_by_filters( - self.ctx, - filters={ - 'resources': requested_resources, - }, - ) - - # Expect cn1, cn2, cn3 and ss in the summaries - p_sums = p_alts.provider_summaries - self.assertEqual(4, len(p_sums)) - - p_sum_rps = set([ps.resource_provider.uuid for ps in p_sums]) - - self.assertEqual(set([cn1_uuid, cn2_uuid, - ss_uuid, cn3_uuid]), - p_sum_rps) - - # Expect three allocation requests: (cn1, ss), (cn2, ss), (cn3) - a_reqs = p_alts.allocation_requests - self.assertEqual(3, len(a_reqs)) - - expected_ar = [] - for ar in a_reqs: - rr_set = set() - for rr in ar.resource_requests: - rr_set.add(rr.resource_provider.uuid) - expected_ar.append(rr_set) - - self.assertEqual(sorted(expected_ar), - sorted([set([cn1.uuid, ss.uuid]), - set([cn2.uuid, ss.uuid]), set([cn3.uuid])]))