diff --git a/src/frostfs_testlib/utils/version_utils.py b/src/frostfs_testlib/utils/version_utils.py index 75ce8a57..2c1f4ab4 100644 --- a/src/frostfs_testlib/utils/version_utils.py +++ b/src/frostfs_testlib/utils/version_utils.py @@ -2,10 +2,11 @@ import logging import re from frostfs_testlib.cli import FrostfsAdm, FrostfsCli -from frostfs_testlib.hosting import Hosting +from frostfs_testlib.hosting import Host, Hosting from frostfs_testlib.resources.cli import FROSTFS_ADM_EXEC, FROSTFS_AUTHMATE_EXEC, FROSTFS_CLI_EXEC, NEOGO_EXECUTABLE from frostfs_testlib.resources.common import DEFAULT_WALLET_CONFIG from frostfs_testlib.shell import Shell +from frostfs_testlib.testing.parallel import parallel logger = logging.getLogger("NeoLogger") @@ -33,53 +34,74 @@ def get_local_binaries_versions(shell: Shell) -> dict[str, str]: return versions +def parallel_binary_verions(host: Host) -> dict[str, str]: + versions_by_host = {} + + binary_path_by_name = {} # Maps binary name to executable path + for service_config in host.config.services: + exec_path = service_config.attributes.get("exec_path") + requires_check = service_config.attributes.get("requires_version_check", "true") + if exec_path: + binary_path_by_name[service_config.name] = { + "exec_path": exec_path, + "check": requires_check.lower() == "true", + } + for cli_config in host.config.clis: + requires_check = cli_config.attributes.get("requires_version_check", "true") + binary_path_by_name[cli_config.name] = { + "exec_path": cli_config.exec_path, + "check": requires_check.lower() == "true", + } + + shell = host.get_shell() + versions_at_host = {} + for binary_name, binary in binary_path_by_name.items(): + try: + binary_path = binary["exec_path"] + result = shell.exec(f"{binary_path} --version") + versions_at_host[binary_name] = {"version": _parse_version(result.stdout), "check": binary["check"]} + except Exception as exc: + logger.error(f"Cannot get version for {binary_path} because of\n{exc}") + versions_at_host[binary_name] = {"version": "Unknown", "check": binary["check"]} + versions_by_host[host.config.address] = versions_at_host + return versions_by_host + + def get_remote_binaries_versions(hosting: Hosting) -> dict[str, str]: versions_by_host = {} - for host in hosting.hosts: - binary_path_by_name = {} # Maps binary name to executable path - for service_config in host.config.services: - exec_path = service_config.attributes.get("exec_path") - requires_check = service_config.attributes.get("requires_version_check", "true") - version_parameter = service_config.attributes.get("custom_version_parameter", "--version") - if exec_path: - binary_path_by_name[service_config.name] = { - "exec_path": exec_path, - "check": requires_check.lower() == "true", - "version_parameter": version_parameter, - } - for cli_config in host.config.clis: - requires_check = cli_config.attributes.get("requires_version_check", "true") - version_parameter = service_config.attributes.get("custom_version_parameter", "--version") - binary_path_by_name[cli_config.name] = { - "exec_path": cli_config.exec_path, - "check": requires_check.lower() == "true", - "version_parameter": version_parameter, - } - - shell = host.get_shell() - versions_at_host = {} - for binary_name, binary in binary_path_by_name.items(): - try: - binary_path = binary["exec_path"] - result = shell.exec(f"{binary_path} {binary['version_parameter']}") - versions_at_host[binary_name] = {"version": _parse_version(result.stdout), "check": binary["check"]} - except Exception as exc: - logger.error(f"Cannot get version for {binary_path} because of\n{exc}") - versions_at_host[binary_name] = {"version": "Unknown", "check": binary["check"]} - versions_by_host[host.config.address] = versions_at_host + future_binary_verions = parallel(parallel_binary_verions, parallel_items=hosting.hosts) + for future in future_binary_verions: + versions_by_host.update(future.result()) # Consolidate versions across all hosts + cheak_versions = {} + exсeptions = [] + exception = set() + previous_host = None versions = {} + captured_version = None for host, binary_versions in versions_by_host.items(): for name, binary in binary_versions.items(): - captured_version = versions.get(name, {}).get("version") version = binary["version"] - if captured_version: - assert captured_version == version, f"Binary {name} has inconsistent version on host {host}" + if not cheak_versions.get(f'{name[:-2]}', None): + captured_version = cheak_versions.get(f'{name[:-2]}',{}).get(host, {}).get(captured_version) + cheak_versions[f'{name[:-2]}'] = {host: {version: name}} else: - versions[name] = {"version": version, "check": binary["check"]} - return versions - + captured_version = list(cheak_versions.get(f'{name[:-2]}',{}).get(previous_host).keys())[0] + cheak_versions[f'{name[:-2]}'].update({host:{version:name}}) + + if captured_version and captured_version != version: + exception.add(name[:-2]) + + versions[name] = {"version": version, "check": binary["check"]} + previous_host = host + if exception: + for i in exception: + for host in versions_by_host.keys(): + for version, name in cheak_versions.get(i).get(host).items(): + exсeptions.append(f'Binary {name} has inconsistent version {version} on host {host}') + exсeptions.append('\n') + return versions, exсeptions def _parse_version(version_output: str) -> str: version = re.search(r"version[:\s]*v?(.+)", version_output, re.IGNORECASE)