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
9 changes: 9 additions & 0 deletions cmd/build
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ if [[ $(which docker) && $(docker --version) ]]; then
exit 1
fi

# Stop any running containers
echo Stopping any running containers
running_containers=$(docker container ls -q --filter name=tr-*)
if [ -n "$running_containers" ]; then
docker container kill $running_containers
else
echo No containers were found running
fi

# Builds all docker images
echo Building docker images

Expand Down
2 changes: 1 addition & 1 deletion cmd/prepare
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@

echo Installing system dependencies

sudo apt-get install openvswitch-common openvswitch-switch python3 libpangocairo-1.0-0
sudo apt-get update && sudo apt-get install openvswitch-common openvswitch-switch python3 libpangocairo-1.0-0

echo Finished installing system dependencies
14 changes: 9 additions & 5 deletions framework/python/src/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ async def start_test_run(self, request: Request, response: Response):

# Check Testrun is not already running
if self._test_run.get_session().get_status() in [
"Starting",
"In Progress",
"Waiting for Device",
"Monitoring"
Expand Down Expand Up @@ -226,22 +225,23 @@ async def get_version(self, response: Response):
return self._generate_msg(False, "Could not fetch current version")

# Set the installed version
json_response["installed_version"] = current_version
json_response["installed_version"] = "v" + current_version

# Check latest version number from GitHub API
version_check = requests.get(LATEST_RELEASE_CHECK, timeout=5)

# Check OK response was received
if version_check.status_code != 200:
response.status_code = 500
LOGGER.error(version_check.content)
return self._generate_msg(False, "Failed to fetch latest version")

# Extract version number from response, removing the leading 'v'
latest_version_no = version_check.json()["name"].strip("v")
LOGGER.debug(f"Latest version available is {latest_version_no}")

# Craft JSON response
json_response["latest_version"] = latest_version_no
json_response["latest_version"] = "v" + latest_version_no
json_response["latest_version_url"] = version_check.json()["html_url"]

# String comparison between current and latest version
Expand Down Expand Up @@ -287,6 +287,7 @@ async def delete_report(self, request: Request, response: Response):
if self._test_run.delete_report(device, timestamp_formatted):
return self._generate_msg(True, "Deleted report")

response.status_code = 500
return self._generate_msg(False, "Error occured whilst deleting report")

async def delete_device(self, request: Request, response: Response):
Expand Down Expand Up @@ -314,8 +315,11 @@ async def delete_device(self, request: Request, response: Response):
return self._generate_msg(False, "Device not found")

# Check that Testrun is not currently running against this device
if self._session.get_target_device() == device:
# TODO: Check if this is the correct status code
if (self._session.get_target_device() == device and
self._session.get_status() not in [
"Cancelled",
"Compliant",
"Non-Compliant"]):
response.status_code = 403
return self._generate_msg(False, "Cannot delete this device whilst " +
"it is being tested")
Expand Down
3 changes: 2 additions & 1 deletion framework/python/src/common/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def remove_report(self, timestamp):

remove_report_target = None
for report in self.reports:
if report.get_started() == timestamp:
report_timestamp = report.get_started().strftime('%Y-%m-%dT%H:%M:%S')
if report_timestamp == timestamp:
remove_report_target = report

if remove_report_target is not None:
Expand Down
5 changes: 3 additions & 2 deletions framework/python/src/common/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self, config_file):

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

def get_started(self):
Expand Down Expand Up @@ -126,7 +126,8 @@ def _load_config(self):
LOGGER.debug(self._config)

def _load_version(self):
version_cmd = util.run_command('dpkg-query --showformat=\'${Version}\' --show testrun')
version_cmd = util.run_command(
'dpkg-query --showformat=\'${Version}\' --show testrun')

if version_cmd:
version = version_cmd[0]
Expand Down
35 changes: 23 additions & 12 deletions framework/python/src/common/testreport.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def generate_page(self, json_data, page_num, max_page):
page += '<div style="break-after:page"></div>'
return page

def generate_body(self, json_data, page_num=1, max_page=1):
def generate_body(self, json_data):
return f'''
<body>
{self.generate_pages(json_data)}
Expand Down Expand Up @@ -268,11 +268,15 @@ def generate_summary(self, json_data):
<div class="summary-vertical-line"></div>
'''
# Add the device information
manufacturer = json_data['device']['manufacturer'] if 'manufacturer' in json_data['device'] else 'Undefined'
model = json_data['device']['model'] if 'model' in json_data['device'] else 'Undefined'
fw = json_data['device']['firmware'] if 'firmware' in json_data['device'] else 'Undefined'
mac = json_data['device']['mac_addr'] if 'mac_addr' in json_data['device'] else 'Undefined'

manufacturer = (json_data['device']['manufacturer']
if 'manufacturer' in json_data['device'] else 'Undefined')
model = (json_data['device']['model']
if 'model' in json_data['device'] else 'Undefined')
fw = (json_data['device']['firmware']
if 'firmware' in json_data['device'] else 'Undefined')
mac = (json_data['device']['mac_addr']
if 'mac_addr' in json_data['device'] else 'Undefined')

summary += self.generate_device_summary_label('Manufacturer',manufacturer)
summary += self.generate_device_summary_label('Model',model)
summary += self.generate_device_summary_label('Firmware',fw)
Expand All @@ -289,12 +293,18 @@ def generate_summary(self, json_data):

def generate_result_summary(self,json_data):
if json_data['status'] == 'Compliant':
result_summary = '''<div class ="summary-color-box summary-box-compliant">'''
result_summary = '''<div class ="summary-color-box
summary-box-compliant">'''
else:
result_summary = '''<div class ="summary-color-box summary-box-non-compliant">'''
result_summary += self.generate_result_summary_item('Test status', 'Complete')
result_summary += self.generate_result_summary_item('Test result', json_data['status'], style='color: white; font-size:24px; font-weight: 700;')
result_summary += self.generate_result_summary_item('Started', json_data['started'])
result_summary = '''<div class ="summary-color-box
summary-box-non-compliant">'''
result_summary += self.generate_result_summary_item('Test status',
'Complete')
result_summary += self.generate_result_summary_item(
'Test result',json_data['status'],
style='color: white; font-size:24px; font-weight: 700;')
result_summary += self.generate_result_summary_item('Started',
json_data['started'])

# Convert the timestamp strings to datetime objects
start_time = datetime.strptime(json_data['started'], '%Y-%m-%d %H:%M:%S')
Expand All @@ -311,7 +321,8 @@ def generate_result_summary(self,json_data):
def generate_result_summary_item(self, key, value, style=None):
summary_item = f'''<div class="summary-box-label">{key}</div>'''
if style is not None:
summary_item += f'''<div style="{style}" class="summary-box-value">{value}</div>'''
summary_item += f'''<div style="{style}"
class="summary-box-value">{value}</div>'''
else:
summary_item += f'''<div class="summary-box-value">{value}</div>'''
return summary_item
Expand Down
4 changes: 2 additions & 2 deletions framework/python/src/core/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def _exit_handler(self, signum, arg): # pylint: disable=unused-argument
self._stop(True)
sys.exit(1)

def stop(self, kill=False):
self.test_run.stop(kill)
def stop(self):
self.test_run.stop()


def parse_args():
Expand Down
32 changes: 19 additions & 13 deletions framework/python/src/core/testrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def load_all_devices(self):
self._session.clear_device_repository()
self._load_devices(device_dir=LOCAL_DEVICES_DIR)

# Temporarily removing loading of template device
# Temporarily removing loading of template device
# configs (feature not required yet)
# self._load_devices(device_dir=RESOURCE_DEVICES_DIR)
return self.get_session().get_device_repository()
Expand All @@ -147,7 +147,8 @@ def _load_devices(self, device_dir):

# Check if device config file exists before loading
if not os.path.exists(device_config_file_path):
LOGGER.error(f'Device configuration file missing from device {device_folder}')
LOGGER.error('Device configuration file missing ' +
f'from device {device_folder}')
continue

# Open device config file
Expand Down Expand Up @@ -227,9 +228,7 @@ def delete_report(self, device: Device, timestamp):
for report_folder in os.listdir(reports_folder):
if report_folder == timestamp:
shutil.rmtree(os.path.join(reports_folder, report_folder))

# TODO: Remove report from device (available in 1.0.2)

device.remove_report(timestamp)
return True

return False
Expand Down Expand Up @@ -317,13 +316,12 @@ def start(self):

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

# Keep application running until stopped
while True:
time.sleep(5)

def stop(self, kill=False):
def stop(self):

# Prevent discovering new devices whilst stopping
if self.get_net_orc().get_listener() is not None:
Expand All @@ -333,7 +331,6 @@ def stop(self, kill=False):

self._stop_tests()
self._stop_network(kill=True)
self._stop_ui()
self.get_session().set_status('Cancelled')

def _register_exits(self):
Expand All @@ -346,7 +343,8 @@ def _exit_handler(self, signum, arg): # pylint: disable=unused-argument
LOGGER.debug('Exit signal received: ' + str(signum))
if signum in (2, signal.SIGTERM):
LOGGER.info('Exit signal received.')
self.stop(kill=True)
self.stop()
self._stop_ui()
sys.exit(1)

def _get_config_abs(self, config_file=None):
Expand All @@ -366,11 +364,11 @@ def get_net_orc(self):
def _start_network(self):
# Start the network orchestrator
if not self.get_net_orc().start():
self.stop(kill=True)
self.stop()
sys.exit(1)

def _stop_network(self, kill=False):
self.get_net_orc().stop(kill=kill)
def _stop_network(self, kill=True):
self.get_net_orc().stop(kill)

def _stop_tests(self):
self._test_orc.stop()
Expand Down Expand Up @@ -402,10 +400,17 @@ def _device_discovered(self, mac_addr):
'Waiting for device to obtain IP')

def _device_stable(self, mac_addr):

# Do not continue testing if Testrun has cancelled during monitor phase
if self.get_session().get_status() == 'Cancelled':
return

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

if result is not None:
self._set_status(result)
self._stop_network()

def get_session(self):
Expand Down Expand Up @@ -437,6 +442,7 @@ def start_ui(self):
LOGGER.info('User interface is ready on http://localhost:8080')

def _stop_ui(self):
LOGGER.info('Stopping user interface')
client = docker.from_env()
try:
container = client.containers.get('tr-ui')
Expand Down
14 changes: 11 additions & 3 deletions framework/python/src/test_orc/test_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def stop(self):
def run_test_modules(self):
"""Iterates through each test module and starts the container."""

# Do not start test modules if status is not in progress, e.g. Stopping
if self.get_session().get_status() != "In Progress":
return

device = self._session.get_target_device()
self._test_in_progress = True
LOGGER.info(
Expand All @@ -100,6 +104,10 @@ def run_test_modules(self):

self._session.stop()

# Do not carry on (generating a report) if Testrun has been stopped
if self.get_session().get_status() != "In Progress":
return None

report = TestReport()
report.from_json(self._generate_report())
device.add_report(report)
Expand Down Expand Up @@ -338,9 +346,9 @@ def _run_test_module(self, module):
log_stream = module.container.logs(stream=True, stdout=True, stderr=True)
while (status == "running" and self._session.get_status() == "In Progress"):
if time.time() > test_module_timeout:
LOGGER.error("Module timeout exceeded, killing module: " + module.name)
self._stop_module(module=module,kill=True)
break
LOGGER.error("Module timeout exceeded, killing module: " + module.name)
self._stop_module(module=module,kill=True)
break
try:
line = next(log_stream).decode("utf-8").strip()
if re.search(LOG_REGEX, line):
Expand Down
9 changes: 9 additions & 0 deletions make/DEBIAN/prerm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# Stop any running containers
echo Stopping any running containers
running_containers=$(docker container ls -q --filter name=tr-*)
if [ -n "$running_containers" ]; then
docker container kill $running_containers
else
echo No containers were found running
fi

# Remove docker images
echo Removing docker images
docker_images=$(sudo docker images --filter=reference="test-run/*" -q)
Expand Down
50 changes: 50 additions & 0 deletions modules/ui/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"root": true,
"ignorePatterns": ["projects/**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates",
"plugin:prettier/recommended"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": ["*.html"],
"extends": [
"plugin:@angular-eslint/template/recommended",
"plugin:@angular-eslint/template/accessibility",
"plugin:prettier/recommended"
],
"rules": {
"prettier/prettier": [
"error",
{
"parser": "angular"
}
]
}
}
]
}
Loading