#!/usr/bin/env python import sys import os import re import json import copy import xmlrpclib def obfuscate_credentials(s): return re.sub(r"([^ ]:).+?(@)", r"\1xxx\2", s) # Map a TARGET_PRODUCT to LAVA parameters. PRODUCT_MAP = { "pandaboard": { "test_device_type": "panda" }, "full_panda": { "test_device_type": "panda" }, "beagleboard": { "test_device_type": "beaglexm" }, "snowball": { "test_device_type": "snowball_sd" }, "iMX53": { "test_device_type": "mx53loco" }, "origen": { "test_device_type": "origen", }, "vexpress": { "test_device_type": "vexpress-a9", }, "vexpress_rtsm": { "test_device_type": "rtsm_ve-a15x4-a7x4", }, } OPTION_SUFFIX = "_OPTION" TIMEOUT_SUFFIX = "_TIMEOUT" # Special token for LAVA_TEST_PLAN that allows the build owner to encode a # reboot in between test actions. REBOOT_TOKEN = '[system-reboot]' def gen_lava_test_shell_action(test_name=None, test_para=None): if not test_para or not test_name: return None key_value_ary = test_para.split() key_value_hash = {} for pair_line in key_value_ary: pair = pair_line.split('=', 1) if len(pair) != 2: continue if pair[0]: key_value_hash[pair[0]] = pair[1] parameters = {} if key_value_hash.get('timeout'): parameters['timeout'] = int(key_value_hash.get('timeout')) if test_name == 'lava-test-shell-url': if key_value_hash.get('url'): parameters["testdef_urls"] = [key_value_hash['url']] else: return None action = { "command": "lava_test_shell", "parameters": parameters } return action else: if not key_value_hash.get('repo'): return None repo = {} if test_name == 'lava-test-shell-git': repo = {"git-repo": key_value_hash.get('repo')} elif test_name == 'lava-test-shell-bzr': repo = {"bzr-repo": key_value_hash.get('repo')} else: return None if key_value_hash.get('revision'): repo["revision"] = key_value_hash.get('revision') if key_value_hash.get('testdef'): repo["testdef"] = key_value_hash.get('testdef') parameters["testdef_repos"] = [repo] action = { "command": "lava_test_shell", "parameters": parameters } return action def gen_reboot_action(test_para=None): action = {"command": "boot_linaro_android_image"} if not test_para: return action key_value_ary = test_para.split() key_value_hash = {} for pair_line in key_value_ary: pair = pair_line.split('=', 1) if len(pair) != 2: continue if pair[0]: key_value_hash[pair[0]] = pair[1] parameters = {} adb_check_val = key_value_hash.get('adb_check') if adb_check_val and adb_check_val.lower() == 'true': parameters['adb_check'] = True action['parameters'] = parameters return action def gen_lava_android_test_actions(tests=[]): actions = [] if len(tests) == 0: return actions test_actions = [] for test in tests: #support the special action for android test, #like android_install_cts_medias if test.startswith("android_"): continue #skip lava-test-shell test if test.startswith("lava-test-shell"): continue if test.startswith(REBOOT_TOKEN): continue ## support for test that specified with option like methanol,methanol(DEFAULT) if test.find('(') >= 0\ and test.rfind(')') > test.find('('): test = test[:test.find('(')].strip() if not test: continue test_actions.append(test) if len(test_actions) > 0: inst_action = { "command": "lava_android_test_install", "parameters": { # ensure only unique test names "tests": list(set(test_actions)) } } actions.append(inst_action) for test in tests: ## support for test that specified with option like methanol,methanol(DEFAULT) ## for this case, get the option from the test test_option = '' if test.find('(') >= 0 and test.rfind(')') > test.find('(') \ and test[:test.find('(')].strip(): test_option = test[test.find('(') + 1 : test.rfind(')')] test = test[:test.find('(')].strip() if test.startswith('lava-test-shell'): run_action = gen_lava_test_shell_action(test_name=test, test_para=test_option) if run_action: actions.append(run_action) continue if test.startswith(REBOOT_TOKEN): run_action = gen_reboot_action(test_para=test_option) if run_action: actions.append(run_action) continue parameters = {} if not test_option: test_option = os.environ.get('%s%s' % (test.upper(), OPTION_SUFFIX)) if test_option: parameters['option'] = test_option timeout_option = os.environ.get('%s%s' % (test.upper(), TIMEOUT_SUFFIX)) if timeout_option: parameters['timeout'] = int(timeout_option) if test.startswith('android_'): run_action = { "command": test, "parameters": parameters } actions.append(run_action) elif test == 'hostshell-workload': config = "config.csv" workload_url = "ssh://linaro-lava@mombin.canonical.com/srv/linaro-private.git.linaro.org/people/bhoj/workload-automation.git" config_list = [] config_prefix = "WORKLOAD_CONFIG_" url_prefix = "WORKLOAD_URL" for var in os.environ.keys(): if var.startswith(config_prefix): config_list.append(var) elif var == url_prefix: workload_url = os.environ.get(var) config_list.sort() for var in config_list: config = os.environ.get(var) run_action = { "command": "lava_android_test_run", "parameters": { "option": "-c %s -g %s " % (config, workload_url), "test_name": test } } actions.append(run_action) actions.append({"command": "boot_linaro_android_image"}) else: parameters['test_name'] = test run_action = { "command": "lava_android_test_run", "parameters": parameters } actions.append(run_action) return actions def gen_test_plan_actions(test_plan=None): if test_plan == None: test_plan = os.environ.get("LAVA_TEST_PLAN") if test_plan == None: test_plan = '0xbench, glmark2, monkey' test_plans = test_plan.split(',') for index in range(len(test_plans)): test_plans[index] = test_plans[index].strip() if test_plans[index] == "test_android_0xbench": test_plans[index] = "0xbench" return gen_lava_android_test_actions(test_plans) def gen_command_action(commands=[], cmd_file=None, parser=None, timeout=None): parameters = None if commands: parameters = {'commands': commands} elif cmd_file: parameters = {'command_file': cmd_file} if parameters and parser: parameters['parser'] = parser if timeout: parameters['timeout'] = int(timeout) action = {"command": "lava_android_test_run_custom", "parameters": parameters} return action def gen_custom_actions(): test_actions = [] prefix = 'LAVA_TEST_' pat_suffix = '_PATTERN' sec_job_prefix = 'LAVA_TEST_PLAN_SECONDARY_' test_list = [] for var in os.environ.keys(): if var.startswith(prefix) and (not var.endswith(pat_suffix)) \ and (var != 'LAVA_TEST_PLAN') \ and (not var.startswith(sec_job_prefix)) \ and (not var.endswith(TIMEOUT_SUFFIX)): test_list.append(var) test_list.sort() for var in test_list: cmd = os.environ.get(var) pattern = os.environ.get('%s%s' % (var, pat_suffix)) timeout = os.environ.get('%s%s' % (var, TIMEOUT_SUFFIX)) test_actions.append(gen_command_action(commands=[cmd], parser=pattern, timeout=timeout)) return test_actions def gen_monkeyrunner_actions(): test_actions = [] prefix = 'MONKEY_RUNNER_URL_' test_list = [] for var in os.environ.keys(): if var.startswith(prefix) and (not var.endswith(TIMEOUT_SUFFIX)): test_list.append(var) test_list.sort() for var in test_list: url = os.environ.get(var) parameters = {"url": url} timeout = os.environ.get('%s%s' % (var, TIMEOUT_SUFFIX)) if timeout: parameters['timeout'] = int(timeout) action = { "command": "lava_android_test_run_monkeyrunner", "parameters": parameters } test_actions.append(action) return test_actions def gen_test_actions(): test_actions = [] test_actions.extend(gen_custom_actions()) test_actions.extend(gen_test_plan_actions()) test_actions.extend(gen_monkeyrunner_actions()) return test_actions 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: $TARGET_PRODUCT $JOB_NAME $BUILD_NUMBER $BUILD_URL""" # Target product name, user defined, e.g. pandaboard target_product = os.environ.get("TARGET_PRODUCT") # Job name, defined by android-build, e.g. linaro-android_leb-panda job_name = os.environ.get("JOB_NAME") frontend_job_name = "~" + job_name.replace("_", "/", 1) # Build number, defined by android-build, e.g. 61 build_number = os.environ.get("BUILD_NUMBER") # Build url, defined by android-build, e.g. # https://android-build.linaro.org/jenkins/job/linaro-android_leb-panda/61/ build_url = os.environ.get("BUILD_URL") # download base URL, this may differ from job URL if we don't host # downloads in Jenkins any more download_url = "http://snapshots.linaro.org/android/%s/%s/" % \ (frontend_job_name, build_number) # User can disable the installation of android binaries (doing this will # disable hardware acceleration) enable_android_install_binaries = os.environ.get("LAVA_ANDROID_BINARIES") # Not set, default to False, because this is relevant only for panda # from Vishal if enable_android_install_binaries == None: enable_android_install_binaries = False elif enable_android_install_binaries.lower() in ['1', 'true', 'yes']: enable_android_install_binaries = True else: enable_android_install_binaries = False # Set the default timeout for all test, # if this value is not set, then use the 18000 seconds as the default value default_timeout = os.environ.get("DEFAULT_TIMEOUT", 18000) # Board-specific parameters if target_product not in PRODUCT_MAP: # We don't know how to test this job, so skip testing. print "Don't know how to test this board. Skip testing." return lava_server = os.environ.get("LAVA_SERVER") if lava_server == None: schema = 'https' url_no_schema = "validation.linaro.org/lava-server/RPC2/" elif lava_server.find('://') >= 0: schema = lava_server[:lava_server.find('://')] url_no_schema = lava_server[lava_server.find('://') + len('://'):] else: ## the case that no https and http specified in the url ## like: validation.linaro.org/lava-server/RPC2/ schema = 'https' url_no_schema = lava_server #for compare with above condition schema_url = '%s://%s' % (schema, url_no_schema) lava_server = url_no_schema lava_user = os.environ.get("LAVA_USER") if lava_user == None: f = open('/var/run/lava/lava-user') lava_user = f.read().strip() f.close() default_stream = '/private/team/linaro/android-daily/' common_actions = [ { "command": "deploy_linaro_android_image", "parameters": { "boot": "%s%s" % (download_url, "/boot.tar.bz2"), "system":"%s%s" % (download_url, "/system.tar.bz2"), "data":"%s%s" % (download_url, "/userdata.tar.bz2") }, "metadata": { "android.name": job_name, "android.build": '%s' % build_number, "android.url": build_url } }] if enable_android_install_binaries: common_actions.append({"command": "android_install_binaries"}) common_actions.append({"command": "boot_linaro_android_image"}) plan_list = ["LAVA_TEST_PLAN"] sec_plan_prefix = "LAVA_TEST_PLAN_SECONDARY_" for var in os.environ.keys(): if var.startswith(sec_plan_prefix): plan_list.append(var) plan_list.sort() # Create a copy of common actions for plan in plan_list: actions = copy.deepcopy(common_actions) if plan == "LAVA_TEST_PLAN": actions.extend(gen_test_actions()) else: actions.extend(gen_test_plan_actions(os.environ.get(plan))) actions.append( { "command": "submit_results_on_host", "parameters": { "server": schema_url, "stream": PRODUCT_MAP[target_product].get( "test_stream", default_stream) } }) config_json = {"job_name": build_url, "image_type": 'android', "timeout": int(default_timeout), "actions": actions } # allow overload lava device_type by build config test_device_type = os.environ.get("LAVA_DEVICE_TYPE") if not test_device_type: test_device_type = PRODUCT_MAP[target_product]["test_device_type"] # allow to submit to a specific device test_device = os.environ.get("LAVA_DEVICE") # test_device set will win over test_device_type # LAVA parameter naming could use more consistency if test_device: config_json["target"] = test_device else: config_json["device_type"] = test_device_type config = json.dumps(config_json, indent=4) print config lava_token_f = os.environ.get("LAVA_TOKEN_FILE") if lava_token_f == None: lava_token_f = '/var/run/lava/lava-token' else: lava_token_f = '/var/run/lava/%s' % lava_token_f with open(lava_token_f) as fd: lava_token = fd.read().strip() try: report_url = ("%(schema)s://" "%(lava_user)s:%(lava_token)s@%(lava_server)s") % dict( schema=schema, lava_user=lava_user, lava_token=lava_token, lava_server=lava_server) server = xmlrpclib.ServerProxy(report_url) lava_job_id = server.scheduler.submit_job(config) lava_server_root = lava_server.rstrip("/") if lava_server_root.endswith("/RPC2"): lava_server_root = lava_server_root[:-len("/RPC2")] except xmlrpclib.ProtocolError, e: print "Error making a LAVA request:", obfuscate_credentials(str(e)) sys.exit(1) print "LAVA Job Id: %s, URL: %s://%s/scheduler/job/%s" % \ (lava_job_id, schema, lava_server_root, lava_job_id) if plan == "LAVA_TEST_PLAN": json.dump({ 'lava_url': "%s://%s" % (schema, lava_server_root), 'job_id': lava_job_id, }, open('out/lava-job-info', 'w')) else: json.dump({ 'lava_url': "%s://%s" % (schema, lava_server_root), 'job_id': lava_job_id, }, open('out/lava-job-info-' + plan , 'w')) if __name__ == "__main__": main()