[yocto] [yocto-autobuilder-helper][PATCH 3/6] trigger-lava-jobs: Add LAVA RPC trigger pipeline script

Aaron Chan aaron.chun.yew.chan at intel.com
Wed Aug 29 06:25:52 PDT 2018


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 <aaron.chun.yew.chan at intel.com>
---
 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__ = "aaron.chun.yew.chan at intel.com"
+'''
+
+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 <aaron.chun.yew.chan at intel.com>
+# 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



More information about the yocto mailing list