blob: bdef4afe201811e1b4d771ecd8d559472f9e3936 [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
19import sys
Matthew Hartfb6fd362020-03-04 21:03:59 +000020import time
21import yaml
22import argparse
Xinyu Zhang1b8f5152020-11-13 16:10:58 +080023import csv
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080024import shutil
Matthew Hartfb6fd362020-03-04 21:03:59 +000025from jinja2 import Environment, FileSystemLoader
26from lava_helper_configs import *
27from lava_helper import test_lava_dispatch_credentials
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080028from lava_submit_jobs import *
Paul Sokolovsky2512ec52022-03-04 00:15:39 +030029import codecov_helper
30
Matthew Hartfb6fd362020-03-04 21:03:59 +000031
32try:
33 from tfm_ci_pylib.utils import save_json, load_json, sort_dict,\
34 load_yaml, test, print_test
35 from tfm_ci_pylib.lava_rpc_connector import LAVA_RPC_connector
36except ImportError:
37 dir_path = os.path.dirname(os.path.realpath(__file__))
38 sys.path.append(os.path.join(dir_path, "../"))
39 from tfm_ci_pylib.utils import save_json, load_json, sort_dict,\
40 load_yaml, test, print_test
41 from tfm_ci_pylib.lava_rpc_connector import LAVA_RPC_connector
42
Xinyu Zhang1b8f5152020-11-13 16:10:58 +080043cfgs = ["Default", "CoreIPC", "CoreIPCTfmLevel2", "CoreIPCTfmLevel3",
44 "Regression", "RegressionIPC",
45 "RegressionIPCTfmLevel2", "RegressionIPCTfmLevel3",
46 "DefaultProfileS", "RegressionProfileS",
47 "DefaultProfileM", "RegressionProfileM", "RegressionProfileM PSOFF",
Xinyu Zhang9b1aef92021-03-12 15:36:44 +080048 "DefaultProfileL", "RegressionProfileL",
Xinyu Zhang1b8f5152020-11-13 16:10:58 +080049 "PsaApiTest (Attest)", "PsaApiTestIPC (Attest)",
50 "PsaApiTestIPCTfmLevel2 (Attest)",
51 "PsaApiTest (Crypto)", "PsaApiTestIPC (Crypto)",
52 "PsaApiTestIPCTfmLevel2 (Crypto)",
53 "PsaApiTest (PS)", "PsaApiTestIPC (PS)",
54 "PsaApiTestIPCTfmLevel2 (PS)",
55 "PsaApiTest (ITS)", "PsaApiTestIPC (ITS)",
56 "PsaApiTestIPCTfmLevel2 (ITS)",
57 "PsaApiTestIPC (FF)",
58 "PsaApiTestIPCTfmLevel2 (FF)",
Paul Sokolovskyb6003112022-02-04 00:36:14 +030059 "PsaApiTest (STORAGE)",
60 "PsaApiTestIPC (STORAGE)",
61 "PsaApiTestIPCTfmLevel2 (STORAGE)",
62 "PsaApiTestIPCTfmLevel3 (STORAGE)",
Xinyu Zhang1b8f5152020-11-13 16:10:58 +080063 "PsaApiTestIPCTfmLevel3 (ITS)", "PsaApiTestIPCTfmLevel3 (PS)",
64 "PsaApiTestIPCTfmLevel3 (Crypto)", "PsaApiTestIPCTfmLevel3 (Attest)",
65 "PsaApiTestIPCTfmLevel3 (FF)"]
66
Paul Sokolovskyf1ff6c12022-02-02 21:42:41 +030067# Convert test config identifiers to LAVA naming convention.
68cfgs = [x.replace(" (", "_").replace(")", "") for x in cfgs]
69
Matthew Hartfb6fd362020-03-04 21:03:59 +000070def wait_for_jobs(user_args):
71 job_list = user_args.job_ids.split(",")
72 job_list = [int(x) for x in job_list if x != '']
73 lava = test_lava_dispatch_credentials(user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080074 finished_jobs = get_finished_jobs(job_list, user_args, lava)
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080075 resubmit_jobs = resubmit_failed_jobs(finished_jobs, user_args)
Paul Sokolovskyc87beee2022-04-30 08:50:47 +030076 if resubmit_jobs:
77 info_print("Waiting for resubmitted jobs: {}".format(resubmit_jobs))
78 finished_resubmit_jobs = get_finished_jobs(resubmit_jobs, user_args, lava)
79 finished_jobs.update(finished_resubmit_jobs)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030080 return finished_jobs
81
82
83def process_finished_jobs(finished_jobs, user_args):
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080084 print_lava_urls(finished_jobs, user_args)
85 job_links(finished_jobs, user_args)
86 boot_report(finished_jobs, user_args)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030087 test_report(finished_jobs, user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080088 failure_report(finished_jobs, user_args)
89 csv_report(finished_jobs)
Paul Sokolovsky2512ec52022-03-04 00:15:39 +030090 codecov_helper.coverage_reports(finished_jobs, user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080091
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030092
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080093def get_finished_jobs(job_list, user_args, lava):
Paul Sokolovsky697f9552022-05-05 10:44:27 +030094 finished_jobs = lava.block_wait_for_jobs(job_list, user_args.dispatch_timeout, 5)
Matthew Hartfb6fd362020-03-04 21:03:59 +000095 unfinished_jobs = [item for item in job_list if item not in finished_jobs]
96 for job in unfinished_jobs:
97 info_print("Cancelling unfinished job: {}".format(job))
98 lava.cancel_job(job)
99 if user_args.artifacts_path:
100 for job, info in finished_jobs.items():
101 info['job_dir'] = os.path.join(user_args.artifacts_path, "{}_{}".format(str(job), info['description']))
102 finished_jobs[job] = info
103 finished_jobs = fetch_artifacts(finished_jobs, user_args, lava)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800104 return finished_jobs
Matthew Hartfb6fd362020-03-04 21:03:59 +0000105
Xinyu Zhangc8a670c2021-05-18 20:20:53 +0800106def resubmit_failed_jobs(jobs, user_args):
107 if not jobs:
108 return []
Xinyu Zhang4aca6d02021-05-31 11:43:32 +0800109 time.sleep(2) # be friendly to LAVA
Xinyu Zhangc8a670c2021-05-18 20:20:53 +0800110 failed_job = []
111 os.makedirs('failed_jobs', exist_ok=True)
112 for job_id, info in jobs.items():
113 if not (info['health'] == "Complete" and info['state'] == "Finished"):
114 job_dir = info['job_dir']
115 def_path = os.path.join(job_dir, 'definition.yaml')
116 os.rename(def_path, 'failed_jobs/{}_definition.yaml'.format(job_id))
117 shutil.rmtree(job_dir)
118 failed_job.append(job_id)
119 for failed_job_id in failed_job:
120 jobs.pop(failed_job_id)
121 resubmitted_jobs = lava_dispatch(user_args, job_dir='failed_jobs')
122 resubmitted_jobs = [int(x) for x in resubmitted_jobs if x != '']
123 return resubmitted_jobs
124
Paul Sokolovskyc2d6d882022-02-25 19:11:18 +0300125
Matthew Hartfb6fd362020-03-04 21:03:59 +0000126def fetch_artifacts(jobs, user_args, lava):
127 if not user_args.artifacts_path:
128 return
129 for job_id, info in jobs.items():
130 job_dir = info['job_dir']
131 info_print("Fetching artifacts for JOB: {} to {}".format(job_id, job_dir))
132 os.makedirs(job_dir, exist_ok=True)
133 def_path = os.path.join(job_dir, 'definition.yaml')
134 target_log = os.path.join(job_dir, 'target_log.txt')
Matthew Hart4a4f1202020-06-12 15:52:46 +0100135 config = os.path.join(job_dir, 'config.tar.bz2')
136 results_file = os.path.join(job_dir, 'results.yaml')
Matthew Hartfb6fd362020-03-04 21:03:59 +0000137 definition, metadata = lava.get_job_definition(job_id, def_path)
138 jobs[job_id]['metadata'] = metadata
139 time.sleep(0.2) # be friendly to LAVA
Matthew Hart4a4f1202020-06-12 15:52:46 +0100140 lava.get_job_log(job_id, target_log)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000141 time.sleep(0.2)
142 lava.get_job_config(job_id, config)
143 time.sleep(0.2)
Matthew Hart4a4f1202020-06-12 15:52:46 +0100144 lava.get_job_results(job_id, results_file)
Paul Sokolovskyc2d6d882022-02-25 19:11:18 +0300145 codecov_helper.extract_trace_data(target_log, job_dir)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000146 return(jobs)
147
148
149def lava_id_to_url(id, user_args):
150 return "{}/scheduler/job/{}".format(user_args.lava_url, id)
151
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800152def generateTestResult(info):
153 if info['health'] == "Complete" and info['state'] == "Finished":
154 return "PASS"
155 else:
156 return "FAIL"
157
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800158def job_links(jobs, user_args):
159 job_links = ""
160 for job, info in jobs.items():
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800161 job_links += "Build Config: {}\n".format(info['metadata']['build_name'])
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800162 job_links += "Build link: {}\n".format(info['metadata']['build_job_url'])
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800163 job_links += "LAVA link: {}\n".format(lava_id_to_url(job, user_args))
Xinyu Zhange9033f62022-10-08 11:15:27 +0800164 job_links += "TFM LOG: {}artifact/{}/target_log.txt\n\n".format(os.getenv("BUILD_URL"), info['job_dir'])
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800165 print(job_links)
166
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800167def csv_report(jobs):
168 lava_jobs = []
169 for job, info in jobs.items():
170 exist = False
171 for record in lava_jobs:
172 if info['metadata']['platform'] == record["Platform"] and \
173 info['metadata']['compiler'] == record["Compiler"] and \
174 info['metadata']['build_type'] == record["Build Type"]:
175 if record[info['metadata']['name']] != "FAIL":
176 record[info['metadata']['name']] = generateTestResult(info)
177 exist = True
178 break
179 if not exist:
180 record = {}
181 record["Platform"] = info['metadata']['platform']
182 record["Compiler"] = info['metadata']['compiler']
183 record["Build Type"] = info['metadata']['build_type']
184 record["Config Name"] = info['metadata']['name']
185 for cfg in cfgs:
186 record[cfg] = "N.A."
187 record[info['metadata']['name']] = generateTestResult(info)
188 lava_jobs.append(record)
189 lava_jobs.sort(key=lambda x: x["Platform"] + x["Compiler"] + x["Build Type"])
190 with open("test_results.csv", "w", newline="") as csvfile:
191 fieldnames = ["Platform", "Compiler", "Build Type"] + cfgs
192 writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore')
193
194 writer.writeheader()
195 writer.writerows(lava_jobs)
196
Matthew Hartfb6fd362020-03-04 21:03:59 +0000197def boot_report(jobs, user_args):
198 incomplete_jobs = []
199 for job, info in jobs.items():
200 if info['health'] != 'Complete':
201 if info['error_reason'] == 'Infrastructure':
202 info_print("Job {} failed with Infrastructure error".format(job))
203 incomplete_jobs.append(job)
204 incomplete_output = [lava_id_to_url(x, user_args) for x in incomplete_jobs];
205 if len(incomplete_jobs) > 0:
206 print("BOOT_RESULT: -1 Failed: {}".format(incomplete_output))
207 else:
208 print("BOOT_RESULT: +1")
209
Xinyu Zhang38a18872020-11-23 16:45:28 +0800210def failure_report(jobs, user_args):
211 failed_report = "FAILURE_TESTS:"
212 for job, info in jobs.items():
213 if info['health'] != "Complete" or info['state'] != "Finished":
Xinyu Zhange9033f62022-10-08 11:15:27 +0800214 failed_report += " {}:{}artifact/{}/target_log.txt\n".format(info['metadata']['build_name'],
215 os.getenv("BUILD_URL"),
216 info['job_dir'])
Xinyu Zhang38a18872020-11-23 16:45:28 +0800217 print(failed_report)
218
Matthew Hartfb6fd362020-03-04 21:03:59 +0000219def remove_lava_dupes(results):
220 for result in results:
221 if result['result'] != 'pass':
222 if result['suite'] == "lava":
223 for other in [x for x in results if x != result]:
224 if other['name'] == result['name']:
225 if other['result'] == 'pass':
226 results.remove(result)
227 return(results)
228
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300229def test_report(jobs, user_args):
Matthew Hartfb6fd362020-03-04 21:03:59 +0000230 # parsing of test results is WIP
231 fail_j = []
232 jinja_data = []
233 for job, info in jobs.items():
Matthew Hart4a4f1202020-06-12 15:52:46 +0100234 results_file = os.path.join(info['job_dir'], 'results.yaml')
235 if not os.path.exists(results_file) or (os.path.getsize(results_file) == 0):
236 fail_j.append(job)
237 continue
238 with open(results_file, "r") as F:
239 res_data = F.read()
Paul Sokolovskyf2f385d2022-01-11 00:36:31 +0300240 results = yaml.safe_load(res_data)
Paul Sokolovsky07f6dfb2022-07-15 12:26:24 +0300241 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 +0000242 info['lava_url'] = lava_id_to_url(job, user_args)
Arthur She38d5f5a2022-09-02 17:32:14 -0700243 info['artifacts_dir'] = info['job_dir']
Matthew Hartfb6fd362020-03-04 21:03:59 +0000244 jinja_data.append({job: [info, non_lava_results]})
245 for result in non_lava_results:
Paul Sokolovsky58f00de2022-02-01 00:26:32 +0300246 if result['result'] == 'fail':
Matthew Hartfb6fd362020-03-04 21:03:59 +0000247 fail_j.append(job) if job not in fail_j else fail_j
248 time.sleep(0.5) # be friendly to LAVA
249 fail_output = [lava_id_to_url(x, user_args) for x in fail_j]
250 if len(fail_j) > 0:
251 print("TEST_RESULT: -1 Failed: {}".format(fail_output))
252 else:
253 print("TEST_RESULT: +1")
254 data = {}
255 data['jobs'] = jinja_data
256 render_jinja(data)
257
258def render_jinja(data):
259 work_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "jinja2_templates")
260 template_loader = FileSystemLoader(searchpath=work_dir)
261 template_env = Environment(loader=template_loader)
262 html = template_env.get_template("test_summary.jinja2").render(data)
263 csv = template_env.get_template("test_summary_csv.jinja2").render(data)
264 with open('test_summary.html', "w") as F:
265 F.write(html)
266 with open('test_summary.csv', "w") as F:
267 F.write(csv)
268
269def print_lava_urls(jobs, user_args):
270 output = [lava_id_to_url(x, user_args) for x in jobs]
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800271 info_print("LAVA jobs triggered for this build: {}".format(output))
Matthew Hartfb6fd362020-03-04 21:03:59 +0000272
273
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800274def info_print(line, silent=True):
275 if not silent:
276 print("INFO: {}".format(line))
Matthew Hartfb6fd362020-03-04 21:03:59 +0000277
278def main(user_args):
279 """ Main logic """
280 user_args.lava_rpc = "RPC2"
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800281 for try_time in range(3):
282 try:
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300283 finished_jobs = wait_for_jobs(user_args)
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800284 break
285 except Exception as e:
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800286 if try_time < 2:
Paul Sokolovskycc51ea92022-02-02 19:34:02 +0300287 print("Exception in wait_for_jobs: {!r}".format(e))
288 print("Trying to get LAVA jobs again...")
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800289 else:
290 raise e
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300291 process_finished_jobs(finished_jobs, user_args)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000292
293def get_cmd_args():
294 """ Parse command line arguments """
295
296 # Parse command line arguments to override config
297 parser = argparse.ArgumentParser(description="Lava Wait Jobs")
298 cmdargs = parser.add_argument_group("Lava Wait Jobs")
299
300 # Configuration control
301 cmdargs.add_argument(
302 "--lava-url", dest="lava_url", action="store", help="LAVA lab URL (without RPC2)"
303 )
304 cmdargs.add_argument(
305 "--job-ids", dest="job_ids", action="store", required=True, help="Comma separated list of job IDS"
306 )
307 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800308 "--lava-token", dest="lava_token", action="store", help="LAVA auth token"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000309 )
310 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800311 "--lava-user", dest="lava_user", action="store", help="LAVA username"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000312 )
313 cmdargs.add_argument(
314 "--use-env", dest="token_from_env", action="store_true", default=False, help="Use LAVA auth info from environment"
315 )
316 cmdargs.add_argument(
317 "--lava-timeout", dest="dispatch_timeout", action="store", type=int, default=3600, help="Time in seconds to wait for all jobs"
318 )
319 cmdargs.add_argument(
320 "--artifacts-path", dest="artifacts_path", action="store", help="Download LAVA artifacts to this directory"
321 )
322 return parser.parse_args()
323
324
325if __name__ == "__main__":
326 main(get_cmd_args())