summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFathi Boudra <fathi.boudra@linaro.org>2019-12-17 17:37:58 +0200
committerFathi Boudra <fathi.boudra@linaro.org>2019-12-17 17:37:58 +0200
commit9984a98f85c5006ed9979439ec0ff08fe47631a0 (patch)
treeb49bb64765bf727f9343b00412e47333e5323590
parent9e402bf7c9094d47fb58412525796001b5cd0e62 (diff)
downloadtf-a-job-configs-9984a98f85c5006ed9979439ec0ff08fe47631a0.tar.gz
post-build-lava: initial helper job to submit lava job
Signed-off-by: Fathi Boudra <fathi.boudra@linaro.org> Change-Id: I0e487cf29a5064fffe68069d2c3a74b307c0cd65
-rw-r--r--post-build-lava.yaml38
-rwxr-xr-xpost-build-lava/builders.sh14
-rw-r--r--post-build-lava/post-build-lava.groovy88
-rw-r--r--post-build-lava/post-build-lava.py599
-rw-r--r--post-build-lava/submit_for_testing.py282
5 files changed, 1021 insertions, 0 deletions
diff --git a/post-build-lava.yaml b/post-build-lava.yaml
new file mode 100644
index 00000000..6d9f8ee6
--- /dev/null
+++ b/post-build-lava.yaml
@@ -0,0 +1,38 @@
+- job:
+ name: post-build-lava
+ project-type: freestyle
+ defaults: global
+ properties:
+ - build-discarder:
+ days-to-keep: 365
+ num-to-keep: 700
+ parameters:
+ - file:
+ name: post_build_lava_parameters
+ - bool:
+ name: SKIP_REPORT
+ default: true
+ - string:
+ name: LAVA_SERVER
+ default: 'tf.validation.linaro.org/RPC2/'
+ disabled: false
+ node: master
+ display-name: 'Post build to LAVA'
+ wrappers:
+ - timestamps
+ - credentials-binding:
+ - text:
+ credential-id: LAVA_USER_TF
+ variable: LAVA_USER
+ - credentials-binding:
+ - text:
+ credential-id: LAVA_TOKEN_TF
+ variable: LAVA_TOKEN
+ builders:
+ - shell:
+ !include-raw: post-build-lava/builders.sh
+ publishers:
+ - groovy-postbuild:
+ script:
+ !include-raw:
+ - post-build-lava/post-build-lava.groovy
diff --git a/post-build-lava/builders.sh b/post-build-lava/builders.sh
new file mode 100755
index 00000000..2b02a82e
--- /dev/null
+++ b/post-build-lava/builders.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+[ -z "${DEVICE_TYPE}" ] && exit 0
+
+# disable job submission
+#export SKIP_LAVA=1
+
+# Send to LAVA
+rm -rf pbl post_build_reports_parameters
+git clone https://git.trustedfirmware.org/ci/tf-a-job-configs.git pbl
+echo "Device type: ${DEVICE_TYPE}"
+python pbl/post-build-lava/post-build-lava.py
diff --git a/post-build-lava/post-build-lava.groovy b/post-build-lava/post-build-lava.groovy
new file mode 100644
index 00000000..cbcc15de
--- /dev/null
+++ b/post-build-lava/post-build-lava.groovy
@@ -0,0 +1,88 @@
+import hudson.model.*
+
+// Add a LAVA job link to the description
+def matcher = manager.getLogMatcher(".*LAVA Job Id.*")
+if (matcher?.matches()) {
+ def lavaJobId = matcher.group(0).split(",")[0].substring(13)
+ if (!lavaJobId.isInteger()) {
+ lavaJobId = matcher.group(0).tokenize("'")[1]
+ }
+ def lavaServer = matcher.group(0).tokenize("/")[1]
+ def lavaJobUrl = "https://${lavaServer}/scheduler/job/${lavaJobId}"
+ def lavaDescription = "&nbsp;LAVA Job Id: <a href='${lavaJobUrl}'>${lavaJobId}</a>"
+
+ def cause = manager.build.getAction(hudson.model.CauseAction.class).getCauses()
+ def upstreamBuild = cause[0].upstreamBuild
+ def upstreamProject = cause[0].upstreamProject
+ def jobName = upstreamProject
+ def jobConfiguration = upstreamProject
+ def jobUrl = manager.hudson.getRootUrl() + "job/${upstreamProject}/${upstreamBuild}"
+ def jobDescription = "<br>&nbsp;Build <a href='${jobUrl}'>${upstreamProject} #${upstreamBuild}</a>"
+
+ manager.build.setDescription(lavaDescription + jobDescription)
+
+ // Multi-configuration project
+ if (upstreamProject.contains("/")) {
+ jobName = upstreamProject.split("/")[0]
+ jobConfiguration = upstreamProject.split("/")[1]
+ }
+
+ def jobs = hudson.model.Hudson.instance.getItem(jobName).getAllJobs()
+
+ for (job in jobs) {
+ if (job.name == jobConfiguration) {
+ if (job.getLastBuild().getDescription() != null) {
+ lavaDescription += "<br>" + job.getLastBuild().getDescription()
+ }
+ job.getLastBuild().setDescription(lavaDescription)
+ }
+ }
+
+ // Add parameters
+ def action = manager.build.getAction(hudson.model.ParametersAction.class)
+ def parameters = [
+ new StringParameterValue("LAVA_SERVER", "${lavaServer}/RPC2/"),
+ new StringParameterValue("LAVA_JOB_ID", "${lavaJobId}"),
+ new StringParameterValue("BUILD_JOB", "${jobUrl}")
+ ]
+ updatedAction = action.createUpdated(parameters)
+ manager.build.replaceAction(updatedAction)
+
+ // Update the pool of jobs to monitor
+ job = hudson.model.Hudson.instance.getItem("check-lava-status")
+ property = job.getProperty(hudson.model.ParametersDefinitionProperty.class)
+ parameter = property.getParameterDefinition("LAVA_JOB_ID_POOL")
+ lavaJobIdPool = parameter.getDefaultValue()
+ lavaJobIdPool += " ${manager.build.number}"
+ parameter.setDefaultValue(lavaJobIdPool)
+ job.save()
+
+ // Call post-build-report with parameters file
+ def skipReport = manager.build.getEnvironment(manager.listener)['SKIP_REPORT']
+ if (!skipReport.toBoolean()) {
+ def project = manager.build.project;
+ def pbrParamsCache = ""
+
+ def sourceJob = hudson.model.Hudson.instance.getItem(jobName)
+ def sourceVariables = sourceJob.getBuildByNumber(upstreamBuild.toInteger()).getEnvironment()
+ for (sourceVariableName in sourceVariables.keySet()) {
+ if (sourceVariableName.startsWith("GERRIT")) {
+ sourceVariableValue = sourceVariables.get(sourceVariableName)
+ pbrParamsCache += "SOURCE_${sourceVariableName}=${sourceVariableValue}\n";
+ }
+ if (sourceVariableName.startsWith("ART_URL")) {
+ sourceVariableValue = sourceVariables.get(sourceVariableName)
+ pbrParamsCache += "${sourceVariableName}=${sourceVariableValue}\n";
+ }
+ }
+
+ pbrParamsCache += "SOURCE_PROJECT_NAME=${jobName}\n";
+ pbrParamsCache += "SOURCE_BUILD_NUMBER=${upstreamBuild}\n";
+ pbrParamsCache += "SOURCE_BUILD_ID=${upstreamBuild}\n";
+ pbrParamsCache += "SOURCE_BUILD_URL=${jobUrl}\n";
+ pbrParamsCache += "LAVA_JOB_IDS=${lavaJobId}\n";
+
+ def pbrParams = project.getWorkspace().child("post_build_reports_parameters");
+ pbrParams.write(pbrParamsCache, null);
+ }
+}
diff --git a/post-build-lava/post-build-lava.py b/post-build-lava/post-build-lava.py
new file mode 100644
index 00000000..41f4753c
--- /dev/null
+++ b/post-build-lava/post-build-lava.py
@@ -0,0 +1,599 @@
+#!/usr/bin/python
+
+import base64
+import collections
+import fileinput
+import json
+import os
+import re
+import sys
+import urllib2
+import xmlrpclib
+
+tests_timeout = {
+ 'bluetooth-enablement': 7200,
+ 'bootchart': 800,
+ 'busybox': 800,
+ 'cyclictest': 90000,
+ 'device-tree': 800,
+ 'e2eaudiotest': 7200,
+ 'ethernet': 800,
+ 'gatortests': 1200,
+ 'kernel-version': 800,
+ 'leb-basic-graphics': 7200,
+ 'ltp': 10800,
+ 'mysql': 800,
+ 'network-tests-basic': 1200,
+ 'perf': 800,
+ 'phpinfo': 800,
+ 'phpmysql': 800,
+ 'pwrmgmt': 1200,
+ 'sdkhelloc': 800,
+ 'sdkhellocxx': 800,
+ 'smoke-tests-basic': 1200,
+ 'toolchain': 800,
+ 'wifi-enablement': 7200,
+}
+
+tests_nano = [
+ 'device-tree',
+ 'gatortests',
+ 'ltp',
+ 'perf',
+ 'pwrmgmt',
+ 'smoke-tests-basic',
+ 'network-tests-basic',
+]
+
+
+# CI base URL
+ci_base_url = 'https://ci.linaro.org/jenkins/job/'
+# Snapshots base URL
+snapshots_url = 'https://snapshots.linaro.org'
+
+class LAVADeviceBase(object):
+ """
+ Base class for definition of the device type and target in lava job.
+ """
+
+ def __init__(self, name=None):
+ self.name = name
+
+
+class LAVADeviceType(LAVADeviceBase):
+ """
+ Representation the definition of the device type in lava job.
+ """
+
+
+class LAVADeviceTarget(LAVADeviceBase):
+ """
+ Representation the definition of the device target in lava job.
+ """
+
+
+def obfuscate_credentials(s):
+ return re.sub(r'([^ ]:).+?(@)', r'\1xxx\2', s)
+
+
+def auth_headers(username, password):
+ return 'Basic ' + base64.encodestring('%s:%s' % (username, password))[:-1]
+
+
+def get_hwpack_type(job_name, hwpack_file_name="Undefined"):
+ hwpack_type = job_name.replace('/', ',')
+ ret_split = dict(
+ token.split('=') for token in hwpack_type.split(',') if '=' in token)
+ try:
+ return ret_split['hwpack']
+ except KeyError, e:
+ # If hwpack key is not found, fallback to hwpack file name
+ return hwpack_file_name.split('_')[1].split('-')[1]
+
+
+def get_rootfs_url(distribution, architecture, rootfs_type):
+ # Rootfs last successful build number
+ ci_url = '%s%s-%s-%s%s%s%s' % \
+ (ci_base_url,
+ distribution,
+ architecture,
+ 'rootfs/rootfs=',
+ rootfs_type,
+ ',label=build',
+ '/lastSuccessfulBuild/buildNumber')
+ request = urllib2.Request(ci_url)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError, e:
+ if hasattr(e, 'reason'):
+ print 'Failed to reach %s.' % ci_url
+ print 'Reason: ', e.reason
+ elif hasattr(e, 'code'):
+ print 'ci.linaro.org could not fulfill the request: %s' % ci_url
+ print 'Error code: ', e.code
+ sys.exit('Failed to get last successful rootfs build number.')
+
+ rootfs_build_number = '%s' % eval(response.read())
+
+ # Rootfs last successful build timestamp
+ ci_url = '%s%s-%s-%s%s%s%s' % \
+ (ci_base_url,
+ distribution,
+ architecture,
+ 'rootfs/rootfs=',
+ rootfs_type,
+ ',label=build',
+ '/lastSuccessfulBuild/buildTimestamp?format=yyyyMMdd')
+ request = urllib2.Request(ci_url)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError, e:
+ if hasattr(e, 'reason'):
+ print 'Failed to reach %s.' % ci_url
+ print 'Reason: ', e.reason
+ elif hasattr(e, 'code'):
+ print 'ci.linaro.org could not fulfill the request: %s' % ci_url
+ print 'Error code: ', e.code
+ sys.exit('Failed to get last successful rootfs build timestamp.')
+
+ rootfs_build_timestamp = '%s' % eval(response.read())
+
+ rootfs_file_name = 'linaro-utopic-%s-%s-%s.tar.gz' % \
+ (rootfs_type,
+ rootfs_build_timestamp,
+ rootfs_build_number)
+
+ rootfs_url = '%s/%s/%s/%s/%s/%s' % \
+ (snapshots_url,
+ distribution,
+ 'images',
+ rootfs_type,
+ rootfs_build_number,
+ rootfs_file_name)
+
+ return rootfs_url, rootfs_build_number
+
+
+def lava_submit(config, lava_server):
+ print config
+
+ skip_lava = os.environ.get('SKIP_LAVA')
+ if skip_lava is None:
+ # LAVA user
+ lava_user = os.environ.get('LAVA_USER')
+ if lava_user is None:
+ f = open('/var/run/lava/lava-user')
+ lava_user = f.read().strip()
+ f.close()
+
+ # LAVA token
+ lava_token = os.environ.get('LAVA_TOKEN')
+ if lava_token is None:
+ f = open('/var/run/lava/lava-token')
+ lava_token = f.read().strip()
+ f.close()
+
+ # LAVA server base URL
+ lava_server_root = lava_server.rstrip('/')
+ if lava_server_root.endswith('/RPC2'):
+ lava_server_root = lava_server_root[:-len('/RPC2')]
+
+ try:
+ server_url = \
+ 'https://{lava_user:>s}:{lava_token:>s}@{lava_server:>s}'
+ server = \
+ xmlrpclib.ServerProxy(server_url.format(
+ lava_user=lava_user,
+ lava_token=lava_token,
+ lava_server=lava_server))
+ lava_job_id = server.scheduler.submit_job(config)
+ job_is_single_node = isinstance(lava_job_id, int)
+ if job_is_single_node:
+ lava_job_details = server.scheduler.job_details(lava_job_id)
+ lava_id = lava_job_details['id']
+ else:
+ lava_job_details = map(lambda sub_id: server.scheduler.job_details(sub_id), lava_job_id)
+ lava_id = lava_job_details[0]['id']
+ print 'LAVA Job Id: %s, URL: https://%s/scheduler/job/%s' % \
+ (lava_job_id, lava_server_root, lava_id)
+ if not job_is_single_node:
+ try:
+ lava_sub_jobs = []
+ for details in lava_job_details:
+ lava_job_role = json.loads(details['definition'])['role']
+ lava_sub_jobs.append('%s:%s:%s' % (details['id'], details['sub_id'], lava_job_role))
+ print 'LAVA Sub-Jobs: %s' % ', '.join(lava_sub_jobs)
+ except (TypeError, ValueError):
+ # ignore ValueError JSON decode errors in case job is YAML based
+ pass
+ except xmlrpclib.ProtocolError, e:
+ print 'Error making a LAVA request:', obfuscate_credentials(str(e))
+ sys.exit(1)
+
+ json.dump({'lava_url': 'https://' + lava_server_root,
+ 'job_id': lava_job_id}, open('lava-job-info', 'w'))
+ else:
+ print 'LAVA job submission skipped.'
+
+ sys.exit()
+
+
+def get_job_list():
+ job_list = ['CUSTOM_JSON_URL']
+ sec_job_prefix = 'CUSTOM_JSON_URL_'
+
+ for var in os.environ.keys():
+ if var.startswith(sec_job_prefix):
+ job_list.append(var)
+ job_list.sort()
+
+ return job_list
+
+
+def replace(fp, pattern, subst):
+ print pattern
+ print subst
+ for line in fileinput.input(fp, inplace=1):
+ if pattern in line:
+ line = line.replace(pattern, subst)
+ sys.stdout.write(line)
+ fileinput.close()
+
+
+def submit_job_from_url():
+ """This routine updates a predefined job with the parameters specific
+ to this particular build"""
+ job_list = get_job_list()
+ for job in job_list:
+ lava_job_url = os.environ.get(job)
+ if lava_job_url is None:
+ print "Error: No CUSTOM_JSON_URL provided"
+ return
+ jobresource = urllib2.urlopen(lava_job_url)
+ jobjson = open('job.json','wb')
+ jobjson.write(jobresource.read())
+ jobjson.close()
+ # Job name, defined by android-build, e.g. linaro-android_leb-panda
+ job_name = os.environ.get("JOB_NAME")
+ default_frontend_job_name = "~" + job_name.replace("_", "/", 1)
+ frontend_job_name = os.environ.get("FRONTEND_JOB_NAME", default_frontend_job_name)
+
+ # Build number, defined by android-build, e.g. 61
+ build_number = os.environ.get("BUILD_NUMBER")
+
+ # download base URL, this may differ from job URL if we don't host
+ # downloads in Jenkins any more
+ download_url = "%s/%s/%s/" % ('%s/android/' % snapshots_url,
+ frontend_job_name,
+ build_number)
+
+ # jenkins job name scheme doesn't apply for 96boards jobs so expect
+ # download_url to be provided by the job.
+ download_url = os.environ.get("DOWNLOAD_URL", download_url)
+
+ # Set the file extension based on the type of artifacts
+ artifact_type = os.environ.get("MAKE_TARGETS", "tarball")
+ if artifact_type == "droidcore":
+ # Check if File extension is already defined
+ file_extension = os.environ.get("IMAGE_EXTENSION", "img")
+ else:
+ file_extension = "tar.bz2"
+
+ boot_subst = "%s%s%s" % (download_url, "/boot.", file_extension)
+ system_subst = "%s%s%s" % (download_url, "/system.", file_extension)
+ userdata_subst = "%s%s%s" % (download_url, "/userdata.", file_extension)
+ cache_subst = "%s%s%s" % (download_url, "/cache.", file_extension)
+
+ replace("job.json", "%%ANDROID_BOOT%%", boot_subst)
+ replace("job.json", "%%ANDROID_SYSTEM%%", system_subst)
+ replace("job.json", "%%ANDROID_DATA%%", userdata_subst)
+ replace("job.json", "%%ANDROID_CACHE%%", cache_subst)
+ replace("job.json", "%%ANDROID_META_NAME%%", job_name)
+ replace("job.json", "%%JOB_NAME%%", job_name)
+ replace("job.json", "%%ANDROID_META_BUILD%%", build_number)
+ replace("job.json", "%%ANDROID_META_URL%%", os.environ.get("BUILD_URL"))
+ replace("job.json", "%%BUNDLE_STREAM%%", os.environ.get('LAVA_STREAM', '/private/team/linaro/android-daily/'))
+ replace("job.json", "%%WA2_JOB_NAME%%", build_number)
+ replace("job.json", "%%DOWNLOAD_URL%%", download_url)
+ replace("job.json", "%%GERRIT_CHANGE_NUMBER%%", os.environ.get("GERRIT_CHANGE_NUMBER", ""))
+ replace("job.json", "%%GERRIT_PATCHSET_NUMBER%%", os.environ.get("GERRIT_PATCHSET_NUMBER", ""))
+ replace("job.json", "%%GERRIT_CHANGE_URL%%", os.environ.get("GERRIT_CHANGE_URL", ""))
+ replace("job.json", "%%GERRIT_CHANGE_ID%%", os.environ.get("GERRIT_CHANGE_ID", ""))
+ replace("job.json", "%%REFERENCE_BUILD_URL%%", os.environ.get("REFERENCE_BUILD_URL", ""))
+ replace("job.json", "%%CTS_MODULE_NAME%%", os.environ.get("CTS_MODULE_NAME", ""))
+
+ # LAVA server URL
+ lava_server = os.environ.get('LAVA_SERVER',
+ 'validation.linaro.org/RPC2/')
+
+ with open("job.json", 'r') as fin:
+ print fin.read()
+
+ # Inject credentials after the job dump to avoid to leak
+ replace("job.json", "%%ART_TOKEN%%", os.environ.get("ART_TOKEN"))
+ replace("job.json", "%%ARTIFACTORIAL_TOKEN%%", os.environ.get("ARTIFACTORIAL_TOKEN"))
+ replace("job.json", "%%QA_REPORTS_TOKEN%%", os.environ.get("QA_REPORTS_TOKEN"))
+ replace("job.json", "%%AP_SSID%%", os.environ.get("AP_SSID"))
+ replace("job.json", "%%AP_KEY%%", os.environ.get("AP_KEY"))
+
+ with open("job.json") as fd:
+ config = fd.read().strip()
+ lava_submit(config=config, lava_server=lava_server)
+
+ sys.exit()
+
+
+def main():
+ '''Script entry point: return some JSON based on calling args.
+ We should be called from Jenkins and expect the following to be defined:
+ $HWPACK_BUILD_NUMBER $HWPACK_JOB_NAME HWPACK_FILE_NAME $DEVICE_TYPE
+ or, alternatively, $TARGET_PRODUCT $JOB_NAME $BUILD_NUMBER $BUILD_URL
+ '''
+
+ # LAVA server URL
+ lava_server = os.environ.get('LAVA_SERVER',
+ 'validation.linaro.org/RPC2/')
+
+ # CI user
+ ci_user = os.environ.get('CI_USER')
+ # CI pass
+ ci_pass = os.environ.get('CI_PASS')
+ if ci_user is not None and ci_pass is not None:
+ auth = auth_headers(ci_user, ci_pass)
+ else:
+ auth = None
+
+ if os.environ.get('TARGET_PRODUCT') is not None:
+ submit_job_from_url()
+
+ # Allow to override completely the generated json
+ # using file provided by the user
+ custom_url = os.environ.get('CUSTOM_JSON_URL')
+ if custom_url is None:
+ custom_url = os.environ.get('CUSTOM_YAML_URL')
+ if custom_url is not None:
+ request = urllib2.Request(custom_url)
+ if auth:
+ request.add_header('Authorization', auth)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError, e:
+ if hasattr(e, 'reason'):
+ print 'Failed to reach %s.' % custom_url
+ print 'Reason: ', e.reason
+ elif hasattr(e, 'code'):
+ print 'ci.linaro.org could not fulfill the request: %s' % \
+ custom_url
+ print 'Error code: ', e.code
+ sys.exit('Failed to get last successful artifact.')
+
+ if os.environ.get('CUSTOM_JSON_URL') is not None:
+ config = json.dumps(json.load(
+ response, object_pairs_hook=collections.OrderedDict),
+ indent=2, separators=(',', ': '))
+ else:
+ config = response.read()
+
+ lava_submit(config, lava_server)
+
+ # Name of the hardware pack project
+ hwpack_job_name = os.environ.get('HWPACK_JOB_NAME')
+ # The hardware pack build number
+ hwpack_build_number = os.environ.get('HWPACK_BUILD_NUMBER')
+ # Hardware pack file name
+ hwpack_file_name = os.environ.get('HWPACK_FILE_NAME', 'Undefined')
+ if hwpack_file_name == 'Undefined':
+ sys.exit('Hardware pack is not defined.')
+
+ # Device type
+ device_type = os.environ.get('DEVICE_TYPE', 'Undefined')
+ if device_type == 'Undefined':
+ sys.exit('Device type is not defined.')
+
+ # Pre-built image URL
+ image_url = os.environ.get('IMAGE_URL', 'Undefined')
+
+ # Hardware pack URL
+ hwpack_url = os.environ.get('HWPACK_URL', 'Undefined')
+
+ # Test definitions repository
+ git_repo = os.environ.get('GIT_REPO',
+ 'git://git.linaro.org/qa/test-definitions.git')
+
+ # Distribution, architecture and hardware pack type
+ distribution = os.environ.get('DISTRIBUTION', 'ubuntu')
+ architecture = os.environ.get('ARCHITECTURE', 'armhf')
+ if hwpack_job_name.startswith('package-and-publish'):
+ ret_split = hwpack_job_name.split('-', 3)
+ hwpack_type = ret_split[3]
+ elif hwpack_job_name.startswith('linux'):
+ hwpack_type = get_hwpack_type(hwpack_job_name)
+ else:
+ ret_split = hwpack_job_name.split('-', 2)
+ (distribution, architecture, hwpack_type) = \
+ ret_split[0], ret_split[1], ret_split[2]
+ hwpack_type = get_hwpack_type(hwpack_job_name, hwpack_file_name)
+
+ # Rootfs type, default is nano-lava
+ rootfs_type = os.getenv('ROOTFS_TYPE', 'nano-lava')
+
+ # Bundle stream name
+ bundle_stream_name = os.environ.get(
+ 'BUNDLE_STREAM_NAME',
+ '/private/team/linaro/developers-and-community-builds/')
+
+ lava_test_plan = os.environ.get('LAVA_TEST_PLAN')
+ if lava_test_plan is None:
+ # tests set specific to an image
+ tests = tests_nano
+ else:
+ lava_test_plan = lava_test_plan.strip("'")
+ tests = lava_test_plan.split()
+
+ # vexpress doesn't support PM, so disable pwrmgmt
+ if device_type in ['vexpress-a9']:
+ try:
+ tests.remove('pwrmgmt')
+ except ValueError:
+ pass
+
+ actions = [{'command': 'deploy_linaro_image'}]
+ deploy_image_parameters = {}
+ metadata = {}
+
+ if image_url == 'Undefined':
+ # Convert CI URLs to snapshots URLs
+ if hwpack_url == 'Undefined':
+ if hwpack_job_name.startswith('package-and-publish'):
+ hwpack_job_name_fixup = hwpack_job_name.replace('.', '_')
+ hwpack_url = '%s/%s/%s/%s/%s/%s' % \
+ (snapshots_url,
+ 'kernel-hwpack',
+ hwpack_job_name_fixup,
+ hwpack_job_name,
+ hwpack_build_number,
+ hwpack_file_name)
+ elif hwpack_job_name.startswith('linux'):
+ hwpack_url = '%s/%s/%s-%s/%s/%s' % \
+ (snapshots_url,
+ 'kernel-hwpack',
+ hwpack_job_name.split('/')[0],
+ hwpack_type,
+ hwpack_build_number,
+ hwpack_file_name)
+ else:
+ hwpack_url = '%s/%s/%s/%s/%s/%s' % \
+ (snapshots_url,
+ distribution,
+ 'hwpacks',
+ hwpack_type,
+ hwpack_build_number,
+ hwpack_file_name)
+
+ (rootfs_url, rootfs_build_number) = get_rootfs_url(distribution,
+ architecture,
+ rootfs_type)
+
+ deploy_image_parameters['hwpack'] = hwpack_url
+ deploy_image_parameters['rootfs'] = rootfs_url
+ metadata['rootfs.type'] = rootfs_type
+ metadata['rootfs.build'] = rootfs_build_number
+ else:
+ deploy_image_parameters['image'] = image_url
+
+ metadata['hwpack.type'] = hwpack_type
+ metadata['hwpack.build'] = hwpack_build_number
+ metadata['distribution'] = distribution
+
+ deploy_image_parameters_url = os.environ.get('DEPLOY_IMAGE_PARAMETERS_URL')
+ if deploy_image_parameters_url is not None:
+ request = urllib2.Request(deploy_image_parameters_url)
+ if auth:
+ request.add_header('Authorization', auth)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError, e:
+ if hasattr(e, 'reason'):
+ print 'Failed to reach %s.' % deploy_image_parameters_url
+ print 'Reason: ', e.reason
+ elif hasattr(e, 'code'):
+ print 'ci.linaro.org could not fulfill the request: %s' % \
+ deploy_image_parameters_url
+ print 'Error code: ', e.code
+ sys.exit('Failed to get last successful artifact.')
+
+ deploy_image_parameters.update(json.load(response, object_pairs_hook=collections.OrderedDict))
+
+ actions[0]['parameters'] = deploy_image_parameters
+
+ metadata_url = os.environ.get('METADATA_URL')
+ if metadata_url is not None:
+ request = urllib2.Request(metadata_url)
+ if auth:
+ request.add_header('Authorization', auth)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError, e:
+ if hasattr(e, 'reason'):
+ print 'Failed to reach %s.' % metadata_url
+ print 'Reason: ', e.reason
+ elif hasattr(e, 'code'):
+ print 'ci.linaro.org could not fulfill the request: %s' % \
+ metadata_url
+ print 'Error code: ', e.code
+ sys.exit('Failed to get last successful artifact.')
+
+ metadata.update(json.load(response, object_pairs_hook=collections.OrderedDict))
+
+ actions[0]['metadata'] = metadata
+
+ if len(tests) == 0:
+ actions.append({
+ 'command': 'boot_linaro_image'
+ })
+
+ boot_image_parameters_url = os.environ.get('BOOT_IMAGE_PARAMETERS_URL')
+ if boot_image_parameters_url is not None:
+ request = urllib2.Request(boot_image_parameters_url)
+ if auth:
+ request.add_header('Authorization', auth)
+ try:
+ response = urllib2.urlopen(request)
+ except urllib2.URLError, e:
+ if hasattr(e, 'reason'):
+ print 'Failed to reach %s.' % boot_image_parameters_url
+ print 'Reason: ', e.reason
+ elif hasattr(e, 'code'):
+ print 'ci.linaro.org could not fulfill the request: %s' % \
+ boot_image_parameters_url
+ print 'Error code: ', e.code
+ sys.exit('Failed to get last successful artifact.')
+
+ boot_image_parameters = json.load(response, object_pairs_hook=collections.OrderedDict)
+ if {'command': 'boot_linaro_image'} not in actions:
+ actions.append({
+ 'command': 'boot_linaro_image'
+ })
+ actions[1]['parameters'] = boot_image_parameters
+
+ if len(tests) > 0:
+ if distribution == 'quantal' or distribution == 'raring':
+ distribution = 'ubuntu'
+ for test in tests:
+ test_list = [({'git-repo': git_repo,
+ 'testdef': '{distribution:>s}/{test:>s}.yaml'.format(
+ distribution=distribution, test=test)})]
+
+ actions.append({
+ 'command': 'lava_test_shell',
+ 'parameters': {
+ 'timeout': tests_timeout.get(test, 18000),
+ 'testdef_repos': test_list
+ }
+ })
+
+ actions.append({
+ 'command': 'submit_results',
+ 'parameters': {
+ 'stream': bundle_stream_name,
+ 'server': '%s%s' % ('https://', lava_server)
+ }
+ })
+
+ # XXX Global timeout in LAVA is hardcoded to 24h (24 * 60 60)
+ # https://bugs.launchpad.net/bugs/1226017
+ # Set to 172800s (48h) to workaround the limitation
+ # A sane default is 900s (15m)
+ config = json.dumps({'timeout': 172800,
+ 'actions': actions,
+ 'job_name': '%s%s/%s/' % (ci_base_url,
+ hwpack_job_name,
+ hwpack_build_number),
+ 'device_type': device_type,
+ }, indent=2, separators=(',', ': '))
+
+ lava_submit(config, lava_server)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/post-build-lava/submit_for_testing.py b/post-build-lava/submit_for_testing.py
new file mode 100644
index 00000000..f4236733
--- /dev/null
+++ b/post-build-lava/submit_for_testing.py
@@ -0,0 +1,282 @@
+import argparse
+import os
+import re
+import requests
+import sys
+import StringIO
+from copy import deepcopy
+from string import Template
+from jinja2 import Environment, FileSystemLoader, StrictUndefined
+from ruamel.yaml import YAML
+
+
+try:
+ from urllib.parse import urlsplit
+except ImportError:
+ from urlparse import urlsplit
+
+
+# Templates base path
+template_base_path = 'configs/openembedded-lkft/lava-job-definitions'
+testplan_base_path = 'configs/openembedded-lkft/lava-job-definitions/'
+testplan_device_path = 'devices/'
+# Snapshots base URL
+snapshots_url = 'https://snapshots.linaro.org/openembedded/lkft'
+
+def parse_template(yaml_string):
+ '''
+ Round trip lava_job through ruamel to test parsing and
+ improve formatting. Comments are preserved.
+
+ In: yaml-formatted string
+ Out: validated yaml-formatted string
+ '''
+ yaml = YAML()
+ # ruamel does not provide a mechanism to dump to string, so use StringIO
+ # to catch it
+ output = StringIO.StringIO()
+ yaml.dump(yaml.load(yaml_string), output)
+ # strip empty lines from output
+ return re.sub(r'^\s*$\n', '', output.getvalue(), flags=re.MULTILINE)
+
+def get_job_name(lava_job_string):
+ '''
+ In: yaml-formatted string
+ Out: LAVA job's name
+ '''
+ yaml = YAML()
+ lava_job = yaml.load(lava_job_string)
+ return lava_job['job_name']
+
+def _load_template(template_name, template_path, device_type):
+ template = ''
+ template_file_name = ''
+
+ if template_name:
+ template_file_name = "%s/%s/%s" % (template_path,
+ device_type,
+ template_name)
+ if os.path.exists(template_file_name):
+ with open(template_file_name, 'r') as f:
+ template = f.read()
+ else:
+ print('template (%s) was specified but not exists' %
+ template_file_name)
+ sys.exit(1)
+
+ return template, template_file_name
+
+
+def _submit_to_squad(lava_job, lava_url_base, qa_server_api, qa_server_base, qa_token, quiet):
+ headers = {
+ "Auth-Token": qa_token
+ }
+
+ try:
+ data = {
+ "definition": lava_job,
+ "backend": urlsplit(lava_url_base).netloc # qa-reports backends are named as lava instances
+ }
+ print("Submit to: %s" % qa_server_api)
+ results = requests.post(qa_server_api, data=data, headers=headers,
+ timeout=31)
+ if results.status_code < 300:
+ print("%s/testjob/%s %s" % (qa_server_base, results.text, get_job_name(lava_job)))
+ else:
+ print(results.status_code)
+ print(results.text)
+ except requests.exceptions.RequestException as err:
+ print("QA Reports submission failed")
+ if not quiet:
+ print("offending job definition:")
+ print(lava_job)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--device-type",
+ help="Device type in LAVA",
+ dest="device_type",
+ required=True)
+ parser.add_argument("--environment",
+ help="User specified the environment name, prefix or suffix won't be used",
+ dest="environment",
+ default="")
+ parser.add_argument("--env-prefix",
+ help="Prefix for the environment name",
+ dest="env_prefix",
+ default="")
+ parser.add_argument("--env-suffix",
+ help="Suffix for the environment name",
+ dest="env_suffix",
+ default="")
+ parser.add_argument("--build-number",
+ help="Build number",
+ dest="build_number",
+ required=True)
+ parser.add_argument("--qa-server-team",
+ help="Team in QA Reports service",
+ dest="qa_server_team",
+ required=True)
+ parser.add_argument("--qa-server-project",
+ help="Project in QA Reports service",
+ dest="qa_server_project",
+ required=True)
+ parser.add_argument("--qa-server",
+ help="QA Reports server",
+ dest="qa_server",
+ default="https://qa-reports.linaro.org")
+ parser.add_argument("--qa-token",
+ help="QA Reports token",
+ dest="qa_token",
+ default=os.environ.get('QA_REPORTS_TOKEN'))
+ parser.add_argument("--lava-server",
+ help="LAVA server URL",
+ dest="lava_server",
+ required=True)
+ parser.add_argument("--git-commit",
+ help="git commit ID",
+ dest="git_commit",
+ required=True)
+ parser.add_argument("--template-path",
+ help="Path to LAVA job templates",
+ dest="template_path",
+ default=template_base_path)
+ parser.add_argument("--testplan-path",
+ help="Path to Jinja2 LAVA job templates",
+ dest="testplan_path",
+ default=testplan_base_path)
+ parser.add_argument("--testplan-device-path",
+ help="Relative path to Jinja2 device deployment fragments",
+ dest="testplan_device_path",
+ default=testplan_device_path)
+ parser.add_argument("--template-base-pre",
+ help="base template used to construct templates, previous",
+ dest="template_base_pre")
+ parser.add_argument("--template-base-post",
+ help="base template used to construct templates, posterior",
+ dest="template_base_post")
+ parser.add_argument("--template-names",
+ help="list of the templates to submit for testing",
+ dest="template_names",
+ nargs="+",
+ default=[])
+ parser.add_argument("--test-plan",
+ help="""list of the Jinja2 templates to submit for testing.
+ It is assumed that the templates produce valid LAVA job
+ definitions. All varaibles are substituted using Jinja2
+ engine. This includes environment variables.""",
+ dest="test_plan",
+ nargs="+",
+ default=[])
+ parser.add_argument("--dry-run",
+ help="""Prepare and write templates to tmp/.
+ Don't submit to actual servers.""",
+ action='store_true',
+ dest="dryrun")
+ parser.add_argument("--quiet",
+ help="Only output the final qa-reports URL",
+ action='store_true',
+ dest="quiet")
+
+ args, _ = parser.parse_known_args()
+
+ output_path = "tmp"
+ if args.dryrun:
+ if not os.path.exists(output_path):
+ os.mkdir(output_path)
+ if args.qa_token is None and not args.dryrun:
+ print("QA_REPORTS_TOKEN is missing")
+ sys.exit(1)
+
+ qa_server_base = args.qa_server
+ if not (qa_server_base.startswith("http://") or qa_server_base.startswith("https://")):
+ qa_server_base = "https://" + qa_server_base
+ qa_server_team = args.qa_server_team
+ qa_server_project = args.qa_server_project
+ qa_server_build = args.git_commit
+
+ if not args.environment:
+ # when user not specify value for the environment option,
+ # use the device_type as before
+ qa_server_env = args.env_prefix + args.device_type + args.env_suffix
+ else:
+ # when user specified value for the environment option,
+ # use the user specified value
+ qa_server_env = args.environment
+
+ qa_server_api = "%s/api/submitjob/%s/%s/%s/%s" % (
+ qa_server_base,
+ qa_server_team,
+ qa_server_project,
+ qa_server_build,
+ qa_server_env)
+ lava_server = args.lava_server
+ if not (lava_server.startswith("http://") or lava_server.startswith("https://")):
+ lava_server = "https://" + lava_server
+ lava_url_base = "%s://%s/" % (urlsplit(lava_server).scheme, urlsplit(lava_server).netloc)
+
+ template_base_pre, _ = _load_template(args.template_base_pre,
+ args.template_path,
+ args.device_type)
+ template_base_post, _ = _load_template(args.template_base_post,
+ args.template_path,
+ args.device_type)
+ lava_jobs = []
+ for test in args.template_names:
+ test_template, template_file_name = _load_template(test,
+ args.template_path,
+ args.device_type)
+ if template_base_pre:
+ test_template = "%s\n%s" % (template_base_pre, test_template)
+ if template_base_post:
+ test_template = "%s\n%s" % (test_template, template_base_post)
+
+ template = Template(test_template)
+ print("using template: %s" % template_file_name)
+ lava_job = template.substitute(os.environ)
+ lava_job = parse_template(lava_job)
+ lava_jobs.append(lava_job)
+
+ if not args.quiet:
+ print(lava_job)
+ if args.dryrun:
+ testpath = os.path.join(output_path, args.device_type, test)
+ if not os.path.exists(os.path.dirname(testpath)):
+ os.makedirs(os.path.dirname(testpath))
+ with open(os.path.join(testpath), 'w') as f:
+ f.write(lava_job)
+
+ THIS_DIR = os.path.abspath(args.testplan_path)
+ # prevent creating templates when variables are missing
+ j2_env = Environment(loader=FileSystemLoader(THIS_DIR, followlinks=True), undefined=StrictUndefined)
+ context = deepcopy(os.environ)
+ context.update({"device_type": os.path.join(args.testplan_device_path, args.device_type)})
+ for test in args.test_plan:
+ ''' Prepare lava jobs '''
+ lava_job = j2_env.get_template(test).render(context)
+ lava_job = parse_template(lava_job)
+ lava_jobs.append(lava_job)
+
+ if not args.quiet:
+ print(lava_job)
+ if args.dryrun:
+ testpath = os.path.join(output_path, args.device_type, test)
+ if not os.path.exists(os.path.dirname(testpath)):
+ os.makedirs(os.path.dirname(testpath))
+ with open(os.path.join(testpath), 'w') as f:
+ f.write(lava_job)
+
+ for lava_job in lava_jobs:
+ ''' Submit lava jobs '''
+ if not args.dryrun:
+ _submit_to_squad(lava_job,
+ lava_url_base,
+ qa_server_api,
+ qa_server_base,
+ args.qa_token,
+ args.quiet)
+
+
+if __name__ == "__main__":
+ main()