trigger-lava-jobs accepts the YAML pipeline lava-job config file generated by run-jinja-parser scripts. This triggers a new job at LAVA end thru RPC and parses the authentication token and user credentials to launch/start the hardware automation on LAVA Dispatcher.Script will exit on error when lava-job return a state either incomplete or canceling stage.
trigger-lava-jobs uses lava_scheduler.py python module where the LAVA classes and library constructed from XML-RPC API which are define and supported by Linaro, LAVA. Signed-off-by: Aaron Chan <[email protected]> --- lava/lava_scheduler.py | 70 ++++++++++++++++ lava/trigger-lava-jobs | 218 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 lava/lava_scheduler.py create mode 100755 lava/trigger-lava-jobs diff --git a/lava/lava_scheduler.py b/lava/lava_scheduler.py new file mode 100644 index 0000000..10839c7 --- /dev/null +++ b/lava/lava_scheduler.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +''' +author__ = "Aaron Chan" +__copyright__ = "Copyright 2018, Intel Corp" +__credits__" = ["Aaron Chan"] +__license__" = "GPL" +__version__" = "1.0" +__maintainer__ = "Aaron Chan" +__email__ = "[email protected]" +''' + +import xmlrpc + +class scheduler(): + + def __init__(self, server, user, token, url): + self.server = server + self.user = user + self.token = token + self.url = url + + @classmethod + # Description: Submit the given job data which is in LAVA + # job JSON or YAML format as a new job to + # LAVA scheduler. + # Return: dict <type> + def lava_jobs_submit(self, server, data): + return server.scheduler.jobs.submit(data) + + @classmethod + # Description: Cancel the given job referred by its id + # Return: Boolean <type> + def lava_jobs_cancel(self, server, jobid): + state = server.scheduler.jobs.cancel(jobid) + if type(state) is bool: return state + + @classmethod + def lava_jobs_resubmit(self, server, jobid): + return server.scheduler.jobs.resubmit(jobid) + + @classmethod + # Description: Return the logs for the given job + # Args: jobid <str>, line <int> - Show only after the given line + # Return: tuple <type> + def lava_jobs_logs(self, server, jobid, line): + return server.scheduler.jobs.logs(jobid, line) + + @classmethod + # Description: Show job details + # Return: Dict <type> + def lava_jobs_show(self, server, jobid): + return server.scheduler.jobs.show(jobid) + + @classmethod + # Description: Return the job definition + # Return: Instance <type> + def lava_jobs_define(self, server, jobid): + return server.scheduler.jobs.definition(jobid) + + @classmethod + def lava_jobs_status(self, server, jobid): + return server.scheduler.job_status(jobid) + + @classmethod + def lava_jobs_output(self, server, jobid, offset): + return server.scheduler.job_output(jobid, offset) + + @classmethod + def lava_jobs_details(self, server, jobid): + return server.scheduler.job_details(jobid) diff --git a/lava/trigger-lava-jobs b/lava/trigger-lava-jobs new file mode 100755 index 0000000..5b7a6dd --- /dev/null +++ b/lava/trigger-lava-jobs @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +# +# ===================================================================================== +# XML-RPC API reference taken from +# -- https://validation.linaro.org/static/docs/v2/data-export.html#xml-rpc +# Developed By : Chan, Aaron <[email protected]> +# Organization : Yocto Project Open Source Technology Center (Intel) +# Date : 27-Aug-2018 (Initial release) +# ===================================================================================== +# +# Triggers a job execution define by YAML template on LAVA server end from autobuilder. +# This script will monitor the lava-job status until the hardware boots up successfully +# and returns the IPv4 addr pre-configure over network boot (PXE) on the board. +# Once the IPv4 addr has been recovered, script will update the auto.conf with +# TEST_TARGET_IP, TEST_SERVER_IP to establish a client-host connection and prepare to +# execute automated harware test case(s) on hardware on the next step. +# +# Options: +# +# $1 - Supply lava-job template in a YAML format (e.g. <filename>.yaml) +# $2 - Supply autobuilder buildername (e.g. nightly-x86-64-bsp, nightly-arm64-bsp) +# $3 - By default set to "None", else parse in the buildnumber to create the NFS path +# $4 - Supply device/board name (same as LAVA device type) +# +import xmlrpc.client +import sys +import os +import time +import re +import json +import netifaces +import time +from shutil import copyfile +from lava_scheduler import * + +sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),"scripts")) +import utils + +# Enable this section on manual run +# os.environ['ABHELPER_JSON'] = "config.json /home/pokybuild/yocto-autobuilder-helper/config-intelqa-x86_64-lava.json" + +def lava_jobsStatus(server, jobid, timeout, bootUp=False, ipaddr=None, iparch=None, timeInt=5): + jobStatus = scheduler.lava_jobs_status(server, jobid)['job_status'] + + while jobStatus == "Submitted" or jobStatus == "Running" or bootUp == False: + time.sleep(timeInt) + timeout += timeInt + for logs in scheduler.lava_jobs_logs(server, jobid, 0): + ipaddr = re.search("Station\s*IP\s*address\s*is\s*(.*)\"", str(logs), re.I) + iparch = re.search("Detected\s*architecture\s*(.*)\.", str(logs), re.I) + dbmsg = re.search("Board\s*boot\s*up\s*successfully", str(logs), re.I) + if ipaddr: ipaddr = ipaddr.group(1) + if iparch: iparch = iparch.group(1) + if dbmsg: + bootUp = True + break + if bootUp: + print("INFO : Board booted up successfully. LAVA is ready to handover to Buildbot-CI [%s]" % str(bootUp)) + break + if timeout > 3000: + scheduler.lava_jobs_cancel(server, jobid) + print("WARNING: Board exceeded bootup time threshold %d, Job will be %s and powered down" % ( + timeout, scheduler.lava_jobs_status(server, jobid)['job_status'])) + break + + jobStatus = scheduler.lava_jobs_status(server, jobid)['job_status'] + print("INFO : Job has current status [%s]" % jobStatus) + # Job Status # + if jobStatus == 'Incomplete': + print("ABORTED: %s test!. Rerun again if required" % jobStatus) + elif jobStatus == 'Cancel': + print("ABORTED: Job has been [%s]led by user" % jobStatus) + elif jobStatus == 'Complete': + print("SUCCESS: Current JobID [%s] has been successfully [%s] and passed" % (jobid, jobStatus)) + elif jobStatus == 'Running': + print("INFO : %s in %d seconds ..." % (jobStatus, timeout)) + elif jobStatus == 'Submitted': + print("INFO : Lava job has been successfully %s and running in progress..." % jobStatus) + else: + print("ERROR : Job is either %s or in an unknown state. Report to LAVA Mailing Lists" % jobStatus) + + return ipaddr, iparch, jobStatus + + +def lava_jobsSubmit(server, hostname, cfgfile, debug=False): + #if os.path.isfile(cfgfile): + with open(cfgfile, 'r') as yaml: + yamlCfg = yaml.read() + yaml.close() + + if debug: print("INFO : Current YAML Job Definition\n%s" % yamlCfg) + jobid = scheduler.lava_jobs_submit(server, yamlCfg) + if jobid is not None: + print("SUCCESS: Job submitted to http://%s/scheduler/job/%s#bottom to LAVA-CI server" % (hostname, jobid)) + (boardIp, boardArch, boardStat) = lava_jobsStatus(server, jobid, 0) + #else: + # print("ERROR: YAML Config not found on the LAVA-Server") + return boardIp, boardArch, boardStat, jobid + + +def lava_jobsDetail(server, jobid, elements, items=[]): + jobInfo = scheduler.lava_jobs_details(server, jobid) + + if type(elements) is str: + return jobInfo[elements] + elif type(elements) is list: + for item in elements: + items.append(jobInfo[item]) + return items + else: + return jobInfo + + +def lava_listmethods(server): + print(server.system.listMethods()) + + +def lava_publisher(username, token, server): + return xmlrpc.client.ServerProxy("http://%s:%s@%s/RPC2/" % (username, token, server)) + + +def check_isfile(filename): + if not os.path.isfile(filename): + print("ERROR: Failed to locate filename %s" % filename) + sys.exit(1) + return True + +def check_until(filename, isfound=None): + if os.path.isfile(filename): + isfound=False + else: + isfound=True + return isfound + +# Starts here +def main(): + """ + For Yocto Project Reference scripts, this tool was developed to trigger a Job in LAVA + server based on URL, TCP/IP port defined on config-intelqa-x86_64-lava.json. + Requirement to run this script to ensure <filename>.yaml, build/build/conf/auto.conf + is present. + """ + yamlconf = sys.argv[1] + autoconf = sys.argv[2] + try : boardinfo = sys.argv[3] + except: boardinfo = None + + ourconfig = utils.loadconfig() + lavadefs = ourconfig["lava-defaults"] + username = lavadefs['username'] + token = lavadefs['token'] + server = lavadefs['server'] + interface = lavadefs['interface'] + + timemin = timesec = 0 + cwd=os.path.join(os.getcwd(), "board_info.json") + + # Instantiate LAVA server connection with RPC + lavaserver = lava_publisher(username, token, server) + + isfile = check_isfile(yamlconf) + if isfile: + target_ip, boardArch, boardStat, jobid = lava_jobsSubmit(lavaserver, server, yamlconf) + + if boardStat == 'Canceling' and boardStat == 'Incomplete': + print("Board/hardware unresponsive or software image loaded is incompatiable. Ending session.") + sys.exit(1) + + if boardinfo is not None: + boardinfo = os.path.join(boardinfo, str(jobid), 'board_info.json') + print("Search if board info exists [%s]" % boardinfo) + while(check_until(boardinfo)): + time.sleep(1) + timesec += 1 + if timesec > 2500: + print("Board discovery exceeds timeout %s. Ending session." % str(timesec)) + sys.exit(1) + print("Board discovery in %s secs ..." % str(timesec)) + (timemin, timesec)=divmod(timesec, 60) + print("Board has been discovered in %s mins, %s secs" % (timemin, timesec)) + + if os.path.isfile(cwd): + os.system(" ls -al %s" % boardinfo) + print("Board info existed, file will be deleted and recopied over to %s" % cwd) + os.remove(cwd) + else: + print("Board info to be copied over to %s" % cwd) + copyfile(boardinfo, cwd) + + os.environ['ABHELPER_JSON'] += (" " + boardinfo) + ourconfig = utils.loadconfig() + target_ip = ourconfig['network']['ipaddr'] + + lavaurl = "http://" + server + str(lava_jobsDetail(lavaserver, jobid, 'absolute_url')) + jobinfo = lava_jobsDetail(lavaserver, jobid, ['actual_device_id', 'start_time', 'end_time']) + server_ip = netifaces.ifaddresses(interface)[2][0]['addr'] + + # Update auto.conf with board IPv4 and server IPv4 addressing + isauto = check_isfile(autoconf) + if isauto: + with open(autoconf, "a") as autof: + autof.writelines("TEST_SERVER_IP = \"%s\"\n" % server_ip) + autof.writelines("TEST_TARGET_IP = \"%s\"\n" % target_ip) + autof.close() + + print("="*50) + print(""" + SUMMARY: + LAVA-url : %s + LAVA-job : %s + LAVA-status : %s + Device-IP : %s + Device-ARCH : %s + """ % (lavaurl, jobinfo, boardStat, target_ip, boardArch)) + print("="*50) + +if __name__ == '__main__': + main() -- 2.11.0 -- _______________________________________________ yocto mailing list [email protected] https://lists.yoctoproject.org/listinfo/yocto
