blob: 2f4268ff9cd0f37494180d006562702d89f3469e [file] [log] [blame]
Matthew Hartfb6fd362020-03-04 21:03:59 +00001#!/usr/bin/env python3
2
3from __future__ import print_function
4
5__copyright__ = """
6/*
Xinyu Zhang78c146a2022-09-05 19:06:40 +08007 * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
Matthew Hartfb6fd362020-03-04 21:03:59 +00008 *
9 * SPDX-License-Identifier: BSD-3-Clause
10 *
11 */
12 """
13
14"""
15Script for waiting for LAVA jobs and parsing the results
16"""
17
18import os
Matthew Hartfb6fd362020-03-04 21:03:59 +000019import time
20import yaml
21import argparse
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080022import shutil
Paul Sokolovskya95abd92022-12-27 13:48:11 +030023import logging
Matthew Hartfb6fd362020-03-04 21:03:59 +000024from jinja2 import Environment, FileSystemLoader
Matthew Hartfb6fd362020-03-04 21:03:59 +000025from lava_helper import test_lava_dispatch_credentials
Xinyu Zhangc918b6e2022-10-08 17:13:17 +080026from lava_submit_jobs import submit_lava_jobs
Paul Sokolovsky2512ec52022-03-04 00:15:39 +030027import codecov_helper
28
Matthew Hartfb6fd362020-03-04 21:03:59 +000029
Paul Sokolovskya95abd92022-12-27 13:48:11 +030030_log = logging.getLogger("lavaci")
31
32
Matthew Hartfb6fd362020-03-04 21:03:59 +000033def wait_for_jobs(user_args):
34 job_list = user_args.job_ids.split(",")
35 job_list = [int(x) for x in job_list if x != '']
36 lava = test_lava_dispatch_credentials(user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080037 finished_jobs = get_finished_jobs(job_list, user_args, lava)
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080038 resubmit_jobs = resubmit_failed_jobs(finished_jobs, user_args)
Paul Sokolovskyc87beee2022-04-30 08:50:47 +030039 if resubmit_jobs:
Paul Sokolovskyf3674562022-12-27 22:20:01 +030040 _log.info("Waiting for resubmitted jobs: %s", resubmit_jobs)
Paul Sokolovskyc87beee2022-04-30 08:50:47 +030041 finished_resubmit_jobs = get_finished_jobs(resubmit_jobs, user_args, lava)
42 finished_jobs.update(finished_resubmit_jobs)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030043 return finished_jobs
44
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030045def process_finished_jobs(finished_jobs, user_args):
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080046 print_lava_urls(finished_jobs, user_args)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030047 test_report(finished_jobs, user_args)
Xinyu Zhang82dab282022-10-09 16:33:19 +080048 job_links(finished_jobs, user_args)
Paul Sokolovsky2512ec52022-03-04 00:15:39 +030049 codecov_helper.coverage_reports(finished_jobs, user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080050
51def get_finished_jobs(job_list, user_args, lava):
Paul Sokolovskya95abd92022-12-27 13:48:11 +030052 _log.info("Waiting for %d LAVA jobs", len(job_list))
Paul Sokolovsky697f9552022-05-05 10:44:27 +030053 finished_jobs = lava.block_wait_for_jobs(job_list, user_args.dispatch_timeout, 5)
Matthew Hartfb6fd362020-03-04 21:03:59 +000054 unfinished_jobs = [item for item in job_list if item not in finished_jobs]
55 for job in unfinished_jobs:
Paul Sokolovskyf3674562022-12-27 22:20:01 +030056 _log.info("Cancelling unfinished job %d", job)
Matthew Hartfb6fd362020-03-04 21:03:59 +000057 lava.cancel_job(job)
58 if user_args.artifacts_path:
59 for job, info in finished_jobs.items():
60 info['job_dir'] = os.path.join(user_args.artifacts_path, "{}_{}".format(str(job), info['description']))
61 finished_jobs[job] = info
62 finished_jobs = fetch_artifacts(finished_jobs, user_args, lava)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080063 return finished_jobs
Matthew Hartfb6fd362020-03-04 21:03:59 +000064
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080065def resubmit_failed_jobs(jobs, user_args):
66 if not jobs:
67 return []
Xinyu Zhang4aca6d02021-05-31 11:43:32 +080068 time.sleep(2) # be friendly to LAVA
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080069 failed_job = []
70 os.makedirs('failed_jobs', exist_ok=True)
71 for job_id, info in jobs.items():
72 if not (info['health'] == "Complete" and info['state'] == "Finished"):
Paul Sokolovskyb7a41a92022-12-28 18:06:45 +030073 _log.warning(
74 "Will resubmit job %d because of its state: %s, health: %s",
Paul Sokolovsky7fa6c9e2022-12-30 15:01:49 +030075 job_id, info["state"], info["health"]
Paul Sokolovskyb7a41a92022-12-28 18:06:45 +030076 )
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080077 job_dir = info['job_dir']
78 def_path = os.path.join(job_dir, 'definition.yaml')
79 os.rename(def_path, 'failed_jobs/{}_definition.yaml'.format(job_id))
80 shutil.rmtree(job_dir)
81 failed_job.append(job_id)
82 for failed_job_id in failed_job:
83 jobs.pop(failed_job_id)
Xinyu Zhangc918b6e2022-10-08 17:13:17 +080084 resubmitted_jobs = submit_lava_jobs(user_args, job_dir='failed_jobs')
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080085 resubmitted_jobs = [int(x) for x in resubmitted_jobs if x != '']
86 return resubmitted_jobs
87
Matthew Hartfb6fd362020-03-04 21:03:59 +000088def fetch_artifacts(jobs, user_args, lava):
89 if not user_args.artifacts_path:
90 return
91 for job_id, info in jobs.items():
92 job_dir = info['job_dir']
Paul Sokolovskydc8281a2022-12-27 21:54:42 +030093 t = time.time()
94 _log.info("Fetching artifacts for job %d to %s", job_id, job_dir)
Matthew Hartfb6fd362020-03-04 21:03:59 +000095 os.makedirs(job_dir, exist_ok=True)
96 def_path = os.path.join(job_dir, 'definition.yaml')
97 target_log = os.path.join(job_dir, 'target_log.txt')
Matthew Hart4a4f1202020-06-12 15:52:46 +010098 config = os.path.join(job_dir, 'config.tar.bz2')
99 results_file = os.path.join(job_dir, 'results.yaml')
Xinyu Zhang82dab282022-10-09 16:33:19 +0800100 definition = lava.get_job_definition(job_id, def_path)
101 jobs[job_id]['metadata'] = definition.get('metadata', [])
Matthew Hartfb6fd362020-03-04 21:03:59 +0000102 time.sleep(0.2) # be friendly to LAVA
Matthew Hart4a4f1202020-06-12 15:52:46 +0100103 lava.get_job_log(job_id, target_log)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000104 time.sleep(0.2)
105 lava.get_job_config(job_id, config)
106 time.sleep(0.2)
Matthew Hart4a4f1202020-06-12 15:52:46 +0100107 lava.get_job_results(job_id, results_file)
Paul Sokolovskydc8281a2022-12-27 21:54:42 +0300108 _log.info("Fetched artifacts in %ds", time.time() - t)
Paul Sokolovskyc2d6d882022-02-25 19:11:18 +0300109 codecov_helper.extract_trace_data(target_log, job_dir)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000110 return(jobs)
111
112
113def lava_id_to_url(id, user_args):
114 return "{}/scheduler/job/{}".format(user_args.lava_url, id)
115
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800116def job_links(jobs, user_args):
117 job_links = ""
118 for job, info in jobs.items():
Xinyu Zhang82dab282022-10-09 16:33:19 +0800119 job_links += "\nLAVA Test Config:\n"
120 job_links += "Config Name: {}\n".format(info['metadata']['build_name'])
121 job_links += "Test Result: {}\n".format(info['result'])
122 job_links += "Device Type: {}\n".format(info['metadata']['device_type'])
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800123 job_links += "Build link: {}\n".format(info['metadata']['build_job_url'])
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800124 job_links += "LAVA link: {}\n".format(lava_id_to_url(job, user_args))
Xinyu Zhang82dab282022-10-09 16:33:19 +0800125 job_links += "TFM LOG: {}artifact/{}/target_log.txt\n".format(os.getenv("BUILD_URL"), info['job_dir'])
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800126 print(job_links)
127
Matthew Hartfb6fd362020-03-04 21:03:59 +0000128def remove_lava_dupes(results):
129 for result in results:
130 if result['result'] != 'pass':
131 if result['suite'] == "lava":
132 for other in [x for x in results if x != result]:
133 if other['name'] == result['name']:
134 if other['result'] == 'pass':
135 results.remove(result)
136 return(results)
137
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300138def test_report(jobs, user_args):
Matthew Hartfb6fd362020-03-04 21:03:59 +0000139 # parsing of test results is WIP
140 fail_j = []
141 jinja_data = []
142 for job, info in jobs.items():
Xinyu Zhang0f78e7a2022-10-17 13:55:52 +0800143 info['result'] = 'SUCCESS'
Xinyu Zhang82dab282022-10-09 16:33:19 +0800144 if info['health'] != 'Complete':
Xinyu Zhang0f78e7a2022-10-17 13:55:52 +0800145 info['result'] = 'FAILURE'
Xinyu Zhang82dab282022-10-09 16:33:19 +0800146 fail_j.append(job)
147 continue
Matthew Hart4a4f1202020-06-12 15:52:46 +0100148 results_file = os.path.join(info['job_dir'], 'results.yaml')
149 if not os.path.exists(results_file) or (os.path.getsize(results_file) == 0):
Xinyu Zhang0f78e7a2022-10-17 13:55:52 +0800150 info['result'] = 'FAILURE'
Matthew Hart4a4f1202020-06-12 15:52:46 +0100151 fail_j.append(job)
152 continue
153 with open(results_file, "r") as F:
154 res_data = F.read()
Paul Sokolovskyf2f385d2022-01-11 00:36:31 +0300155 results = yaml.safe_load(res_data)
Paul Sokolovsky07f6dfb2022-07-15 12:26:24 +0300156 non_lava_results = [x for x in results if x['suite'] != 'lava' or x['name'] == 'lava-test-monitor']
Matthew Hartfb6fd362020-03-04 21:03:59 +0000157 info['lava_url'] = lava_id_to_url(job, user_args)
Arthur She38d5f5a2022-09-02 17:32:14 -0700158 info['artifacts_dir'] = info['job_dir']
Matthew Hartfb6fd362020-03-04 21:03:59 +0000159 jinja_data.append({job: [info, non_lava_results]})
160 for result in non_lava_results:
Paul Sokolovsky58f00de2022-02-01 00:26:32 +0300161 if result['result'] == 'fail':
Xinyu Zhang0f78e7a2022-10-17 13:55:52 +0800162 info['result'] = 'FAILURE'
Matthew Hartfb6fd362020-03-04 21:03:59 +0000163 fail_j.append(job) if job not in fail_j else fail_j
164 time.sleep(0.5) # be friendly to LAVA
Matthew Hartfb6fd362020-03-04 21:03:59 +0000165 data = {}
166 data['jobs'] = jinja_data
167 render_jinja(data)
168
169def render_jinja(data):
170 work_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "jinja2_templates")
171 template_loader = FileSystemLoader(searchpath=work_dir)
172 template_env = Environment(loader=template_loader)
173 html = template_env.get_template("test_summary.jinja2").render(data)
174 csv = template_env.get_template("test_summary_csv.jinja2").render(data)
175 with open('test_summary.html', "w") as F:
176 F.write(html)
177 with open('test_summary.csv', "w") as F:
178 F.write(csv)
179
180def print_lava_urls(jobs, user_args):
181 output = [lava_id_to_url(x, user_args) for x in jobs]
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800182 info_print("LAVA jobs triggered for this build: {}".format(output))
Matthew Hartfb6fd362020-03-04 21:03:59 +0000183
184
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800185def info_print(line, silent=True):
186 if not silent:
187 print("INFO: {}".format(line))
Matthew Hartfb6fd362020-03-04 21:03:59 +0000188
Paul Sokolovskyde25e1f2023-01-02 14:29:21 +0300189
190# WARNING: Setting this to >1 is a last resort, temporary stop-gap measure,
191# which will overload LAVA and jeopardize stability of the entire TF CI.
192INEFFICIENT_RETRIES = 1
193
194
Matthew Hartfb6fd362020-03-04 21:03:59 +0000195def main(user_args):
196 """ Main logic """
Paul Sokolovskyde25e1f2023-01-02 14:29:21 +0300197 for try_time in range(INEFFICIENT_RETRIES):
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800198 try:
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300199 finished_jobs = wait_for_jobs(user_args)
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800200 break
201 except Exception as e:
Paul Sokolovskyde25e1f2023-01-02 14:29:21 +0300202 if try_time < INEFFICIENT_RETRIES - 1:
Paul Sokolovskyf3674562022-12-27 22:20:01 +0300203 _log.exception("Exception in wait_for_jobs")
204 _log.info("Will try to get LAVA jobs again, this was try: %d", try_time)
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800205 else:
206 raise e
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300207 process_finished_jobs(finished_jobs, user_args)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000208
209def get_cmd_args():
210 """ Parse command line arguments """
211
212 # Parse command line arguments to override config
213 parser = argparse.ArgumentParser(description="Lava Wait Jobs")
214 cmdargs = parser.add_argument_group("Lava Wait Jobs")
215
216 # Configuration control
217 cmdargs.add_argument(
218 "--lava-url", dest="lava_url", action="store", help="LAVA lab URL (without RPC2)"
219 )
220 cmdargs.add_argument(
221 "--job-ids", dest="job_ids", action="store", required=True, help="Comma separated list of job IDS"
222 )
223 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800224 "--lava-token", dest="lava_token", action="store", help="LAVA auth token"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000225 )
226 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800227 "--lava-user", dest="lava_user", action="store", help="LAVA username"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000228 )
229 cmdargs.add_argument(
230 "--use-env", dest="token_from_env", action="store_true", default=False, help="Use LAVA auth info from environment"
231 )
232 cmdargs.add_argument(
233 "--lava-timeout", dest="dispatch_timeout", action="store", type=int, default=3600, help="Time in seconds to wait for all jobs"
234 )
235 cmdargs.add_argument(
236 "--artifacts-path", dest="artifacts_path", action="store", help="Download LAVA artifacts to this directory"
237 )
238 return parser.parse_args()
239
240
241if __name__ == "__main__":
Paul Sokolovskya95abd92022-12-27 13:48:11 +0300242 logging.basicConfig(level=logging.INFO)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000243 main(get_cmd_args())