From e6c4603d26f6a5e85725cd9abbda1314734e9a34 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 11 Jun 2026 21:46:46 +0200 Subject: [PATCH 01/10] bacnet protocol version async --- .../protocol/python/src/protocol_bacnet.py | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/modules/test/protocol/python/src/protocol_bacnet.py b/modules/test/protocol/python/src/protocol_bacnet.py index 0981b7c89..aaa41b224 100644 --- a/modules/test/protocol/python/src/protocol_bacnet.py +++ b/modules/test/protocol/python/src/protocol_bacnet.py @@ -112,19 +112,35 @@ def validate_device(self): LOGGER.error('Error occurred when validating device', exc_info=True) return result, description - - def validate_protocol_version(self, device: BACnetDevice) -> tuple[bool, str]: + async def validate_protocol_version( + self, + device: BACnetDevice + ) -> tuple[bool, str]: LOGGER.info( f'Resolving protocol version for BACnet device: {device.device_id}' ) try: - version = self.bacnet.read( - f'{device.ip} device {device.device_id} protocolVersion') - revision = self.bacnet.read( - f'{device.ip} device {device.device_id} protocolRevision') - protocol_version = f'{version}.{revision}' - result = True - result_description = f'Device uses BACnet version {protocol_version}' + dut = await BAC0.device( + device.ip, + device.device_id, + self.bacnet + ) + version = await dut.read_property( + ('device', device.device_id, 'protocolVersion') + ) + revision = await dut.read_property( + ('device', device.device_id, 'protocolRevision') + ) + if version is None or revision is None: + result = False + result_description = ( + f'Failed to resolve protocol version: version={version}, ' + f'revision={revision}') + LOGGER.error(result_description) + else: + protocol_version = f'{version}.{revision}' + result = True + result_description = f'Device uses BACnet version {protocol_version}' except (UnknownPropertyError, ReadPropertyException, NoResponseFromController, DeviceNotConnected) as e: result = False From 348afc1828c24b96ffb7c496cb0d74126b4c5a54 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 11 Jun 2026 21:46:59 +0200 Subject: [PATCH 02/10] handle bacnet async calls --- .../protocol/python/src/protocol_module.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/test/protocol/python/src/protocol_module.py b/modules/test/protocol/python/src/protocol_module.py index 646f912d1..95a43a7c9 100644 --- a/modules/test/protocol/python/src/protocol_module.py +++ b/modules/test/protocol/python/src/protocol_module.py @@ -27,11 +27,17 @@ class ProtocolModule(TestModule): def __init__(self, module): self._supports_bacnet = False + self._bacnet_loop = None super().__init__(module_name=module, log_name=LOG_NAME) global LOGGER LOGGER = self._get_logger() self._bacnet = BACnet(log=LOGGER, device_hw_addr=self._device_mac) + def _get_bacnet_loop(self): + if self._bacnet_loop is None: + self._bacnet_loop = asyncio.new_event_loop() + return self._bacnet_loop + def _protocol_valid_bacnet(self): LOGGER.info('Running protocol.valid_bacnet') result = None @@ -47,11 +53,8 @@ def _protocol_valid_bacnet(self): local_address = self.get_local_ip(interface_name) if local_address: local_address += '/24' - try: - loop = asyncio.get_running_loop() - loop.run_until_complete(self._bacnet.discover(local_address)) - except RuntimeError: - asyncio.run(self._bacnet.discover(local_address)) + self._get_bacnet_loop().run_until_complete( + self._bacnet.discover(local_address)) result = self._bacnet.validate_device() if result[0]: self._supports_bacnet = True @@ -78,8 +81,11 @@ def _protocol_bacnet_version(self): if len(self._bacnet.devices) > 0: for device in self._bacnet.devices: LOGGER.debug(f'Checking BACnet version for device: {device}') + loop = self._get_bacnet_loop() result_status, result_description = \ - self._bacnet.validate_protocol_version(device) + loop.run_until_complete( + self._bacnet.validate_protocol_version(device) + ) break LOGGER.info(result_description) From 4bd7bca62dc701f7d956abb55a8fff21589a6964 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sat, 13 Jun 2026 20:27:44 +0200 Subject: [PATCH 03/10] update python version in base docker container --- modules/test/base/base.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/test/base/base.Dockerfile b/modules/test/base/base.Dockerfile index 6a13b42e0..bc0357d10 100644 --- a/modules/test/base/base.Dockerfile +++ b/modules/test/base/base.Dockerfile @@ -14,7 +14,7 @@ # Builder stage # Image name: testrun/base-test -FROM python:3.10-slim AS builder +FROM python:3.13-slim AS builder ARG MODULE_NAME=base ARG MODULE_DIR=modules/test/$MODULE_NAME @@ -65,7 +65,7 @@ COPY $MODULE_DIR/usr/local/etc/oui.txt /usr/local/etc/oui.txt RUN wget https://standards-oui.ieee.org/oui.txt -O /usr/local/etc/oui.txt || echo "Unable to update the MAC OUI database" # Operational stage -FROM python:3.10-slim +FROM python:3.13-slim # Install common software RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -yq net-tools iputils-ping tzdata tcpdump iproute2 jq dos2unix nmap wget procps --fix-missing From 3ec2301271744e221520fbbb0d0a25a41d8e1485 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sat, 13 Jun 2026 20:28:14 +0200 Subject: [PATCH 04/10] update protocol module --- modules/test/protocol/python/requirements.txt | 4 ++-- .../protocol/python/src/protocol_modbus.py | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/test/protocol/python/requirements.txt b/modules/test/protocol/python/requirements.txt index 4d9dc64b9..01f066d18 100644 --- a/modules/test/protocol/python/requirements.txt +++ b/modules/test/protocol/python/requirements.txt @@ -1,7 +1,7 @@ # Dependencies to user defined packages # Package dependencies should always be defined before the user defined # packages to prevent auto-upgrades of stable dependencies -bacpypes3==0.0.104 +bacpypes3==0.0.106 colorama==0.4.6 # User defined packages @@ -11,4 +11,4 @@ BAC0==2025.9.15 pytz==2024.2 # Required for Modbus protocol tests -pymodbus==3.7.4 +pymodbus==3.13.0 diff --git a/modules/test/protocol/python/src/protocol_modbus.py b/modules/test/protocol/python/src/protocol_modbus.py index a722f928e..83d32214e 100644 --- a/modules/test/protocol/python/src/protocol_modbus.py +++ b/modules/test/protocol/python/src/protocol_modbus.py @@ -129,8 +129,8 @@ def read_holding_registers(self, LOGGER.info(f'Reading holding registers: {address}:{count}') try: response = self.client.read_holding_registers(address, - count, - slave=device_id) + count=count, + device_id=device_id) if response.isError(): LOGGER.error(f'Failed to read holding registers: {address}:{count}') LOGGER.error('Read Response: ' + str(response)) @@ -149,9 +149,9 @@ def read_input_registers(self, registers = None LOGGER.info(f'Reading input registers: {address}:{count}') try: - response = self.client.read_input_registers(address, - count, - slave=device_id) + response = self.client.read_input_registers(address=address, + count=count, + device_id=device_id) if response.isError(): LOGGER.error(f'Failed to read input registers: {address}:{count}') LOGGER.error('Read Response: ' + str(response)) @@ -170,7 +170,7 @@ def read_coils(self, coils = None LOGGER.info(f'Reading coil registers: {address}:{count}') try: - response = self.client.read_coils(address, count, slave=device_id) + response = self.client.read_coils(address=address, count=count, device_id=device_id) if response.isError(): LOGGER.error(f'Failed to read coil registers: {address}:{count}') LOGGER.error('Read Response: ' + str(response)) @@ -189,9 +189,9 @@ def read_discrete_inputs(self, inputs = None LOGGER.info(f'Reading discrete inputs: {address}:{count}') try: - response = self.client.read_discrete_inputs(address, - count, - slave=device_id) + response = self.client.read_discrete_inputs(address=address, + count=count, + device_id=device_id) if response.isError(): LOGGER.error(f'Failed to read discrete inputs: {address}:{count}') LOGGER.error('Read Response: ' + str(response)) From 2f76c8594f0c796a32799f4c4581d99f081a22ac Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sat, 13 Jun 2026 20:28:31 +0200 Subject: [PATCH 05/10] update create sectificate script --- test_vm/create_certificate.sh | 73 +++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/test_vm/create_certificate.sh b/test_vm/create_certificate.sh index 229c10a7b..9a61e8f0a 100755 --- a/test_vm/create_certificate.sh +++ b/test_vm/create_certificate.sh @@ -6,35 +6,42 @@ if [ -z "$1" ]; then exit 1 fi +if [ "$EUID" -eq 0 ]; then + echo "Please run this script as a normal user, not with sudo." + echo "The script uses Vagrant SSH and only escalates the necessary host operations when required." + exit 1 +fi + # Save the initial working directory WORKDIR="$(pwd)" -# Check and install sshpass if not present -if ! command -v sshpass &> /dev/null; then - echo "sshpass not found. Installing..." - if [ -x "$(command -v apt)" ]; then - sudo apt update - sudo apt install -y sshpass - elif [ -x "$(command -v yum)" ]; then - sudo yum install -y epel-release - sudo yum install -y sshpass - elif [ -x "$(command -v brew)" ]; then - brew install hudochenkov/sshpass/sshpass - else - echo "Please install sshpass manually." - exit 1 - fi +if ! command -v vagrant >/dev/null 2>&1; then + echo "Error: vagrant command not found. Please install Vagrant and run this script from the test_vm folder." + exit 1 fi -VM_USER=vagrant -VM_PASS=vagrant VM_IP="$1" CA_DIR=~/myCA -SSHPASS="sshpass -p $VM_PASS" +SSH_CONFIG_FILE="$(mktemp)" +trap 'rm -f "$SSH_CONFIG_FILE"' EXIT + +if ! vagrant ssh-config > "$SSH_CONFIG_FILE" 2>/dev/null; then + echo "Error: failed to generate Vagrant SSH config. Run this script in a valid Vagrant VM directory." + exit 1 +fi + +SSH_HOST="$(grep -E '^Host ' "$SSH_CONFIG_FILE" | awk '{print $2}' | head -n1)" +if [ -z "$SSH_HOST" ]; then + echo "Error: could not determine Vagrant SSH host from ssh-config." + exit 1 +fi + +SSH_CMD=(ssh -F "$SSH_CONFIG_FILE" -o StrictHostKeyChecking=no) +SCP_CMD=(scp -F "$SSH_CONFIG_FILE" -o StrictHostKeyChecking=no) -# 1. Generate key and CSR on VM via ssh -$SSHPASS ssh -o StrictHostKeyChecking=no ${VM_USER}@${VM_IP} "cat > /home/vagrant/openssl_ip.cnf" < /home/vagrant/openssl_ip.cnf" < /etc/nginx/sites-available/default < /etc/nginx/sites-available/default < Date: Sat, 13 Jun 2026 20:28:50 +0200 Subject: [PATCH 06/10] update connection module --- modules/test/conn/python/requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/test/conn/python/requirements.txt b/modules/test/conn/python/requirements.txt index 0eaca2e07..25c6c5d1c 100644 --- a/modules/test/conn/python/requirements.txt +++ b/modules/test/conn/python/requirements.txt @@ -2,11 +2,11 @@ # Package dependencies should always be defined before the user defined # packages to prevent auto-upgrades of stable dependencies cffi==2.0.0 -cryptography==46.0.6 -pycparser==2.22 -six==1.16.0 +cryptography==48.0.1 +pycparser==3.0 +six==1.17.0 # User defined packages -pyOpenSSL==25.3.0 +pyOpenSSL==26.2.0 scapy==2.7.0 python-dateutil==2.9.0.post0 From bb5c28f47e35419e3577e1eb0a784f6339d7ff2d Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sat, 13 Jun 2026 20:29:02 +0200 Subject: [PATCH 07/10] update ntp module --- modules/test/ntp/python/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/test/ntp/python/requirements.txt b/modules/test/ntp/python/requirements.txt index dc5c39f2f..c9b5d21f4 100644 --- a/modules/test/ntp/python/requirements.txt +++ b/modules/test/ntp/python/requirements.txt @@ -5,5 +5,5 @@ # User defined packages scapy==2.7.0 pyshark==0.6 -aiohttp==3.13.5 +aiohttp==3.14.1 ntplib==0.4.0 \ No newline at end of file From 848c7e65e11a6af08a9865e8ed49c708129c29d0 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sat, 13 Jun 2026 20:29:18 +0200 Subject: [PATCH 08/10] update service module --- modules/test/services/python/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/test/services/python/requirements.txt b/modules/test/services/python/requirements.txt index 02acc1e19..8e523682f 100644 --- a/modules/test/services/python/requirements.txt +++ b/modules/test/services/python/requirements.txt @@ -3,4 +3,4 @@ # packages to prevent auto-upgrades of stable dependencies # User defined packages -xmltodict==0.14.2 +xmltodict==1.0.4 From 00b7ae36de1f892bef59143804e2dd4d088ad516 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sat, 13 Jun 2026 20:29:30 +0200 Subject: [PATCH 09/10] update tls module --- modules/test/tls/python/requirements.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/test/tls/python/requirements.txt b/modules/test/tls/python/requirements.txt index 9d0578d6e..d45d59d12 100644 --- a/modules/test/tls/python/requirements.txt +++ b/modules/test/tls/python/requirements.txt @@ -2,20 +2,20 @@ # Package dependencies should always be defined before the user defined # packages to prevent auto-upgrades of stable dependencies appdirs==1.4.4 -certifi==2024.8.30 +certifi==2026.5.20 cffi==2.0.0 -charset-normalizer==3.3.2 -idna==3.8 -packaging==24.1 -pycparser==2.22 +charset-normalizer==3.4.7 +idna==3.18 +packaging==26.2 +pycparser==3.0 pyshark==0.6 -termcolor==2.4.0 -urllib3==2.6.3 +termcolor==3.3.0 +urllib3==2.7.0 # User defined packages -cryptography==46.0.6 -pyOpenSSL==25.3.0 -lxml==5.1.0 # Requirement of pyshark but if upgraded automatically above 5.1 will cause a -requests==2.33.0 +cryptography==48.0.1 +pyOpenSSL==26.2.0 +lxml==6.1.1 +requests==2.34.2 python-nmap==0.7.1 From 406886b262adfa7873d420a88939482103331be5 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sun, 14 Jun 2026 19:58:24 +0200 Subject: [PATCH 10/10] pylint --- modules/test/protocol/python/src/protocol_modbus.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/test/protocol/python/src/protocol_modbus.py b/modules/test/protocol/python/src/protocol_modbus.py index 83d32214e..714bc79c2 100644 --- a/modules/test/protocol/python/src/protocol_modbus.py +++ b/modules/test/protocol/python/src/protocol_modbus.py @@ -170,7 +170,11 @@ def read_coils(self, coils = None LOGGER.info(f'Reading coil registers: {address}:{count}') try: - response = self.client.read_coils(address=address, count=count, device_id=device_id) + response = self.client.read_coils( + address=address, + count=count, + device_id=device_id + ) if response.isError(): LOGGER.error(f'Failed to read coil registers: {address}:{count}') LOGGER.error('Read Response: ' + str(response))