From 3c4c569ec19bfad2837f525373beea266457939d Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Tue, 27 Jan 2026 10:17:00 +0000 Subject: [PATCH 1/3] Make dlt/datarouter start only if configured. --- itf/core/base/target/hw_target.py | 22 ++++++++++++---------- itf/core/base/target/qemu_target.py | 24 ++++++++++++++---------- itf/core/base/target/qvp_target.py | 22 ++++++++++++---------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/itf/core/base/target/hw_target.py b/itf/core/base/target/hw_target.py index 0940b3c..446d891 100644 --- a/itf/core/base/target/hw_target.py +++ b/itf/core/base/target/hw_target.py @@ -29,13 +29,15 @@ def hw_target(target_config, test_config): diagnostic_ip = None with nullcontext(): - with DltReceive( - target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, - ): - target = Target(test_config.ecu, test_config.os, diagnostic_ip) - target.register_processors() - yield target - target.teardown() + dlt = None + if target_config.data_router_config: + dlt = DltReceive( + target_ip=target_config.ip_address, + protocol=Protocol.UDP, + data_router_config=target_config.data_router_config, + binary_path=test_config.dlt_receive_path, + ) + target = Target(test_config.ecu, test_config.os, diagnostic_ip) + target.register_processors() + yield target + target.teardown() diff --git a/itf/core/base/target/qemu_target.py b/itf/core/base/target/qemu_target.py index e598473..78d8a74 100644 --- a/itf/core/base/target/qemu_target.py +++ b/itf/core/base/target/qemu_target.py @@ -41,13 +41,17 @@ def qemu_target(target_config, test_config): with Qemu( target_config.qemu_image_path, None, target_config.qemu_ram_size, target_config.qemu_num_cores ) if target_config.qemu_image_path else nullcontext() as qemu_process: - with DltReceive( - target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, - ): - target = TargetQemu(test_config.ecu, test_config.os) - target.register_processors(qemu_process) - yield target - target.teardown() + + dlt = None + if target_config.data_router_config: + dlt = DltReceive( + target_ip=target_config.ip_address, + protocol=Protocol.UDP, + data_router_config=target_config.data_router_config, + binary_path=test_config.dlt_receive_path, + ) + + target = TargetQemu(test_config.ecu, test_config.os) + target.register_processors(qemu_process) + yield target + target.teardown() diff --git a/itf/core/base/target/qvp_target.py b/itf/core/base/target/qvp_target.py index f7d9358..0a57002 100644 --- a/itf/core/base/target/qvp_target.py +++ b/itf/core/base/target/qvp_target.py @@ -42,13 +42,15 @@ def qvp_target(target_config, test_config): Currently, only ITF tests against an already running QQVP instance is supported. """ with nullcontext() as qvp_process: - with DltReceive( - target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, - ): - target = TargetQvp(test_config.ecu, test_config.os) - target.register_processors(qvp_process) - yield target - target.teardown() + dlt = None + if target_config.data_router_config: + dlt = DltReceive( + target_ip=target_config.ip_address, + protocol=Protocol.UDP, + data_router_config=target_config.data_router_config, + binary_path=test_config.dlt_receive_path, + ) + target = TargetQvp(test_config.ecu, test_config.os) + target.register_processors(qvp_process) + yield target + target.teardown() From 3a1e6795aa09238ccb1a7d9ac2995820aaf8660b Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Tue, 27 Jan 2026 11:04:14 +0000 Subject: [PATCH 2/3] Adding host_ip config field --- itf/core/base/target/config/base_processor.py | 13 ++-- itf/core/base/target/config/config.py | 2 + .../target/config/performance_processor.py | 29 +++++++-- itf/core/base/target/qemu_target.py | 10 +-- itf/core/qemu/qemu.py | 65 +++++++++---------- itf/core/qemu/qemu_process.py | 20 +++--- 6 files changed, 75 insertions(+), 64 deletions(-) diff --git a/itf/core/base/target/config/base_processor.py b/itf/core/base/target/config/base_processor.py index c8677a8..e4d80ae 100644 --- a/itf/core/base/target/config/base_processor.py +++ b/itf/core/base/target/config/base_processor.py @@ -11,19 +11,20 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* import json +from typing import Optional class BaseProcessor: def __init__( self, name: str, - ip_address: str = None, + ip_address: Optional[str] = None, ssh_port: int = 22, - diagnostic_ip_address: str = None, - diagnostic_address: int = None, - serial_device: str = None, + diagnostic_ip_address: Optional[str] = None, + diagnostic_address: Optional[int] = None, + serial_device: Optional[str] = None, use_doip: bool = False, - params: dict = None, + params: Optional[dict] = None, ): """Initialize the BaseProcessor class. @@ -48,7 +49,7 @@ def __init__( self.__dict__.update(**params) @property - def name(self): + def name(self) -> str: return self.__name @property diff --git a/itf/core/base/target/config/config.py b/itf/core/base/target/config/config.py index 39fef5a..2c473e5 100644 --- a/itf/core/base/target/config/config.py +++ b/itf/core/base/target/config/config.py @@ -44,8 +44,10 @@ def load_configuration(config_file: str): ip_address=perf_config["ip_address"], ssh_port=perf_config["ssh_port"], ext_ip_address=perf_config["ext_ip_address"], + host_ip = perf_config.get("host_ip", None), diagnostic_ip_address=perf_config["diagnostic_ip_address"], diagnostic_address=int(perf_config["diagnostic_address"], 16), + diagnostic_host_ip = perf_config.get("diagnostic_host_ip", None), serial_device=perf_config["serial_device"], network_interfaces=perf_config["network_interfaces"], ecu_name=perf_config["ecu_name"], diff --git a/itf/core/base/target/config/performance_processor.py b/itf/core/base/target/config/performance_processor.py index 2b307ac..5e5d18d 100644 --- a/itf/core/base/target/config/performance_processor.py +++ b/itf/core/base/target/config/performance_processor.py @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* from itf.core.base.target.config.base_processor import BaseProcessor +from typing import Optional class PerformanceProcessor(BaseProcessor): @@ -18,18 +19,20 @@ class PerformanceProcessor(BaseProcessor): def __init__( self, name: str, - ip_address: str = None, + ip_address: Optional[str] = None, ssh_port: int = 22, - ext_ip_address: str = None, - diagnostic_ip_address: str = None, + ext_ip_address: Optional[str] = None, + host_ip: Optional[str] = None, + diagnostic_ip_address: Optional[str] = None, diagnostic_address: int = 0, - serial_device: str = None, + diagnostic_host_ip: Optional[str] = None, + serial_device: Optional[str] = None, network_interfaces: list = [], - ecu_name: str = None, - data_router_config: dict = None, + ecu_name: Optional[str] = None, + data_router_config: Optional[dict] = None, qemu_num_cores: int = 2, qemu_ram_size: str = "1G", - params: dict = None, + params: Optional[dict] = None, ): """Initialize the PerformanceProcessor class. @@ -37,8 +40,10 @@ def __init__( :param str ip_address: The IP address of the processor. :param int ssh_port: The SSH port for the processor. :param str ext_ip_address: The external IP address of the processor. + :param str host_ip: The ip of the host, used to find the correct network device. :param str diagnostic_ip_address: The internal IP address for diagnostics. :param int diagnostic_address: The diagnostic address of the processor. + :param str host_diagnosticip: The ip of the host for the diagnostic interface, used to find the correct network device. :param str serial_device: The serial device for the processor. :param list network_interfaces: The network interfaces for the processor. :param str ecu_name: The ECU name for the processor. @@ -58,6 +63,8 @@ def __init__( params=params, ) self.__ext_ip_address = ext_ip_address + self.__host_ip = host_ip + self.__diagnostic_host_ip = diagnostic_host_ip self.__network_interfaces = network_interfaces self.__ecu_name = ecu_name self.__data_router_config = data_router_config @@ -69,6 +76,14 @@ def __init__( def ext_ip_address(self): return self.__ext_ip_address + @property + def host_ip(self) -> Optional[str]: + return self.__host_ip + + @property + def host_diagnostic_ip(self) -> Optional[str]: + return self.__diagnostic_host_ip + @property def ecu_name(self): return self.__ecu_name diff --git a/itf/core/base/target/qemu_target.py b/itf/core/base/target/qemu_target.py index 78d8a74..38057df 100644 --- a/itf/core/base/target/qemu_target.py +++ b/itf/core/base/target/qemu_target.py @@ -17,7 +17,7 @@ from itf.core.base.target.config.ecu import Ecu from itf.core.base.target.processors.qemu_processor import TargetProcessorQemu from itf.core.dlt.dlt_receive import DltReceive, Protocol -from itf.core.qemu.qemu_process import QemuProcess as Qemu +from itf.core.qemu.qemu_process import QemuProcess class TargetQemu(Target): @@ -38,12 +38,11 @@ def qemu_target(target_config, test_config): Currently, only ITF tests against an already running Qemu instance is supported. """ - with Qemu( - target_config.qemu_image_path, None, target_config.qemu_ram_size, target_config.qemu_num_cores - ) if target_config.qemu_image_path else nullcontext() as qemu_process: + with QemuProcess(target_config) if target_config else nullcontext() as qemu_process: dlt = None - if target_config.data_router_config: + + if target_config and target_config.data_router_config: dlt = DltReceive( target_ip=target_config.ip_address, protocol=Protocol.UDP, @@ -53,5 +52,6 @@ def qemu_target(target_config, test_config): target = TargetQemu(test_config.ecu, test_config.os) target.register_processors(qemu_process) + yield target target.teardown() diff --git a/itf/core/qemu/qemu.py b/itf/core/qemu/qemu.py index 6caae39..e7a88db 100644 --- a/itf/core/qemu/qemu.py +++ b/itf/core/qemu/qemu.py @@ -16,6 +16,9 @@ import subprocess import netifaces import logging +from typing import Optional + +from itf.core.base.target.config.ecu import Ecu logger = logging.getLogger(__name__) @@ -25,42 +28,26 @@ class Qemu: This class shall be used to start an qemu instance based on pre-configured Qemu parameters. """ - def __init__( - self, - path_to_image, - path_to_bootloader=None, - ram="1G", - cores="2", - cpu="Cascadelake-Server-v5", - host_first_network_device_ip_address="160.48.199.77", - host_second_network_device_ip_address="192.168.1.99", - ): + def __init__(self, config: Ecu): """Create a QEMU instance with the specified parameters. - - :param str path_to_image: The path to the Qemu image file. - :param str path_to_bootloader: The path to the Qemu bootloader file. - :param str ram: The amount of RAM to allocate to the QEMU instance. - :param str cores: The number of CPU cores to allocate to the QEMU instance. - :param str cpu: The CPU model to emulate. - Default is Cascadelake-Server-v5 used to emulate modern Intel CPU features. - For older Ubuntu versions change that to host in case of errors. - :param str host_first_network_device_ip_address: The IP address of the first network device on the host. - :param str host_second_network_device_ip_address: The IP address of the second network device on the host. """ + + self.__config = config self.__qemu_path = "/usr/bin/qemu-system-x86_64" self.__first_network_device_name = "unknown" self.__second_network_device_name = "unknown" self.__first_network_adapter_mac = "52:54:11:22:33:01" self.__second_network_adapter_mac = "52:54:11:22:33:02" - self.__first_network_device_ip_address = host_first_network_device_ip_address - self.__second_network_device_ip_address = host_second_network_device_ip_address - self.__path_to_image = path_to_image - self.__path_to_bootloader = path_to_bootloader - self.__ram = ram - self.__cores = cores - self.__cpu = cpu + self.__first_network_device_ip_address = config.host_ip if config.host_ip else "160.48.199.77" + self.__second_network_device_ip_address: Optional[str] = config.host_diagnostic_ip + + self.__path_to_image = config.qemu_image_path + self.__path_to_bootloader = None + self.__ram = config.qemu_ram_size + self.__cores = config.qemu_num_cores + self.__cpu = "Cascadelake-Server-v5" self.__check_qemu_is_installed() self.__find_available_kvm_support() @@ -69,13 +56,13 @@ def __init__( self._subprocess = None - def __enter__(self): + def __enter__(self) -> subprocess.Popen: return self.start() def __exit__(self, exc_type, exc_val, exc_tb): self.stop() - def start(self, subprocess_params=None): + def start(self, subprocess_params=None) -> subprocess.Popen: logger.debug(self.__build_qemu_command()) subprocess_args = {"args": shlex.split(self.__build_qemu_command())} if subprocess_params: @@ -84,6 +71,9 @@ def start(self, subprocess_params=None): return self._subprocess def stop(self): + if self._subprocess is None: + raise RuntimeError(f"{self.__class__.__name__}.stop() called before start()") + if self._subprocess.poll() is None: self._subprocess.terminate() self._subprocess.wait(2) @@ -130,11 +120,13 @@ def __find_tap_devices(self): except KeyError: pass - if "unknown" in (self.__first_network_device_name, self.__second_network_device_name): + if (self.__first_network_device_name == "unknown" and self.__second_network_device_name == "unknown"): logger.fatal("Could not find correct tap devices. Please setup network for Qemu first!") + logger.fatal(f"Tried to find devices with IPs: {self.__first_network_device_ip_address} {self.__second_network_device_ip_address}") sys.exit(-1) + logger.info(f"Found devices {self.__first_network_device_ip_address} {self.__second_network_device_ip_address}") - def __build_qemu_command(self): + def __build_qemu_command(self) -> str: return ( f"{self.__qemu_path}" " --enable-kvm" # Use hardware virtualization for better performance @@ -145,18 +137,21 @@ def __build_qemu_command(self): " -nographic" # Disable graphical display (console-only) " -serial mon:stdio" # Redirect serial output to console " -object rng-random,filename=/dev/urandom,id=rng0" # Provide hardware random number generation - f" {self.__first_network_adapter()}" - f" {self.__second_network_adapter()}" + f"{self.__first_network_adapter()}" + f"{self.__second_network_adapter()}" " -device virtio-rng-pci,rng=rng0" # Provide hardware random number generation ) - def __first_network_adapter(self): + def __first_network_adapter(self) -> str: return ( f" -netdev tap,id=t1,ifname={self.__first_network_device_name},script=no,downscript=no" f" -device virtio-net-pci,netdev=t1,id=nic1,mac={self.__first_network_adapter_mac},guest_csum=off" ) - def __second_network_adapter(self): + def __second_network_adapter(self) -> str: + if not self.__config.host_diagnostic_ip: + return "" + return ( f" -netdev tap,id=t2,ifname={self.__second_network_device_name},script=no,downscript=no" f" -device virtio-net-pci,netdev=t2,id=nic2,mac={self.__second_network_adapter_mac},guest_csum=off" diff --git a/itf/core/qemu/qemu_process.py b/itf/core/qemu/qemu_process.py index 62d27a2..0a99172 100644 --- a/itf/core/qemu/qemu_process.py +++ b/itf/core/qemu/qemu_process.py @@ -15,28 +15,26 @@ from itf.core.utils.process.console import PipeConsole from itf.core.qemu.qemu import Qemu +from itf.core.base.target.config.ecu import Ecu logger = logging.getLogger(__name__) class QemuProcess: - def __init__(self, path_to_qemu_image, path_to_bootloader, available_ram, available_cores): - self._path_to_qemu_image = path_to_qemu_image - self._path_to_bootloader = path_to_bootloader - self._available_ram = available_ram - self._available_cores = available_cores - self._qemu = Qemu( - self._path_to_qemu_image, self._path_to_bootloader, self._available_ram, self._available_cores - ) + + def __init__(self, config: Ecu): + + self._path_to_qemu_image = config.qemu_image_path + self._qemu = Qemu(config) self._console = None - def __enter__(self): + def __enter__(self) -> "QemuProcess": return self.start() def __exit__(self, exc_type, exc_val, exc_tb): self.stop() - def start(self): + def start(self) -> "QemuProcess": logger.info("Starting Qemu...") logger.info(f"Using QEMU image: {self._path_to_qemu_image}") subprocess_params = { @@ -58,5 +56,5 @@ def restart(self): self.start() @property - def console(self): + def console(self) -> PipeConsole: return self._console From a10f5ea0ea075a339478a340160d34c83431740f Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Fri, 30 Jan 2026 17:03:18 +0000 Subject: [PATCH 3/3] Adding back context manager init for targets --- itf/core/base/target/hw_target.py | 21 ++++++++++++--------- itf/core/base/target/qemu_target.py | 24 ++++++++++++------------ itf/core/base/target/qvp_target.py | 11 +++++++---- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/itf/core/base/target/hw_target.py b/itf/core/base/target/hw_target.py index 446d891..4efbcb6 100644 --- a/itf/core/base/target/hw_target.py +++ b/itf/core/base/target/hw_target.py @@ -30,14 +30,17 @@ def hw_target(target_config, test_config): with nullcontext(): dlt = None + if target_config.data_router_config: dlt = DltReceive( - target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, - ) - target = Target(test_config.ecu, test_config.os, diagnostic_ip) - target.register_processors() - yield target - target.teardown() + target_ip=target_config.ip_address, + protocol=Protocol.UDP, + data_router_config=target_config.data_router_config, + binary_path=test_config.dlt_receive_path, + ) + + with dlt if dlt else nullcontext(): + target = Target(test_config.ecu, test_config.os, diagnostic_ip) + target.register_processors() + yield target + target.teardown() diff --git a/itf/core/base/target/qemu_target.py b/itf/core/base/target/qemu_target.py index 38057df..7153445 100644 --- a/itf/core/base/target/qemu_target.py +++ b/itf/core/base/target/qemu_target.py @@ -39,19 +39,19 @@ def qemu_target(target_config, test_config): Currently, only ITF tests against an already running Qemu instance is supported. """ with QemuProcess(target_config) if target_config else nullcontext() as qemu_process: - dlt = None - if target_config and target_config.data_router_config: + if target_config.data_router_config: dlt = DltReceive( - target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, - ) - - target = TargetQemu(test_config.ecu, test_config.os) - target.register_processors(qemu_process) + target_ip=target_config.ip_address, + protocol=Protocol.UDP, + data_router_config=target_config.data_router_config, + binary_path=test_config.dlt_receive_path, + ) + + with dlt if dlt else nullcontext(): + target = TargetQemu(test_config.ecu, test_config.os) + target.register_processors(qemu_process) + yield target + target.teardown() - yield target - target.teardown() diff --git a/itf/core/base/target/qvp_target.py b/itf/core/base/target/qvp_target.py index 0a57002..e243a3c 100644 --- a/itf/core/base/target/qvp_target.py +++ b/itf/core/base/target/qvp_target.py @@ -43,6 +43,7 @@ def qvp_target(target_config, test_config): """ with nullcontext() as qvp_process: dlt = None + if target_config.data_router_config: dlt = DltReceive( target_ip=target_config.ip_address, @@ -50,7 +51,9 @@ def qvp_target(target_config, test_config): data_router_config=target_config.data_router_config, binary_path=test_config.dlt_receive_path, ) - target = TargetQvp(test_config.ecu, test_config.os) - target.register_processors(qvp_process) - yield target - target.teardown() + + with dlt if dlt else nullcontext(): + target = TargetQvp(test_config.ecu, test_config.os) + target.register_processors(qvp_process) + yield target + target.teardown()