From dc472e144a8e9edba90c4dc05a2e7e2220854387 Mon Sep 17 00:00:00 2001 From: Tetsuro Nakamura Date: Fri, 14 Sep 2018 07:16:29 +0900 Subject: [PATCH] Add alloc cands test with nested and aggregates When placement picks up allocation candidates, the aggregates of nested providers are assumed as the same as root providers. This means it ignores the aggregates of the nested provider itself. This could result in the lack of allocation candidates when an aggregate on a nested provider but not on the root has been specified in the `member_of` query parameter. This patch adds test cases for the bug. The fix is done in a follow up. Related-Bug: #1792503 Change-Id: Ic532bc231e89eaec4cb5ad01d27bbe840b01a5f7 --- .../tests/functional/fixtures/gabbits.py | 79 ++++++++++++ .../allocation-candidates-bug-1792503.yaml | 118 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 placement/tests/functional/gabbits/allocation-candidates-bug-1792503.yaml diff --git a/placement/tests/functional/fixtures/gabbits.py b/placement/tests/functional/fixtures/gabbits.py index dea10bbd2..da03dd721 100644 --- a/placement/tests/functional/fixtures/gabbits.py +++ b/placement/tests/functional/fixtures/gabbits.py @@ -277,6 +277,85 @@ class SharedStorageFixture(APIFixture): 8, allocation_ratio=1.0) +class NUMAAggregateFixture(APIFixture): + """An APIFixture that has two compute nodes without a resource themselves. + They are associated by aggregate to a provider of shared storage and both + compute nodes have two numa node resource providers with CPUs. One of the + numa node is associated to another sharing storage by a different + aggregate. + + +-----------------------+ + | sharing storage (ss1) | + | DISK_GB:2000 | + | agg: [aggA] | + +-----------+-----------+ + | + +---------------+----------------+ + +---------------|--------------+ +--------------|--------------+ + | +-------------+------------+ | | +------------+------------+ | + | | compute node (cn1) | | | |compute node (cn2) | | + | | agg: [aggA] | | | | agg: [aggA, aggB] | | + | +-----+-------------+------+ | | +----+-------------+------+ | + | | nested | nested | | | nested | nested | + | +-----+------+ +----+------+ | | +----+------+ +----+------+ | + | | numa1_1 | | numa1_2 | | | | numa2_1 | | numa2_2 | | + | | CPU: 24 | | CPU: 24 | | | | CPU: 24 | | CPU: 24 | | + | | agg:[aggC]| | | | | | | | | | + | +-----+------+ +-----------+ | | +-----------+ +-----------+ | + +-------|----------------------+ +-----------------------------+ + | aggC + +-----+-----------------+ + | sharing storage (ss2) | + | DISK_GB:2000 | + | agg: [aggC] | + +-----------------------+ + """ + + def start_fixture(self): + super(NUMAAggregateFixture, self).start_fixture() + + aggA_uuid = uuidutils.generate_uuid() + aggB_uuid = uuidutils.generate_uuid() + aggC_uuid = uuidutils.generate_uuid() + + cn1 = tb.create_provider(self.context, 'cn1', aggA_uuid) + cn2 = tb.create_provider(self.context, 'cn2', aggA_uuid, aggB_uuid) + ss1 = tb.create_provider(self.context, 'ss1', aggA_uuid) + ss2 = tb.create_provider(self.context, 'ss2', aggC_uuid) + + numa1_1 = tb.create_provider( + self.context, 'numa1_1', aggC_uuid, parent=cn1.uuid) + numa1_2 = tb.create_provider(self.context, 'numa1_2', parent=cn1.uuid) + numa2_1 = tb.create_provider(self.context, 'numa2_1', parent=cn2.uuid) + numa2_2 = tb.create_provider(self.context, 'numa2_2', parent=cn2.uuid) + + os.environ['AGGA_UUID'] = aggA_uuid + os.environ['AGGB_UUID'] = aggB_uuid + os.environ['AGGC_UUID'] = aggC_uuid + + os.environ['CN1_UUID'] = cn1.uuid + os.environ['CN2_UUID'] = cn2.uuid + os.environ['SS1_UUID'] = ss1.uuid + os.environ['SS2_UUID'] = ss2.uuid + + os.environ['NUMA1_1_UUID'] = numa1_1.uuid + os.environ['NUMA1_2_UUID'] = numa1_2.uuid + os.environ['NUMA2_1_UUID'] = numa2_1.uuid + os.environ['NUMA2_2_UUID'] = numa2_2.uuid + + # Populate compute node inventory for VCPU and RAM + for numa in (numa1_1, numa1_2, numa2_1, numa2_2): + tb.add_inventory(numa, fields.ResourceClass.VCPU, 24, + allocation_ratio=16.0) + + # Populate shared storage provider with DISK_GB inventory and + # mark it shared among any provider associated via aggregate + for ss in (ss1, ss2): + tb.add_inventory(ss, fields.ResourceClass.DISK_GB, 2000, + reserved=100, allocation_ratio=1.0) + tb.set_traits(ss, 'MISC_SHARES_VIA_AGGREGATE') + + class NonSharedStorageFixture(APIFixture): """An APIFixture that has two compute nodes with local storage that do not use shared storage. diff --git a/placement/tests/functional/gabbits/allocation-candidates-bug-1792503.yaml b/placement/tests/functional/gabbits/allocation-candidates-bug-1792503.yaml new file mode 100644 index 000000000..e1a33e6cc --- /dev/null +++ b/placement/tests/functional/gabbits/allocation-candidates-bug-1792503.yaml @@ -0,0 +1,118 @@ +# Tests of allocation candidates API + +fixtures: + - NUMAAggregateFixture + +defaults: + request_headers: + x-auth-token: admin + accept: application/json + openstack-api-version: placement 1.29 + +tests: + +- name: get allocation candidates without aggregate + GET: /allocation_candidates?resources=VCPU:1 + response_json_paths: + $.allocation_requests.`len`: 4 + $.allocation_requests..allocations["$ENVIRON['NUMA1_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA1_2_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_1_UUID']"].resources.VCPU: 1 + +- name: get allocation candidates with aggregate A + GET: /allocation_candidates?resources=VCPU:1&member_of=$ENVIRON['AGGA_UUID'] + response_json_paths: + # Aggregate A is on the root rps (both cn1 and cn2) so it spans on the + # whole tree. We have full allocations here. + $.allocation_requests.`len`: 4 + $.allocation_requests..allocations["$ENVIRON['NUMA1_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA1_2_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_2_UUID']"].resources.VCPU: 1 + +- name: get allocation candidates with aggregate B + GET: /allocation_candidates?resources=VCPU:1&member_of=$ENVIRON['AGGB_UUID'] + response_json_paths: + # Aggregate B is on the root of cn2 so it spans on the + # whole tree including rps of NUMA2_1 and NUMA2_2. + $.allocation_requests.`len`: 2 + $.allocation_requests..allocations["$ENVIRON['NUMA2_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_2_UUID']"].resources.VCPU: 1 + +- name: get allocation candidates with aggregate C + GET: /allocation_candidates?resources=VCPU:1&member_of=$ENVIRON['AGGC_UUID'] + response_json_paths: + # Aggregate C is *NOT* on the root, so we should get only NUMA1_1 + # here that is only the rp in aggregate C. + # --------------------- + # Bug#1792503: It lacks allocation candidates when an aggregate on the + # nested rp but not on the root rp has been specified in + # the `member_of` query parameter. + # --------------------- + # $.allocation_requests.`len`: 1 + # $.allocation_requests..allocations["$ENVIRON['NUMA1_1_UUID']"].resources.VCPU: 1 + $.allocation_requests.`len`: 0 + +- name: get allocation candidates with shared storage + GET: /allocation_candidates?resources=VCPU:1,DISK_GB:1000 + response_json_paths: + # Since `members_of` query parameter is not specified, sharing rp 1 is + # being shared with the *whole* trees of CN1 and CN2. Sharing rp 2 is + # being shared with the *whole* tree of CN1. + # As a result, there should be 6 allocation candidates: + # [ + # (numa1-1, ss1), (numa1-2, ss1), (numa2-1, ss1), (numa2-2, ss1), + # (numa1-1, ss2), + # ] + $.allocation_requests.`len`: 6 + $.allocation_requests..allocations["$ENVIRON['NUMA1_1_UUID']"].resources.VCPU: [1, 1] + $.allocation_requests..allocations["$ENVIRON['NUMA1_2_UUID']"].resources.VCPU: [1, 1] + $.allocation_requests..allocations["$ENVIRON['NUMA2_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_2_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: [1000, 1000, 1000, 1000] + $.allocation_requests..allocations["$ENVIRON['SS2_UUID']"].resources.DISK_GB: [1000, 1000] + +- name: get allocation candidates with shared storage with aggregate A + GET: /allocation_candidates?resources=VCPU:1,DISK_GB:1000&member_of=$ENVIRON['AGGA_UUID'] + response_json_paths: + $.allocation_requests.`len`: 4 + # Since aggregate A is specified, which is on the root CN1, sharing + # rp 1 can be allocation candidates with the *whole* trees in CN1. + # Sharing rp 2 can't in the allocation candidates since it is not + # under aggregate A but under aggregate C. + # As a result, there should be 4 allocation candidates: + # [ + # (numa1-1, ss1), (numa1-2, ss1), (numa2-1, ss1), (numa2-2, ss1) + # ] + $.allocation_requests..allocations["$ENVIRON['NUMA1_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA1_2_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_1_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['NUMA2_2_UUID']"].resources.VCPU: 1 + $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: [1000, 1000, 1000, 1000] + +- name: get allocation candidates with shared storage with aggregate B + GET: /allocation_candidates?resources=VCPU:1,DISK_GB:1000&member_of=$ENVIRON['AGGB_UUID'] + response_json_paths: + # We don't have shared disk in aggregate B. + $.allocation_requests.`len`: 0 + +- name: get allocation candidates with shared storage with aggregate C + GET: /allocation_candidates?resources=VCPU:1,DISK_GB:1000&member_of=$ENVIRON['AGGC_UUID'] + response_json_paths: + # Since aggregate C is specified, which is on *non-root*, NUMA1_1, + # sharing provider 2 is not shared with the whole tree. It is shared + # with rps only with aggregate C for their own (opposite to not on root). + # As a result, there should be 1 allocation candidate: + # [ + # (numa1-1, ss2), + # ] + # --------------------- + # Bug#1792503: It lacks allocation candidates when an aggregate on the + # nested rp but not on the root rp has been specified in + # the `member_of` query parameter. + # --------------------- + # $.allocation_requests.`len`: 1 + # $.allocation_requests..allocations["$ENVIRON['NUMA1_1_UUID']"].resources.VCPU: 1 + # $.allocation_requests..allocations["$ENVIRON['SS2_UUID']"].resources.DISK_GB: 1000 + $.allocation_requests.`len`: 0