diff --git a/.flake8 b/.flake8 index 0771a566b1..cc06c3ef8a 100644 --- a/.flake8 +++ b/.flake8 @@ -4,16 +4,20 @@ jobs = 0 # Ignore rules that conflict with ruff or are intentionally suppressed. # -# Handled by ruff formatter (ruff removes noqa comments for these, causing flake8 failures): +# Handled by ruff (suppressed in flake8 to avoid duplicate enforcement): # E501 — line too long; ruff formatter manages line length # E201 — whitespace after '('; ruff formatter manages whitespace # W503 — line break before binary operator; PEP 8 reversed this, ruff uses W504 (after) # F821 — undefined name; used in global_config files with dynamic exec() loading +# N815 — mixedCaseClassAttribute; enforced by ruff via extend-select, suppressed per-line with # noqa: N815 +# +# Suppressed in flake8 only (enforced by ruff via extend-select + per-line # noqa): +# N802 — function name should be lowercase; ast.NodeVisitor/logging.Formatter method overrides # # Intentionally suppressed (not enforced by either linter): -# N802 — function name should be lowercase; method overrides like formatTime from logging.Formatter +# TODO: rename exceptions to use Error suffix and remove N818 from ignore # N818 — exception name should end with Error; existing names like MissingTemplateVariables -ignore = N802, E501, F821, E201, W503, N818 +ignore = N802, N815, E501, F821, E201, W503, N818 exclude = doc, .tox, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 19be376c3a..fa63682727 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: stages: [pre-commit] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.1 + rev: v0.15.12 hooks: - id: ruff stages: [pre-commit] diff --git a/conftest.py b/conftest.py index 2387199374..9e3697a7cd 100644 --- a/conftest.py +++ b/conftest.py @@ -734,7 +734,7 @@ def pytest_runtest_setup(item): if "incremental" in item.keywords: previousfailed = getattr(item.parent, "_previousfailed", None) if previousfailed is not None: - pytest.xfail("previous test failed (%s)" % previousfailed.name) + pytest.xfail(f"previous test failed ({previousfailed.name})") def pytest_runtest_call(item): diff --git a/libs/net/netattachdef.py b/libs/net/netattachdef.py index e354e57b3f..d0ea1beb4b 100644 --- a/libs/net/netattachdef.py +++ b/libs/net/netattachdef.py @@ -35,7 +35,7 @@ class IpamStatic(Ipam): """ type: str = field(default="static", init=False) - addresses: list["IpamStatic.Address"] + addresses: list[IpamStatic.Address] routes: list[IpamRoute] | None = None @dataclass diff --git a/libs/net/traffic_generator.py b/libs/net/traffic_generator.py index 2ec5451617..31ef055d1f 100644 --- a/libs/net/traffic_generator.py +++ b/libs/net/traffic_generator.py @@ -1,7 +1,8 @@ import contextlib import logging from abc import ABC, abstractmethod -from typing import Final, Generator +from collections.abc import Generator +from typing import Final, Self from ocp_resources.pod import Pod from ocp_utilities.exceptions import CommandExecFailed @@ -32,7 +33,7 @@ def server_ip(self) -> str: return self._server_ip @abstractmethod - def __enter__(self) -> "BaseTcpClient": + def __enter__(self) -> Self: pass @abstractmethod @@ -70,7 +71,7 @@ def __init__( self._cmd += f" --bind {bind_ip}" if bind_ip else "" self._cmd += f" --bind-dev {bind_dev}" if bind_dev else "" - def __enter__(self) -> "TcpServer": + def __enter__(self) -> Self: self._vm.console( commands=[f"{self._cmd} &"], timeout=_DEFAULT_CMD_TIMEOUT_SEC, @@ -121,7 +122,7 @@ def __init__( self._cmd += f" --bind-dev {bind_dev}" if bind_dev else "" self._cmd += f" --set-mss {maximum_segment_size}" if maximum_segment_size else "" - def __enter__(self) -> "VMTcpClient": + def __enter__(self) -> Self: self._vm.console( commands=[f"{self._cmd} &"], timeout=_DEFAULT_CMD_TIMEOUT_SEC, @@ -190,7 +191,7 @@ def __init__( self._container = container or _IPERF_BIN self._cmd += f" --bind {bind_interface}" if bind_interface else "" - def __enter__(self) -> "PodTcpClient": + def __enter__(self) -> Self: # run the command in the background using nohup to ensure it keeps running after the exec session ends self._pod.execute( command=["sh", "-c", f"nohup {self._cmd} >/tmp/{_IPERF_BIN}.log 2>&1 &"], container=self._container @@ -220,7 +221,7 @@ def active_tcp_connections( client_vm: BaseVirtualMachine, server_vm: BaseVirtualMachine, iface_name: str, -) -> Generator[list[tuple[VMTcpClient, TcpServer]], None, None]: +) -> Generator[list[tuple[VMTcpClient, TcpServer]]]: """Start iperf3 client-server connections for all IPs on the server's interface. The helper assumed the ip addresses are up. @@ -258,7 +259,7 @@ def client_server_active_connection( port: int = IPERF_SERVER_PORT, maximum_segment_size: int = 0, ip_family: int = 4, -) -> Generator[tuple[VMTcpClient, TcpServer], None, None]: +) -> Generator[tuple[VMTcpClient, TcpServer]]: """Start iperf3 client-server connection with continuous TCP traffic flow. Automatically starts an iperf3 server and client, with traffic flowing continuously diff --git a/libs/storage/config.py b/libs/storage/config.py index 8224229180..48dbdbd461 100644 --- a/libs/storage/config.py +++ b/libs/storage/config.py @@ -26,7 +26,7 @@ def __init__(self, name: str): self.name = name self.storage_config = self.get_storage_config() - def supported_storage_classes(self) -> list["StorageClass"]: + def supported_storage_classes(self) -> list[StorageClass]: return [ StorageClass( name=StorageClassNames.CEPH_RBD_VIRTUALIZATION, diff --git a/pyproject.toml b/pyproject.toml index dd1144fdee..48ddbc97ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,11 @@ output-format = "grouped" exclude = [".git", ".venv", ".mypy_cache", ".tox", "__pycache__"] [tool.ruff.lint] -extend-select = ["PLC0415"] +extend-select = [ + "PLC0415", # import-outside-top-level + "N802", # function name should be lowercase — method overrides from stdlib + "N815", # mixedCase variable in class scope — k8s API field names use camelCase +] # TODO: Each ignored rule below should be addressed in a dedicated PR. ignore = [ "SIM102", # collapsible-if — nested ifs are often more readable @@ -32,11 +36,18 @@ ignore = [ "B023", # loop variable not bound in function — needs careful refactor "G201", # .error(exc_info=True) vs .exception() "SIM115", # context manager for file opening — may need structural changes + "SIM118", # in-dict-keys — k8s resource fields require explicit .keys() (not plain dicts) + "PERF102", # incorrect-dict-iterator — k8s resource fields require explicit .keys()/.items() + "B026", # star-arg unpacking after keyword arg — required when wrapping TimeoutSampler ] [tool.ruff.lint.per-file-ignores] # F821: undefined name — global_config files use dynamic exec() loading via pytest_testconfig "tests/global_config*.py" = ["F821"] +# N802: ast.NodeVisitor method overrides use camelCase (visit_ImportFrom, etc.) +"scripts/tests_analyzer/pytest_marker_analyzer.py" = ["N802"] +# N802: logging.Formatter.formatTime override +"utilities/logger.py" = ["N802"] [tool.ruff.lint.isort] order-by-type = true diff --git a/scripts/quarantine_stats/generate_dashboard.py b/scripts/quarantine_stats/generate_dashboard.py index bf0fa61645..1e754a2bbb 100755 --- a/scripts/quarantine_stats/generate_dashboard.py +++ b/scripts/quarantine_stats/generate_dashboard.py @@ -1615,10 +1615,7 @@ def _generate_quarantined_details_by_version(self) -> str: all_tab_buttons: list[str] = [] all_tab_contents: list[str] = [] first_tab = True - repo_index = 0 - - for _, version_stats_list in self.repo_stats.items(): - repo_index += 1 + for repo_index, (_, version_stats_list) in enumerate(self.repo_stats.items(), start=1): repo_id = f"repo{repo_index}" for version_stat in version_stats_list: diff --git a/scripts/tests_analyzer/pytest_marker_analyzer.py b/scripts/tests_analyzer/pytest_marker_analyzer.py index 424f5ded49..8a915f200a 100644 --- a/scripts/tests_analyzer/pytest_marker_analyzer.py +++ b/scripts/tests_analyzer/pytest_marker_analyzer.py @@ -815,14 +815,12 @@ def _is_type_checking_guard(node: ast.If) -> bool: if isinstance(test, ast.Name) and test.id == "TYPE_CHECKING": return True # ``if typing.TYPE_CHECKING:`` - if ( + return ( isinstance(test, ast.Attribute) and isinstance(test.value, ast.Name) and test.value.id == "typing" and test.attr == "TYPE_CHECKING" - ): - return True - return False + ) class FixtureVisitor(ast.NodeVisitor): @@ -1779,12 +1777,7 @@ def _extract_modified_symbols( # or potentially impactful executable code. if line_number <= len(source_lines): line_content = source_lines[line_number - 1].strip() - if ( - not line_content - or line_content.startswith("#") - or line_content.startswith(("import ", "from ")) - or line_content.startswith(('"""', "'''", '"', "'")) - ): + if not line_content or line_content.startswith(("#", "import ", "from ", '"""', "'''", '"', "'")): has_unattributed = True else: # Executable module-level code — conservative fallback @@ -2010,7 +2003,7 @@ def _analyze_single_test_dependencies( to_visit = [] for dep_file in current_level: - if dep_file in visited or not dep_file.suffix == ".py": + if dep_file in visited or dep_file.suffix != ".py": continue visited.add(dep_file) diff --git a/tests/chaos/oadp/conftest.py b/tests/chaos/oadp/conftest.py index 8f5520560a..33f1a4bb61 100644 --- a/tests/chaos/oadp/conftest.py +++ b/tests/chaos/oadp/conftest.py @@ -76,7 +76,6 @@ def rebooted_vm_source_node(rhel_vm_with_dv_running, oadp_backup_in_progress, wo LOGGER.info(f"Waiting for node {vm_node.name} to come back online") wait_for_node_status(node=vm_node, status=True, wait_timeout=TIMEOUT_10MIN) - return @pytest.fixture() diff --git a/tests/chaos/utils.py b/tests/chaos/utils.py index d3fbc3160d..e427d1ff8b 100644 --- a/tests/chaos/utils.py +++ b/tests/chaos/utils.py @@ -6,7 +6,7 @@ import threading import time from contextlib import contextmanager -from datetime import datetime +from datetime import UTC, datetime from kubernetes.dynamic.exceptions import ResourceNotFoundError from ocp_resources.daemonset import DaemonSet @@ -139,7 +139,7 @@ def _monitor_nginx_server( timeout_watch = TimeoutWatch(timeout=_sampling_duration) while timeout_watch.remaining_time() > 0: if not is_http_ok(utility_pods=_utility_pods, node=_control_plane_host_node, url=_url): - raise Exception("Wrong status code from server.") + raise RuntimeError(f"HTTP health check failed for URL {_url}: expected status 200 (OK).") time.sleep(_sampling_interval) LOGGER.info("HTTP querying finished successfully.") @@ -238,7 +238,7 @@ def collect_cluster_health_info(client, hco_namespace, additional_namespaces): log_content = json.dumps( { - f"{datetime.utcnow().strftime('%Y/%m/%d %H:%M:%S')}": [ + f"{datetime.now(tz=UTC).strftime('%Y/%m/%d %H:%M:%S')}": [ pods_status, deployments_replicas, daemonset_replicas, diff --git a/tests/deprecated_api/test_deprecation_audit_logs.py b/tests/deprecated_api/test_deprecation_audit_logs.py index 97ec818067..2aed611543 100644 --- a/tests/deprecated_api/test_deprecation_audit_logs.py +++ b/tests/deprecated_api/test_deprecation_audit_logs.py @@ -41,10 +41,7 @@ def skip_component_check(user_agent, deprecation_version): return True # Skip if deprecated version is greater than DEPRECATED_API_MAX_VERSION - if Version(deprecation_version) > Version(DEPRECATED_API_MAX_VERSION): - return True - - return False + return Version(deprecation_version) > Version(DEPRECATED_API_MAX_VERSION) def failure_not_in_component_list(component, annotations, audit_log_entry_dict): diff --git a/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py b/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py index 2ba5cff507..7c0db36ae6 100644 --- a/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py +++ b/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py @@ -246,10 +246,10 @@ def data_sources_managed_by_data_import_crons_scope_function( @pytest.fixture() def data_sources_names_from_templates_scope_function(base_templates): - return set([ + return { get_parameters_from_template(template=template, parameter_subset=DATA_SOURCE_NAME)[DATA_SOURCE_NAME] for template in base_templates - ]) + } @pytest.fixture() diff --git a/tests/infrastructure/golden_images/update_boot_source/utils.py b/tests/infrastructure/golden_images/update_boot_source/utils.py index 2bbb4320ee..fd0660cc7f 100644 --- a/tests/infrastructure/golden_images/update_boot_source/utils.py +++ b/tests/infrastructure/golden_images/update_boot_source/utils.py @@ -87,7 +87,7 @@ def get_all_dic_volume_names(client: DynamicClient, namespace: str) -> list[str] list[str]: Combined list of PVC and VolumeSnapshot names managed by DataImportCron. """ - def _fetch_volume_names(resource_cls: type[PersistentVolumeClaim] | type[VolumeSnapshot]) -> list[str]: + def _fetch_volume_names(resource_cls: type[PersistentVolumeClaim | VolumeSnapshot]) -> list[str]: return [ volume.name for volume in resource_cls.get( diff --git a/tests/infrastructure/instance_types/test_common_vm_preference.py b/tests/infrastructure/instance_types/test_common_vm_preference.py index 45ccd66bd3..4a06c60e4e 100644 --- a/tests/infrastructure/instance_types/test_common_vm_preference.py +++ b/tests/infrastructure/instance_types/test_common_vm_preference.py @@ -70,7 +70,7 @@ def vm_cluster_preferences_expected_list(): @pytest.mark.polarion("CNV-9981") def test_base_preferences_common_annotation(base_vm_cluster_preferences, vm_cluster_preferences_expected_list): - assert set([preference.name for preference in base_vm_cluster_preferences]) == set( + assert {preference.name for preference in base_vm_cluster_preferences} == set( vm_cluster_preferences_expected_list ), "Not all base CNV cluster preferences exist" diff --git a/tests/infrastructure/vm_console_proxy/utils.py b/tests/infrastructure/vm_console_proxy/utils.py index 2313edb417..c2edba77fb 100644 --- a/tests/infrastructure/vm_console_proxy/utils.py +++ b/tests/infrastructure/vm_console_proxy/utils.py @@ -1,7 +1,7 @@ from __future__ import annotations import logging -from typing import Any, Type +from typing import Any import requests from kubernetes.dynamic import DynamicClient @@ -56,11 +56,13 @@ def create_vnc_console_token( response.raise_for_status() # Raise HTTPError for bad responses <4xx><5xx> return response.json()["token"] except requests.RequestException as exp: - logging.error(f"Request error occurred: {exp}") + LOGGER.error(f"Request error occurred: {exp}") raise -def get_vm_console_proxy_resource(resource_kind: Type, client: DynamicClient, namespace: str | None = None) -> Type: +def get_vm_console_proxy_resource( + resource_kind: type[Resource], client: DynamicClient, namespace: str | None = None +) -> Resource: if namespace: vm_console_proxy_resource_object = resource_kind( name=VM_CONSOLE_PROXY, diff --git a/tests/install_upgrade_operators/crypto_policy/test_hco_override_api_server_crypto_policy.py b/tests/install_upgrade_operators/crypto_policy/test_hco_override_api_server_crypto_policy.py index 0dd6aa90d0..01eb225513 100644 --- a/tests/install_upgrade_operators/crypto_policy/test_hco_override_api_server_crypto_policy.py +++ b/tests/install_upgrade_operators/crypto_policy/test_hco_override_api_server_crypto_policy.py @@ -72,7 +72,7 @@ def test_hco_overriding_apiserver_crypto_policy( conflicting_resources = { resource.kind: crypto_policy for resource, crypto_policy in sample.items() - if expected_all_managed_crs_crypto_policies[resource] != sample[resource] + if expected_all_managed_crs_crypto_policies[resource] != crypto_policy } assert not conflicting_resources, ( "API server crypto policy overrides HCO crypto policy\n" diff --git a/tests/install_upgrade_operators/crypto_policy/test_update_specific_component_crypto_policy.py b/tests/install_upgrade_operators/crypto_policy/test_update_specific_component_crypto_policy.py index e62ef9bb56..51d2dd7628 100644 --- a/tests/install_upgrade_operators/crypto_policy/test_update_specific_component_crypto_policy.py +++ b/tests/install_upgrade_operators/crypto_policy/test_update_specific_component_crypto_policy.py @@ -91,7 +91,7 @@ def updated_cr_with_custom_crypto_policy( hco_namespace=hco_namespace, expected_conditions={ **DEFAULT_HCO_CONDITIONS, - **{"TaintedConfiguration": Resource.Condition.Status.TRUE}, + "TaintedConfiguration": Resource.Condition.Status.TRUE, }, ) yield {"resource": resource, "tls_policy": value} diff --git a/tests/install_upgrade_operators/csv/csv_permissions_audit/test_csv_permissions_audit.py b/tests/install_upgrade_operators/csv/csv_permissions_audit/test_csv_permissions_audit.py index fc97a9c1cc..0d9d5c2df9 100644 --- a/tests/install_upgrade_operators/csv/csv_permissions_audit/test_csv_permissions_audit.py +++ b/tests/install_upgrade_operators/csv/csv_permissions_audit/test_csv_permissions_audit.py @@ -66,7 +66,7 @@ def csv_permissions(admin_client): @pytest.mark.polarion("CNV-9805") def test_new_operator_in_csv(operators_from_csv): - assert sorted(list(operators_from_csv)) == sorted(CNV_OPERATORS), ( + assert sorted(operators_from_csv) == sorted(CNV_OPERATORS), ( f"Expected cnv operators:{CNV_OPERATORS} does not match operators {operators_from_csv} " ) diff --git a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cdi.py b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cdi.py index 1b6cd64882..9155dc5165 100644 --- a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cdi.py +++ b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cdi.py @@ -57,7 +57,7 @@ def test_cdi_json_patch( admin_client=admin_client, hco_namespace=hco_namespace, expected_conditions={ - **{"TaintedConfiguration": Resource.Condition.Status.TRUE}, + "TaintedConfiguration": Resource.Condition.Status.TRUE, }, ) validate_cdi_json_patch( diff --git a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cnao.py b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cnao.py index acc9c444f3..a0e7937d66 100644 --- a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cnao.py +++ b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_cnao.py @@ -61,7 +61,7 @@ def test_cnao_json_patch( admin_client=admin_client, hco_namespace=hco_namespace, expected_conditions={ - **{"TaintedConfiguration": Resource.Condition.Status.TRUE}, + "TaintedConfiguration": Resource.Condition.Status.TRUE, }, ) cnao_spec = cnao_resource.instance.spec diff --git a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_kubevirt.py b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_kubevirt.py index 1d7c550d42..be0c7b441c 100644 --- a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_kubevirt.py +++ b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_kubevirt.py @@ -54,7 +54,7 @@ def test_kubevirt_json_patch( admin_client=admin_client, hco_namespace=hco_namespace, expected_conditions={ - **{"TaintedConfiguration": Resource.Condition.Status.TRUE}, + "TaintedConfiguration": Resource.Condition.Status.TRUE, }, ) validate_kubevirt_json_patch(kubevirt_resource=kubevirt_resource) diff --git a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_multiple_updates.py b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_multiple_updates.py index 240c4bc1e7..c8d8db8974 100644 --- a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_multiple_updates.py +++ b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_multiple_updates.py @@ -91,7 +91,7 @@ def test_multiple_json_patch( admin_client=admin_client, hco_namespace=hco_namespace, expected_conditions={ - **{"TaintedConfiguration": Resource.Condition.Status.TRUE}, + "TaintedConfiguration": Resource.Condition.Status.TRUE, }, ) validate_cdi_json_patch( @@ -110,13 +110,13 @@ def test_multiple_json_patch_metrics(self, prometheus, kubevirt_all_unsafe_modif ) for component in [COMPONENT_CDI, COMPONENT_KUBEVIRT] } - for component_name in component_metrics_dict: + for component_name, previous_value in component_metrics_dict.items(): LOGGER.info(f"Waiting for metrics: {QUERY_STRING} for component: {component_name}") wait_for_metrics_value_update( prometheus=prometheus, component_name=component_name, query_string=QUERY_STRING, - previous_value=component_metrics_dict[component_name], + previous_value=previous_value, ) @pytest.mark.polarion("CNV-8813") diff --git a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_ssp.py b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_ssp.py index b8f58b395e..6464261837 100644 --- a/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_ssp.py +++ b/tests/install_upgrade_operators/json_patch/test_json_patch_annotation_ssp.py @@ -57,7 +57,7 @@ def test_ssp_json_patch( admin_client=admin_client, hco_namespace=hco_namespace, expected_conditions={ - **{"TaintedConfiguration": Resource.Condition.Status.TRUE}, + "TaintedConfiguration": Resource.Condition.Status.TRUE, }, ) ssp_replicas_current_value = ssp_resource_scope_function.instance.spec.templateValidator.replicas diff --git a/tests/install_upgrade_operators/must_gather/utils.py b/tests/install_upgrade_operators/must_gather/utils.py index 6ad520f821..9ed2426070 100644 --- a/tests/install_upgrade_operators/must_gather/utils.py +++ b/tests/install_upgrade_operators/must_gather/utils.py @@ -53,7 +53,7 @@ # To be removed after the issue is fixed in openshift -class ResourceFieldEqBugWorkaround(object): +class ResourceFieldEqBugWorkaround: def __enter__(self): self.prev_eq_func = ResourceField.__eq__ @@ -386,7 +386,7 @@ def validate_must_gather_vm_file_collection( admin_client=admin_client, ) assert all(entry in str(exeption_found.value) for entry in not_collected_vm_names + ["path_not_found"]), ( - f"Failed to find {not_collected_vm_names} in exception message: {str(exeption_found.value)}" + f"Failed to find {not_collected_vm_names} in exception message: {exeption_found.value!s}" ) diff --git a/tests/install_upgrade_operators/product_upgrade/conftest.py b/tests/install_upgrade_operators/product_upgrade/conftest.py index 29b6fc428d..8f75354762 100644 --- a/tests/install_upgrade_operators/product_upgrade/conftest.py +++ b/tests/install_upgrade_operators/product_upgrade/conftest.py @@ -1,6 +1,6 @@ import logging import os -from datetime import datetime, timezone +from datetime import UTC, datetime import pytest from ocp_resources.cluster_version import ClusterVersion @@ -295,7 +295,7 @@ def prometheus_scope_function(): @pytest.fixture(scope="session") def upgrade_start_timestamp(): - return datetime.now(tz=timezone.utc) + return datetime.now(tz=UTC) @pytest.fixture(scope="session") diff --git a/tests/install_upgrade_operators/product_upgrade/utils.py b/tests/install_upgrade_operators/product_upgrade/utils.py index 540fed1f9a..138b3cc4df 100644 --- a/tests/install_upgrade_operators/product_upgrade/utils.py +++ b/tests/install_upgrade_operators/product_upgrade/utils.py @@ -3,7 +3,7 @@ import json import logging import re -from datetime import datetime, timezone +from datetime import UTC, datetime from pprint import pformat from threading import Thread from typing import TYPE_CHECKING, Any @@ -504,7 +504,7 @@ def query_alerts_fired_in_range( Returns: List of result dicts, each containing 'metric' labels and 'values' timestamps. """ - duration_seconds = int((datetime.now(tz=timezone.utc) - start_time).total_seconds()) + duration_seconds = int((datetime.now(tz=UTC) - start_time).total_seconds()) query = f'ALERTS{{alertstate="{FIRING_STATE}",kubernetes_operator_part_of="kubevirt"}}[{duration_seconds}s]' return prometheus.query_sampler(query=query) diff --git a/tests/network/bgp/evpn/libevpn.py b/tests/network/bgp/evpn/libevpn.py index 709d3258f0..3e43304a14 100644 --- a/tests/network/bgp/evpn/libevpn.py +++ b/tests/network/bgp/evpn/libevpn.py @@ -74,7 +74,7 @@ def __init__( super().__init__(pod=pod, server_ip=server_ip, server_port=server_port, container=container) self._netns = netns - def __enter__(self) -> "EndpointTcpClient": + def __enter__(self) -> EndpointTcpClient: run_cmd = f"ip netns exec {self._netns} {self._cmd}" self._pod.execute( command=["sh", "-c", f"nohup {run_cmd} >/tmp/iperf3.log 2>&1 &"], diff --git a/tests/network/kubemacpool/explicit_range/conftest.py b/tests/network/kubemacpool/explicit_range/conftest.py index d9c1212fc1..c15bad6f34 100644 --- a/tests/network/kubemacpool/explicit_range/conftest.py +++ b/tests/network/kubemacpool/explicit_range/conftest.py @@ -1,4 +1,4 @@ -from typing import Generator +from collections.abc import Generator import pytest from kubernetes.dynamic import DynamicClient diff --git a/tests/network/kubemacpool/utils.py b/tests/network/kubemacpool/utils.py index 85d8aef5e8..a2a364d7e4 100644 --- a/tests/network/kubemacpool/utils.py +++ b/tests/network/kubemacpool/utils.py @@ -53,8 +53,7 @@ def vm_network_config(mac_pool, all_nads, end_ip_octet, mac_uid): def create_vm(name, namespace, iface_config, node_selector, client, mac_pool): network_data_data = {} _data = { - iface: {"addresses": [f"{iface_config[iface].ip_address}/24"]} - for iface in ("eth%d" % idx for idx in range(1, 5)) + iface: {"addresses": [f"{iface_config[iface].ip_address}/24"]} for iface in (f"eth{idx}" for idx in range(1, 5)) } cloud_init_data = prepare_cloud_init_user_data(section="runcmd", data=ARP_ISOLATION_SYSCTL_CMD) diff --git a/tests/network/l2_bridge/libl2bridge.py b/tests/network/l2_bridge/libl2bridge.py index 93f8b05684..00916379ae 100644 --- a/tests/network/l2_bridge/libl2bridge.py +++ b/tests/network/l2_bridge/libl2bridge.py @@ -154,7 +154,7 @@ def hot_plug_interface( def hot_unplug_interface(vm, hot_plugged_interface_name): interfaces = vm.get_interfaces() unplugged_interface = next(interface for interface in interfaces if interface["name"] == hot_plugged_interface_name) - unplugged_interface.update(dict(state="absent")) + unplugged_interface.update({"state": "absent"}) update_hot_plug_config_in_vm(vm=vm, interfaces=interfaces) diff --git a/tests/network/localnet/liblocalnet.py b/tests/network/localnet/liblocalnet.py index aef40d037e..58d9c965fa 100644 --- a/tests/network/localnet/liblocalnet.py +++ b/tests/network/localnet/liblocalnet.py @@ -195,7 +195,7 @@ def create_nncp_localnet_on_secondary_node_nic( node_nic_name: str, client: DynamicClient, mtu: int | None = None, -) -> Generator[libnncp.NodeNetworkConfigurationPolicy, None, None]: +) -> Generator[libnncp.NodeNetworkConfigurationPolicy]: """Create NNCP to configure an OVS bridge on a secondary NIC across all worker nodes. Note: diff --git a/tests/network/nmstate/test_connectivity_after_nmstate_changes.py b/tests/network/nmstate/test_connectivity_after_nmstate_changes.py index e57b878454..ce774fd3a4 100644 --- a/tests/network/nmstate/test_connectivity_after_nmstate_changes.py +++ b/tests/network/nmstate/test_connectivity_after_nmstate_changes.py @@ -282,11 +282,9 @@ def test_nmstate_restart_and_check_connectivity( nmstate_ds=nmstate_ds, ) LOGGER.info( - ( - f"Check connectivity from {nmstate_linux_bridge_attached_running_vma.name} " - f"to {nmstate_linux_bridge_attached_running_vmb.name} " - f"IP {vmb_dst_ip}. NMstate restart number: {idx + 1}" - ) + f"Check connectivity from {nmstate_linux_bridge_attached_running_vma.name} " + f"to {nmstate_linux_bridge_attached_running_vmb.name} " + f"IP {vmb_dst_ip}. NMstate restart number: {idx + 1}" ) assert_ping_successful( diff --git a/tests/network/provider_migration/libprovider.py b/tests/network/provider_migration/libprovider.py index ce2b184ab3..be1d4a9239 100644 --- a/tests/network/provider_migration/libprovider.py +++ b/tests/network/provider_migration/libprovider.py @@ -114,5 +114,4 @@ def extract_vm_primary_network_data(vm: vim.VirtualMachine) -> tuple[str, str]: for net in getattr(vm.guest, "net", []): if net.macAddress == device.macAddress and net.ipAddress: return net.macAddress, net.ipAddress[0] - else: - raise IfaceNotFoundError("No network interface found in the VM or no IP address assigned.") + raise IfaceNotFoundError("No network interface found in the VM or no IP address assigned.") diff --git a/tests/network/user_defined_network/ip_specification/conftest.py b/tests/network/user_defined_network/ip_specification/conftest.py index 2da0b71442..e6f8dd1067 100644 --- a/tests/network/user_defined_network/ip_specification/conftest.py +++ b/tests/network/user_defined_network/ip_specification/conftest.py @@ -20,7 +20,7 @@ def vm_under_test( namespaced_layer2_user_defined_network: Layer2UserDefinedNetwork, udn_affinity_label: tuple[str, str], admin_client: DynamicClient, -) -> Generator[BaseVirtualMachine, None, None]: +) -> Generator[BaseVirtualMachine]: with udn_vm( namespace_name=udn_namespace.name, name="ip-spec-vm-under-test", @@ -37,7 +37,7 @@ def vm_for_connectivity_ref( namespaced_layer2_user_defined_network: Layer2UserDefinedNetwork, udn_affinity_label: tuple[str, str], admin_client: DynamicClient, -) -> Generator[BaseVirtualMachine, None, None]: +) -> Generator[BaseVirtualMachine]: with udn_vm( namespace_name=udn_namespace.name, name="vm-for-connectivity-ref", @@ -77,7 +77,7 @@ def ip_to_request( @pytest.fixture(scope="module") def client_server_tcp_connectivity_between_vms( vm_for_connectivity_ref: BaseVirtualMachine, vm_under_test: BaseVirtualMachine -) -> Generator[tuple[TcpClient, TcpServer], None, None]: +) -> Generator[tuple[TcpClient, TcpServer]]: with client_server_active_connection( client_vm=vm_for_connectivity_ref, server_vm=vm_under_test, diff --git a/tests/network/user_defined_network/test_user_defined_network_passt.py b/tests/network/user_defined_network/test_user_defined_network_passt.py index cf05e9bf4a..55cc9b45c1 100644 --- a/tests/network/user_defined_network/test_user_defined_network_passt.py +++ b/tests/network/user_defined_network/test_user_defined_network_passt.py @@ -1,4 +1,4 @@ -from typing import Generator +from collections.abc import Generator import pytest from kubernetes.dynamic import DynamicClient @@ -31,7 +31,7 @@ def wait_for_ready_vm_with_restart(vm: BaseVirtualMachine) -> bool: @pytest.fixture(scope="module") def passt_enabled_in_hco( hyperconverged_resource_scope_module: HyperConverged, -) -> Generator[None, None, None]: +) -> Generator[None]: with ResourceEditorValidateHCOReconcile( patches={ hyperconverged_resource_scope_module: { @@ -51,7 +51,7 @@ def passt_running_vm_pair( udn_affinity_label: tuple[str, str], admin_client: DynamicClient, passt_enabled_in_hco, -) -> Generator[tuple[BaseVirtualMachine, BaseVirtualMachine], None, None]: +) -> Generator[tuple[BaseVirtualMachine, BaseVirtualMachine]]: with ( udn_vm( namespace_name=udn_namespace.name, diff --git a/tests/observability/metrics/test_vms_metrics.py b/tests/observability/metrics/test_vms_metrics.py index fb4368c33d..36d317cc34 100644 --- a/tests/observability/metrics/test_vms_metrics.py +++ b/tests/observability/metrics/test_vms_metrics.py @@ -1,5 +1,5 @@ import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from urllib.parse import urlparse import bitmath @@ -48,9 +48,7 @@ def get_last_transition_time(vm): for condition in vm.instance.get("status", {}).get("conditions"): if condition.get("type") == vm.Condition.READY: last_transition_time = condition.get("lastTransitionTime") - return int( - (datetime.strptime(last_transition_time, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)).timestamp() - ) + return int((datetime.strptime(last_transition_time, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=UTC)).timestamp()) def check_vm_last_transition_metric_value(prometheus, metric, vm): diff --git a/tests/observability/metrics/utils.py b/tests/observability/metrics/utils.py index 7f8b1c9a6a..ee1931a746 100644 --- a/tests/observability/metrics/utils.py +++ b/tests/observability/metrics/utils.py @@ -3,9 +3,10 @@ import re import shlex import urllib +from collections.abc import Generator from contextlib import contextmanager -from datetime import datetime, timezone -from typing import Any, Generator, Optional +from datetime import UTC, datetime +from typing import Any import bitmath from kubernetes.dynamic import DynamicClient @@ -230,7 +231,7 @@ def enable_swap_fedora_vm(vm: VirtualMachineForTests) -> None: vm.ssh_exec.executor(sudo=True).run_cmd(cmd=shlex.split("sysctl vm.swappiness=100")) -def get_vm_cpu_info_from_prometheus(prometheus: Prometheus, vm_name: str) -> Optional[int]: +def get_vm_cpu_info_from_prometheus(prometheus: Prometheus, vm_name: str) -> int | None: query = urllib.parse.quote_plus( f'kubevirt_vmi_node_cpu_affinity{{kubernetes_vmi_label_kubevirt_io_domain="{vm_name}"}}' ) @@ -373,9 +374,7 @@ def compare_network_traffic_bytes_and_metrics( rx_tx_indicator = True else: break - if rx_tx_indicator: - return True - return False + return bool(rx_tx_indicator) def validate_network_traffic_metrics_value( @@ -464,7 +463,7 @@ def compare_kubevirt_vmi_info_metric_with_vm_info( def timestamp_to_seconds(timestamp: str) -> int: # Parse the timestamp with UTC timezone and convert to seconds dt = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%SZ") - dt = dt.replace(tzinfo=timezone.utc) # Ensure it is treated as UTC + dt = dt.replace(tzinfo=UTC) # Ensure it is treated as UTC return int(dt.timestamp()) diff --git a/tests/scale/test_scale_benchmark.py b/tests/scale/test_scale_benchmark.py index 2dfd6a2ff8..a26fdebe53 100644 --- a/tests/scale/test_scale_benchmark.py +++ b/tests/scale/test_scale_benchmark.py @@ -110,7 +110,7 @@ def delete_resources(resources): def save_must_gather_logs(must_gather_image_url): logs_path = os.path.join( os.path.expanduser("~"), - f"must_gather_{datetime.datetime.utcnow().strftime('%Y_%m_%d_%H_%M_%S')}", + f"must_gather_{datetime.datetime.now(tz=datetime.UTC).strftime('%Y_%m_%d_%H_%M_%S')}", ) os.makedirs(logs_path) return run_must_gather( @@ -239,9 +239,9 @@ def vms_info(scale_test_param): OS_FLAVOR_FEDORA: {"latest_labels": FEDORA_LATEST_LABELS}, OS_FLAVOR_WINDOWS: {"latest_labels": WINDOWS_LATEST_LABELS}, } - for os_name in vms_info_dict: + for os_name, os_info in vms_info_dict.items(): for storage_type_key in SCALE_STORAGE_TYPES: - vms_info_dict[os_name][storage_type_key] = { + os_info[storage_type_key] = { "vms_per_batch": scale_test_param["vms"][os_name][storage_type_key]["vms_per_batch"], "number_of_batches": scale_test_param["vms"][os_name][storage_type_key]["number_of_batches"], } diff --git a/tests/storage/cdi_upload/test_upload_virtctl.py b/tests/storage/cdi_upload/test_upload_virtctl.py index 9ebc73b4ef..468ff79ddb 100644 --- a/tests/storage/cdi_upload/test_upload_virtctl.py +++ b/tests/storage/cdi_upload/test_upload_virtctl.py @@ -48,7 +48,7 @@ def get_population_method_by_provisioner(storage_class, cluster_csi_drivers_name @pytest.fixture(scope="function") def skip_no_reencrypt_route(upload_proxy_route): - if not upload_proxy_route.termination == "reencrypt": + if upload_proxy_route.termination != "reencrypt": pytest.skip("Skip testing. The upload proxy route is not re-encrypt.") diff --git a/tests/storage/cdi_upload/utils.py b/tests/storage/cdi_upload/utils.py index f4c63dd0d6..d0bdb30591 100644 --- a/tests/storage/cdi_upload/utils.py +++ b/tests/storage/cdi_upload/utils.py @@ -1,5 +1,4 @@ import logging -from typing import Optional from kubernetes.dynamic import DynamicClient from ocp_resources.resource import Resource @@ -8,7 +7,7 @@ LOGGER = logging.getLogger(__name__) -def get_storage_profile_minimum_supported_pvc_size(storage_class_name: str, client: DynamicClient) -> Optional[str]: +def get_storage_profile_minimum_supported_pvc_size(storage_class_name: str, client: DynamicClient) -> str | None: """ Get the minimum supported PVC size from the storage profile annotations. diff --git a/tests/storage/cross_cluster_live_migration/utils.py b/tests/storage/cross_cluster_live_migration/utils.py index 93d05a3274..7c92fba415 100644 --- a/tests/storage/cross_cluster_live_migration/utils.py +++ b/tests/storage/cross_cluster_live_migration/utils.py @@ -1,5 +1,5 @@ import logging -from typing import Generator +from collections.abc import Generator from kubernetes.dynamic import DynamicClient from ocp_resources.hyperconverged import HyperConverged @@ -22,7 +22,7 @@ def configure_hco_live_migration_network( client: DynamicClient, hco_namespace: Namespace, network_for_live_migration: NetworkAttachmentDefinition | None = None, -) -> Generator[None, None, None]: +) -> Generator[None]: """ Configure HCO live migration network. diff --git a/tests/storage/golden_image/test_cached_snapshots.py b/tests/storage/golden_image/test_cached_snapshots.py index 7fdf9ebcd1..6f221a50af 100644 --- a/tests/storage/golden_image/test_cached_snapshots.py +++ b/tests/storage/golden_image/test_cached_snapshots.py @@ -47,7 +47,7 @@ def skip_if_no_storage_profile_with_snapshot_import_cron_format( snapshot_storage_class_name_scope_module, ): sc_storage_profile = StorageProfile(name=snapshot_storage_class_name_scope_module) - if not sc_storage_profile.instance.status.get("dataImportCronSourceFormat") == "snapshot": + if sc_storage_profile.instance.status.get("dataImportCronSourceFormat") != "snapshot": pytest.skip(f"Cant create cached snapshot for {snapshot_storage_class_name_scope_module} storageclass") diff --git a/tests/storage/test_cdi_certificate.py b/tests/storage/test_cdi_certificate.py index 528fb03628..5ad83f1cd0 100644 --- a/tests/storage/test_cdi_certificate.py +++ b/tests/storage/test_cdi_certificate.py @@ -57,7 +57,7 @@ def x509_cert_is_valid(cert, seconds): except subprocess.CalledProcessError as e: if "Certificate will expire" in e.output: return False - raise e + raise return True @@ -81,9 +81,9 @@ def valid_cdi_certificates(secrets): start = secret.certificate_not_before end = secret.certificate_not_after - start_dt = datetime.datetime.strptime(start, RFC3339_FORMAT).replace(tzinfo=datetime.timezone.utc) - end_dt = datetime.datetime.strptime(end, RFC3339_FORMAT).replace(tzinfo=datetime.timezone.utc) - now_dt = datetime.datetime.now(datetime.timezone.utc) + start_dt = datetime.datetime.strptime(start, RFC3339_FORMAT).replace(tzinfo=datetime.UTC) + end_dt = datetime.datetime.strptime(end, RFC3339_FORMAT).replace(tzinfo=datetime.UTC) + now_dt = datetime.datetime.now(datetime.UTC) assert start_dt <= now_dt <= end_dt, f"Certificate of {cdi_secret} not valid at current time" diff --git a/tests/storage/vm_export/utils.py b/tests/storage/vm_export/utils.py index 100c1af67f..cceae57ed7 100644 --- a/tests/storage/vm_export/utils.py +++ b/tests/storage/vm_export/utils.py @@ -5,8 +5,8 @@ import io import logging import shlex +from collections.abc import Generator from contextlib import contextmanager -from typing import Generator import yaml from kubernetes.dynamic import DynamicClient diff --git a/tests/utils.py b/tests/utils.py index 1ab4742c0e..9f14d60bd4 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,9 +4,9 @@ import re import shlex import tarfile +from collections.abc import Generator from contextlib import contextmanager from io import BytesIO -from typing import Generator, Optional import bitmath import requests @@ -548,12 +548,12 @@ def create_cirros_vm( client: DynamicClient, dv_name: str, vm_name: str, - node: Optional[str] = None, - wait_running: Optional[bool] = True, - volume_mode: Optional[str] = None, - cpu_model: Optional[str] = None, - annotations: Optional[str] = None, -) -> Generator[VirtualMachineForTests, None, None]: + node: str | None = None, + wait_running: bool | None = True, + volume_mode: str | None = None, + cpu_model: str | None = None, + annotations: dict[str, str] | None = None, +) -> Generator[VirtualMachineForTests]: artifactory_secret = get_artifactory_secret(namespace=namespace) artifactory_config_map = get_artifactory_config_map(namespace=namespace) diff --git a/tests/virt/cluster/common_templates/general/test_base_template.py b/tests/virt/cluster/common_templates/general/test_base_template.py index 0526ffc829..87b92710f4 100644 --- a/tests/virt/cluster/common_templates/general/test_base_template.py +++ b/tests/virt/cluster/common_templates/general/test_base_template.py @@ -367,7 +367,7 @@ def test_common_templates_golden_images_params(base_templates): for gi_params in template_parameters_dict if gi_params["name"] in [DATA_SOURCE_NAME, DATA_SOURCE_NAMESPACE] ] - if not len(golden_images_params) == 2: + if len(golden_images_params) != 2: unmatched_templates.update({template.name: "Missing golden images parameters"}) for gi_params in golden_images_params: # DATA_SOURCE_NAME contains either: @@ -514,7 +514,7 @@ def test_hyperv_features_exist_in_windows_templates(os_base_templates): templates_with_wrong_hyperv_labels = {} for template in os_base_templates: template_hyperv_features = template.instance.objects[0].spec.template.spec.domain.features.get("hyperv") - if sorted(list(template_hyperv_features.keys())) != sorted(HYPERV_FEATURES_LABELS_VM_YAML): + if sorted(template_hyperv_features.keys()) != sorted(HYPERV_FEATURES_LABELS_VM_YAML): templates_with_wrong_hyperv_labels[template.name] = list(template_hyperv_features.keys()) assert not templates_with_wrong_hyperv_labels, ( f"Windows templates are missing hyperV labels.\n" diff --git a/tests/virt/cluster/common_templates/utils.py b/tests/virt/cluster/common_templates/utils.py index 5c19842061..4de38f0de3 100644 --- a/tests/virt/cluster/common_templates/utils.py +++ b/tests/virt/cluster/common_templates/utils.py @@ -2,8 +2,8 @@ import logging import re import shlex -from datetime import datetime, timedelta, timezone -from typing import Generator, Optional +from collections.abc import Generator +from datetime import UTC, datetime, timedelta, timezone import bitmath import pytest @@ -174,7 +174,7 @@ def _get_vm_timezone_diff(): if virtctl_info["userName"].lower() not in windows_info: data_mismatch.append("user name mismatch") # Windows date format - 11/4/2020 (-m/-d/Y) - if datetime.fromtimestamp(timestamp=virtctl_time, tz=timezone.utc).strftime("%-m/%-d/%Y") not in windows_info: + if datetime.fromtimestamp(timestamp=virtctl_time, tz=UTC).strftime("%-m/%-d/%Y") not in windows_info: data_mismatch.append("login time mismatch") assert not data_mismatch, ( @@ -476,7 +476,7 @@ def check_vm_xml_tablet_device(vm, admin_client): def get_matrix_os_golden_image_data_source( admin_client: DynamicClient, golden_images_namespace: Namespace, os_matrix: dict[str, dict] -) -> Generator[DataSource, None, None]: +) -> Generator[DataSource]: """Retrieves or creates a DataSource object in golden image namespace specified in the OS matrix. Args: @@ -499,8 +499,8 @@ def matrix_os_vm_from_template( data_source_object: DataSource, os_matrix: dict[str, dict], cpu_model: str | None = None, - request: Optional[FixtureRequest] = None, - data_volume_template: Optional[dict[str, dict]] = None, + request: FixtureRequest | None = None, + data_volume_template: dict[str, dict] | None = None, ) -> VirtualMachineForTestsFromTemplate: param_dict = request.param if request else {} os_matrix_key = [*os_matrix][0] diff --git a/tests/virt/cluster/longevity_tests/utils.py b/tests/virt/cluster/longevity_tests/utils.py index e94e3bf281..e668808648 100644 --- a/tests/virt/cluster/longevity_tests/utils.py +++ b/tests/virt/cluster/longevity_tests/utils.py @@ -1,7 +1,6 @@ import logging import shlex import shutil -import socket from threading import Thread from ocp_resources import pod @@ -138,7 +137,7 @@ def _start_win_upgrade(vm): timeout=TIMEOUT_40MIN, ) LOGGER.info(f"VM {vm.name}: Finished upgrades download/install stage") - except socket.timeout: + except TimeoutError: LOGGER.warning(f"VM {vm.name}: Finished upgrades download/install stage but the script was stuck") upgrade_threads_list = [] diff --git a/tests/virt/cluster/strict_reconcile/test_strict_reconcile.py b/tests/virt/cluster/strict_reconcile/test_strict_reconcile.py index 741b2f0e4b..d96b91e26f 100644 --- a/tests/virt/cluster/strict_reconcile/test_strict_reconcile.py +++ b/tests/virt/cluster/strict_reconcile/test_strict_reconcile.py @@ -61,11 +61,13 @@ def restore_and_log_error( updated_resource, expected_value, actual_value, + original_error, ): updated_resource.restore() # Only restore explicitly, if virt-operator fails to revert automatically. - LOGGER.error(f"Timeout waiting for {resource.kind}: {resource.name} resource being reconciled.") - LOGGER.error(f"Expected: {expected_value}\n Actual: {actual_value}") - raise + raise TimeoutExpiredError( + f"Timeout waiting for {resource.kind}: {resource.name} resource being reconciled." + f" Expected: {expected_value}, Actual: {actual_value}" + ) from original_error def verify_resource_reconciled(admin_client, hco_namespace, resource): @@ -102,12 +104,13 @@ def verify_reconciled_role_or_clusterrole_resource(resource, resource_dict): entity.append(sample_rules) if original_resource_dict["rules"][0]["verbs"] == sample_rules[0]["verbs"]: break - except TimeoutExpiredError: + except TimeoutExpiredError as timeout_error: restore_and_log_error( resource=resource, updated_resource=updated_and_reconciled_resource["updated_resource"], expected_value=original_resource_dict["rules"][0]["verbs"], actual_value=entity[-1][0]["verbs"], + original_error=timeout_error, ) @@ -126,12 +129,13 @@ def verify_reconciled_rolebinding_or_clusterrolebinding_resource(resource, resou and original_resource_dict["subjects"][0]["name"] == sample_subjects[0]["name"] ): break - except TimeoutExpiredError: + except TimeoutExpiredError as timeout_error: restore_and_log_error( resource=resource, updated_resource=updated_and_reconciled_resource["updated_resource"], expected_value=original_resource_dict["subjects"][0]["name"], actual_value=entity[-1][0]["name"], + original_error=timeout_error, ) @@ -146,12 +150,13 @@ def verify_reconciled_configmap_resource(resource, resource_dict): entity.append(sample_data) if CM_DATA["ca-bundle"] != sample_data["ca-bundle"]: break - except TimeoutExpiredError: + except TimeoutExpiredError as timeout_error: restore_and_log_error( resource=resource, updated_resource=updated_and_reconciled_resource["updated_resource"], expected_value="Expecting ca-bundle, after reconcile", actual_value=entity[-1]["ca-bundle"], + original_error=timeout_error, ) @@ -166,12 +171,13 @@ def verify_reconciled_secret_resource(resource, resource_dict): entity.append(sample_data) if SECRET_DATA["tls.crt"] != sample_data["tls.crt"]: break - except TimeoutExpiredError: + except TimeoutExpiredError as timeout_error: restore_and_log_error( resource=resource, updated_resource=updated_and_reconciled_resource["updated_resource"], expected_value="Expecting tls.crt in base64 format, after reconcile", actual_value=entity[-1]["tls.crt"], + original_error=timeout_error, ) diff --git a/tests/virt/cluster/vm_lifecycle/test_vm_run_strategy.py b/tests/virt/cluster/vm_lifecycle/test_vm_run_strategy.py index 2108eb93d1..c388ae4fe5 100644 --- a/tests/virt/cluster/vm_lifecycle/test_vm_run_strategy.py +++ b/tests/virt/cluster/vm_lifecycle/test_vm_run_strategy.py @@ -138,7 +138,7 @@ def _vm_run_action(): except ApiException as e: if re.search(pattern=rf"{'|'.join(expected_exceptions)}", string=str(e)): return True - raise e + raise else: getattr(vm, vm_action)(wait=True, timeout=TIMEOUT_10MIN) return True diff --git a/tests/virt/node/descheduler/conftest.py b/tests/virt/node/descheduler/conftest.py index ade5c543f2..8720008c7b 100644 --- a/tests/virt/node/descheduler/conftest.py +++ b/tests/virt/node/descheduler/conftest.py @@ -305,9 +305,9 @@ def nodes_taints_before_descheduler_test_run(nodes): # clean up taints leftovers nodes_taints_after = {node: node.instance.spec.taints for node in nodes} - for node in nodes_taints_before: - if nodes_taints_after[node] != nodes_taints_before[node]: - ResourceEditor(patches={node: {"spec": {"taints": nodes_taints_before[node]}}}).update() + for node, taints_before in nodes_taints_before.items(): + if nodes_taints_after[node] != taints_before: + ResourceEditor(patches={node: {"spec": {"taints": taints_before}}}).update() @pytest.fixture() diff --git a/tests/virt/node/general/test_windows_vtpm_bitlocker.py b/tests/virt/node/general/test_windows_vtpm_bitlocker.py index f4f754de23..d8f22ef4fa 100644 --- a/tests/virt/node/general/test_windows_vtpm_bitlocker.py +++ b/tests/virt/node/general/test_windows_vtpm_bitlocker.py @@ -48,9 +48,7 @@ def _wait_encryption_finish(vm): try: for sample in sampler: if sample: - if all([ - True if msg in sample[0] else False for msg in ["100.0%", "Fully Encrypted", "Protection On"] - ]): + if all(msg in sample[0] for msg in ["100.0%", "Fully Encrypted", "Protection On"]): return except TimeoutExpiredError: LOGGER.error("Failed to encrypt disk") diff --git a/tests/virt/node/gpu/vgpu/test_rhel_vm_with_vgpu.py b/tests/virt/node/gpu/vgpu/test_rhel_vm_with_vgpu.py index 6c3ee00adb..41f7c1c691 100644 --- a/tests/virt/node/gpu/vgpu/test_rhel_vm_with_vgpu.py +++ b/tests/virt/node/gpu/vgpu/test_rhel_vm_with_vgpu.py @@ -98,7 +98,7 @@ def node_mdevtype_gpu_vm( @pytest.fixture(scope="class") def vm_with_no_gpu(gpu_vma, node_mdevtype_gpu_vm): - return [vm.name for vm in [gpu_vma, node_mdevtype_gpu_vm] if not get_num_gpu_devices_in_rhel_vm(vm=vm) == 1] + return [vm.name for vm in [gpu_vma, node_mdevtype_gpu_vm] if get_num_gpu_devices_in_rhel_vm(vm=vm) != 1] @pytest.mark.parametrize( @@ -168,7 +168,7 @@ def test_access_vgpus_in_both_rhel_vm_using_same_gpu(self, gpu_vma, gpu_vmb): """ Test vGPU is accessible in both the RHEL VMs, using same GPU, using GPUs spec. """ - vm_with_no_gpu = [vm.name for vm in [gpu_vma, gpu_vmb] if not get_num_gpu_devices_in_rhel_vm(vm=vm) == 1] + vm_with_no_gpu = [vm.name for vm in [gpu_vma, gpu_vmb] if get_num_gpu_devices_in_rhel_vm(vm=vm) != 1] assert not vm_with_no_gpu, f"GPU does not exist in following vms: {vm_with_no_gpu}" diff --git a/tests/virt/utils.py b/tests/virt/utils.py index 9e1d8113cb..02c8c3d86c 100644 --- a/tests/virt/utils.py +++ b/tests/virt/utils.py @@ -2,8 +2,9 @@ import logging import shlex +from collections.abc import Generator from contextlib import contextmanager -from typing import Any, Generator +from typing import Any import bitmath from kubernetes.dynamic import DynamicClient @@ -87,7 +88,7 @@ def append_feature_gate_to_hco(feature_gate, resource, client, namespace): hco_namespace=namespace, expected_conditions={ **DEFAULT_HCO_CONDITIONS, - **{"TaintedConfiguration": Resource.Condition.Status.TRUE}, + "TaintedConfiguration": Resource.Condition.Status.TRUE, }, ) yield @@ -433,7 +434,7 @@ def verify_guest_boot_time(vm_list, initial_boot_time): def get_or_create_golden_image_data_source( admin_client: DynamicClient, golden_images_namespace: Namespace, os_dict: dict[str, Any] -) -> Generator[DataSource, None, None]: +) -> Generator[DataSource]: """Retrieves or creates a DataSource object in golden image namespace specified in the OS matrix. Args: diff --git a/utilities/console.py b/utilities/console.py index a17a0597fe..9ad746566c 100644 --- a/utilities/console.py +++ b/utilities/console.py @@ -16,7 +16,7 @@ LOGGER = logging.getLogger(__name__) -class Console(object): +class Console: def __init__( self, vm: VirtualMachine, diff --git a/utilities/exceptions.py b/utilities/exceptions.py index f31a594ce1..16fac982e7 100644 --- a/utilities/exceptions.py +++ b/utilities/exceptions.py @@ -42,7 +42,7 @@ def run(self): self._cconn.send(None) except Exception as e: self._cconn.send(e) - raise e + raise @property def exception(self): diff --git a/utilities/hco.py b/utilities/hco.py index 9aadf1a0c1..86f5a74392 100644 --- a/utilities/hco.py +++ b/utilities/hco.py @@ -127,20 +127,17 @@ def wait_for_hco_conditions( expected_conditions=EXPECTED_STATUS_CONDITIONS[resource], consecutive_checks_count=consecutive_checks_count, ) - try: - utilities.infra.wait_for_consistent_resource_conditions( - dynamic_client=admin_client, - namespace=hco_namespace.name, - expected_conditions=expected_conditions or DEFAULT_HCO_CONDITIONS, - resource_kind=HyperConverged, - condition_key1=condition_key1, - condition_key2=condition_key2, - total_timeout=wait_timeout, - polling_interval=sleep, - consecutive_checks_count=consecutive_checks_count, - ) - except TimeoutExpiredError: - raise + utilities.infra.wait_for_consistent_resource_conditions( + dynamic_client=admin_client, + namespace=hco_namespace.name, + expected_conditions=expected_conditions or DEFAULT_HCO_CONDITIONS, + resource_kind=HyperConverged, + condition_key1=condition_key1, + condition_key2=condition_key2, + total_timeout=wait_timeout, + polling_interval=sleep, + consecutive_checks_count=consecutive_checks_count, + ) def wait_for_ds(ds): diff --git a/utilities/infra.py b/utilities/infra.py index 438e6878a0..788b345eab 100644 --- a/utilities/infra.py +++ b/utilities/infra.py @@ -12,10 +12,11 @@ import tempfile import time import zipfile +from collections.abc import Generator from contextlib import contextmanager from functools import cache from subprocess import PIPE, CalledProcessError, Popen -from typing import Any, Generator +from typing import Any import netaddr import requests @@ -692,8 +693,7 @@ def download_and_extract_file_from_cluster(tmpdir, url): with requests.get(url, verify=False, stream=True) as created_request: created_request.raise_for_status() with open(local_file_name, "wb") as file_downloaded: - for chunk in created_request.iter_content(chunk_size=8192): - file_downloaded.write(chunk) + file_downloaded.writelines(created_request.iter_content(chunk_size=8192)) LOGGER.info("Extract the downloaded archive.") if url.endswith(zip_file_extension): archive_file_object = zipfile.ZipFile(file=local_file_name) @@ -849,7 +849,7 @@ def get_node_audit_log_entries(log: str, node: str, log_entry: str) -> tuple[boo return True, lines -def get_node_audit_log_line_dict(logs: list[str], node: str, log_entry: str) -> Generator[dict[str, Any], None, None]: +def get_node_audit_log_line_dict(logs: list[str], node: str, log_entry: str) -> Generator[dict[str, Any]]: """ Parse audit log entries into dictionaries. diff --git a/utilities/junit_ai_utils.py b/utilities/junit_ai_utils.py index c8e77516b1..f048ea4c4c 100644 --- a/utilities/junit_ai_utils.py +++ b/utilities/junit_ai_utils.py @@ -101,8 +101,8 @@ def enrich_junit_xml(session) -> None: ) response.raise_for_status() result = response.json() - except Exception as ex: - logger.exception(f"Failed to enrich JUnit XML, original preserved. {ex}") + except (requests.RequestException, OSError, ValueError) as error: + logger.error(f"Failed to enrich JUnit XML, original preserved. Error: {error}") return if enriched_xml := result.get("enriched_xml"): diff --git a/utilities/oadp.py b/utilities/oadp.py index 896f175821..c1cd8c2419 100644 --- a/utilities/oadp.py +++ b/utilities/oadp.py @@ -122,7 +122,7 @@ def __init__( self.wait_complete = wait_complete self.timeout = timeout - def __enter__(self) -> "VeleroBackup": + def __enter__(self) -> Self: super().__enter__() if self.wait_complete: self.wait_for_status( @@ -148,7 +148,7 @@ def create_rhel_vm( client: DynamicClient, wait_running: bool = True, volume_mode: str | None = None, -) -> Generator["VirtualMachineForTests", None, None]: +) -> Generator[VirtualMachineForTests]: artifactory_secret = None artifactory_config_map = None diff --git a/utilities/operator.py b/utilities/operator.py index 446a10fbd8..80d66b21ca 100644 --- a/utilities/operator.py +++ b/utilities/operator.py @@ -204,7 +204,7 @@ def collect_mcp_data_on_update_timeout(machine_config_pools_list, not_matching_m LOGGER.error( f"Out of MCPs {mcps_to_check}, following MCPs {not_matching_mcps} were not at desired " f"condition {condition_type} before timeout.\n" - f"Current MCP status={str({mcp.name: mcp.instance.status.conditions for mcp in machine_config_pools_list})}" + f"Current MCP status={ {mcp.name: mcp.instance.status.conditions for mcp in machine_config_pools_list}!s}" ) collect_ocp_must_gather(since_time=since_time) diff --git a/utilities/sanity.py b/utilities/sanity.py index f8263c3a31..98b8efca73 100644 --- a/utilities/sanity.py +++ b/utilities/sanity.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any from _pytest.fixtures import FixtureRequest from kubernetes.client import ApiException @@ -22,7 +22,7 @@ from utilities.pytest_utils import exit_pytest_execution -def storage_sanity_check(cluster_storage_classes_names: List[str]) -> bool: +def storage_sanity_check(cluster_storage_classes_names: list[str]) -> bool: """ Verify cluster has all expected storage classes from pytest configuration. @@ -37,7 +37,7 @@ def storage_sanity_check(cluster_storage_classes_names: List[str]) -> bool: True if all expected storage classes from configuration exist on the cluster, False otherwise. """ - config_sc = list([[*csc][0] for csc in py_config["storage_class_matrix"]]) + config_sc = [[*csc][0] for csc in py_config["storage_class_matrix"]] exists_sc = [scn for scn in config_sc if scn in cluster_storage_classes_names] if sorted(config_sc) != sorted(exists_sc): LOGGER.error(f"Expected {config_sc}, On cluster {exists_sc}") @@ -204,8 +204,8 @@ def check_vm_creation_capability(admin_client: DynamicClient, namespace: str) -> def cluster_sanity( request: FixtureRequest, admin_client: DynamicClient, - cluster_storage_classes_names: List[str], - nodes: List[Node], + cluster_storage_classes_names: list[str], + nodes: list[Node], hco_namespace: Namespace, junitxml_property: Any | None = None, ) -> None: diff --git a/utilities/storage.py b/utilities/storage.py index c69dcf96fd..dc16635262 100644 --- a/utilities/storage.py +++ b/utilities/storage.py @@ -2,8 +2,9 @@ import math import os import shlex +from collections.abc import Generator from contextlib import contextmanager -from typing import Any, Dict, Generator +from typing import Any import cachetools.func import kubernetes @@ -65,6 +66,7 @@ LOGGER = logging.getLogger(__name__) +_DEFAULT_DISK_SERIAL_COMMAND = shlex.split("sudo ls /dev/disk/by-id") def create_dummy_first_consumer_pod(volume_mode=DataVolume.VolumeMode.FILE, dv=None, pvc=None): @@ -180,13 +182,13 @@ def create_dv( def data_volume( namespace: Namespace, client: DynamicClient, - storage_class_matrix: Dict[str, Dict[str, Any]] | None = None, + storage_class_matrix: dict[str, dict[str, Any]] | None = None, storage_class: str | None = None, request: FixtureRequest | None = None, - os_matrix: Dict[str, Dict[str, Any]] | None = None, + os_matrix: dict[str, dict[str, Any]] | None = None, check_dv_exists: bool = False, bind_immediate: bool | None = None, -) -> Generator[DataVolume, None, None]: +) -> Generator[DataVolume]: """ DV creation using create_dv. @@ -304,8 +306,7 @@ def get_downloaded_artifact(remote_name, local_name): with requests.get(url, headers=artifactory_header, verify=False, stream=True) as created_request: created_request.raise_for_status() with open(local_name, "wb") as file_downloaded: - for chunk in created_request.iter_content(chunk_size=8192): - file_downloaded.write(chunk) + file_downloaded.writelines(created_request.iter_content(chunk_size=8192)) try: assert os.path.isfile(local_name) return True @@ -692,7 +693,7 @@ def run_command_on_cirros_vm_and_check_output(vm, command, expected_result): vm_console.expect(expected_result, timeout=20) -def assert_disk_serial(vm, command=shlex.split("sudo ls /dev/disk/by-id")): +def assert_disk_serial(vm, command=_DEFAULT_DISK_SERIAL_COMMAND): assert ( HOTPLUG_DISK_SERIAL in run_ssh_commands(host=vm.ssh_exec, commands=command, wait_timeout=TIMEOUT_2MIN, sleep=TIMEOUT_5SEC)[0] diff --git a/utilities/unittests/test_data_collector.py b/utilities/unittests/test_data_collector.py index 6bedf87a44..58ffa727ca 100644 --- a/utilities/unittests/test_data_collector.py +++ b/utilities/unittests/test_data_collector.py @@ -277,7 +277,7 @@ def test_write_to_file_custom_mode(self, mock_file_open, mock_makedirs): mock_file_open.assert_called_once_with("/test/dir/test.txt", "a") @patch("os.makedirs") - @patch("builtins.open", side_effect=IOError("Permission denied")) + @patch("builtins.open", side_effect=OSError("Permission denied")) @patch("utilities.data_collector.LOGGER") def test_write_to_file_exception_handling(self, mock_logger, mock_file_open, mock_makedirs): """Test write_to_file handles exceptions gracefully""" diff --git a/utilities/unittests/test_database.py b/utilities/unittests/test_database.py index 02b2f52b71..40024a5664 100644 --- a/utilities/unittests/test_database.py +++ b/utilities/unittests/test_database.py @@ -9,7 +9,7 @@ # Add utilities to Python path for imports sys.path.insert(0, str(Path(__file__).parent.parent)) -from database import CNV_TEST_DB, Base, CnvTestTable, Database # noqa: E402 +from database import CNV_TEST_DB, Base, CnvTestTable, Database class TestCnvTestTable: diff --git a/utilities/unittests/test_guest_support.py b/utilities/unittests/test_guest_support.py index 1108fe13d8..f768e7d001 100644 --- a/utilities/unittests/test_guest_support.py +++ b/utilities/unittests/test_guest_support.py @@ -17,7 +17,7 @@ utilities.virt = mock_virt # Import after setting up mocks to avoid circular dependency -from utilities.guest_support import ( # noqa: E402 +from utilities.guest_support import ( assert_windows_efi, check_vm_xml_hyperv, check_windows_vm_hvinfo, diff --git a/utilities/unittests/test_hco.py b/utilities/unittests/test_hco.py index dd1a2aa3ba..b764fdecba 100644 --- a/utilities/unittests/test_hco.py +++ b/utilities/unittests/test_hco.py @@ -39,7 +39,7 @@ del sys.modules["utilities.hco"] # Import after setting up mocks to avoid circular dependency -from utilities.hco import ( # noqa: E402 +from utilities.hco import ( CDI, DEFAULT_HCO_PROGRESSING_CONDITIONS, HCO_JSONPATCH_ANNOTATION_COMPONENT_DICT, diff --git a/utilities/unittests/test_oadp.py b/utilities/unittests/test_oadp.py index 48525fba6d..ef74484150 100644 --- a/utilities/unittests/test_oadp.py +++ b/utilities/unittests/test_oadp.py @@ -2,7 +2,6 @@ """Unit tests for oadp module""" -# flake8: noqa: E402 import sys from re import escape from shlex import quote diff --git a/utilities/unittests/test_operator.py b/utilities/unittests/test_operator.py index 1e354556ac..8a1ba82ebd 100644 --- a/utilities/unittests/test_operator.py +++ b/utilities/unittests/test_operator.py @@ -9,7 +9,7 @@ from timeout_sampler import TimeoutExpiredError # Import after setting up mocks to avoid circular dependency -from utilities.operator import ( # noqa: E402 +from utilities.operator import ( approve_install_plan, cluster_with_icsp, collect_mcp_data_on_update_timeout, diff --git a/utilities/unittests/test_pytest_matrix_utils.py b/utilities/unittests/test_pytest_matrix_utils.py index 772ade5f47..9f806abfeb 100644 --- a/utilities/unittests/test_pytest_matrix_utils.py +++ b/utilities/unittests/test_pytest_matrix_utils.py @@ -7,7 +7,7 @@ import pytest -from utilities.pytest_matrix_utils import ( # noqa: E402 +from utilities.pytest_matrix_utils import ( hpp_matrix, immediate_matrix, online_resize_matrix, diff --git a/utilities/unittests/test_ssp.py b/utilities/unittests/test_ssp.py index 296f89d519..08fd5abed4 100644 --- a/utilities/unittests/test_ssp.py +++ b/utilities/unittests/test_ssp.py @@ -28,7 +28,7 @@ del sys.modules["utilities.ssp"] # Import after setting up mocks to avoid circular dependency -from utilities.ssp import ( # noqa: E402 +from utilities.ssp import ( cluster_instance_type_for_hot_plug, create_custom_template_from_url, get_cim_instance_json,