aboutsummaryrefslogtreecommitdiff
path: root/lava_results_app/dbutils.py
diff options
context:
space:
mode:
authorNeil Williams <neil.williams@linaro.org>2016-05-12 14:33:45 +0100
committerStevan Radakovic <stevan.radakovic@linaro.org>2016-05-25 13:50:57 +0000
commit796d212cee0c8ef6095ad8ece07d519979c98a9c (patch)
tree744d98820d58b1652abec7ec522077d402277510 /lava_results_app/dbutils.py
parentfaa4bcaffebc47c941b14bbf1f4442e9eed1320f (diff)
Create metadata on the number of test definitions
Display the metadata on the results page for the job. Allow for use to compare with the number of test definitions for which there are results. Handle multinode jobs to prevent duplicate TestData in future. Closes: #2259 Change-Id: I3a404e2e30eaa28fa1f63930f99794322a2b171f
Diffstat (limited to 'lava_results_app/dbutils.py')
-rw-r--r--lava_results_app/dbutils.py124
1 files changed, 87 insertions, 37 deletions
diff --git a/lava_results_app/dbutils.py b/lava_results_app/dbutils.py
index 374197048..d947a3315 100644
--- a/lava_results_app/dbutils.py
+++ b/lava_results_app/dbutils.py
@@ -28,21 +28,11 @@ from lava_results_app.models import (
ActionData,
MetaType,
)
+from django.core.exceptions import MultipleObjectsReturned
from lava_dispatcher.pipeline.action import Timeout
# pylint: disable=no-member
-METADATA_MAPPING_DESCRIPTION = {
- "boot.commands": ["job", "actions", "boot", "commands"],
- "boot.method": ["job", "actions", "boot", "method"],
- "boot.type": ["job", "actions", "boot", "type"],
- "deploy.os": ["job", "actions", "deploy", "os"],
- "deploy.ramdisk-type": ["job", "actions", "deploy", "ramdisk-type"],
- "target.hostname": ["device", "hostname"],
- "target.device_type": ["device", "device_type"]
-}
-
-
def _test_case(name, suite, result, testset=None, testshell=False):
"""
Create a TestCase for the specified name and result
@@ -125,11 +115,11 @@ def _check_for_testset(result_dict, suite):
return testset
-def map_scanned_results(scanned_dict, job):
+def map_scanned_results(scanned_dict, job): # pylint: disable=too-many-branches
"""
Sanity checker on the logged results dictionary
:param scanned_dict: results logged via the slave
- :param suite: the current test suite
+ :param job: the current test job
:return: False on error, else True
"""
logger = logging.getLogger('dispatcher-master')
@@ -172,21 +162,70 @@ def map_scanned_results(scanned_dict, job):
return True
-def _get_nested_value(data, mapping):
- # get the value from a nested dictionary based on keys given in 'mapping'.
- value = data
- for key in mapping:
- try:
- value = value[key]
- except TypeError:
- # check case when nested value is list and not dict.
- for item in value:
- if key in item:
- value = item[key]
- except KeyError:
- return None
+def _get_job_metadata(data): # pylint: disable=too-many-branches
+ if not isinstance(data, list):
+ return None
+ retval = {}
+ for action in data:
+ deploy = [reduce(dict.get, ['deploy'], action)]
+ count = 0
+ for block in deploy:
+ if not block:
+ continue
+ namespace = block.get('namespace', None)
+ prefix = "deploy.%d.%s" % (count, namespace) if namespace else 'deploy.%d' % count
+ value = block.get('method', None)
+ if value:
+ retval['%s.method' % prefix] = value
+ count += 1
+ boot = [reduce(dict.get, ['boot'], action)]
+ count = 0
+ for block in boot:
+ if not block:
+ continue
+ namespace = block.get('namespace', None)
+ prefix = "boot.%d.%s" % (count, namespace) if namespace else 'boot.%d' % count
+ value = block.get('commands', None)
+ if value:
+ retval['%s.commands' % prefix] = value
+ value = block.get('method', None)
+ if value:
+ retval['%s.method' % prefix] = value
+ value = block.get('type', None)
+ if value:
+ retval['%s.type' % prefix] = value
+ count += 1
+ test = [reduce(dict.get, ['test'], action)]
+ count = 0
+ for block in test:
+ if not block:
+ continue
+ namespace = block.get('namespace', None)
+ definitions = [reduce(dict.get, ['definitions'], block)][0]
+ for definition in definitions:
+ if definition['from'] == 'inline':
+ # an inline repo without test cases will not get reported.
+ if 'lava-test-case' in [reduce(dict.get, ['repository', 'run', 'steps'], definition)][0]:
+ prefix = "test.%d.%s" % (count, namespace) if namespace else 'test.%d' % count
+ retval['%s.inline' % prefix] = definition['name']
+ else:
+ prefix = "test.%d.%s" % (count, namespace) if namespace else 'test.%d' % count
+ # FIXME: what happens with remote definition without lava-test-case?
+ retval['%s.definition.name' % prefix] = definition['name']
+ retval['%s.definition.path' % prefix] = definition['path']
+ retval['%s.definition.from' % prefix] = definition['from']
+ retval['%s.definition.repository' % prefix] = definition['repository']
+ count += 1
+ return retval
+
- return value
+def _get_device_metadata(data):
+ hostname = data.get('hostname', None)
+ devicetype = data.get('device_type', None)
+ return {
+ 'target.hostname': hostname,
+ 'target.device_type': devicetype
+ }
def build_action(action_data, testdata, submission):
@@ -215,6 +254,7 @@ def build_action(action_data, testdata, submission):
if 'max_retries' in action_data:
max_retry = action_data['max_retries']
+ # maps the static testdata derived from the definition to the runtime pipeline construction
action = ActionData.objects.create(
action_name=action_data['name'],
action_level=action_data['level'],
@@ -254,16 +294,26 @@ def map_metadata(description, job):
except yaml.YAMLError as exc:
logger.exception("[%s] %s", job.id, exc)
return False
- testdata = TestData.objects.create(testjob=job)
+ try:
+ testdata, created = TestData.objects.get_or_create(testjob=job)
+ except MultipleObjectsReturned:
+ # only happens for small number of jobs affected by original bug.
+ logger.info("[%s] skipping alteration of duplicated TestData", job.id)
+ return False
+ if not created:
+ # prevent updates of existing TestData
+ return False
testdata.save()
- # Add metadata from description data.
- for key in METADATA_MAPPING_DESCRIPTION:
- value = _get_nested_value(
- description_data,
- METADATA_MAPPING_DESCRIPTION[key]
- )
- if value:
- testdata.attributes.create(name=key, value=value)
+
+ # get job-action metadata
+ action_values = _get_job_metadata(description_data['job']['actions'])
+ for key, value in action_values.items():
+ testdata.attributes.create(name=key, value=value)
+
+ # get metadata from device
+ device_values = _get_device_metadata(description_data['device'])
+ for key, value in device_values.items():
+ testdata.attributes.create(name=key, value=value)
# Add metadata from job submission data.
if "metadata" in submission_data:
@@ -292,7 +342,7 @@ def export_testcase(testcase):
Returns string versions of selected elements of a TestCase
Unicode causes issues with CSV and can complicate YAML parsing
with non-python parsers.
- :param testcases: list of TestCase objects
+ :param testcase: list of TestCase objects
:return: Dictionary containing relevant information formatted for export
"""
actiondata = testcase.action_data