aboutsummaryrefslogtreecommitdiffstats
path: root/userdata/conf/lava/lava-boot/lava-boot
diff options
context:
space:
mode:
Diffstat (limited to 'userdata/conf/lava/lava-boot/lava-boot')
-rwxr-xr-xuserdata/conf/lava/lava-boot/lava-boot373
1 files changed, 373 insertions, 0 deletions
diff --git a/userdata/conf/lava/lava-boot/lava-boot b/userdata/conf/lava/lava-boot/lava-boot
new file mode 100755
index 0000000..b6d3d7f
--- /dev/null
+++ b/userdata/conf/lava/lava-boot/lava-boot
@@ -0,0 +1,373 @@
+#!/usr/bin/python
+
+import argparse
+import atexit
+import json
+import os
+import re
+import requests
+import string
+import subprocess
+import sys
+import time
+import xmlrpclib
+import yaml
+from random import randint
+
+import ssl
+
+ssl._create_default_https_context = ssl._create_unverified_context
+
+
+# mapping device tree blob - device type
+device = {
+ 'beaglebone-black' : { 'dtb' : 'am335x-boneblack.dtb', 'kernel' : 'zImage' },
+ 'arndale' : { 'dtb' : 'exynos5250-arndale.dtb', 'kernel' : 'zImage' },
+ 'arndale-octa' : { 'dtb' : 'exynos5250-arndale-octa.dtb', 'kernel' : 'zImage'},
+ 'mustang' : { 'dtb' : 'apm-mustang.dtb', 'kernel' : 'Image', 'arch' : 'arm64'},
+ 'juno' : { 'dtb' : 'juno.dtb', 'kernel' : 'Image', 'arch' : 'arm64'},
+}
+
+replacetable = {
+'ROOTFS_ARM' : "https://releases.linaro.org/14.12/ubuntu/utopic-images/nano/linaro-utopic-nano-20141212-693.tar.gz",
+'ROOTFS_ARM64' : "http://people.linaro.org/~riku.voipio/linaro-utopic-nano64-20150114-87.tar.gz"
+}
+
+
+def obfuscate_credentials(s):
+ return re.sub(r'([^ ]:).+?(@)', r'\1xxx\2', s)
+
+def print_time():
+ return "["+time.strftime("%H:%M:%S", time.gmtime())+"]"
+
+def get_path(filename):
+ if os.path.exists(filename):
+ return filename
+ corepath = os.path.dirname(sys.argv[0])
+ longpath=os.path.join(corepath,filename)
+ if os.path.exists(longpath):
+ return longpath
+ else:
+ print '%s Error unable to open %s' % (print_time(), filename)
+ sys.exit(1)
+
+def read_settings(arguments):
+ settings_file=os.path.expanduser("~/.lava.yaml")
+ with open(settings_file) as f:
+ settings=yaml.safe_load(f.read())
+
+ # user asked for specific server
+ if arguments.server is not None:
+ for setting in settings:
+ if ( 'server' in setting and
+ setting['server'] == arguments.server ):
+ return setting
+ sys.stderr.write("server %s not found in ~/.lava.yaml\n" % arguments.server)
+ sys.exit(3)
+
+ # users with only one lava server in their config
+ if 'server' in settings:
+ return settings
+
+ # if a list of servers, use first
+ for setting in settings:
+ if 'server' in setting:
+ return setting
+
+ # give up!
+ sys.stderr.write("no useful server ~/.lava.yaml!\n")
+ sys.exit(3)
+
+def setup_replacetable(settings, variables, mydtb):
+ for key, value in settings.iteritems():
+ replacetable[key.upper()]=value
+
+ device_type=None
+ if not mydtb.endswith(".dtb") and mydtb in device:
+ device_type=mydtb
+ else:
+ for machine, features in device.iteritems():
+ if features['dtb'] == mydtb:
+ device_type = machine
+ break
+
+ if device_type == None:
+ sys.stderr.write("Invalid value for dtb/device: %s\n" % mydtb )
+ sys.exit(2)
+
+ replacetable['DEVICE_TYPE']=device_type
+
+ if 'arch' in device[device_type] and device[device_type]['arch'] == 'arm64':
+ replacetable['ROOTFS'] = replacetable['ROOTFS_ARM64']
+ else:
+ replacetable['ROOTFS'] = replacetable['ROOTFS_ARM']
+
+ if os.path.isdir(".git"):
+ try:
+ mystr=subprocess.check_output(["git","log","-n1","--pretty=oneline"]).splitlines()[0]
+ replacetable['GIT_COMMIT']=re.sub(r'([^\s\w.-]|_)+','',mystr)
+ except subprocess.CalledProcessError, e:
+ print "%s Git commit unavailable" %s (print_time())
+
+ if variables is None:
+ return
+
+ for variable in variables:
+ varlist=variable.split("=")
+ if len(varlist) == 2:
+ replacetable[varlist[0].upper()]=varlist[1]
+ else:
+ sys.stderr.write("Invalid --variable parameter: %s\n" % variable )
+ sys.exit(2)
+
+
+def substitute_file(filename):
+ with open(get_path(filename)) as f:
+ template = string.Template(f.read())
+ try:
+ replaced=template.substitute(replacetable)
+ except KeyError, e:
+ sys.stderr.write("Missing key in replacement table: %s\n" % str(e) )
+ sys.exit(2)
+ return replaced
+
+def upload_file(upload_url, filename, replacename):
+ upload_string=substitute_file(filename)
+ basename=os.path.basename(filename)
+ replacetable[replacename]=basename
+ print '%s Uploading %s' % (print_time(), basename)
+ r = requests.post(upload_url, files={'filename': (basename, upload_string) })
+
+def upload_kernel(upload_url, searchdir):
+
+ device_type=replacetable['DEVICE_TYPE']
+ lava_device=device[device_type]
+ kernel_name= lava_device['kernel']
+ dtb = lava_device['dtb']
+
+ for root, dirs, files in os.walk(searchdir + "/arch/"):
+ for f in files:
+ if f == kernel_name:
+ replacetable['KERNEL']=kernel_name
+ kernel=os.path.join(root,f)
+ print '%s Uploading kernel %s' % (print_time(), kernel)
+ r = requests.post(upload_url,
+ files={'filename': open(kernel, 'rb')})
+
+ if f == dtb:
+ replacetable['DTB']=dtb
+ dtb=os.path.join(root,f)
+ print '%s Uploading dtb %s for %s' % (print_time(), dtb, device_type)
+ r = requests.post(upload_url,
+ files={'filename': open(dtb, 'rb')})
+
+ if not 'DTB' in replacetable:
+ sys.stderr.write("Failed to upload dtb: %s \n" % dtb )
+ sys.exit(2)
+ if not 'KERNEL' in replacetable:
+ sys.stderr.write("Failed to upload kernel: %s \n" % kernel_name )
+ sys.exit(2)
+
+def poll_job(server, job_id, settings, upload_url, poll_type):
+ return_code=2
+ prev_status=""
+ while True:
+ status=server.scheduler.job_status(job_id)
+ s=status['job_status']
+ if s == 'Complete':
+ if poll_type=='script':
+ r = requests.get("%s/return_code" % upload_url)
+ if r.status_code == 200:
+ sys.stdout.write("\n%s LAVA Job %s, script finished: %s\n" % (print_time(), job_id, r.text))
+ sys.stdout.flush()
+ if r.text=='success':
+ return 0
+ else:
+ return 3
+
+ return_code=0
+ break
+ elif s == 'Incomplete' or s == 'Canceled' or s == 'Canceling':
+ return_code=1
+ if prev_status == 'Submitted':
+ return_code=4
+ break
+
+ if prev_status != s:
+ sys.stdout.write("\n%s LAVA Job %s, status: %s " % (print_time(), job_id, s))
+ sys.stdout.flush()
+ elif poll_type=='hack' and s == "Running":
+ r = requests.get("%s/ssh" % upload_url)
+ if r.status_code == 200:
+ sys.stdout.write("\n%s LAVA Job %s, hacking session: %s\n" % (print_time(), job_id, r.text))
+ sys.stdout.flush()
+ start_hacking(settings, r.text)
+
+ sys.stdout.write(".")
+ sys.stdout.flush()
+ else:
+ sys.stdout.write(".")
+ sys.stdout.flush()
+ prev_status=s
+ time.sleep(30)
+ sys.stdout.write("\n%s LAVA Job %s finished, status: %s\n" % (print_time(), job_id, s))
+ sys.stdout.flush()
+ return return_code
+
+def start_hacking(settings, ipaddr):
+
+ sshfile=""
+ jump=""
+ if 'proxy-ssh' in settings:
+ jump="-o 'ProxyCommand=ssh %s netcat %%h %%p'" % (settings['proxy-ssh'])
+ if 'sshfile' in settings:
+ sshfile="-o IdentityFile=%s" % (settings['sshfile'])
+
+ # FIXME check that ipaddr is really only and ipaddr
+ ssh_command = "ssh %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null %s root@%s" % (sshfile, jump, ipaddr)
+ print "%s session started with: %s" %(print_time(), ssh_command)
+ subprocess.call(ssh_command, shell=True)
+ # FIXME xmlrpc call cancel session to tear it down
+ sys.exit(0)
+
+def random_port():
+ return str(randint(8000,50000))
+
+def start_proxy(settings):
+ # FIXME: try another TCP port if first one is reserved
+ proxyport=random_port()
+ proxy="http://"+settings['proxy-hostname']+":"+proxyport
+ replacetable['PROXY']=proxy
+ if 'proxy-ssh' in settings:
+ ssh_host=settings['proxy-ssh']
+ port_forward=proxyport+":localhost:"+proxyport
+ subprocess.check_call(["scp", get_path("proxy.py"), ssh_host+":"],
+ stdout=subprocess.PIPE)
+ proxy_process=subprocess.Popen(["ssh","-t","-t","-L" ,port_forward, ssh_host, "~/proxy.py", proxyport],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ print '%s Proxy started at: %s, URL: %s' % (print_time(), ssh_host, proxy)
+ else:
+ ssh_host=None
+ proxy_process=subprocess.Popen([get_path("proxy.py"), proxyport],
+ stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ print '%s Proxy started at: localhost, URL: %s' % (print_time(), proxy)
+ atexit.register(exit_cleanup, proxy_process)
+ time.sleep(2)
+ return "http://localhost:"+proxyport+"/"
+
+def exit_cleanup(proxy_process):
+ proxy_process.kill()
+
+
+def lava_submit(template, settings):
+ lava_server=settings['server']
+ lava_user=settings['user']
+ lava_token=settings['token']
+ if 'https' in settings and settings['https'] is not True:
+ server_url = 'http://{lava_user:>s}:{lava_token:>s}@{lava_server:>s}/RPC2'
+ result_url = 'http://' + lava_server + "/RPC2"
+ else:
+ server_url = 'https://{lava_user:>s}:{lava_token:>s}@{lava_server:>s}/RPC2'
+ result_url = 'https://' + lava_server + "/RPC2"
+ replacetable['RESULT_URL']= result_url
+ replacetable['JOB_NAME']="lava-boot job for "+lava_user
+
+ config=substitute_file(template)
+ if template.endswith(".yaml"):
+ config=json.dumps(yaml.safe_load(config), indent=2)
+
+ try:
+ 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)
+ except xmlrpclib.ProtocolError, e:
+ print 'Error making a LAVA request:', obfuscate_credentials(str(e))
+
+ print '%s LAVA Job %s, URL: http://%s/scheduler/job/%s' % \
+ (print_time(), lava_job_id, lava_server, lava_job_id)
+ return server, lava_job_id
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Generate LAVA job definition')
+ parser.add_argument('-s', '--server',
+ help='server selection from ~/.lava.yaml, default first one')
+ parser.add_argument('-j', '--job',
+ help='Job template for LAVA, json or yaml')
+ parser.add_argument('-t', '--testcase',
+ help='lava-test-shell definition (optional)')
+ parser.add_argument('-r', '--run-script',
+ help='run script on target device')
+ parser.add_argument('-k', '--kerneldir',
+ help='kernel build dir')
+ parser.add_argument('-d', '--device', default='arndale',
+ help='lava device type or dtb file to use')
+ parser.add_argument('-x', '--hack', action='store_true',
+ help='start a hacking session')
+ parser.add_argument('-z','--skip-lava', action='store_true',
+ help="Dont submit to lava")
+ parser.add_argument('-q','--quiet', action='store_true',
+ help="Dont print job log")
+ parser.add_argument('-v','--variable', action='append',
+ help="with -v foo=bar ${FOO} will replaced with bar in job template")
+ parser.add_argument('-a','--async', action='store_true',
+ help="dont wait for lava job to finish")
+
+ arguments = parser.parse_args()
+
+ settings=read_settings(arguments)
+ setup_replacetable(settings, arguments.variable, arguments.device)
+ poll_type=None
+
+ if ( arguments.kerneldir is not None or
+ arguments.testcase is not None or
+ arguments.run_script is not None or
+ arguments.hack is True ):
+ upload_url=start_proxy(settings)
+ if arguments.testcase is not None:
+ testcase=arguments.testcase
+ else:
+ testcase=None
+ if arguments.kerneldir is not None:
+ upload_kernel(upload_url, arguments.kerneldir)
+ if arguments.run_script is not None:
+ poll_type='script'
+ upload_file(upload_url, arguments.run_script, 'SCRIPT')
+ if arguments.testcase is None:
+ testcase="test_definition.yaml"
+ if testcase is not None:
+ upload_file(upload_url, testcase, 'TESTCASE')
+ else:
+ upload_url=None
+
+ if arguments.skip_lava:
+ sys.exit(0)
+ if arguments.hack:
+ poll_type='hack'
+ if arguments.job is not None:
+ job=arguments.job
+ elif arguments.hack:
+ job= "hack.yaml"
+ elif arguments.run_script:
+ job="template.yaml"
+ else:
+ print "no job definition given"
+ sys.exit(1)
+
+ server, job_id = lava_submit(job, settings)
+ if arguments.async:
+ sys.exit(0)
+
+ return_code=poll_job(server, job_id, settings, upload_url, poll_type)
+
+ if arguments.quiet is False and return_code != 4:
+ job_log=server.scheduler.job_output(job_id)
+ print job_log
+
+ sys.exit(return_code)
+
+if __name__ == '__main__':
+ main()