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
2 changes: 1 addition & 1 deletion framework/python/src/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def __init__(self, test_run):
self._router.add_api_route("/device", self.save_device, methods=["POST"])

# TODO: Make this configurable in system.json
origins = ["http://localhost:4200"]
origins = ["http://localhost:8080", "http://localhost:4200"]

self._app = FastAPI()
self._app.include_router(self._router)
Expand Down
9 changes: 7 additions & 2 deletions framework/python/src/common/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@
_CONF_DIR = 'local'
_CONF_FILE_NAME = 'system.json'



# Set log level
log_level = _DEFAULT_LEVEL

try:
with open(os.path.join(_CONF_DIR, _CONF_FILE_NAME),
encoding='UTF-8') as config_json_file:
system_conf_json = json.load(config_json_file)

log_level_str = system_conf_json['log_level']
log_level = logging.getLevelName(log_level_str)
if 'log_level' in system_conf_json:
log_level_str = system_conf_json['log_level']
log_level = logging.getLevelName(log_level_str)
except OSError:
# TODO: Print out warning that log level is incorrect or missing
log_level = _DEFAULT_LEVEL
Expand Down
8 changes: 5 additions & 3 deletions framework/python/src/common/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import datetime
import json
import os
from common import util

NETWORK_KEY = 'network'
DEVICE_INTF_KEY = 'device_intf'
Expand Down Expand Up @@ -45,7 +46,7 @@ def __init__(self, config_file):
self._load_config()

def start(self):
self._status = 'Waiting for device'
self._status = 'Waiting for Device'
self._started = datetime.datetime.now()

def get_started(self):
Expand Down Expand Up @@ -83,7 +84,7 @@ def _load_config(self):
config_file_json = json.load(f)

# Network interfaces
if (NETWORK_KEY in config_file_json
if (NETWORK_KEY in config_file_json
and DEVICE_INTF_KEY in config_file_json.get(NETWORK_KEY)
and INTERNET_INTF_KEY in config_file_json.get(NETWORK_KEY)):
self._config[NETWORK_KEY][DEVICE_INTF_KEY] = config_file_json.get(NETWORK_KEY, {}).get(DEVICE_INTF_KEY)
Expand All @@ -110,6 +111,7 @@ def _load_config(self):
def _save_config(self):
with open(self._config_file, 'w', encoding='utf-8') as f:
f.write(json.dumps(self._config, indent=2))
util.set_file_owner(owner=util.get_host_user(), path=self._config_file)

def get_runtime(self):
return self._config.get(RUNTIME_KEY)
Expand Down Expand Up @@ -193,7 +195,7 @@ def get_all_reports(self):
for device_report in device_reports:
reports.append(device_report.to_json())

return reports
return sorted(reports, key=lambda report: report['started'], reverse=True)

def add_total_tests(self, no_tests):
self._total_tests += no_tests
Expand Down
3 changes: 3 additions & 0 deletions framework/python/src/common/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,6 @@ def get_user():
else:
LOGGER.error('An exception occurred:', e)
return user

def set_file_owner(path, owner):
run_command(f'chown -R {owner} {path}')
71 changes: 61 additions & 10 deletions framework/python/src/core/testrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
Run using the provided command scripts in the cmd folder.
E.g sudo cmd/start
"""
import docker
import json
import os
import sys
Expand Down Expand Up @@ -113,10 +114,12 @@ def __init__(self,

else:

# Start UI container
self.start_ui()

# Build UI image
self._api = Api(self)
self._api.start()
# Start UI container

# Hold until API ends
while True:
Expand Down Expand Up @@ -254,14 +257,14 @@ def save_device(self, device: Device, device_json):

def start(self):

self._session.start()
self.get_session().start()

self._start_network()

if self._net_only:
LOGGER.info('Network only option configured, no tests will be run')

self.get_net_orc().listener.register_callback(
self.get_net_orc().get_listener().register_callback(
self._device_discovered,
[NetworkEvent.DEVICE_DISCOVERED]
)
Expand All @@ -286,10 +289,10 @@ def start(self):
)

self.get_net_orc().start_listener()
self._set_status('Waiting for device')
self._set_status('Waiting for Device')
LOGGER.info('Waiting for devices on the network...')

time.sleep(self._session.get_runtime())
time.sleep(self.get_session().get_runtime())

if not (self._test_orc.test_in_progress() or
self.get_net_orc().monitor_in_progress()):
Expand All @@ -310,6 +313,7 @@ def stop(self, kill=False):

self._stop_tests()
self._stop_network(kill=kill)
self._stop_ui()

def _register_exits(self):
signal.signal(signal.SIGINT, self._exit_handler)
Expand Down Expand Up @@ -352,7 +356,7 @@ def _stop_tests(self):

def get_device(self, mac_addr):
"""Returns a loaded device object from the device mac address."""
for device in self._session.get_device_repository():
for device in self.get_session().get_device_repository():
if device.mac_addr == mac_addr:
return device
return None
Expand All @@ -377,12 +381,59 @@ def _device_discovered(self, mac_addr):

def _device_stable(self, mac_addr):
LOGGER.info(f'Device with mac address {mac_addr} is ready for testing.')
self._set_status('In progress')
self._test_orc.run_test_modules()
self._set_status('Complete')
self._set_status('In Progress')
result = self._test_orc.run_test_modules()
self._set_status(result)

def _set_status(self, status):
self._session.set_status(status)
self.get_session().set_status(status)

def get_session(self):
return self._session

def start_ui(self):

LOGGER.info('Starting UI')

self._build_ui()

client = docker.from_env()

client.containers.run(
image='test-run/ui',
auto_remove=True,
name='tr-ui',
hostname='testrun.io',
detach=True,
ports={
'80': 8080
}
)

# TODO: Make port configurable
LOGGER.info('User interface is ready on http://localhost:8080')

def _build_ui(self):

# TODO: Improve this process
build_file = os.path.join(root_dir,
'modules',
'ui',
'ui.Dockerfile')
client = docker.from_env()

LOGGER.debug('Building user interface')

client.images.build(dockerfile=build_file,
path=root_dir,
forcerm=True,
tag='test-run/ui')

def _stop_ui(self):
client = docker.from_env()
try:
container = client.containers.get('tr-ui')
if container is not None:
container.kill()
except docker.errors.NotFound:
return
3 changes: 1 addition & 2 deletions framework/python/src/net_orc/network_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,8 +656,7 @@ def restore_net(self):

LOGGER.info('Clearing baseline network')

if hasattr(self, 'listener') and self.get_listener(
) is not None and self.get_listener().is_running():
if self.get_listener() is not None and self.get_listener().is_running():
self.get_listener().stop_listener()

client = docker.from_env()
Expand Down
6 changes: 4 additions & 2 deletions framework/python/src/test_orc/test_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ def run_test_modules(self):
LOGGER.debug("Old test results cleaned")
self._test_in_progress = False

return report.get_status()

def _write_reports(self, test_report):
out_dir = os.path.join(
self._root_path, RUNTIME_DIR,
Expand Down Expand Up @@ -134,7 +136,7 @@ def _calculate_result(self):
test_case = self.get_test_case(test_result["name"])
if (test_case.required_result.lower() == "required"
and test_result["result"].lower() == "non-compliant"):
result = "non-compliant"
result = "Non-Compliant"
return result

def _cleanup_old_test_results(self, device):
Expand Down Expand Up @@ -289,7 +291,7 @@ def _run_test_module(self, module):

log_stream = module.container.logs(stream=True, stdout=True, stderr=True)
while (time.time() < test_module_timeout and status == "running"
and self._session.get_status() == "In progress"):
and self._session.get_status() == "In Progress"):
try:
line = next(log_stream).decode("utf-8").strip()
if re.search(LOG_REGEX, line):
Expand Down
20 changes: 10 additions & 10 deletions modules/test/base/python/src/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,43 +83,43 @@ def run_tests(self):
result = None
test['start'] = datetime.now().isoformat()
if ('enabled' in test and test['enabled']) or 'enabled' not in test:
LOGGER.info('Attempting to run test: ' + test['name'])
LOGGER.debug('Attempting to run test: ' + test['name'])
# Resolve the correct python method by test name and run test
if hasattr(self, test_method_name):
if 'config' in test:
result = getattr(self, test_method_name)(config=test['config'])
else:
result = getattr(self, test_method_name)()
else:
LOGGER.info(f'Test {test["name"]} not resolved. Skipping')
LOGGER.info(f'Test {test["name"]} not implemented. Skipping')
result = None
else:
LOGGER.info(f'Test {test["name"]} disabled. Skipping')
LOGGER.debug(f'Test {test["name"]} is disabled. Skipping')
if result is not None:
if isinstance(result, bool):
test['result'] = 'compliant' if result else 'non-compliant'
test['result'] = 'Compliant' if result else 'Non-Compliant'
else:
if result[0] is None:
test['result'] = 'skipped'
test['result'] = 'Skipped'
if len(result)>1:
test['result_details'] = result[1]
else:
test['result'] = 'compliant' if result[0] else 'non-compliant'
test['result'] = 'Compliant' if result[0] else 'Non-Compliant'
test['result_details'] = result[1]
else:
test['result'] = 'skipped'
test['result'] = 'Skipped'

# Generate the short result description based on result value
if test['result'] == 'compliant':
if test['result'] == 'Compliant':
test['result_description'] = test[
'short_description'] if 'short_description' in test else test[
'name'] + ' passed - see result details for more info'
elif test['result'] == 'non-compliant':
elif test['result'] == 'Non-Compliant':
test['result_description'] = test[
'name'] + ' failed - see result details for more info'
else:
test['result_description'] = test[
'name'] + ' skipped - see result details for more info'
'name'] + ' Skipped - see result details for more info'

test['end'] = datetime.now().isoformat()
duration = datetime.fromisoformat(test['end']) - datetime.fromisoformat(
Expand Down
Loading