blob: cddc2edfb417f99ba26581f7df1db0dbec8e5964 [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))
164 job_links += "TFM LOG: <BUILD_ARTIFACT_URL>{}/target_log.txt\n\n".format(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 Zhang78c146a2022-09-05 19:06:40 +0800214 failed_report += " {}:<BUILD_ARTIFACT_URL>{}/target_log.txt\n".format(info['metadata']['build_name'],
215 info['job_dir'])
Xinyu Zhang38a18872020-11-23 16:45:28 +0800216 print(failed_report)
217
Matthew Hartfb6fd362020-03-04 21:03:59 +0000218def remove_lava_dupes(results):
219 for result in results:
220 if result['result'] != 'pass':
221 if result['suite'] == "lava":
222 for other in [x for x in results if x != result]:
223 if other['name'] == result['name']:
224 if other['result'] == 'pass':
225 results.remove(result)
226 return(results)
227
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300228def test_report(jobs, user_args):
Matthew Hartfb6fd362020-03-04 21:03:59 +0000229 # parsing of test results is WIP
230 fail_j = []
231 jinja_data = []
232 for job, info in jobs.items():
Matthew Hart4a4f1202020-06-12 15:52:46 +0100233 results_file = os.path.join(info['job_dir'], 'results.yaml')
234 if not os.path.exists(results_file) or (os.path.getsize(results_file) == 0):
235 fail_j.append(job)
236 continue
237 with open(results_file, "r") as F:
238 res_data = F.read()
Paul Sokolovskyf2f385d2022-01-11 00:36:31 +0300239 results = yaml.safe_load(res_data)
Paul Sokolovsky07f6dfb2022-07-15 12:26:24 +0300240 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 +0000241 info['lava_url'] = lava_id_to_url(job, user_args)
Arthur She38d5f5a2022-09-02 17:32:14 -0700242 info['artifacts_dir'] = info['job_dir']
Matthew Hartfb6fd362020-03-04 21:03:59 +0000243 jinja_data.append({job: [info, non_lava_results]})
244 for result in non_lava_results:
Paul Sokolovsky58f00de2022-02-01 00:26:32 +0300245 if result['result'] == 'fail':
Matthew Hartfb6fd362020-03-04 21:03:59 +0000246 fail_j.append(job) if job not in fail_j else fail_j
247 time.sleep(0.5) # be friendly to LAVA
248 fail_output = [lava_id_to_url(x, user_args) for x in fail_j]
249 if len(fail_j) > 0:
250 print("TEST_RESULT: -1 Failed: {}".format(fail_output))
251 else:
252 print("TEST_RESULT: +1")
253 data = {}
254 data['jobs'] = jinja_data
255 render_jinja(data)
256
257def render_jinja(data):
258 work_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "jinja2_templates")
259 template_loader = FileSystemLoader(searchpath=work_dir)
260 template_env = Environment(loader=template_loader)
261 html = template_env.get_template("test_summary.jinja2").render(data)
262 csv = template_env.get_template("test_summary_csv.jinja2").render(data)
263 with open('test_summary.html', "w") as F:
264 F.write(html)
265 with open('test_summary.csv', "w") as F:
266 F.write(csv)
267
268def print_lava_urls(jobs, user_args):
269 output = [lava_id_to_url(x, user_args) for x in jobs]
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800270 info_print("LAVA jobs triggered for this build: {}".format(output))
Matthew Hartfb6fd362020-03-04 21:03:59 +0000271
272
Xinyu Zhang78c146a2022-09-05 19:06:40 +0800273def info_print(line, silent=True):
274 if not silent:
275 print("INFO: {}".format(line))
Matthew Hartfb6fd362020-03-04 21:03:59 +0000276
277def main(user_args):
278 """ Main logic """
279 user_args.lava_rpc = "RPC2"
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800280 for try_time in range(3):
281 try:
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300282 finished_jobs = wait_for_jobs(user_args)
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800283 break
284 except Exception as e:
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800285 if try_time < 2:
Paul Sokolovskycc51ea92022-02-02 19:34:02 +0300286 print("Exception in wait_for_jobs: {!r}".format(e))
287 print("Trying to get LAVA jobs again...")
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800288 else:
289 raise e
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300290 process_finished_jobs(finished_jobs, user_args)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000291
292def get_cmd_args():
293 """ Parse command line arguments """
294
295 # Parse command line arguments to override config
296 parser = argparse.ArgumentParser(description="Lava Wait Jobs")
297 cmdargs = parser.add_argument_group("Lava Wait Jobs")
298
299 # Configuration control
300 cmdargs.add_argument(
301 "--lava-url", dest="lava_url", action="store", help="LAVA lab URL (without RPC2)"
302 )
303 cmdargs.add_argument(
304 "--job-ids", dest="job_ids", action="store", required=True, help="Comma separated list of job IDS"
305 )
306 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800307 "--lava-token", dest="lava_token", action="store", help="LAVA auth token"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000308 )
309 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800310 "--lava-user", dest="lava_user", action="store", help="LAVA username"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000311 )
312 cmdargs.add_argument(
313 "--use-env", dest="token_from_env", action="store_true", default=False, help="Use LAVA auth info from environment"
314 )
315 cmdargs.add_argument(
316 "--lava-timeout", dest="dispatch_timeout", action="store", type=int, default=3600, help="Time in seconds to wait for all jobs"
317 )
318 cmdargs.add_argument(
319 "--artifacts-path", dest="artifacts_path", action="store", help="Download LAVA artifacts to this directory"
320 )
321 return parser.parse_args()
322
323
324if __name__ == "__main__":
325 main(get_cmd_args())