diff --git a/lisa/environment.py b/lisa/environment.py index d0fb21d7bc..e99b165222 100644 --- a/lisa/environment.py +++ b/lisa/environment.py @@ -135,7 +135,7 @@ def check(self, capability: Any) -> search_space.ResultReason: return result - def _generate_min_capability(self, capability: Any) -> Any: + def _choose_value(self, capability: Any) -> Any: env = EnvironmentSpace(topology=self.topology) assert isinstance(capability, EnvironmentSpace), f"actual: {type(capability)}" assert capability.nodes @@ -145,7 +145,7 @@ def _generate_min_capability(self, capability: Any) -> Any: else: current_cap = capability.nodes[index] - env.nodes.append(current_req.generate_min_capability(current_cap)) + env.nodes.append(current_req.choose_value(current_cap)) return env @@ -327,12 +327,11 @@ def create_node_from_requirement( ) -> Node: min_requirement = cast( schema.Capability, - node_requirement.generate_min_capability(node_requirement), - ) - assert isinstance(min_requirement.node_count, int), ( - f"must be int after generate_min_capability, " - f"actual: {min_requirement.node_count}" + node_requirement.choose_value(node_requirement), ) + assert isinstance( + min_requirement.node_count, int + ), f"must be int after choose_value, actual: {min_requirement.node_count}" # node count should be expanded in platform already assert min_requirement.node_count == 1, f"actual: {min_requirement.node_count}" mock_runbook = schema.RemoteNode( diff --git a/lisa/features/gpu.py b/lisa/features/gpu.py index b0c61a72a1..49ac56146d 100644 --- a/lisa/features/gpu.py +++ b/lisa/features/gpu.py @@ -29,7 +29,7 @@ def __hash__(self) -> int: def _get_key(self) -> str: return f"{self.type}/{self.is_enabled}" - def _generate_min_capability(self, capability: Any) -> Any: + def _choose_value(self, capability: Any) -> Any: return self diff --git a/lisa/features/nvme.py b/lisa/features/nvme.py index 94e9bca2c8..fa8d6d4642 100644 --- a/lisa/features/nvme.py +++ b/lisa/features/nvme.py @@ -252,12 +252,12 @@ def check(self, capability: Any) -> search_space.ResultReason: return result - def _generate_min_capability(self, capability: Any) -> Any: + def _choose_value(self, capability: Any) -> Any: assert isinstance(capability, NvmeSettings), f"actual: {type(capability)}" min_value = NvmeSettings() if self.disk_count or capability.disk_count: - min_value.disk_count = search_space.generate_min_capability_countspace( + min_value.disk_count = search_space.choose_value_countspace( self.disk_count, capability.disk_count ) diff --git a/lisa/microsoft/testsuites/network/sriov.py b/lisa/microsoft/testsuites/network/sriov.py index d643b050f6..d2ee28dfbc 100644 --- a/lisa/microsoft/testsuites/network/sriov.py +++ b/lisa/microsoft/testsuites/network/sriov.py @@ -193,7 +193,7 @@ def verify_sriov_single_vf_connection_max_cpu( @TestCaseMetadata( description=""" - This case needs 2 nodes and 8 nics. And it verifies module of sriov network + This case needs 2 nodes and max nics. And it verifies module of sriov network interface is loaded and each synthetic nic is paired with one VF, and check rx statistics of source and tx statistics of dest increase after send 200 Mb file from source to dest. @@ -211,8 +211,8 @@ def verify_sriov_single_vf_connection_max_cpu( requirement=simple_requirement( min_count=2, network_interface=schema.NetworkInterfaceOptionSettings( - nic_count=8, data_path=schema.NetworkDataPath.Sriov, + nic_count=search_space.IntRange(min=2, choose_max_value=True), ), ), ) @@ -223,7 +223,7 @@ def verify_sriov_max_vf_connection(self, environment: Environment) -> None: @TestCaseMetadata( description=""" - This case needs 2 nodes, 8 nics and 64 Vcpus. And it verifies module of sriov + This case needs 2 nodes, max nics and 64 Vcpus. And it verifies module of sriov network interface is loaded and each synthetic nic is paired with one VF, and check rx statistics of source and tx statistics of dest increase after send 200 Mb file from source to dest. @@ -242,8 +242,8 @@ def verify_sriov_max_vf_connection(self, environment: Environment) -> None: min_count=2, min_core_count=64, network_interface=schema.NetworkInterfaceOptionSettings( - nic_count=8, data_path=schema.NetworkDataPath.Sriov, + nic_count=search_space.IntRange(min=2, choose_max_value=True), ), ), ) @@ -332,7 +332,7 @@ def verify_sriov_disable_enable_on_guest(self, environment: Environment) -> None requirement=simple_requirement( network_interface=schema.NetworkInterfaceOptionSettings( data_path=schema.NetworkDataPath.Sriov, - max_nic_count=8, + max_nic_count=search_space.IntRange(min=8), ), ), ) @@ -369,7 +369,7 @@ def verify_sriov_add_max_nics( @TestCaseMetadata( description=""" - This case verify VM works well when provisioning with max (8) sriov nics. + This case verify VM works well when provisioning with max sriov nics. Steps, 1. Provision VM with max network interfaces with enabling accelerated network. @@ -377,8 +377,10 @@ def verify_sriov_add_max_nics( """, priority=2, requirement=simple_requirement( - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + data_path=schema.NetworkDataPath.Sriov, + nic_count=search_space.IntRange(min=2, choose_max_value=True), + ), ), ) def verify_sriov_provision_with_max_nics(self, environment: Environment) -> None: @@ -387,7 +389,7 @@ def verify_sriov_provision_with_max_nics(self, environment: Environment) -> None @TestCaseMetadata( description=""" - This case verify VM works well when provisioning with max (8) sriov nics. + This case verify VM works well when provisioning with max sriov nics. Steps, 1. Provision VM with max network interfaces with enabling accelerated network. @@ -397,8 +399,10 @@ def verify_sriov_provision_with_max_nics(self, environment: Environment) -> None """, priority=2, requirement=simple_requirement( - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + data_path=schema.NetworkDataPath.Sriov, + nic_count=search_space.IntRange(min=2, choose_max_value=True), + ), ), ) def verify_sriov_provision_with_max_nics_reboot( @@ -413,7 +417,7 @@ def verify_sriov_provision_with_max_nics_reboot( @TestCaseMetadata( description=""" - This case verify VM works well when provisioning with max (8) sriov nics. + This case verify VM works well when provisioning with max sriov nics. Steps, 1. Provision VM with max network interfaces with enabling accelerated network. @@ -423,8 +427,10 @@ def verify_sriov_provision_with_max_nics_reboot( """, priority=2, requirement=simple_requirement( - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + data_path=schema.NetworkDataPath.Sriov, + nic_count=search_space.IntRange(min=2, choose_max_value=True), + ), ), ) def verify_sriov_provision_with_max_nics_reboot_from_platform( @@ -440,7 +446,7 @@ def verify_sriov_provision_with_max_nics_reboot_from_platform( @TestCaseMetadata( description=""" - This case verify VM works well when provisioning with max (8) sriov nics. + This case verify VM works well when provisioning with max sriov nics. Steps, 1. Provision VM with max network interfaces with enabling accelerated network. @@ -450,8 +456,10 @@ def verify_sriov_provision_with_max_nics_reboot_from_platform( """, priority=2, requirement=simple_requirement( - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + data_path=schema.NetworkDataPath.Sriov, + nic_count=search_space.IntRange(min=2, choose_max_value=True), + ), ), ) def verify_sriov_provision_with_max_nics_stop_start_from_platform( @@ -479,8 +487,10 @@ def verify_sriov_provision_with_max_nics_stop_start_from_platform( priority=1, requirement=simple_requirement( min_count=2, - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + data_path=schema.NetworkDataPath.Sriov, + nic_count=search_space.IntRange(min=2, choose_max_value=True), + ), ), ) def verify_sriov_reload_modules(self, environment: Environment) -> None: @@ -528,7 +538,7 @@ def verify_sriov_reload_modules(self, environment: Environment) -> None: requirement=simple_requirement( min_count=2, network_interface=schema.NetworkInterfaceOptionSettings( - nic_count=search_space.IntRange(min=3, max=8), + nic_count=search_space.IntRange(min=3), data_path=schema.NetworkDataPath.Sriov, ), # BSD is unsupported since this is testing to patches to the linux kernel diff --git a/lisa/microsoft/testsuites/network/stress.py b/lisa/microsoft/testsuites/network/stress.py index 953b68c5d7..82546a4294 100644 --- a/lisa/microsoft/testsuites/network/stress.py +++ b/lisa/microsoft/testsuites/network/stress.py @@ -25,6 +25,7 @@ ) from lisa.features import StartStop from lisa.nic import NicInfo +from lisa.search_space import IntRange from lisa.sut_orchestrator import AZURE from lisa.tools import Cat, Iperf3 @@ -137,7 +138,7 @@ def stress_sriov_disable_enable(self, environment: Environment) -> None: @TestCaseMetadata( description=""" - This case verify VM works well when provison with max (8) synthetic nics. + This case verify VM works well when provison with max synthetic nics. Steps, 1. Provision VM with max network interfaces with synthetic network. @@ -148,8 +149,8 @@ def stress_sriov_disable_enable(self, environment: Environment) -> None: """, priority=2, requirement=simple_requirement( - min_nic_count=8, network_interface=schema.NetworkInterfaceOptionSettings( + nic_count=IntRange(min=2, choose_max_value=True), data_path=schema.NetworkDataPath.Synthetic, ), ), @@ -165,7 +166,7 @@ def stress_synthetic_provision_with_max_nics_reboot( @TestCaseMetadata( description=""" - This case verify VM works well when provison with max (8) synthetic nics. + This case verify VM works well when provison with max synthetic nics. Steps, 1. Provision VM with max network interfaces with synthetic network. @@ -176,8 +177,8 @@ def stress_synthetic_provision_with_max_nics_reboot( """, priority=2, requirement=simple_requirement( - min_nic_count=8, network_interface=schema.NetworkInterfaceOptionSettings( + nic_count=IntRange(min=2, choose_max_value=True), data_path=schema.NetworkDataPath.Synthetic, ), ), @@ -194,7 +195,7 @@ def stress_synthetic_with_max_nics_reboot_from_platform( @TestCaseMetadata( description=""" - This case verify VM works well when provison with max (8) synthetic nics. + This case verify VM works well when provison with max synthetic nics. Steps, 1. Provision VM with max network interfaces with synthetic network. @@ -205,8 +206,8 @@ def stress_synthetic_with_max_nics_reboot_from_platform( """, priority=2, requirement=simple_requirement( - min_nic_count=8, network_interface=schema.NetworkInterfaceOptionSettings( + nic_count=IntRange(min=2, choose_max_value=True), data_path=schema.NetworkDataPath.Synthetic, ), ), @@ -224,7 +225,7 @@ def stress_synthetic_with_max_nics_stop_start_from_platform( @TestCaseMetadata( description=""" - This case verify VM works well when provisioning with max (8) sriov nics. + This case verify VM works well when provisioning with max sriov nics. Steps, 1. Provision VM with max network interfaces with enabling accelerated network. @@ -235,8 +236,10 @@ def stress_synthetic_with_max_nics_stop_start_from_platform( """, priority=2, requirement=simple_requirement( - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + nic_count=IntRange(min=2, choose_max_value=True), + data_path=schema.NetworkDataPath.Sriov, + ), ), ) def stress_sriov_with_max_nics_reboot(self, environment: Environment) -> None: @@ -250,7 +253,7 @@ def stress_sriov_with_max_nics_reboot(self, environment: Environment) -> None: @TestCaseMetadata( description=""" - This case verify VM works well when provisioning with max (8) sriov nics. + This case verify VM works well when provisioning with max sriov nics. Steps, 1. Provision VM with max network interfaces with enabling accelerated network. @@ -261,8 +264,10 @@ def stress_sriov_with_max_nics_reboot(self, environment: Environment) -> None: """, priority=2, requirement=simple_requirement( - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + nic_count=IntRange(min=2, choose_max_value=True), + data_path=schema.NetworkDataPath.Sriov, + ), ), ) def stress_sriov_with_max_nics_reboot_from_platform( @@ -279,7 +284,7 @@ def stress_sriov_with_max_nics_reboot_from_platform( @TestCaseMetadata( description=""" - This case verify VM works well when provisioning with max (8) sriov nics. + This case verify VM works well when provisioning with max sriov nics. Steps, 1. Provision VM with max network interfaces with enabling accelerated network. @@ -290,8 +295,10 @@ def stress_sriov_with_max_nics_reboot_from_platform( """, priority=2, requirement=simple_requirement( - min_nic_count=8, - network_interface=features.Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + nic_count=IntRange(min=2, choose_max_value=True), + data_path=schema.NetworkDataPath.Sriov, + ) ), ) def stress_sriov_with_max_nics_stop_start_from_platform( diff --git a/lisa/microsoft/testsuites/network/synthetic.py b/lisa/microsoft/testsuites/network/synthetic.py index 3503d73cc5..201a4376e9 100644 --- a/lisa/microsoft/testsuites/network/synthetic.py +++ b/lisa/microsoft/testsuites/network/synthetic.py @@ -9,6 +9,7 @@ simple_requirement, ) from lisa.features import NetworkInterface, StartStop +from lisa.search_space import IntRange from .common import initialize_nic_info, remove_extra_nics, restore_extra_nics @@ -25,7 +26,7 @@ class Synthetic(TestSuite): @TestCaseMetadata( description=""" - This case verify VM works well when provison with max (8) synthetic nics. + This case verify VM works well when provison with max synthetic nics. Steps, 1. Provision VM with max network interfaces with synthetic network. @@ -33,10 +34,10 @@ class Synthetic(TestSuite): """, priority=2, requirement=simple_requirement( - min_nic_count=8, network_interface=schema.NetworkInterfaceOptionSettings( data_path=schema.NetworkDataPath.Synthetic, - ), + nic_count=IntRange(min=2, choose_max_value=True), + ) ), ) def verify_synthetic_provision_with_max_nics( @@ -46,7 +47,7 @@ def verify_synthetic_provision_with_max_nics( @TestCaseMetadata( description=""" - This case verify VM works well when provison with max (8) synthetic nics. + This case verify VM works well when provison with max synthetic nics. Steps, 1. Provision VM with max network interfaces with synthetic network. @@ -56,10 +57,10 @@ def verify_synthetic_provision_with_max_nics( """, priority=2, requirement=simple_requirement( - min_nic_count=8, network_interface=schema.NetworkInterfaceOptionSettings( data_path=schema.NetworkDataPath.Synthetic, - ), + nic_count=IntRange(min=2, choose_max_value=True), + ) ), ) def verify_synthetic_provision_with_max_nics_reboot( @@ -72,7 +73,7 @@ def verify_synthetic_provision_with_max_nics_reboot( @TestCaseMetadata( description=""" - This case verify VM works well when provison with max (8) synthetic nics. + This case verify VM works well when provison with max synthetic nics. Steps, 1. Provision VM with max network interfaces with synthetic network. @@ -82,10 +83,10 @@ def verify_synthetic_provision_with_max_nics_reboot( """, priority=2, requirement=simple_requirement( - min_nic_count=8, network_interface=schema.NetworkInterfaceOptionSettings( data_path=schema.NetworkDataPath.Synthetic, - ), + nic_count=IntRange(min=2, choose_max_value=True), + ) ), ) def verify_synthetic_provision_with_max_nics_reboot_from_platform( @@ -99,7 +100,7 @@ def verify_synthetic_provision_with_max_nics_reboot_from_platform( @TestCaseMetadata( description=""" - This case verify VM works well when provison with max (8) synthetic nics. + This case verify VM works well when provison with max synthetic nics. Steps, 1. Provision VM with max network interfaces with synthetic network. @@ -109,10 +110,10 @@ def verify_synthetic_provision_with_max_nics_reboot_from_platform( """, priority=2, requirement=simple_requirement( - min_nic_count=8, network_interface=schema.NetworkInterfaceOptionSettings( data_path=schema.NetworkDataPath.Synthetic, - ), + nic_count=IntRange(min=2, choose_max_value=True), + ) ), ) def verify_synthetic_provision_with_max_nics_stop_start_from_platform( @@ -139,8 +140,7 @@ def verify_synthetic_provision_with_max_nics_stop_start_from_platform( use_new_environment=True, requirement=simple_requirement( network_interface=schema.NetworkInterfaceOptionSettings( - data_path=schema.NetworkDataPath.Synthetic, - max_nic_count=8, + data_path=schema.NetworkDataPath.Synthetic ), ), ) @@ -172,8 +172,7 @@ def verify_synthetic_add_max_nics_one_time_after_provision( use_new_environment=True, requirement=simple_requirement( network_interface=schema.NetworkInterfaceOptionSettings( - data_path=schema.NetworkDataPath.Synthetic, - max_nic_count=8, + data_path=schema.NetworkDataPath.Synthetic ), ), ) diff --git a/lisa/microsoft/testsuites/nvme/nvme.py b/lisa/microsoft/testsuites/nvme/nvme.py index 6d9aeeee56..dd2812c226 100644 --- a/lisa/microsoft/testsuites/nvme/nvme.py +++ b/lisa/microsoft/testsuites/nvme/nvme.py @@ -14,6 +14,7 @@ simple_requirement, ) from lisa.features import Nvme, NvmeSettings, Sriov +from lisa.search_space import IntRange from lisa.sut_orchestrator.azure.platform_ import AzurePlatform from lisa.tools import Cat, Df, Echo, Fdisk, Lscpu, Lspci, Mkfs, Mount, Nvmecli from lisa.tools.fdisk import FileSystem @@ -77,12 +78,14 @@ def verify_nvme_basic(self, environment: Environment, node: Node) -> None: @TestCaseMetadata( description=""" - This case runs nvme_basic_validation test against 10 NVMe disks. + This case runs nvme_basic_validation test against max NVMe disks. The test steps are same as `nvme_basic_validation`. """, priority=2, requirement=simple_requirement( - supported_features=[NvmeSettings(disk_count=10)], + supported_features=[ + NvmeSettings(disk_count=IntRange(min=8, choose_max_value=True)) + ], ), ) def verify_nvme_max_disk(self, environment: Environment, node: Node) -> None: diff --git a/lisa/microsoft/testsuites/power/power.py b/lisa/microsoft/testsuites/power/power.py index eeb0410e0f..20e58e3ad2 100644 --- a/lisa/microsoft/testsuites/power/power.py +++ b/lisa/microsoft/testsuites/power/power.py @@ -22,11 +22,13 @@ TestCaseMetadata, TestSuite, TestSuiteMetadata, + schema, ) from lisa.features import Disk, HibernationEnabled, Sriov, Synthetic from lisa.features.availability import AvailabilityTypeNoRedundancy from lisa.node import Node from lisa.operating_system import BSD, Windows +from lisa.search_space import IntRange from lisa.sut_orchestrator.azure.features import AzureExtension from lisa.testsuite import simple_requirement from lisa.tools import Date, Hwclock, StressNg @@ -217,9 +219,11 @@ def verify_hibernation_with_memory_workload(self, node: Node, log: Logger) -> No """, priority=3, requirement=simple_requirement( - min_nic_count=8, min_os_disk_size=200, - network_interface=Synthetic(), + network_interface=schema.NetworkInterfaceOptionSettings( + data_path=schema.NetworkDataPath.Synthetic, + nic_count=IntRange(min=2, choose_max_value=True), + ), supported_features=[HibernationEnabled(), AvailabilityTypeNoRedundancy()], ), ) @@ -236,9 +240,11 @@ def verify_hibernation_synthetic_network_max_nics( """, priority=3, requirement=simple_requirement( - min_nic_count=8, min_os_disk_size=200, - network_interface=Sriov(), + network_interface=schema.NetworkInterfaceOptionSettings( + data_path=schema.NetworkDataPath.Sriov, + nic_count=IntRange(min=2, choose_max_value=True), + ), supported_features=[HibernationEnabled(), AvailabilityTypeNoRedundancy()], ), ) @@ -255,10 +261,11 @@ def verify_hibernation_sriov_network_max_nics( """, priority=3, requirement=simple_requirement( - min_nic_count=8, min_os_disk_size=200, supported_features=[HibernationEnabled(), AvailabilityTypeNoRedundancy()], - min_data_disk_count=32, + disk=schema.DiskOptionSettings( + data_disk_count=IntRange(min=8, choose_max_value=True) + ), ), ) def verify_hibernation_max_data_disks(self, node: Node, log: Logger) -> None: diff --git a/lisa/runners/lisa_runner.py b/lisa/runners/lisa_runner.py index 468bdc8de5..a7b2117f27 100644 --- a/lisa/runners/lisa_runner.py +++ b/lisa/runners/lisa_runner.py @@ -810,7 +810,7 @@ def _merge_test_requirements( if platform_requirement.node_count: # get minimun node count from the runbook. - node_count = search_space.generate_min_capability_countspace( + node_count = search_space.choose_value_countspace( platform_requirement.node_count, platform_requirement.node_count, ) diff --git a/lisa/schema.py b/lisa/schema.py index c114c55954..65bc8d6a28 100644 --- a/lisa/schema.py +++ b/lisa/schema.py @@ -951,8 +951,8 @@ def _call_requirement_method( # Use SetSpace methods directly if method == search_space.RequirementMethod.intersect: value.host_type = self_host_type.intersect(cap_host_type) - elif method == search_space.RequirementMethod.generate_min_capability: - value.host_type = self_host_type.generate_min_capability(cap_host_type) + elif method == search_space.RequirementMethod.choose_value: + value.host_type = self_host_type.choose_value(cap_host_type) elif capability.host_type is not None: value.host_type = capability.host_type else: @@ -1057,12 +1057,10 @@ def __repr__(self) -> str: @property def cost(self) -> float: - core_count = search_space.generate_min_capability_countspace( + core_count = search_space.choose_value_countspace( self.core_count, self.core_count ) - gpu_count = search_space.generate_min_capability_countspace( - self.gpu_count, self.gpu_count - ) + gpu_count = search_space.choose_value_countspace(self.gpu_count, self.gpu_count) return core_count + gpu_count * 100 @property @@ -1173,7 +1171,7 @@ def expand_by_node_count(self) -> List[Any]: # expand node count in requirement to one, # so that's easy to compare equalization later. expanded_requirements: List[NodeSpace] = [] - node_count = search_space.generate_min_capability_countspace( + node_count = search_space.choose_value_countspace( self.node_count, self.node_count ) for _ in range(node_count): @@ -1236,7 +1234,7 @@ def _call_requirement_method( if ( capability.features - and method == search_space.RequirementMethod.generate_min_capability + and method == search_space.RequirementMethod.choose_value ): # The requirement features are ignored, if cap doesn't have it. value.features = search_space.SetSpace[FeatureSettings](is_allow_set=True) @@ -1262,7 +1260,7 @@ def _call_requirement_method( if ( capability.excluded_features - and method == search_space.RequirementMethod.generate_min_capability + and method == search_space.RequirementMethod.choose_value ): # TODO: the min value for excluded feature is not clear. It may need # to be improved with real scenarios. diff --git a/lisa/search_space.py b/lisa/search_space.py index dcc6d32667..b981fc6602 100644 --- a/lisa/search_space.py +++ b/lisa/search_space.py @@ -15,7 +15,7 @@ class RequirementMethod(Enum): - generate_min_capability: str = "generate_min_capability" + choose_value: str = "choose_value" intersect: str = "intersect" @@ -58,9 +58,9 @@ class RequirementMixin: def check(self, capability: Any) -> ResultReason: raise NotImplementedError() - def generate_min_capability(self, capability: Any) -> Any: + def choose_value(self, capability: Any) -> Any: self._validate_result(capability) - return self._generate_min_capability(capability) + return self._choose_value(capability) def intersect(self, capability: Any) -> Any: self._validate_result(capability) @@ -71,9 +71,9 @@ def _call_requirement_method( ) -> Any: raise NotImplementedError(method) - def _generate_min_capability(self, capability: Any) -> Any: + def _choose_value(self, capability: Any) -> Any: return self._call_requirement_method( - method=RequirementMethod.generate_min_capability, + method=RequirementMethod.choose_value, capability=capability, ) @@ -99,6 +99,7 @@ class IntRange(RequirementMixin): min: int = 0 max: int = field(default=sys.maxsize) max_inclusive: bool = True + choose_max_value: bool = False def __post_init__(self, *args: Any, **kwargs: Any) -> None: if self.min > self.max: @@ -115,7 +116,8 @@ def __repr__(self) -> str: max_inclusive = "" if max_value: max_inclusive = "(inc)" if self.max_inclusive else "(exc)" - return f"[{self.min},{max_value}{max_inclusive}]" + choose_max_value = "(maximized)" if self.choose_max_value else "" + return f"[{self.min},{max_value}{max_inclusive}]{choose_max_value}" def __eq__(self, __o: object) -> bool: assert isinstance(__o, IntRange), f"actual type: {type(__o)}" @@ -123,6 +125,7 @@ def __eq__(self, __o: object) -> bool: self.min == __o.min and self.max == __o.max and self.max_inclusive == __o.max_inclusive + and self.choose_max_value == __o.choose_max_value ) def check(self, capability: Any) -> ResultReason: @@ -181,23 +184,42 @@ def check(self, capability: Any) -> ResultReason: return result - def _generate_min_capability(self, capability: Any) -> int: + def _choose_value(self, capability: Any) -> int: if isinstance(capability, int): result: int = capability elif isinstance(capability, IntRange): - if self.min < capability.min: - result = capability.min - else: - result = self.min + req_max = self.max - 1 if not self.max_inclusive else self.max + cap_max = ( + capability.max - 1 if not capability.max_inclusive else capability.max + ) + result = ( + min(req_max, cap_max) + if self.choose_max_value or capability.choose_max_value + else max(self.min, capability.min) + ) else: assert isinstance(capability, list), f"actual: {type(capability)}" - result = self.max if self.max_inclusive else self.max - 1 + choose_max_value = self.choose_max_value or any( + [x.choose_max_value for x in capability if isinstance(x, IntRange)] + ) + # If choosing max value, start with min requirement + # and increase to max capability. + # Else start with max requirement and decrease to min capability. + result = self.min if choose_max_value else self.max for cap_item in capability: temp_result = self.check(cap_item) if temp_result.result: - temp_min = self.generate_min_capability(cap_item) - result = min(temp_min, result) + temp_val = self.choose_value(cap_item) + result = ( + max(temp_val, result) + if choose_max_value + else min(temp_val, result) + ) + if result == sys.maxsize: + raise NotMeetRequirementException( + "cannot choose value, max value is unbounded" + ) return result def _intersect(self, capability: Any) -> Any: @@ -205,7 +227,10 @@ def _intersect(self, capability: Any) -> Any: return capability elif isinstance(capability, IntRange): result = IntRange( - min=self.min, max=self.max, max_inclusive=self.max_inclusive + min=self.min, + max=self.max, + max_inclusive=self.max_inclusive, + choose_max_value=self.choose_max_value or capability.choose_max_value, ) if self.min < capability.min: result.min = capability.min @@ -339,7 +364,7 @@ def update(self, *s: Iterable[T]) -> None: super().update(*s) self.items.extend(*s) - def _generate_min_capability(self, capability: Any) -> Optional[Set[T]]: + def _choose_value(self, capability: Any) -> Optional[Set[T]]: result: Optional[SetSpace[T]] = None if self.is_allow_set and len(self) > 0: assert isinstance(capability, SetSpace), f"actual: {type(capability)}" @@ -352,7 +377,7 @@ def _generate_min_capability(self, capability: Any) -> Optional[Set[T]]: return result def _intersect(self, capability: Any) -> Any: - return self._generate_min_capability(capability) + return self._choose_value(capability) def decode_set_space(data: Any) -> Any: @@ -439,33 +464,35 @@ def check_countspace(requirement: CountSpace, capability: CountSpace) -> ResultR return result -def generate_min_capability_countspace( - requirement: CountSpace, capability: CountSpace -) -> int: +def choose_value_countspace(requirement: CountSpace, capability: CountSpace) -> int: check_result = check_countspace(requirement, capability) if not check_result.result: raise NotMeetRequirementException( - "cannot get min value, capability doesn't support requirement: " + "cannot choose value, capability doesn't support requirement: " f"{check_result.reasons}" ) - if requirement is None: + if requirement is None or (isinstance(requirement, list) and not requirement): if capability: requirement = capability - result: int = sys.maxsize else: - result = 0 + return 0 if isinstance(requirement, int): result = requirement elif isinstance(requirement, IntRange): - result = requirement.generate_min_capability(capability) + result = requirement.choose_value(capability) else: assert isinstance(requirement, list), f"actual: {type(requirement)}" - result = sys.maxsize + choose_max = any( + req_item.choose_max_value + for req_item in requirement + if isinstance(req_item, IntRange) + ) + result = 0 if choose_max else sys.maxsize for req_item in requirement: temp_result = req_item.check(capability) if temp_result.result: - temp_min = req_item.generate_min_capability(capability) - result = min(result, temp_min) + temp_val = req_item.choose_value(capability) + result = max(temp_val, result) if choose_max else min(temp_val, result) return result @@ -518,7 +545,7 @@ def check_setspace( return result -def generate_min_capability_setspace_by_priority( +def choose_value_setspace_by_priority( requirement: Optional[Union[SetSpace[T], T]], capability: Optional[Union[SetSpace[T], T]], priority_list: List[T], @@ -526,7 +553,7 @@ def generate_min_capability_setspace_by_priority( check_result = check_setspace(requirement, capability) if not check_result.result: raise NotMeetRequirementException( - "cannot get min value, capability doesn't support requirement" + "cannot choose value, capability doesn't support requirement" f"{check_result.reasons}" ) @@ -642,33 +669,37 @@ def _call_requirement_method( if requirement is None: if capability is not None: requirement = capability - if ( - isinstance(requirement, list) - and method == RequirementMethod.generate_min_capability - ): + if isinstance(requirement, list) and method == RequirementMethod.choose_value: result = None + choose_max_value = any( + [x.choose_max_value for x in requirement if isinstance(x, IntRange)] + ) for req_item in requirement: temp_result = req_item.check(capability) if temp_result.result: - temp_min = getattr(req_item, method.value)(capability) + temp_val = getattr(req_item, method.value)(capability) if result is None: - result = temp_min + result = temp_val else: # TODO: multiple matches found, not supported well yet # It can be improved by implement __eq__, __lt__ functions. - result = min(result, temp_min) + result = ( + max(result, temp_val) + if choose_max_value + else min(result, temp_val) + ) elif requirement is not None: result = getattr(requirement, method.value)(capability) return result -def generate_min_capability( +def choose_value( requirement: Union[T_SEARCH_SPACE, List[T_SEARCH_SPACE], None], capability: Union[T_SEARCH_SPACE, List[T_SEARCH_SPACE], None], ) -> Any: return _call_requirement_method( - RequirementMethod.generate_min_capability, + RequirementMethod.choose_value, requirement=requirement, capability=capability, ) diff --git a/lisa/sut_orchestrator/aws/features.py b/lisa/sut_orchestrator/aws/features.py index 2ba651f50b..e56f2b25d0 100644 --- a/lisa/sut_orchestrator/aws/features.py +++ b/lisa/sut_orchestrator/aws/features.py @@ -450,7 +450,7 @@ def _call_requirement_method( value.data_disk_iops = 0 value.data_disk_size = 0 - if method == RequirementMethod.generate_min_capability: + if method == RequirementMethod.choose_value: assert isinstance( value.data_disk_type, schema.DiskType ), f"actual: {type(value.data_disk_type)}" diff --git a/lisa/sut_orchestrator/aws/platform_.py b/lisa/sut_orchestrator/aws/platform_.py index 1fba0a5d29..e29e5e9536 100644 --- a/lisa/sut_orchestrator/aws/platform_.py +++ b/lisa/sut_orchestrator/aws/platform_.py @@ -326,7 +326,7 @@ def _prepare_environment( # noqa: C901 1. If predefined location exists on node level, check conflict and use it. 2. If predefined vm size exists on node level, check exists and use it. 3. check capability for each node by order of pattern. - 4. get min capability for each match + 4. choose a value for each capability match """ is_success: bool = True ec2_resource = boto3.resource("ec2") @@ -1109,9 +1109,7 @@ def _generate_min_capability( aws_capability: AwsCapability, location: str, ) -> schema.NodeSpace: - min_cap: schema.NodeSpace = requirement.generate_min_capability( - aws_capability.capability - ) + min_cap: schema.NodeSpace = requirement.choose_value(aws_capability.capability) # Apply aws specified values. aws_node_runbook = min_cap.get_extended_runbook(AwsNodeSchema, AWS) if aws_node_runbook.location: diff --git a/lisa/sut_orchestrator/azure/features.py b/lisa/sut_orchestrator/azure/features.py index 439479a952..2deb7dcbff 100644 --- a/lisa/sut_orchestrator/azure/features.py +++ b/lisa/sut_orchestrator/azure/features.py @@ -1658,7 +1658,7 @@ def _call_requirement_method( # noqa: C901 value.data_disk_throughput = 0 value.data_disk_size = 0 - if method == RequirementMethod.generate_min_capability: + if method == RequirementMethod.choose_value: assert isinstance( value.data_disk_type, schema.DiskType ), f"actual: {type(value.data_disk_type)}" diff --git a/lisa/sut_orchestrator/azure/platform_.py b/lisa/sut_orchestrator/azure/platform_.py index 1fbf751f62..f5f2792275 100644 --- a/lisa/sut_orchestrator/azure/platform_.py +++ b/lisa/sut_orchestrator/azure/platform_.py @@ -525,7 +525,7 @@ def _prepare_environment(self, environment: Environment, log: Logger) -> bool: 1. If predefined location exists on node level, check conflict and use it. 2. If predefined vm size exists on node level, check exists and use it. 3. check capability for each node by order of pattern. - 4. get min capability for each match + 4. choose a value for each capability match """ if not environment.runbook.nodes_requirement: @@ -2281,7 +2281,7 @@ def _generate_min_capability( azure_capability: AzureCapability, location: str, ) -> schema.NodeSpace: - min_cap: schema.NodeSpace = requirement.generate_min_capability( + min_cap: schema.NodeSpace = requirement.choose_value( azure_capability.capability ) # Apply azure specified values. They will pass into arm template diff --git a/lisa/sut_orchestrator/baremetal/platform_.py b/lisa/sut_orchestrator/baremetal/platform_.py index 4d2c5e2008..fff1598c11 100644 --- a/lisa/sut_orchestrator/baremetal/platform_.py +++ b/lisa/sut_orchestrator/baremetal/platform_.py @@ -247,7 +247,7 @@ def _check_capability( if not node_space.check(client_capability): return False - node_requirement = node_space.generate_min_capability(client_capability) + node_requirement = node_space.choose_value(client_capability) nodes_requirement.append(node_requirement) environment.runbook.nodes_requirement = nodes_requirement diff --git a/lisa/sut_orchestrator/hyperv/platform_.py b/lisa/sut_orchestrator/hyperv/platform_.py index 8c0126a309..d4c38770cd 100644 --- a/lisa/sut_orchestrator/hyperv/platform_.py +++ b/lisa/sut_orchestrator/hyperv/platform_.py @@ -119,7 +119,7 @@ def _prepare_environment(self, environment: Environment, log: Logger) -> bool: if not node_space.check(nodes_capabilities): return False - requirement = node_space.generate_min_capability(nodes_capabilities) + requirement = node_space.choose_value(nodes_capabilities) nodes_requirement.append(requirement) if not self._is_host_resources_enough(nodes_requirement, log): diff --git a/lisa/sut_orchestrator/libvirt/platform.py b/lisa/sut_orchestrator/libvirt/platform.py index 8ad10dd3c8..541739c0f6 100644 --- a/lisa/sut_orchestrator/libvirt/platform.py +++ b/lisa/sut_orchestrator/libvirt/platform.py @@ -285,7 +285,7 @@ def _configure_node_capabilities( # Rectify the general node capabilities with this node's specific # requirements. - node_requirement = node_space.generate_min_capability(nodes_capabilities) + node_requirement = node_space.choose_value(nodes_capabilities) nodes_requirement.append(node_requirement) if not self._check_host_capabilities(nodes_requirement, host_capabilities, log): @@ -391,7 +391,7 @@ def _check_host_capabilities( # Note: Unlike other orchestrators, we don't want to fill up the capacity of # the host in case the test is running on a dev box. def _get_count_space_min(self, count_space: search_space.CountSpace) -> int: - return search_space.generate_min_capability_countspace(count_space, count_space) + return search_space.choose_value_countspace(count_space, count_space) def _deploy_nodes(self, environment: Environment, log: Logger) -> None: self._configure_nodes(environment, log) diff --git a/selftests/azure/test_disk_feature.py b/selftests/azure/test_disk_feature.py index bbe7017481..b4cab0b1ee 100644 --- a/selftests/azure/test_disk_feature.py +++ b/selftests/azure/test_disk_feature.py @@ -48,7 +48,7 @@ def test_disk_type_no_common(self) -> None: reason = req.check(cap) self.assertFalse(reason.result) with self.assertRaises(LisaException) as cm: - req.generate_min_capability(cap) + req.choose_value(cap) self.assertIsInstance(cm.exception, LisaException) self.assertIn("capability doesn't support requirement", str(cm.exception)) @@ -183,7 +183,7 @@ def _assert_disk( ) -> None: reason = req.check(cap) self.assertTrue(reason.result, f"check reasons: {reason.reasons}") - min_value: features.AzureDiskOptionSettings = req.generate_min_capability(cap) + min_value: features.AzureDiskOptionSettings = req.choose_value(cap) self.assertEqual(disk_type, min_value.data_disk_type) self.assertEqual(data_disk_count, min_value.data_disk_count) self.assertEqual(data_disk_caching_type, min_value.data_disk_caching_type) diff --git a/selftests/test_env_requirement.py b/selftests/test_env_requirement.py index f9417557d3..2b09fdf8b7 100644 --- a/selftests/test_env_requirement.py +++ b/selftests/test_env_requirement.py @@ -14,7 +14,7 @@ ResultReason, SetSpace, check, - generate_min_capability, + choose_value, ) from lisa.testsuite import ( DEFAULT_REQUIREMENT, @@ -46,15 +46,13 @@ def check(self, capability: Any) -> ResultReason: return result - def _generate_min_capability(self, capability: Any) -> Any: + def _choose_value(self, capability: Any) -> Any: assert isinstance( capability, UtTestCaseRequirement ), f"actual: {type(capability)}" - environment = generate_min_capability(self.environment, capability.environment) - platform_type = generate_min_capability( - self.platform_type, capability.platform_type - ) - os = generate_min_capability(self.os_type, capability.os_type) + environment = choose_value(self.environment, capability.environment) + platform_type = choose_value(self.platform_type, capability.platform_type) + os = choose_value(self.os_type, capability.os_type) result = TestCaseSchema( environment=environment, platform_type=platform_type, operating_system=os ) @@ -114,12 +112,12 @@ def ut_node_requirement( class RequirementTestCase(SearchSpaceTestCase): def test_supported_simple_requirement(self) -> None: n1 = schema.NodeSpace() - n1 = n1.generate_min_capability(n1) + n1 = n1.choose_value(n1) n4 = schema.load_by_type( schema.NodeSpace, {"type": constants.ENVIRONMENTS_NODES_REQUIREMENT, "core_count": 4}, ) - n4 = n4.generate_min_capability(n4) + n4 = n4.choose_value(n4) n4g1 = schema.load_by_type( schema.NodeSpace, { @@ -128,12 +126,12 @@ def test_supported_simple_requirement(self) -> None: "gpu_count": 1, }, ) - n4g1 = n4g1.generate_min_capability(n4g1) + n4g1 = n4g1.choose_value(n4g1) n6 = schema.load_by_type( schema.NodeSpace, {"type": constants.ENVIRONMENTS_NODES_REQUIREMENT, "core_count": 6}, ) - n6 = n6.generate_min_capability(n6) + n6 = n6.choose_value(n6) n6g2 = schema.load_by_type( schema.NodeSpace, { @@ -142,7 +140,7 @@ def test_supported_simple_requirement(self) -> None: "gpu_count": 2, }, ) - n6g2 = n6g2.generate_min_capability(n6g2) + n6g2 = n6g2.choose_value(n6g2) n6g1 = schema.load_by_type( schema.NodeSpace, { @@ -151,12 +149,12 @@ def test_supported_simple_requirement(self) -> None: "gpu_count": 1, }, ) - n6g1 = n6g1.generate_min_capability(n6g1) + n6g1 = n6g1.choose_value(n6g1) n10 = schema.load_by_type( schema.NodeSpace, {"type": constants.ENVIRONMENTS_NODES_REQUIREMENT, "core_count": 10}, ) - n10 = n10.generate_min_capability(n10) + n10 = n10.choose_value(n10) partial_testcase_schema = partial( TestCaseSchema, diff --git a/selftests/test_platform.py b/selftests/test_platform.py index df894fbf1b..2d4aab1629 100644 --- a/selftests/test_platform.py +++ b/selftests/test_platform.py @@ -81,7 +81,7 @@ def _prepare_environment(self, environment: Environment, log: Logger) -> bool: if self._mock_runbook.return_prepared and requirements: min_capabilities: List[schema.NodeSpace] = [] for node_space in requirements: - min_capabilities.append(node_space.generate_min_capability(node_space)) + min_capabilities.append(node_space.choose_value(node_space)) environment.runbook.nodes_requirement = min_capabilities return self._mock_runbook.return_prepared diff --git a/selftests/test_search_space.py b/selftests/test_search_space.py index e125a0e5a9..26e8c04701 100644 --- a/selftests/test_search_space.py +++ b/selftests/test_search_space.py @@ -15,8 +15,8 @@ SetSpace, check, check_countspace, - generate_min_capability, - generate_min_capability_countspace, + choose_value, + choose_value_countspace, ) from lisa.util import LisaException from lisa.util.logger import get_logger @@ -37,12 +37,10 @@ def check(self, capability: Any) -> ResultReason: assert isinstance(capability, MockItem), f"actual: {type(capability)}" return check_countspace(self.number, capability.number) - def _generate_min_capability(self, capability: Any) -> MockSchema: + def _choose_value(self, capability: Any) -> MockSchema: result = MockSchema() assert isinstance(capability, MockItem), f"actual: {type(capability)}" - result.number = generate_min_capability_countspace( - self.number, capability.number - ) + result.number = choose_value_countspace(self.number, capability.number) return result @@ -163,12 +161,12 @@ def test_supported_set_space(self) -> None: ], ) - def test_generate_min_capability_not_supported(self) -> None: + def test_choose_value_not_supported(self) -> None: requirement = IntRange(min=5) capability = IntRange(max=4) with self.assertRaises(expected_exception=LisaException) as cm: - requirement.generate_min_capability(capability) + requirement.choose_value(capability) self.assertIn("doesn't support", str(cm.exception)) def test_int_range_validation(self) -> None: @@ -204,7 +202,7 @@ def _verify_matrix( ) if expected_meet[r_index][c_index]: - actual_min = requirement.generate_min_capability(capability) + actual_min = requirement.choose_value(capability) if expected_min[r_index][c_index] != actual_min: self._log.info(extra_msg) self._log.info( @@ -226,7 +224,7 @@ def _verify_matrix( extra_msg=extra_msg, ) if expected_meet[r_index][c_index]: - actual_min = generate_min_capability_countspace( + actual_min = choose_value_countspace( requirement, capability # type:ignore ) if expected_min[r_index][c_index] != actual_min: @@ -242,7 +240,7 @@ def _verify_matrix( ) if expected_meet[r_index][c_index]: - actual_min = generate_min_capability( + actual_min = choose_value( requirement, capability # type:ignore ) self.assertEqual(