Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions modules/test/protocol/bin/start_test_module
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/bin/bash

# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Setup and start the connection test module

# Define where the python source files are located
PYTHON_SRC_DIR=/testrun/python/src

# Fetch module name
MODULE_NAME=$1

# Default interface should be veth0 for all containers
DEFAULT_IFACE=veth0

# Allow a user to define an interface by passing it into this script
DEFINED_IFACE=$2

# Select which interace to use
if [[ -z $DEFINED_IFACE || "$DEFINED_IFACE" == "null" ]]
then
echo "No interface defined, defaulting to veth0"
INTF=$DEFAULT_IFACE
else
INTF=$DEFINED_IFACE
fi

# Create and set permissions on the log files
LOG_FILE=/runtime/output/$MODULE_NAME.log
RESULT_FILE=/runtime/output/$MODULE_NAME-result.json
touch $LOG_FILE
touch $RESULT_FILE
chown $HOST_USER $LOG_FILE
chown $HOST_USER $RESULT_FILE

# Run the python script that will execute the tests for this module
# -u flag allows python print statements
# to be logged by docker by running unbuffered
python3 -u $PYTHON_SRC_DIR/run.py "-m $MODULE_NAME"

echo Module has finished
55 changes: 55 additions & 0 deletions modules/test/protocol/conf/module_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"config": {
"meta": {
"name": "protocol",
"display_name": "Protocol",
"description": "Protocol tests"
},
"network": true,
"docker": {
"depends_on": "base",
"enable_container": true,
"timeout": 300
},
"tests":[
{
"name": "protocol.valid_bacnet",
"description": "Can valid BACnet traffic be seen",
"expected_behavior": "BACnet traffic can be seen on the network and packets are valid and not malformed",
"required_result": "Required"
},
{
"name": "protocol.valid_modbus",
"description": "Can valid Modbus traffic be seen",
"expected_behavior": "Any Modbus functionality works as expected and valid modbus traffic can be observed",
"required_result": "Required",
"config":{
"port": 502,
"device_id": 1,
"registers":{
"holding":{
"enabled": true,
"address_start": 0,
"count": 5
},
"input":{
"enabled": true,
"address_start": 0,
"count": 5
},
"coil":{
"enabled": true,
"address_start": 0,
"count": 1
},
"discrete":{
"enabled": true,
"address_start": 0,
"count": 1
}
}
}
}
]
}
}
34 changes: 34 additions & 0 deletions modules/test/protocol/protocol.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Image name: test-run/protocol-test
FROM test-run/base-test:latest

ARG MODULE_NAME=protocol
ARG MODULE_DIR=modules/test/$MODULE_NAME

#Load the requirements file
COPY $MODULE_DIR/python/requirements.txt /testrun/python

#Install all python requirements for the module
RUN pip3 install -r /testrun/python/requirements.txt

# Copy over all configuration files
COPY $MODULE_DIR/conf /testrun/conf

# Copy over all binary files
COPY $MODULE_DIR/bin /testrun/bin

# Copy over all python files
COPY $MODULE_DIR/python /testrun/python
7 changes: 7 additions & 0 deletions modules/test/protocol/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Required for BACnet protocol tests
netifaces
BAC0
pytz

# Required for Modbus protocol tests
pymodbus
71 changes: 71 additions & 0 deletions modules/test/protocol/python/src/protocol_bacnet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module run all the BACnet related methods for testing"""

import BAC0
import logging

LOGGER = None
BAC0_LOG = '/root/.BAC0/BAC0.log'

class BACnet():
"""BACnet Test module"""

def __init__(self, log):
# Set the log
global LOGGER
LOGGER = log

# Setup the BAC0 Log
BAC0.log_level(log_file=logging.DEBUG, stdout=logging.INFO, stderr=logging.CRITICAL)

self.devices = []

def discover(self, local_ip=None):
LOGGER.info("Performing BACnet discovery...")
bacnet = BAC0.lite(local_ip)
LOGGER.info("Local BACnet object: " + str(bacnet))
try:
bacnet.discover(global_broadcast=True)
except Exception as e:
LOGGER.error(e)
LOGGER.info("BACnet discovery complete")
with open(BAC0_LOG,'r',encoding='utf-8') as f:
bac0_log = f.read()
LOGGER.info("BAC0 Log:\n" + bac0_log)
self.devices = bacnet.devices

# Check if the device being tested is in the discovered devices list
def validate_device(self, local_ip, device_ip):
result = None
LOGGER.info("Validating BACnet device: " + device_ip)
self.discover(local_ip + '/24')
LOGGER.info("BACnet Devices Found: " + str(len(self.devices)))
if len(self.devices) > 0:
# Load a fail result initially and pass only
# if we can validate it's the right device responding
result = False, (
f'Could not confirm discovered BACnet device is the ' +
'same as device being tested')
for device in self.devices:
name, vendor, address, device_id = device
LOGGER.info("Checking Device: " + str(device))
if device_ip in address:
result = True, 'Device IP matches discovered device'
break
else:
result = None, 'BACnet discovery could not resolve any devices'
if result is not None:
LOGGER.info(result[1])
return result
Loading