diff --git a/net_orc/python/src/network_orchestrator.py b/net_orc/python/src/network_orchestrator.py index 726eef3b9..77af509f2 100644 --- a/net_orc/python/src/network_orchestrator.py +++ b/net_orc/python/src/network_orchestrator.py @@ -469,7 +469,7 @@ def _start_network_service(self, net_module): privileged=True, detach=True, mounts=net_module.mounts, - environment={'HOST_USER': getpass.getuser()}) + environment={'HOST_USER': self._get_host_user()}) except docker.errors.ContainerError as error: LOGGER.error('Container run error') LOGGER.error(error) @@ -477,6 +477,46 @@ def _start_network_service(self, net_module): if network != 'host': self._attach_service_to_network(net_module) + def _get_host_user(self): + user = self._get_os_user() + + # If primary method failed, try secondary + if user is None: + user = self._get_user() + + LOGGER.debug("Network orchestrator host user: " + user) + return user + + def _get_os_user(self): + user = None + try: + user = os.getlogin() + except OSError as e: + # Handle the OSError exception + LOGGER.error("An OS error occurred while retrieving the login name.") + except Exception as e: + # Catch any other unexpected exceptions + LOGGER.error("An exception occurred:", e) + return user + + def _get_user(self): + user = None + try: + user = getpass.getuser() + except (KeyError, ImportError, ModuleNotFoundError, OSError) as e: + # Handle specific exceptions individually + if isinstance(e, KeyError): + LOGGER.error("USER environment variable not set or unavailable.") + elif isinstance(e, ImportError): + LOGGER.error("Unable to import the getpass module.") + elif isinstance(e, ModuleNotFoundError): + LOGGER.error("The getpass module was not found.") + elif isinstance(e, OSError): + LOGGER.error("An OS error occurred while retrieving the username.") + else: + LOGGER.error("An exception occurred:", e) + return user + def _stop_service_module(self, net_module, kill=False): LOGGER.debug('Stopping Service container ' + net_module.container_name) try: diff --git a/net_orc/python/src/network_validator.py b/net_orc/python/src/network_validator.py index e76e49a5c..4a3a2a080 100644 --- a/net_orc/python/src/network_validator.py +++ b/net_orc/python/src/network_validator.py @@ -150,7 +150,7 @@ def _start_network_device(self, device): privileged=True, detach=True, mounts=device.mounts, - environment={'HOST_USER': getpass.getuser()}) + environment={'HOST_USER': self._get_host_user()}) except docker.errors.ContainerError as error: LOGGER.error('Container run error') LOGGER.error(error) @@ -167,6 +167,46 @@ def _start_network_device(self, device): LOGGER.info('Validation device ' + device.name + ' has finished') + def _get_host_user(self): + user = self._get_os_user() + + # If primary method failed, try secondary + if user is None: + user = self._get_user() + + LOGGER.debug("Network validator host user: " + user) + return user + + def _get_os_user(self): + user = None + try: + user = os.getlogin() + except OSError as e: + # Handle the OSError exception + LOGGER.error("An OS error occurred while retrieving the login name.") + except Exception as e: + # Catch any other unexpected exceptions + LOGGER.error("An exception occurred:", e) + return user + + def _get_user(self): + user = None + try: + user = getpass.getuser() + except (KeyError, ImportError, ModuleNotFoundError, OSError) as e: + # Handle specific exceptions individually + if isinstance(e, KeyError): + LOGGER.error("USER environment variable not set or unavailable.") + elif isinstance(e, ImportError): + LOGGER.error("Unable to import the getpass module.") + elif isinstance(e, ModuleNotFoundError): + LOGGER.error("The getpass module was not found.") + elif isinstance(e, OSError): + LOGGER.error("An OS error occurred while retrieving the username.") + else: + LOGGER.error("An exception occurred:", e) + return user + def _get_device_status(self, module): container = self._get_device_container(module) if container is not None: diff --git a/test_orc/modules/conn/bin/start_test_module b/test_orc/modules/conn/bin/start_test_module new file mode 100644 index 000000000..4550849ce --- /dev/null +++ b/test_orc/modules/conn/bin/start_test_module @@ -0,0 +1,39 @@ +#!/bin/bash + +# 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:$HOST_USER $LOG_FILE +chown $HOST_USER:$HOST_USER $RESULT_FILE + +# Run the python scrip 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 \ No newline at end of file diff --git a/test_orc/modules/conn/conf/module_config.json b/test_orc/modules/conn/conf/module_config.json new file mode 100644 index 000000000..e73846340 --- /dev/null +++ b/test_orc/modules/conn/conf/module_config.json @@ -0,0 +1,22 @@ +{ + "config": { + "meta": { + "name": "connection", + "display_name": "Connection", + "description": "Connection tests" + }, + "network": true, + "docker": { + "depends_on": "base", + "enable_container": true, + "timeout": 30 + }, + "tests":[ + { + "name": "connection.target_ping", + "description": "The device under test responds to an ICMP echo (ping) request.", + "expected_behavior": "The device under test responds to an ICMP echo (ping) request." + } + ] + } +} \ No newline at end of file diff --git a/test_orc/modules/conn/conn.Dockerfile b/test_orc/modules/conn/conn.Dockerfile new file mode 100644 index 000000000..f6a2c86b4 --- /dev/null +++ b/test_orc/modules/conn/conn.Dockerfile @@ -0,0 +1,11 @@ +# Image name: test-run/conn-test +FROM test-run/base-test:latest + +# Copy over all configuration files +COPY modules/conn/conf /testrun/conf + +# Load device binary files +COPY modules/conn/bin /testrun/bin + +# Copy over all python files +COPY modules/conn/python /testrun/python \ No newline at end of file diff --git a/test_orc/modules/conn/python/src/connection_module.py b/test_orc/modules/conn/python/src/connection_module.py new file mode 100644 index 000000000..086f32a04 --- /dev/null +++ b/test_orc/modules/conn/python/src/connection_module.py @@ -0,0 +1,49 @@ +# 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. + +"""Connection test module""" +import util +import sys +from test_module import TestModule + +LOG_NAME = "test_connection" +LOGGER = None + + +class ConnectionModule(TestModule): + """Connection Test module""" + + def __init__(self, module): + super().__init__(module_name=module, log_name=LOG_NAME) + global LOGGER + LOGGER = self._get_logger() + + def _connection_target_ping(self): + LOGGER.info("Running connection.target_ping") + + # If the ipv4 address wasn't resolved yet, try again + if self._device_ipv4_addr is None: + self._device_ipv4_addr = self._get_device_ipv4(self) + + if self._device_ipv4_addr is None: + LOGGER.error("No device IP could be resolved") + sys.exit(1) + else: + return self._ping(self._device_ipv4_addr) + + + def _ping(self, host): + cmd = 'ping -c 1 ' + str(host) + success = util.run_command(cmd, output=False) + return success \ No newline at end of file diff --git a/test_orc/modules/conn/python/src/run.py b/test_orc/modules/conn/python/src/run.py new file mode 100644 index 000000000..5165b58c6 --- /dev/null +++ b/test_orc/modules/conn/python/src/run.py @@ -0,0 +1,68 @@ +# 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. + +"""Run NMAP test module""" +import argparse +import signal +import sys +import logger + +from connection_module import ConnectionModule + +LOGGER = logger.get_logger('connection_module') + + +class ConnectionModuleRunner: + """Run the Connection module tests.""" + + def __init__(self, module): + + signal.signal(signal.SIGINT, self._handler) + signal.signal(signal.SIGTERM, self._handler) + signal.signal(signal.SIGABRT, self._handler) + signal.signal(signal.SIGQUIT, self._handler) + + LOGGER.info('Starting connection module') + + self._test_module = ConnectionModule(module) + self._test_module.run_tests() + + def _handler(self, signum): + LOGGER.debug('SigtermEnum: ' + str(signal.SIGTERM)) + LOGGER.debug('Exit signal received: ' + str(signum)) + if signum in (2, signal.SIGTERM): + LOGGER.info('Exit signal received. Stopping connection test module...') + LOGGER.info('Test module stopped') + sys.exit(1) + + +def run(): + parser = argparse.ArgumentParser( + description='Connection Module Help', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '-m', + '--module', + help='Define the module name to be used to create the log file') + + args = parser.parse_args() + + # For some reason passing in the args from bash adds an extra + # space before the argument so we'll just strip out extra space + ConnectionModuleRunner(args.module.strip()) + + +if __name__ == '__main__': + run() diff --git a/test_orc/python/src/test_orchestrator.py b/test_orc/python/src/test_orchestrator.py index 08b720150..e122221f5 100644 --- a/test_orc/python/src/test_orchestrator.py +++ b/test_orc/python/src/test_orchestrator.py @@ -153,7 +153,7 @@ def _run_test_module(self, module, device): read_only=True), ], environment={ - "HOST_USER": getpass.getuser(), + "HOST_USER": self._get_host_user(), "DEVICE_MAC": device.mac_addr, "DEVICE_TEST_MODULES": device.test_modules, "IPV4_SUBNET": self._net_orc.network_config.ipv4_network, @@ -206,6 +206,47 @@ def _get_module_container(self, module): LOGGER.error(error) return container + def _get_host_user(self): + user = self._get_os_user() + + # If primary method failed, try secondary + if user is None: + user = self._get_user() + + LOGGER.debug("Test orchestrator host user: " + user) + return user + + def _get_os_user(self): + user = None + try: + user = os.getlogin() + except OSError as e: + # Handle the OSError exception + LOGGER.error("An OS error occurred while retrieving the login name.") + except Exception as e: + # Catch any other unexpected exceptions + LOGGER.error("An exception occurred:", e) + return user + + def _get_user(self): + user = None + try: + user = getpass.getuser() + except (KeyError, ImportError, ModuleNotFoundError, OSError) as e: + # Handle specific exceptions individually + if isinstance(e, KeyError): + LOGGER.error("USER environment variable not set or unavailable.") + elif isinstance(e, ImportError): + LOGGER.error("Unable to import the getpass module.") + elif isinstance(e, ModuleNotFoundError): + LOGGER.error("The getpass module was not found.") + elif isinstance(e, OSError): + LOGGER.error("An OS error occurred while retrieving the username.") + else: + LOGGER.error("An exception occurred:", e) + return user + + def _load_test_modules(self): """Load network modules from module_config.json.""" LOGGER.debug("Loading test modules from /" + TEST_MODULES_DIR)