mirror of
git://git.yoctoproject.org/yocto-autobuilder-helper.git
synced 2025-10-22 23:02:21 +02:00
trigger-lava-jobs: Add LAVA RPC trigger pipeline script
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@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
fc933b86e5
commit
bbf23b22b4
70
lava/lava_scheduler.py
Normal file
70
lava/lava_scheduler.py
Normal file
|
@ -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@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)
|
218
lava/trigger-lava-jobs
Executable file
218
lava/trigger-lava-jobs
Executable file
|
@ -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@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()
|
Loading…
Reference in New Issue
Block a user