# Tests of allocation candidates API with root_required fixtures: - NUMANetworkFixture defaults: request_headers: x-auth-token: admin accept: application/json openstack-api-version: placement 1.35 tests: - name: root_required before microversion GET: /allocation_candidates?resources=VCPU:1&root_required=HW_CPU_X86_AVX2 request_headers: openstack-api-version: placement 1.34 status: 400 response_strings: - Invalid query string parameters - "'root_required' does not match any of the regexes" - name: conflicting required and forbidden GET: /allocation_candidates?resources=VCPU:1&root_required=HW_CPU_X86_AVX2,HW_CPU_X86_SSE,!HW_CPU_X86_AVX2 status: 400 response_strings: - "Conflicting required and forbidden traits found in root_required: HW_CPU_X86_AVX2" response_json_paths: errors[0].code: placement.query.bad_value - name: nonexistent required GET: /allocation_candidates?resources=VCPU:1&root_required=CUSTOM_NO_EXIST,HW_CPU_X86_SSE,!HW_CPU_X86_AVX status: 400 response_strings: - "No such trait(s): CUSTOM_NO_EXIST" - name: nonexistent forbidden GET: /allocation_candidates?resources=VCPU:1&root_required=!CUSTOM_NO_EXIST,HW_CPU_X86_SSE,!HW_CPU_X86_AVX status: 400 response_strings: - "No such trait(s): CUSTOM_NO_EXIST" - name: multiple root_required is an error GET: /allocation_candidates?resources=VCPU:1&root_required=MISC_SHARES_VIA_AGGREGATE&root_required=!HW_NUMA_ROOT status: 400 response_strings: - Query parameter 'root_required' may be specified only once. response_json_paths: errors[0].code: placement.query.duplicate_key - name: no hits for a required trait that is on children in one tree and absent from the other GET: /allocation_candidates?resources=VCPU:1&root_required=HW_NUMA_ROOT status: 200 response_json_paths: # No root has HW_NUMA_ROOT $.allocation_requests.`len`: 0 - name: required trait on a sharing root GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=MISC_SHARES_VIA_AGGREGATE status: 200 response_json_paths: # MISC_SHARES is on the sharing root, but not on any of the anchor roots $.allocation_requests.`len`: 0 - name: root_required trait on children GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=HW_NUMA_ROOT status: 200 response_json_paths: # HW_NUMA_ROOT is on child providers, not on any root $.allocation_requests.`len`: 0 - name: required trait not on any provider GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=HW_CPU_X86_AVX2 status: 200 response_json_paths: # HW_CPU_X86_AVX2 isn't anywhere in the env. $.allocation_requests.`len`: 0 - name: limit to multiattach-capable unsuffixed no sharing GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024&root_required=COMPUTE_VOLUME_MULTI_ATTACH status: 200 response_json_paths: # We only get results from cn1 because only it has MULTI_ATTACH # We get candidates where VCPU and MEMORY_MB are provided by the same or # alternate NUMA roots. $.allocation_requests.`len`: 4 $.allocation_requests..allocations["$ENVIRON['NUMA0_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['NUMA0_UUID']"].resources.MEMORY_MB: [1024, 1024] $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.MEMORY_MB: [1024, 1024] - name: limit to multiattach-capable separate granular no isolate no sharing GET: /allocation_candidates?resources1=VCPU:1&resources2=MEMORY_MB:1024&group_policy=none&root_required=COMPUTE_VOLUME_MULTI_ATTACH status: 200 response_json_paths: # Same as above $.allocation_requests.`len`: 4 # Prove we didn't break provider summaries $.provider_summaries["$ENVIRON['NUMA0_UUID']"].resources[VCPU][capacity]: 4 $.provider_summaries["$ENVIRON['NUMA1_UUID']"].resources[MEMORY_MB][capacity]: 2048 - name: limit to multiattach-capable separate granular isolate no sharing GET: /allocation_candidates?resources1=VCPU:1&resources2=MEMORY_MB:1024&group_policy=isolate&root_required=COMPUTE_VOLUME_MULTI_ATTACH status: 200 response_json_paths: # Now we (perhaps unrealistically) only get candidates where VCPU and # MEMORY_MB are on alternate NUMA roots. $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['NUMA0_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['NUMA0_UUID']"].resources.MEMORY_MB: 1024 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.MEMORY_MB: 1024 - name: limit to multiattach-capable unsuffixed sharing GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=COMPUTE_VOLUME_MULTI_ATTACH status: 200 response_json_paths: # We only get results from cn1 because only it has MULTI_ATTACH # We get candidates where VCPU and MEMORY_MB are provided by the same or # alternate NUMA roots. DISK_GB is always provided by the sharing provider. $.allocation_requests.`len`: 4 $.provider_summaries["$ENVIRON['NUMA0_UUID']"].traits: - HW_NUMA_ROOT $.provider_summaries["$ENVIRON['NUMA1_UUID']"].traits: - HW_NUMA_ROOT - CUSTOM_FOO - name: limit to multiattach-capable granular sharing GET: /allocation_candidates?resources1=VCPU:1,MEMORY_MB:1024&resources2=DISK_GB:100&&group_policy=none&root_required=COMPUTE_VOLUME_MULTI_ATTACH status: 200 response_json_paths: # We only get results from cn1 because only it has MULTI_ATTACH # We only get candidates where VCPU and MEMORY_MB are provided by the same # NUMA root, because requested in the same suffixed group. DISK_GB is # always provided by the sharing provider. $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['NUMA0_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['NUMA0_UUID']"].resources.MEMORY_MB: 1024 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.MEMORY_MB: 1024 - name: trait exists on root and child in separate trees case 1 unsuffixed required GET: /allocation_candidates?resources=VCPU:1,DISK_GB:100&required=CUSTOM_FOO status: 200 response_json_paths: # We get a candidates from cn2 and cn2+ss1 because cn2 has all the # resources and the trait. # We get a candidate from numa1+ss1 because (even in the unsuffixed group) # regular `required` is tied to the resource in that group. $.allocation_requests.`len`: 3 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: [100, 100] $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.DISK_GB: 100 - name: trait exists on root and child in separate trees case 2 unsuffixed root_required GET: /allocation_candidates?resources=VCPU:1,DISK_GB:100&root_required=CUSTOM_FOO status: 200 response_json_paths: # We only get candidates from cn2 and cn2+ss1 because only cn2 has FOO on # the root $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: 100 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.DISK_GB: 100 - name: trait exists on root and child in separate trees case 3 suffixed required GET: /allocation_candidates?resources1=VCPU:1&required1=CUSTOM_FOO&resources2=DISK_GB:100&group_policy=none status: 200 response_json_paths: # We get a candidates from cn2 because has all the resources and the trait; # and from cn2+ss1 because group_policy=none and the required trait is on # the group with the VCPU. # We get a candidate from numa1+ss1 because the required trait is on the # group with the VCPU. $.allocation_requests.`len`: 3 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: [100, 100] $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.DISK_GB: 100 - name: trait exists on root and child in separate trees case 4 suffixed root_required GET: /allocation_candidates?resources1=VCPU:1&resources2=DISK_GB:100&group_policy=none&root_required=CUSTOM_FOO status: 200 response_json_paths: # We only get candidates from cn2 and cn2+ss1 because only cn2 has FOO on # the root $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: 100 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.DISK_GB: 100 - name: no filtering for a forbidden trait that is on children in one tree and absent from the other GET: /allocation_candidates?resources=VCPU:3&root_required=!HW_NUMA_ROOT status: 200 response_json_paths: # No root has HW_NUMA_ROOT, so we hit all providers of VCPU with adequate capacity $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: 3 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: 3 - name: forbidden trait on a sharing root GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=!MISC_SHARES_VIA_AGGREGATE status: 200 response_json_paths: # This does not filter out candidates including the sharing provider, of # which there are five (four from the combinations of VCPU+MEMORY_MB on cn1 # because non-isolated; one using VCPU+MEMORY_MB from cn2). The sixth is # where cn2 provides all the resources. $.allocation_requests.`len`: 6 $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: [100, 100, 100, 100, 100] - name: combine required with irrelevant forbidden # This time the irrelevant forbidden is on a child provider GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=CUSTOM_FOO,!HW_NUMA_ROOT status: 200 response_json_paths: # This is as above, but filtered to the candidates involving cn2, which has # CUSTOM_FOO on the root. $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.MEMORY_MB: [1024, 1024] $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.DISK_GB: 100 $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: 100 - name: redundant required and forbidden GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=CUSTOM_FOO,!COMPUTE_VOLUME_MULTI_ATTACH status: 200 response_json_paths: # Same result as above. The forbidden multi-attach and the required foo are # both doing the same thing. $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: [1, 1] $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.MEMORY_MB: [1024, 1024] $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.DISK_GB: 100 $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: 100 - name: forbiddens cancel each other GET: /allocation_candidates?resources=VCPU:1,MEMORY_MB:1024,DISK_GB:100&root_required=!CUSTOM_FOO,!COMPUTE_VOLUME_MULTI_ATTACH status: 200 response_json_paths: # !foo gets rid of cn2; !multi-attach gets rid of cn1. $.allocation_requests.`len`: 0 - name: isolate foo granular sharing GET: /allocation_candidates?resources1=VCPU:1,MEMORY_MB:1024&resources2=DISK_GB:100&&group_policy=none&root_required=!CUSTOM_FOO status: 200 response_json_paths: # We only get results from cn1 because cn2 has the forbidden foo trait. # We only get candidates where VCPU and MEMORY_MB are provided by the same # NUMA root, because requested in the same suffixed group. DISK_GB is # always provided by the sharing provider. $.allocation_requests.`len`: 2 $.allocation_requests..allocations["$ENVIRON['NUMA0_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: 1 $.allocation_requests..allocations["$ENVIRON['SS1_UUID']"].resources.DISK_GB: [100, 100] - name: unsuffixed required and root_required same trait GET: /allocation_candidates?resources=VCPU:1&required=CUSTOM_FOO&root_required=CUSTOM_FOO status: 200 response_json_paths: # required=FOO would have limited us to getting VCPU from numa1 and cn2 # BUT root_required=FOO should further restrict us to just cn2 $.allocation_requests.`len`: 1 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: 1 - name: granular required and root_required same trait GET: /allocation_candidates?resources1=VCPU:1&required1=CUSTOM_FOO&root_required=CUSTOM_FOO status: 200 response_json_paths: # same as above $.allocation_requests.`len`: 1 $.allocation_requests..allocations["$ENVIRON['CN2_UUID']"].resources.VCPU: 1 - name: required positive and root_required negative same trait GET: /allocation_candidates?resources1=VCPU:1&required1=CUSTOM_FOO&root_required=!CUSTOM_FOO status: 200 response_json_paths: # Both numa1 and cn2 match required1=FOO, but since we're forbidding FOO on # the root, we should only get numa1 $.allocation_requests.`len`: 1 $.allocation_requests..allocations["$ENVIRON['NUMA1_UUID']"].resources.VCPU: 1 - name: required negative and root_required positive same trait GET: /allocation_candidates?resources1=VCPU:1&required1=!CUSTOM_FOO&root_required=CUSTOM_FOO status: 200 response_json_paths: # The only provider of VCPU that doesn't have FOO is numa0. But numa0 is on # cn1, which doesn't have the required FOO on the root. $.allocation_requests.`len`: 0