Skip to content
67 changes: 67 additions & 0 deletions Tests/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0

import re
import datetime
import logging

import click
import yaml

from scs_cert_lib import load_spec, annotate_validity


logger = logging.getLogger(__name__)


def select_valid(versions: list) -> list:
return [version for version in versions if version['_explicit_validity']]


@click.group()
def cli():
return


@cli.command()
@click.option('--version', '-V', 'version', type=str, default=None)
@click.option('--tests', '-t', 'tests', type=str, default=None)
@click.option('--section', '-S', 'sections', type=str, multiple=True)
@click.argument('specpath', type=click.Path(exists=True))
def select(specpath, version, sections, tests):
with open(specpath, "r", encoding="UTF-8") as specfile:
spec = load_spec(yaml.load(specfile, Loader=yaml.SafeLoader))
checkdate = datetime.date.today()
annotate_validity(spec['timeline'], spec['versions'], checkdate)
if version is None:
versions = select_valid(spec['versions'].values())
else:
versions = [spec['versions'].get(version)]
if versions[0] is None:
raise RuntimeError(f"Requested version '{version}' not found")
if not versions:
raise RuntimeError(f"No valid version found for {checkdate}")
title = spec['name']
if sections:
title += f" [sections: {', '.join(sections)}]"
if tests:
title += f" [tests: '{tests}']"
tests_re = re.compile(tests)
# collect all testcases we need
testcase_lookup = spec['testcases']
all_testcase_ids = set()
for version in versions:
for tc_id in version['testcase_ids']:
if sections and testcase_lookup.get(tc_id, {}).get('section') not in sections:
continue
if tests and not tests_re.match(tc_id):
continue
all_testcase_ids.add(tc_id)
logger.info(f"{title}: {len(all_testcase_ids)} testcases")
if all_testcase_ids:
print('\n'.join(sorted(all_testcase_ids)))


if __name__ == "__main__":
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
cli()
59 changes: 44 additions & 15 deletions Tests/iaas/openstack_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
SPDX-License-Identifier: CC-BY-SA 4.0
"""

from datetime import datetime
import getopt
import logging
import os
import sys

import openstack
import yaml

from scs_0100_flavor_naming.flavor_names import compute_flavor_spec
from scs_0100_flavor_naming.flavor_names_check import \
Expand Down Expand Up @@ -55,6 +57,8 @@ def usage(rcode=1, file=sys.stderr):
print("Options: [-c/--os-cloud OS_CLOUD] sets cloud environment (default from OS_CLOUD env)", file=file)
print("Runs specified testcases against the OpenStack cloud OS_CLOUD", file=file)
print("and reports inconsistencies, errors etc. It returns 0 on success.", file=file)
print("Instead of listing testcase-ids, you can supply a single dash (-)", file=file)
print("to have them read from stdin, one testcase-id per line.", file=file)
sys.exit(rcode)


Expand Down Expand Up @@ -179,7 +183,8 @@ def __init__(self):
def __getattr__(self, key):
val = self._values.get(key)
if val is None:
logger.debug(f'... {key}')
# uncomment for super serious debugging
# logger.log(f'... {key}')
try:
ret = self._functions[key](self)
except BaseException as e:
Expand All @@ -203,28 +208,24 @@ def add_value(self, name, value):
self._values[name] = value


def harness(name, *check_fns):
def harness(name, results, *check_fns):
"""Harness for evaluating testcase `name`.

Logs beginning of computation.
Logs beginning and end of computation.
Calls each fn in `check_fns`.
Prints (to stdout) 'name: RESULT', where RESULT is one of

- 'ABORT' if an exception occurs during the function calls
- 'FAIL' if one of the functions has a falsy result
- 'PASS' otherwise
Records result to `results`.
"""
logger.debug(f'** {name}')
logger.info(f'*** {name}')
try:
result = all(check_fn() for check_fn in check_fns)
except BaseException:
logger.debug('exception during check', exc_info=True)
result = 'ABORT'
value = 0
else:
result = ['FAIL', 'PASS'][min(1, result)]
# this is quite redundant
# logger.debug(f'** computation end for {name}')
print(f"{name}: {result}")
value = 1 if result else -1
result = ['FAIL', 'ABORT', 'PASS'][value + 1]
logger.info(f'+++ {name}: {result}')
results[name] = value


def run_preflight_checks(container):
Expand All @@ -241,6 +242,15 @@ def run_preflight_checks(container):
raise RuntimeError("OpenStack user is missing member role.")


class _LogHandler(logging.Handler):
def __init__(self, level=logging.NOTSET, log=None):
super().__init__(level=level)
self.log = [] if log is None else log

def handle(self, record):
self.log.append(f'{record.levelname}: {record.msg}')


def main(argv):
# configure logging, disable verbose library logging
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG)
Expand All @@ -264,6 +274,9 @@ def main(argv):
else:
usage(2)

if len(args) == 1 and args[0] == '-':
args = sys.stdin.read().splitlines()

testcases = [t for t in args if t.startswith('scs-')]
if len(testcases) != len(args):
unknown = [a for a in args if a not in testcases]
Expand All @@ -281,8 +294,24 @@ def main(argv):
for testcase in testcases:
print(f"{testcase}: ABORT")
raise

results = {}
log = []
logging.root.addHandler(_LogHandler(level=logging.DEBUG, log=log))
for testcase in testcases:
harness(testcase, lambda: getattr(c, testcase.replace('-', '_')))
harness(testcase, results, lambda: getattr(c, testcase.replace('-', '_')))
report = {
'creator': 'openstack_test.py v0.1.0',
'checked_at': datetime.now(),
'tests': {
key: {'result': value}
for key, value in results.items()
},
'log': log,
}
# don't do explicit_start here because that can easily be done by the caller using "echo ---",
# and then the caller can even add fields such as uuid, subject, and scope
yaml.safe_dump(report, sys.stdout, default_flow_style=False, sort_keys=False, explicit_start=False)
return 0


Expand Down
Loading
Loading