Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
fe10e73
Implement test orchestrator (#4)
jhughesoti Apr 25, 2023
6f3a7fe
Add issue report templates (#7)
jboddey Apr 26, 2023
e05c383
Discover devices on the network (#5)
jboddey Apr 26, 2023
823709e
Test run sync (#8)
jhughesoti Apr 28, 2023
ba6afc4
Quick refactor (#9)
jboddey Apr 28, 2023
c87a976
Fix duplicate sleep calls
jhughesoti Apr 28, 2023
34ce211
Add net orc (#11)
jhughesoti May 2, 2023
ceba453
Add the DNS test module (#12)
jhughesoti May 4, 2023
0837a9c
Add baseline and pylint tests (#25)
noursaidi May 16, 2023
4171e5f
Discover devices on the network (#22)
jboddey May 16, 2023
be829a3
Build dependencies first (#21)
jboddey May 16, 2023
84d9ff9
Port scan test module (#23)
jhughesoti May 17, 2023
07432ee
Fix device configs
jboddey May 17, 2023
7b27e23
Remove unecessary files
jhughesoti May 17, 2023
5ac8726
Cleanup duplicate properties
jhughesoti May 17, 2023
2c4efe8
Cleanup install script
jhughesoti May 17, 2023
25fd8a5
Formatting (#26)
jhughesoti May 22, 2023
41aaaf7
Test results (#27)
jhughesoti May 23, 2023
ea60b41
Test results (#28)
jhughesoti May 25, 2023
b6a6cdc
Fix pylint test and skip internet tests so CI passes (#29)
noursaidi May 25, 2023
3d53ecb
Increase pylint score (#31)
jboddey May 25, 2023
b91fff5
Pylint (#32)
jhughesoti May 30, 2023
b84a026
Add license header (#36)
jhughesoti May 30, 2023
622b12d
merge main
jhughesoti May 31, 2023
38d71aa
Ovs (#35)
jhughesoti May 31, 2023
00be9cb
remove ovs files added back in during merge
jhughesoti May 31, 2023
f331239
Nmap (#38)
jhughesoti Jun 5, 2023
2a68fba
Create startup capture (#37)
jboddey Jun 5, 2023
8e8e154
Connection (#40)
jhughesoti Jun 7, 2023
6ff220b
Conn mac oui (#42)
jhughesoti Jun 8, 2023
4ca8f44
Con mac address (#43)
jhughesoti Jun 8, 2023
ff04f43
Dns (#44)
jhughesoti Jun 8, 2023
752f701
File permissions (#45)
jhughesoti Jun 9, 2023
f6e4e93
Add connection single ip test (#47)
jhughesoti Jun 12, 2023
bca0db8
Nmap results (#49)
jhughesoti Jun 15, 2023
5b56a79
Framework restructure (#50)
jboddey Jun 15, 2023
7bb9366
Ip control (#51)
jhughesoti Jun 20, 2023
b0d14c2
Move config to /local (#52)
jboddey Jun 23, 2023
94e937f
Add documentation (#53)
jboddey Jun 23, 2023
098de20
Sync dev to main (#56)
jboddey Jun 28, 2023
f185bb1
Fix missing results on udp tests when tcp ports are also defined (#59)
jhughesoti Jun 29, 2023
355c838
Add licence header (#61)
jboddey Jul 3, 2023
d374640
Resolve merge conflicts
jboddey Jul 3, 2023
8d65386
Resolve merge conflict
jboddey Jul 3, 2023
26f8c5b
Add network docs (#63)
jboddey Jul 4, 2023
4a5c1ea
Dhcp (#64)
jhughesoti Jul 5, 2023
af8367c
Dhcp (#67)
jhughesoti Jul 6, 2023
7dd5772
Add connection.dhcp_address test (#68)
jhughesoti Jul 6, 2023
9ef0d4f
Add NTP tests (#60)
jboddey Jul 12, 2023
2ae337d
Add ipv6 tests (#65)
jboddey Jul 12, 2023
59ab65f
Merge branch 'main' into dev
jboddey Jul 12, 2023
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
41 changes: 41 additions & 0 deletions docs/network/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Network Overview

## Table of Contents
1) Network Overview (this page)
2) [Addresses](addresses.md)
3) [Add a new network service](add_new_service.md)

Test Run provides several built-in network services that can be utilized for testing purposes. These services are already available and can be used without any additional configuration.

The following network services are provided:

### Internet Connectivity (Gateway Service)

The gateway service provides internet connectivity to the test network. It allows devices in the network to access external resources and communicate with the internet.

### DHCPv4 Service

The DHCPv4 service provides Dynamic Host Configuration Protocol (DHCP) functionality for IPv4 addressing. It includes the following components:

- Primary DHCP Server: A primary DHCP server is available to assign IPv4 addresses to DHCP clients in the network.
- Secondary DHCP Server (Failover Configuration): A secondary DHCP server operates in failover configuration with the primary server to provide high availability and redundancy.

#### Configuration

The configuration of the DHCPv4 service can be modified using the provided GRPC (gRPC Remote Procedure Call) service.

### IPv6 SLAAC Addressing

The primary DHCP server also provides IPv6 Stateless Address Autoconfiguration (SLAAC) addressing for devices in the network. IPv6 addresses are automatically assigned to devices using SLAAC where test devices support it.

### NTP Service

The Network Time Protocol (NTP) service provides time synchronization for devices in the network. It ensures that all devices have accurate and synchronized time information.

### DNS Service

The DNS (Domain Name System) service resolves domain names to their corresponding IP addresses. It allows devices in the network to access external resources using domain names.

### 802.1x Authentication (Radius Module)

The radius module provides 802.1x authentication for devices in the network. It ensures secure and authenticated access to the network. The issuing CA (Certificate Authority) certificate can be specified by the user if required.
94 changes: 94 additions & 0 deletions docs/network/add_new_service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Adding a New Network Service

The Test Run framework allows users to add their own network services with ease. A template network service can be used to get started quickly, this can be found at [modules/network/template](../../modules/network/template). Otherwise, see below for details of the requirements for new network services.

To add a new network service to Test Run, follow the procedure below:

1. Create a folder under `modules/network/` with the name of the network service in lowercase, using only alphanumeric characters and hyphens (`-`).
2. Inside the created folder, include the following files and folders:
- `{module}.Dockerfile`: Dockerfile for building the network service image. Replace `{module}` with the name of the module.
- `conf/`: Folder containing the module configuration files.
- `bin/`: Folder containing the startup script for the network service.
- Any additional application code can be placed in its own folder.

### Example `module_config.json`

```json
{
"config": {
"meta": {
"name": "{module}",
"display_name": "Network Service Name",
"description": "Description of the network service"
},
"network": {
"interface": "veth0",
"enable_wan": false,
"ip_index": 2
},
"grpc": {
"port": 5001
},
"docker": {
"depends_on": "base",
"mounts": [
{
"source": "runtime/network",
"target": "/runtime/network"
}
]
}
}
}
```

### Example of {module}.Dockerfile

```Dockerfile
# Image name: test-run/{module}
FROM test-run/base:latest

ARG MODULE_NAME={module}
ARG MODULE_DIR=modules/network/$MODULE_NAME

# Install network service dependencies
# ...

# 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

# Do not specify a CMD or Entrypoint as Test Run will automatically start your service as required
```

### Example of start_network_service script

```bash
#!/bin/bash

CONFIG_FILE=/etc/network_service/config.conf
# ...

echo "Starting Network Service..."

# Perform any required setup steps
# ...

# Start the network service
# ...

# Monitor for changes in the config file
# ...

# Restart the network service when the config changes
# ...
```




18 changes: 18 additions & 0 deletions docs/network/addresses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Network Addresses

Each network service is configured with an IPv4 and IPv6 address. For IPv4 addressing, the last number in the IPv4 address is fixed (ensuring the IP is unique). See below for a table of network addresses:

| Name | Mac address | IPv4 address | IPv6 address |
|---------------------|----------------------|--------------|------------------------------|
| Internet gateway | 9a:02:57:1e:8f:01 | 10.10.10.1 | fd10:77be:4186::1 |
| DHCP primary | 9a:02:57:1e:8f:02 | 10.10.10.2 | fd10:77be:4186::2 |
| DHCP secondary | 9a:02:57:1e:8f:03 | 10.10.10.3 | fd10:77be:4186::3 |
| DNS server | 9a:02:57:1e:8f:04 | 10.10.10.4 | fd10:77be:4186::4 |
| NTP server | 9a:02:57:1e:8f:05 | 10.10.10.5 | fd10:77be:4186::5 |
| Radius authenticator| 9a:02:57:1e:8f:07 | 10.10.10.7 | fd10:77be:4186::7 |
| Active test module | 9a:02:57:1e:8f:09 | 10.10.10.9 | fd10:77be:4186::9 |


The default network range is 10.10.10.0/24 and devices will be assigned addresses in that range via DHCP. The range may change when requested by a test module. In which case, network services will be restarted and accessible on the new range, with the same final host ID. The default IPv6 network is fd10:77be:4186::/64 and addresses will be assigned to devices on the network using IPv6 SLAAC.

When creating a new network module, please ensure that the ip_index value in the module_config.json is unique otherwise unexpected behaviour will occur.
12 changes: 6 additions & 6 deletions framework/python/src/net_orc/network_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def _get_os_user(self):
LOGGER.error('An OS error occurred while retrieving the login name.')
except Exception as error:
# Catch any other unexpected exceptions
LOGGER.error('An exception occurred:', error)
LOGGER.error('An exception occurred:', error)
return user

def _get_user(self):
Expand All @@ -203,15 +203,15 @@ def _get_user(self):
except (KeyError, ImportError, ModuleNotFoundError, OSError) as e:
# Handle specific exceptions individually
if isinstance(e, KeyError):
LOGGER.error("USER environment variable not set or unavailable.")
LOGGER.error('USER environment variable not set or unavailable.')
elif isinstance(e, ImportError):
LOGGER.error("Unable to import the getpass module.")
LOGGER.error('Unable to import the getpass module.')
elif isinstance(e, ModuleNotFoundError):
LOGGER.error("The getpass module was not found.")
LOGGER.error('The getpass module was not found.')
elif isinstance(e, OSError):
LOGGER.error("An OS error occurred while retrieving the username.")
LOGGER.error('An OS error occurred while retrieving the username.')
else:
LOGGER.error("An exception occurred:", e)
LOGGER.error('An exception occurred:', e)
return user

def _get_device_status(self, module):
Expand Down
17 changes: 8 additions & 9 deletions framework/python/src/test_orc/test_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@
# limitations under the License.

"""Provides high level management of the test orchestrator."""
import getpass
import os
import json
import time
import shutil
import docker
from docker.types import Mount
from common import logger
from common import logger, util
from test_orc.module import TestModule
from common import util

LOG_NAME = "test_orc"
LOGGER = logger.get_logger("test_orc")
Expand Down Expand Up @@ -61,7 +59,7 @@ def start(self):
# Setup the output directory
self._host_user = util.get_host_user()
os.makedirs(RUNTIME_DIR, exist_ok=True)
util.run_command(f'chown -R {self._host_user} {RUNTIME_DIR}')
util.run_command(f"chown -R {self._host_user} {RUNTIME_DIR}")

self._load_test_modules()
self.build_test_modules()
Expand Down Expand Up @@ -102,15 +100,15 @@ def _generate_results(self, device):
results[module.name] = module_results
except (FileNotFoundError, PermissionError,
json.JSONDecodeError) as results_error:
LOGGER.error("Error occured whilst obbtaining results for module " + module.name)
LOGGER.error(f"Error occured whilst obbtaining results for module {module.name}")
LOGGER.debug(results_error)

out_file = os.path.join(
self._root_path,
"runtime/test/" + device.mac_addr.replace(":", "") + "/results.json")
with open(out_file, "w", encoding="utf-8") as f:
json.dump(results, f, indent=2)
util.run_command(f'chown -R {self._host_user} {out_file}')
util.run_command(f"chown -R {self._host_user} {out_file}")
return results

def test_in_progress(self):
Expand Down Expand Up @@ -140,18 +138,19 @@ def _run_test_module(self, module, device):
container_runtime_dir = os.path.join(
self._root_path, "runtime/test/" + device.mac_addr.replace(":", "") +
"/" + module.name)
network_runtime_dir = os.path.join(self._root_path, "runtime/network")
os.makedirs(container_runtime_dir)

network_runtime_dir = os.path.join(self._root_path, "runtime/network")

device_startup_capture = os.path.join(
self._root_path, "runtime/test/" + device.mac_addr.replace(":", "") +
"/startup.pcap")
util.run_command(f'chown -R {self._host_user} {device_startup_capture}')
util.run_command(f"chown -R {self._host_user} {device_startup_capture}")

device_monitor_capture = os.path.join(
self._root_path, "runtime/test/" + device.mac_addr.replace(":", "") +
"/monitor.pcap")
util.run_command(f'chown -R {self._host_user} {device_monitor_capture}')
util.run_command(f"chown -R {self._host_user} {device_monitor_capture}")

client = docker.from_env()

Expand Down
18 changes: 9 additions & 9 deletions local/system.json.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"network": {
"device_intf": "enx123456789123",
"internet_intf": "enx123456789124"
},
"log_level": "INFO",
"startup_timeout": 60,
"monitor_period": 300,
"runtime": 1200
{
"network": {
"device_intf": "enx123456789123",
"internet_intf": "enx123456789124"
},
"log_level": "INFO",
"startup_timeout": 60,
"monitor_period": 300,
"runtime": 1200
}
4 changes: 4 additions & 0 deletions modules/network/base/base.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ FROM ubuntu:jammy

ARG MODULE_NAME=base
ARG MODULE_DIR=modules/network/$MODULE_NAME
ARG COMMON_DIR=framework/python/src/common

# Install common software
RUN apt-get update && apt-get install -y net-tools iputils-ping tcpdump iproute2 jq python3 python3-pip dos2unix

# Install common python modules
COPY $COMMON_DIR/ /testrun/python/src/common

# Setup the base python requirements
COPY $MODULE_DIR/python /testrun/python

Expand Down
25 changes: 25 additions & 0 deletions modules/network/base/bin/setup_python_path
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

ROOT_DIRECTORY="/testrun/python/src"

# Function to recursively add subdirectories to PYTHONPATH
add_subdirectories_to_pythonpath() {
local directory=$1
local subdirectories=( "$directory"/* )
local subdirectory

for subdirectory in "${subdirectories[@]}"; do
if [[ -d "$subdirectory" && ! "$subdirectory" = *'__pycache__' ]]; then
export PYTHONPATH="$PYTHONPATH:$subdirectory"
add_subdirectories_to_pythonpath "$subdirectory"
fi
done
}

# Set PYTHONPATH initially to an empty string
export PYTHONPATH="$ROOT_DIRECTORY"

# Add all subdirectories to PYTHONPATH
add_subdirectories_to_pythonpath "$ROOT_DIRECTORY"

echo "$PYTHONPATH"
6 changes: 3 additions & 3 deletions modules/network/base/bin/start_grpc
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.

GRPC_DIR="/testrun/python/src/grpc"
GRPC_DIR="/testrun/python/src/grpc_server"
GRPC_PROTO_DIR="proto"
GRPC_PROTO_FILE="grpc.proto"

#Move into the grpc directory
pushd $GRPC_DIR >/dev/null 2>&1

#Build the grpc proto file every time before starting server
python3 -m grpc_tools.protoc --proto_path=. ./$GRPC_PROTO_DIR/$GRPC_PROTO_FILE --python_out=. --grpc_python_out=.
python3 -u -m grpc_tools.protoc --proto_path=. ./$GRPC_PROTO_DIR/$GRPC_PROTO_FILE --python_out=. --grpc_python_out=.

popd >/dev/null 2>&1

#Start the grpc server
python3 -u $GRPC_DIR/start_server.py $@
python3 -u $GRPC_DIR/start_server.py $@ &

18 changes: 12 additions & 6 deletions modules/network/base/bin/start_module
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ useradd $HOST_USER
sysctl net.ipv6.conf.all.disable_ipv6=0
sysctl -p

#Read in the config file
# Read in the config file
CONF_FILE="/testrun/conf/module_config.json"
CONF=`cat $CONF_FILE`

Expand Down Expand Up @@ -60,10 +60,16 @@ else
INTF=$DEFINED_IFACE
fi

echo "Starting module $MODULE_NAME on local interface $INTF..."
# Setup the PYTHONPATH so all imports work as expected
echo "Setting up PYTHONPATH..."
export PYTHONPATH=$($BIN_DIR/setup_python_path)
echo "PYTHONPATH: $PYTHONPATH"

echo "Configuring binary files..."
$BIN_DIR/setup_binaries $BIN_DIR

echo "Starting module $MODULE_NAME on local interface $INTF..."

# Wait for interface to become ready
$BIN_DIR/wait_for_interface $INTF

Expand All @@ -80,14 +86,14 @@ then
if [[ ! -z $GRPC_PORT && ! $GRPC_PORT == "null" ]]
then
echo "gRPC port resolved from config: $GRPC_PORT"
$BIN_DIR/start_grpc "-p $GRPC_PORT" &
$BIN_DIR/start_grpc "-p $GRPC_PORT"
else
$BIN_DIR/start_grpc &
$BIN_DIR/start_grpc
fi
fi

#Small pause to let all core services stabalize
# Small pause to let all core services stabalize
sleep 3

#Start the networking service
# Start the networking service
$BIN_DIR/start_network_service $MODULE_NAME $INTF
3 changes: 2 additions & 1 deletion modules/network/base/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
grpcio
grpcio-tools
grpcio-tools
netifaces
Loading