diff --git a/nova/scheduler/request_filter.py b/nova/scheduler/request_filter.py index 58aea774880d..c132e2e4162a 100644 --- a/nova/scheduler/request_filter.py +++ b/nova/scheduler/request_filter.py @@ -66,7 +66,7 @@ def isolate_aggregates(ctxt, request_spec): return False # Get required traits set in flavor and image - res_req = utils.ResourceRequest(request_spec) + res_req = utils.ResourceRequest.from_request_spec(request_spec) required_traits = res_req.all_required_traits keys = ['trait:%s' % trait for trait in required_traits] diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py index 845103a008c2..fa376c5d12fb 100644 --- a/nova/scheduler/utils.py +++ b/nova/scheduler/utils.py @@ -57,7 +57,30 @@ class ResourceRequest(object): XS_KEYPAT = re.compile(r"^(%s)([a-zA-Z0-9_-]{1,64})?:(.*)$" % '|'.join((XS_RES_PREFIX, XS_TRAIT_PREFIX))) - def __init__(self, request_spec, enable_pinning_translate=True): + def __init__(self): + """Create an empty ResourceRequest + + Do not call this directly, use the existing static factory methods + from_*() + """ + self._rg_by_id: ty.Dict[str, objects.RequestGroup] = {} + self._group_policy: ty.Optional[str] = None + # Default to the configured limit but _limit can be + # set to None to indicate "no limit". + self._limit = CONF.scheduler.max_placement_results + self._root_required: ty.Set[str] = set() + self._root_forbidden: ty.Set[str] = set() + self.suffixed_groups_from_flavor = 0 + # TODO(stephenfin): Remove this parameter once we drop support for + # 'vcpu_pin_set' + self.cpu_pinning_requested = False + + @classmethod + def from_request_spec( + cls, + request_spec: 'objects.RequestSpec', + enable_pinning_translate: bool = True + ) -> 'ResourceRequest': """Create a new instance of ResourceRequest from a RequestSpec. Examines the flavor, flavor extra specs, (optional) image metadata, @@ -102,17 +125,13 @@ class ResourceRequest(object): :param request_spec: An instance of ``objects.RequestSpec``. :param enable_pinning_translate: True if the CPU policy extra specs should be translated to placement resources and traits. + :return: a ResourceRequest instance """ - # { ident: RequestGroup } - self._rg_by_id = {} - self._group_policy = None + res_req = cls() # root_required+=these - self._root_required = request_spec.root_required + res_req._root_required = request_spec.root_required # root_required+=!these - self._root_forbidden = request_spec.root_forbidden - # Default to the configured limit but _limit can be - # set to None to indicate "no limit". - self._limit = CONF.scheduler.max_placement_results + res_req._root_forbidden = request_spec.root_forbidden # TODO(efried): Handle member_of[$S], which will need to be reconciled # with destination.aggregates handling in resources_from_request_spec @@ -124,36 +143,35 @@ class ResourceRequest(object): image = objects.ImageMeta(properties=objects.ImageMetaProps()) # Parse the flavor extra specs - self._process_extra_specs(request_spec.flavor) + res_req._process_extra_specs(request_spec.flavor) - self.suffixed_groups_from_flavor = self.get_num_of_suffixed_groups() + # NOTE(gibi): this assumes that _process_extra_specs() was already + # called but _process_requested_resources() hasn't called it yet. + res_req.suffixed_groups_from_flavor = ( + res_req.get_num_of_suffixed_groups()) # Now parse the (optional) image metadata - self._process_image_meta(image) - - # TODO(stephenfin): Remove this parameter once we drop support for - # 'vcpu_pin_set' - self.cpu_pinning_requested = False + res_req._process_image_meta(image) if enable_pinning_translate: # Next up, let's handle those pesky CPU pinning policies - self._translate_pinning_policies(request_spec.flavor, image) + res_req._translate_pinning_policies(request_spec.flavor, image) # Add on any request groups that came from outside of the flavor/image, # e.g. from ports or device profiles. - self._process_requested_resources(request_spec) + res_req._process_requested_resources(request_spec) # Parse the flavor itself, though we'll only use these fields if they # don't conflict with something already provided by the flavor extra # specs. These are all added to the unsuffixed request group. - merged_resources = self.merged_resources() + merged_resources = res_req.merged_resources() if (orc.VCPU not in merged_resources and orc.PCPU not in merged_resources): - self._add_resource(orc.VCPU, request_spec.vcpus) + res_req._add_resource(orc.VCPU, request_spec.vcpus) if orc.MEMORY_MB not in merged_resources: - self._add_resource(orc.MEMORY_MB, request_spec.memory_mb) + res_req._add_resource(orc.MEMORY_MB, request_spec.memory_mb) if orc.DISK_GB not in merged_resources: disk = request_spec.ephemeral_gb @@ -162,15 +180,17 @@ class ResourceRequest(object): disk += request_spec.root_gb if disk: - self._add_resource(orc.DISK_GB, disk) + res_req._add_resource(orc.DISK_GB, disk) - self._translate_memory_encryption(request_spec.flavor, image) + res_req._translate_memory_encryption(request_spec.flavor, image) - self._translate_vpmems_request(request_spec.flavor) + res_req._translate_vpmems_request(request_spec.flavor) - self._translate_vtpm_request(request_spec.flavor, image) + res_req._translate_vtpm_request(request_spec.flavor, image) - self.strip_zeros() + res_req.strip_zeros() + + return res_req def _process_requested_resources(self, request_spec): requested_resources = (request_spec.requested_resources @@ -554,7 +574,7 @@ def resources_from_flavor(instance, flavor): # just merge together all the resources specified in the flavor and pass # them along. This will need to be adjusted when nested and/or shared RPs # are in play. - res_req = ResourceRequest(req_spec) + res_req = ResourceRequest.from_request_spec(req_spec) return res_req.merged_resources() @@ -573,7 +593,8 @@ def resources_from_request_spec(ctxt, spec_obj, host_manager, :return: A ResourceRequest object. :raises NoValidHost: If the specified host/node is not found in the DB. """ - res_req = ResourceRequest(spec_obj, enable_pinning_translate) + res_req = ResourceRequest.from_request_spec( + spec_obj, enable_pinning_translate) # values to get the destination target compute uuid target_host = None diff --git a/nova/tests/functional/test_report_client.py b/nova/tests/functional/test_report_client.py index b48d9a3d1fe2..aea7ee56c54a 100644 --- a/nova/tests/functional/test_report_client.py +++ b/nova/tests/functional/test_report_client.py @@ -1006,7 +1006,7 @@ class SchedulerReportClientTests(test.TestCase): vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0) req_spec = objects.RequestSpec(flavor=flavor, is_bfv=False) self.client.get_allocation_candidates( - self.context, utils.ResourceRequest(req_spec)) + self.context, utils.ResourceRequest.from_request_spec(req_spec)) def _set_up_provider_tree(self): r"""Create two compute nodes in placement ("this" one, and another one) @@ -1221,7 +1221,7 @@ class SchedulerReportClientTests(test.TestCase): req_spec = objects.RequestSpec(flavor=flavor, is_bfv=False) self._set_up_provider_tree() acs = self.client.get_allocation_candidates( - self.context, utils.ResourceRequest(req_spec))[0] + self.context, utils.ResourceRequest.from_request_spec(req_spec))[0] # We're not going to validate all the allocations - Placement has # tests for that - just make sure they're there. self.assertEqual(3, len(acs)) @@ -1284,7 +1284,7 @@ class SchedulerReportClientTests(test.TestCase): (ot.COMPUTE_STATUS_DISABLED, ot.COMPUTE_VOLUME_EXTEND, 'CUSTOM_FOO')) acs, _, ver = self.client.get_allocation_candidates( - self.context, utils.ResourceRequest(req_spec)) + self.context, utils.ResourceRequest.from_request_spec(req_spec)) self.assertEqual('1.35', ver) # This prints which ddt permutation we're using if it fails. self.assertEqual(data['expected_acs'], len(acs), data) diff --git a/nova/tests/unit/scheduler/client/test_report.py b/nova/tests/unit/scheduler/client/test_report.py index 19e5e5f6b908..da1f0d7892c0 100644 --- a/nova/tests/unit/scheduler/client/test_report.py +++ b/nova/tests/unit/scheduler/client/test_report.py @@ -2087,7 +2087,7 @@ class TestProviderOperations(SchedulerReportClientTestCase): 'group_policy3': 'none', }) req_spec = objects.RequestSpec(flavor=flavor, is_bfv=False) - resources = scheduler_utils.ResourceRequest(req_spec) + resources = scheduler_utils.ResourceRequest.from_request_spec(req_spec) resources.get_request_group(None).aggregates = [ ['agg1', 'agg2', 'agg3'], ['agg1', 'agg2']] forbidden_aggs = set(['agg1', 'agg5', 'agg6']) @@ -2145,7 +2145,7 @@ class TestProviderOperations(SchedulerReportClientTestCase): 'group_policy': 'bogus', }) req_spec = objects.RequestSpec(flavor=flavor, is_bfv=False) - resources = scheduler_utils.ResourceRequest(req_spec) + resources = scheduler_utils.ResourceRequest.from_request_spec(req_spec) expected_path = '/allocation_candidates' expected_query = [ ('limit', '42'), @@ -2189,7 +2189,7 @@ class TestProviderOperations(SchedulerReportClientTestCase): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0) req_spec = objects.RequestSpec(flavor=flavor, is_bfv=False) - resources = scheduler_utils.ResourceRequest(req_spec) + resources = scheduler_utils.ResourceRequest.from_request_spec(req_spec) res = self.client.get_allocation_candidates(self.context, resources) diff --git a/nova/tests/unit/scheduler/test_utils.py b/nova/tests/unit/scheduler/test_utils.py index 2a5aeb6e0872..29d59f47b3bd 100644 --- a/nova/tests/unit/scheduler/test_utils.py +++ b/nova/tests/unit/scheduler/test_utils.py @@ -834,7 +834,7 @@ class TestUtils(TestUtilsBase): actual = utils.resources_from_flavor(instance, flavor) self.assertEqual(expected, actual) - def test_resource_request_init(self): + def test_resource_request_from_request_spec(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0) @@ -848,10 +848,10 @@ class TestUtils(TestUtilsBase): }, ) rs = objects.RequestSpec(flavor=flavor, is_bfv=False) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) - def test_resource_request_init_with_extra_specs(self): + def test_resource_request_from_request_spec_with_extra_specs(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, extra_specs={ @@ -934,7 +934,7 @@ class TestUtils(TestUtilsBase): ) rs = objects.RequestSpec(flavor=flavor, is_bfv=False) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) expected_querystring = ( 'group_policy=isolate&' @@ -949,7 +949,7 @@ class TestUtils(TestUtilsBase): ) self.assertEqual(expected_querystring, rr.to_querystring()) - def _test_resource_request_init_with_legacy_extra_specs(self): + def _test_resource_request_from_rs_with_legacy_extra_specs(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, extra_specs={ @@ -960,7 +960,7 @@ class TestUtils(TestUtilsBase): return objects.RequestSpec(flavor=flavor, is_bfv=False) - def test_resource_request_init_with_legacy_extra_specs(self): + def test_resource_request_from_request_spec_with_legacy_extra_specs(self): expected = FakeResourceRequest() expected._rg_by_id[None] = objects.RequestGroup( use_same_provider=False, @@ -976,12 +976,14 @@ class TestUtils(TestUtilsBase): 'HW_CPU_HYPERTHREADING', }, ) - rs = self._test_resource_request_init_with_legacy_extra_specs() - rr = utils.ResourceRequest(rs) + rs = self._test_resource_request_from_rs_with_legacy_extra_specs() + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) self.assertTrue(rr.cpu_pinning_requested) - def test_resource_request_init_with_legacy_extra_specs_no_translate(self): + def test_resource_request_from_rs_with_legacy_extra_specs_no_translate( + self + ): expected = FakeResourceRequest() expected._rg_by_id[None] = objects.RequestGroup( use_same_provider=False, @@ -996,12 +998,13 @@ class TestUtils(TestUtilsBase): # because enable_pinning_translate=False forbidden_traits=set(), ) - rs = self._test_resource_request_init_with_legacy_extra_specs() - rr = utils.ResourceRequest(rs, enable_pinning_translate=False) + rs = self._test_resource_request_from_rs_with_legacy_extra_specs() + rr = utils.ResourceRequest.from_request_spec( + rs, enable_pinning_translate=False) self.assertResourceRequestsEqual(expected, rr) self.assertFalse(rr.cpu_pinning_requested) - def test_resource_request_init_with_image_props(self): + def test_resource_request_from_request_spec_with_image_props(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0) image = objects.ImageMeta.from_dict({ @@ -1024,10 +1027,10 @@ class TestUtils(TestUtilsBase): } ) rs = objects.RequestSpec(flavor=flavor, image=image, is_bfv=False) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) - def _test_resource_request_init_with_legacy_image_props(self): + def _test_resource_request_from_rs_with_legacy_image_props(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0) image = objects.ImageMeta.from_dict({ @@ -1039,7 +1042,7 @@ class TestUtils(TestUtilsBase): }) return objects.RequestSpec(flavor=flavor, image=image, is_bfv=False) - def test_resource_request_init_with_legacy_image_props(self): + def test_resource_request_from_request_spec_with_legacy_image_props(self): expected = FakeResourceRequest() expected._rg_by_id[None] = objects.RequestGroup( use_same_provider=False, @@ -1054,12 +1057,14 @@ class TestUtils(TestUtilsBase): 'HW_CPU_HYPERTHREADING', }, ) - rs = self._test_resource_request_init_with_legacy_image_props() - rr = utils.ResourceRequest(rs) + rs = self._test_resource_request_from_rs_with_legacy_image_props() + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) self.assertTrue(rr.cpu_pinning_requested) - def test_resource_request_init_with_legacy_image_props_no_translate(self): + def test_resource_request_from_rs_with_legacy_image_props_no_translate( + self + ): expected = FakeResourceRequest() expected._rg_by_id[None] = objects.RequestGroup( use_same_provider=False, @@ -1074,12 +1079,15 @@ class TestUtils(TestUtilsBase): # because enable_pinning_translate=False required_traits=set(), ) - rs = self._test_resource_request_init_with_legacy_image_props() - rr = utils.ResourceRequest(rs, enable_pinning_translate=False) + rs = self._test_resource_request_from_rs_with_legacy_image_props() + rr = utils.ResourceRequest.from_request_spec( + rs, enable_pinning_translate=False) self.assertResourceRequestsEqual(expected, rr) self.assertFalse(rr.cpu_pinning_requested) - def _test_resource_request_init_with_mixed_cpus(self, extra_specs): + def _test_resource_request_from_request_spec_with_mixed_cpus( + self, extra_specs + ): flavor = objects.Flavor( vcpus=4, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, extra_specs=extra_specs) @@ -1095,10 +1103,12 @@ class TestUtils(TestUtilsBase): }, required_traits=set(), ) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) - def test_resource_request_init_with_mixed_cpus_dedicated(self): + def test_resource_request_from_request_spec_with_mixed_cpus_dedicated( + self + ): """Ensure the mixed instance, which is generated through 'hw:cpu_dedicated_mask' extra spec, properly requests the PCPU, VCPU, MEMORY_MB and DISK_GB resources. @@ -1107,9 +1117,10 @@ class TestUtils(TestUtilsBase): 'hw:cpu_policy': 'mixed', 'hw:cpu_dedicated_mask': '2,3' } - self._test_resource_request_init_with_mixed_cpus(extra_specs) + self._test_resource_request_from_request_spec_with_mixed_cpus( + extra_specs) - def test_resource_request_init_with_mixed_cpus_realtime(self): + def test_resource_request_from_request_spec_with_mixed_cpus_realtime(self): """Ensure the mixed instance, which is generated through real-time CPU interface, properly requests the PCPU, VCPU, MEMORY_BM and DISK_GB resources. @@ -1119,9 +1130,12 @@ class TestUtils(TestUtilsBase): "hw:cpu_realtime": "yes", "hw:cpu_realtime_mask": '2,3' } - self._test_resource_request_init_with_mixed_cpus(extra_specs) + self._test_resource_request_from_request_spec_with_mixed_cpus( + extra_specs) - def _test_resource_request_init_with_mixed_cpus_iso_emu(self, extra_specs): + def _test_resource_request_from_request_spec_with_mixed_cpus_iso_emu( + self, extra_specs + ): flavor = objects.Flavor( vcpus=4, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, extra_specs=extra_specs) @@ -1139,10 +1153,10 @@ class TestUtils(TestUtilsBase): }, required_traits=set(), ) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) - def test_resource_request_init_with_mixed_cpus_iso_emu_realtime(self): + def test_resource_request_from_rswith_mixed_cpus_iso_emu_realtime(self): """Ensure the mixed instance, which is generated through the 'hw:cpu_dedicated_mask' extra spec, specs, properly requests the PCPU, VCPU, MEMORY_MB, DISK_GB resources, ensure an extra PCPU resource is @@ -1153,9 +1167,10 @@ class TestUtils(TestUtilsBase): 'hw:cpu_dedicated_mask': '2,3', 'hw:emulator_threads_policy': 'isolate', } - self._test_resource_request_init_with_mixed_cpus_iso_emu(extra_specs) + self._test_resource_request_from_request_spec_with_mixed_cpus_iso_emu( + extra_specs) - def test_resource_request_init_with_mixed_cpus_iso_emu_dedicated(self): + def test_resource_request_from_rs_with_mixed_cpus_iso_emu_dedicated(self): """Ensure the mixed instance, which is generated through realtime extra specs, properly requests the PCPU, VCPU, MEMORY_MB, DISK_GB resources, ensure an extra PCPU resource is requested due to a ISOLATE emulator @@ -1167,9 +1182,10 @@ class TestUtils(TestUtilsBase): "hw:cpu_realtime_mask": '2,3', 'hw:emulator_threads_policy': 'isolate', } - self._test_resource_request_init_with_mixed_cpus_iso_emu(extra_specs) + self._test_resource_request_from_request_spec_with_mixed_cpus_iso_emu( + extra_specs) - def test_resource_request_init_is_bfv(self): + def test_resource_request_from_request_spec_is_bfv(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=1555) @@ -1185,10 +1201,10 @@ class TestUtils(TestUtilsBase): }, ) rs = objects.RequestSpec(flavor=flavor, is_bfv=True) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) - def test_resource_request_with_vpmems(self): + def test_resource_request_from_request_spec_with_vpmems(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, extra_specs={'hw:pmem': '4GB, 4GB,SMALL'}) @@ -1205,10 +1221,10 @@ class TestUtils(TestUtilsBase): }, ) rs = objects.RequestSpec(flavor=flavor, is_bfv=False) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) - def test_resource_request_with_vtpm_1_2(self): + def test_resource_request_from_request_spec_with_vtpm_1_2(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, extra_specs={'hw:tpm_version': '1.2', 'hw:tpm_model': 'tpm-tis'}, @@ -1230,10 +1246,10 @@ class TestUtils(TestUtilsBase): }, ) rs = objects.RequestSpec(flavor=flavor, image=image, is_bfv=False) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) - def test_resource_request_with_vtpm_2_0(self): + def test_resource_request_from_request_spec_with_vtpm_2_0(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, extra_specs={'hw:tpm_version': '2.0', 'hw:tpm_model': 'tpm-crb'}, @@ -1255,14 +1271,14 @@ class TestUtils(TestUtilsBase): }, ) rs = objects.RequestSpec(flavor=flavor, image=image, is_bfv=False) - rr = utils.ResourceRequest(rs) + rr = utils.ResourceRequest.from_request_spec(rs) self.assertResourceRequestsEqual(expected, rr) def test_resource_request_add_group_inserts_the_group(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0) rs = objects.RequestSpec(flavor=flavor, is_bfv=False) - req = utils.ResourceRequest(rs) + req = utils.ResourceRequest.from_request_spec(rs) rg1 = objects.RequestGroup(requester_id='foo', required_traits={'CUSTOM_FOO'}) req._add_request_group(rg1) @@ -1279,7 +1295,7 @@ class TestUtils(TestUtilsBase): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0) rs = objects.RequestSpec(flavor=flavor, is_bfv=False) - req = utils.ResourceRequest(rs) + req = utils.ResourceRequest.from_request_spec(rs) rg = objects.RequestGroup(requester_id='foo') self.assertRaises(ValueError, req._add_request_group, rg) @@ -1620,7 +1636,7 @@ class TestEncryptedMemoryTranslation(TestUtilsBase): def _get_resource_request(self, extra_specs, image): reqspec = self._get_request_spec(extra_specs, image) - return utils.ResourceRequest(reqspec) + return utils.ResourceRequest.from_request_spec(reqspec) def _get_expected_resource_request(self, mem_encryption_context): expected_resources = { @@ -1707,7 +1723,7 @@ class TestEncryptedMemoryTranslation(TestUtilsBase): ) exc = self.assertRaises( exception.FlavorImageConflict, - utils.ResourceRequest, reqspec + utils.ResourceRequest.from_request_spec, reqspec ) error_data = { 'flavor_name': self.flavor_name, diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index c1dc34daf42a..1c5ac2fcd7aa 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -655,7 +655,8 @@ def mdev_uuid2name(mdev_uuid: str) -> str: def get_flags_by_flavor_specs(flavor: 'objects.Flavor') -> ty.Set[str]: req_spec = objects.RequestSpec(flavor=flavor) - resource_request = scheduler_utils.ResourceRequest(req_spec) + resource_request = scheduler_utils.ResourceRequest.from_request_spec( + req_spec) required_traits = resource_request.all_required_traits flags = [TRAITS_CPU_MAPPING[trait] for trait in required_traits