blob: 08e94f239771ccad30151c430e3b578c79f80df0 [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 Zhangf2b7cbf2021-05-18 20:17:34 +08007 * Copyright (c) 2020-2021, 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)
76 finished_resubmit_jobs = get_finished_jobs(resubmit_jobs, user_args, lava)
77 finished_jobs.update(finished_resubmit_jobs)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030078 return finished_jobs
79
80
81def process_finished_jobs(finished_jobs, user_args):
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080082 print_lava_urls(finished_jobs, user_args)
83 job_links(finished_jobs, user_args)
84 boot_report(finished_jobs, user_args)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030085 test_report(finished_jobs, user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080086 failure_report(finished_jobs, user_args)
87 csv_report(finished_jobs)
Paul Sokolovsky2512ec52022-03-04 00:15:39 +030088 codecov_helper.coverage_reports(finished_jobs, user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080089
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030090
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080091def get_finished_jobs(job_list, user_args, lava):
Matthew Hartfb6fd362020-03-04 21:03:59 +000092 finished_jobs = lava.block_wait_for_jobs(job_list, user_args.dispatch_timeout, 0.5)
93 unfinished_jobs = [item for item in job_list if item not in finished_jobs]
94 for job in unfinished_jobs:
95 info_print("Cancelling unfinished job: {}".format(job))
96 lava.cancel_job(job)
97 if user_args.artifacts_path:
98 for job, info in finished_jobs.items():
99 info['job_dir'] = os.path.join(user_args.artifacts_path, "{}_{}".format(str(job), info['description']))
100 finished_jobs[job] = info
101 finished_jobs = fetch_artifacts(finished_jobs, user_args, lava)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800102 return finished_jobs
Matthew Hartfb6fd362020-03-04 21:03:59 +0000103
Xinyu Zhangc8a670c2021-05-18 20:20:53 +0800104def resubmit_failed_jobs(jobs, user_args):
105 if not jobs:
106 return []
Xinyu Zhang4aca6d02021-05-31 11:43:32 +0800107 time.sleep(2) # be friendly to LAVA
Xinyu Zhangc8a670c2021-05-18 20:20:53 +0800108 failed_job = []
109 os.makedirs('failed_jobs', exist_ok=True)
110 for job_id, info in jobs.items():
111 if not (info['health'] == "Complete" and info['state'] == "Finished"):
112 job_dir = info['job_dir']
113 def_path = os.path.join(job_dir, 'definition.yaml')
114 os.rename(def_path, 'failed_jobs/{}_definition.yaml'.format(job_id))
115 shutil.rmtree(job_dir)
116 failed_job.append(job_id)
117 for failed_job_id in failed_job:
118 jobs.pop(failed_job_id)
119 resubmitted_jobs = lava_dispatch(user_args, job_dir='failed_jobs')
120 resubmitted_jobs = [int(x) for x in resubmitted_jobs if x != '']
121 return resubmitted_jobs
122
Paul Sokolovskyc2d6d882022-02-25 19:11:18 +0300123
Matthew Hartfb6fd362020-03-04 21:03:59 +0000124def fetch_artifacts(jobs, user_args, lava):
125 if not user_args.artifacts_path:
126 return
127 for job_id, info in jobs.items():
128 job_dir = info['job_dir']
129 info_print("Fetching artifacts for JOB: {} to {}".format(job_id, job_dir))
130 os.makedirs(job_dir, exist_ok=True)
131 def_path = os.path.join(job_dir, 'definition.yaml')
132 target_log = os.path.join(job_dir, 'target_log.txt')
Matthew Hart4a4f1202020-06-12 15:52:46 +0100133 config = os.path.join(job_dir, 'config.tar.bz2')
134 results_file = os.path.join(job_dir, 'results.yaml')
Matthew Hartfb6fd362020-03-04 21:03:59 +0000135 definition, metadata = lava.get_job_definition(job_id, def_path)
136 jobs[job_id]['metadata'] = metadata
137 time.sleep(0.2) # be friendly to LAVA
Matthew Hart4a4f1202020-06-12 15:52:46 +0100138 lava.get_job_log(job_id, target_log)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000139 time.sleep(0.2)
140 lava.get_job_config(job_id, config)
141 time.sleep(0.2)
Matthew Hart4a4f1202020-06-12 15:52:46 +0100142 lava.get_job_results(job_id, results_file)
Paul Sokolovskyc2d6d882022-02-25 19:11:18 +0300143 codecov_helper.extract_trace_data(target_log, job_dir)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000144 return(jobs)
145
146
147def lava_id_to_url(id, user_args):
148 return "{}/scheduler/job/{}".format(user_args.lava_url, id)
149
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800150def generateTestResult(info):
151 if info['health'] == "Complete" and info['state'] == "Finished":
152 return "PASS"
153 else:
154 return "FAIL"
155
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800156def job_links(jobs, user_args):
157 job_links = ""
158 for job, info in jobs.items():
159 job_links += "Build Config: {} ".format(info['metadata']['build_name'])
160 job_links += "LAVA link: {} ".format(lava_id_to_url(job, user_args))
161 job_links += "Build link: {}\n".format(info['metadata']['build_job_url'])
162 print(job_links)
163
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800164def csv_report(jobs):
165 lava_jobs = []
166 for job, info in jobs.items():
167 exist = False
168 for record in lava_jobs:
169 if info['metadata']['platform'] == record["Platform"] and \
170 info['metadata']['compiler'] == record["Compiler"] and \
171 info['metadata']['build_type'] == record["Build Type"]:
172 if record[info['metadata']['name']] != "FAIL":
173 record[info['metadata']['name']] = generateTestResult(info)
174 exist = True
175 break
176 if not exist:
177 record = {}
178 record["Platform"] = info['metadata']['platform']
179 record["Compiler"] = info['metadata']['compiler']
180 record["Build Type"] = info['metadata']['build_type']
181 record["Config Name"] = info['metadata']['name']
182 for cfg in cfgs:
183 record[cfg] = "N.A."
184 record[info['metadata']['name']] = generateTestResult(info)
185 lava_jobs.append(record)
186 lava_jobs.sort(key=lambda x: x["Platform"] + x["Compiler"] + x["Build Type"])
187 with open("test_results.csv", "w", newline="") as csvfile:
188 fieldnames = ["Platform", "Compiler", "Build Type"] + cfgs
189 writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore')
190
191 writer.writeheader()
192 writer.writerows(lava_jobs)
193
Matthew Hartfb6fd362020-03-04 21:03:59 +0000194def boot_report(jobs, user_args):
195 incomplete_jobs = []
196 for job, info in jobs.items():
197 if info['health'] != 'Complete':
198 if info['error_reason'] == 'Infrastructure':
199 info_print("Job {} failed with Infrastructure error".format(job))
200 incomplete_jobs.append(job)
201 incomplete_output = [lava_id_to_url(x, user_args) for x in incomplete_jobs];
202 if len(incomplete_jobs) > 0:
203 print("BOOT_RESULT: -1 Failed: {}".format(incomplete_output))
204 else:
205 print("BOOT_RESULT: +1")
206
Xinyu Zhang38a18872020-11-23 16:45:28 +0800207def failure_report(jobs, user_args):
208 failed_report = "FAILURE_TESTS:"
209 for job, info in jobs.items():
210 if info['health'] != "Complete" or info['state'] != "Finished":
211 failed_report += " {}:{}".format(info['metadata']['build_name'],
212 lava_id_to_url(job, user_args))
213 print(failed_report)
214
Matthew Hartfb6fd362020-03-04 21:03:59 +0000215def remove_lava_dupes(results):
216 for result in results:
217 if result['result'] != 'pass':
218 if result['suite'] == "lava":
219 for other in [x for x in results if x != result]:
220 if other['name'] == result['name']:
221 if other['result'] == 'pass':
222 results.remove(result)
223 return(results)
224
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300225def test_report(jobs, user_args):
Matthew Hartfb6fd362020-03-04 21:03:59 +0000226 # parsing of test results is WIP
227 fail_j = []
228 jinja_data = []
229 for job, info in jobs.items():
Matthew Hart4a4f1202020-06-12 15:52:46 +0100230 results_file = os.path.join(info['job_dir'], 'results.yaml')
231 if not os.path.exists(results_file) or (os.path.getsize(results_file) == 0):
232 fail_j.append(job)
233 continue
234 with open(results_file, "r") as F:
235 res_data = F.read()
Paul Sokolovskyf2f385d2022-01-11 00:36:31 +0300236 results = yaml.safe_load(res_data)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000237 non_lava_results = [x for x in results if x['suite'] != 'lava']
238 info['lava_url'] = lava_id_to_url(job, user_args)
239 info['artifacts_dir'] = "tf-m-ci-scripts/{}".format(info['job_dir'])
240 jinja_data.append({job: [info, non_lava_results]})
241 for result in non_lava_results:
Paul Sokolovsky58f00de2022-02-01 00:26:32 +0300242 if result['result'] == 'fail':
Matthew Hartfb6fd362020-03-04 21:03:59 +0000243 fail_j.append(job) if job not in fail_j else fail_j
244 time.sleep(0.5) # be friendly to LAVA
245 fail_output = [lava_id_to_url(x, user_args) for x in fail_j]
246 if len(fail_j) > 0:
247 print("TEST_RESULT: -1 Failed: {}".format(fail_output))
248 else:
249 print("TEST_RESULT: +1")
250 data = {}
251 data['jobs'] = jinja_data
252 render_jinja(data)
253
254def render_jinja(data):
255 work_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "jinja2_templates")
256 template_loader = FileSystemLoader(searchpath=work_dir)
257 template_env = Environment(loader=template_loader)
258 html = template_env.get_template("test_summary.jinja2").render(data)
259 csv = template_env.get_template("test_summary_csv.jinja2").render(data)
260 with open('test_summary.html', "w") as F:
261 F.write(html)
262 with open('test_summary.csv', "w") as F:
263 F.write(csv)
264
265def print_lava_urls(jobs, user_args):
266 output = [lava_id_to_url(x, user_args) for x in jobs]
267 print("LAVA jobs triggered for this build: {}".format(output))
268
269
270def info_print(line):
271 print("INFO: {}".format(line))
272
273def main(user_args):
274 """ Main logic """
275 user_args.lava_rpc = "RPC2"
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800276 for try_time in range(3):
277 try:
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300278 finished_jobs = wait_for_jobs(user_args)
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800279 break
280 except Exception as e:
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800281 if try_time < 2:
Paul Sokolovskycc51ea92022-02-02 19:34:02 +0300282 print("Exception in wait_for_jobs: {!r}".format(e))
283 print("Trying to get LAVA jobs again...")
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800284 else:
285 raise e
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300286 process_finished_jobs(finished_jobs, user_args)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000287
288def get_cmd_args():
289 """ Parse command line arguments """
290
291 # Parse command line arguments to override config
292 parser = argparse.ArgumentParser(description="Lava Wait Jobs")
293 cmdargs = parser.add_argument_group("Lava Wait Jobs")
294
295 # Configuration control
296 cmdargs.add_argument(
297 "--lava-url", dest="lava_url", action="store", help="LAVA lab URL (without RPC2)"
298 )
299 cmdargs.add_argument(
300 "--job-ids", dest="job_ids", action="store", required=True, help="Comma separated list of job IDS"
301 )
302 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800303 "--lava-token", dest="lava_token", action="store", help="LAVA auth token"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000304 )
305 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800306 "--lava-user", dest="lava_user", action="store", help="LAVA username"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000307 )
308 cmdargs.add_argument(
309 "--use-env", dest="token_from_env", action="store_true", default=False, help="Use LAVA auth info from environment"
310 )
311 cmdargs.add_argument(
312 "--lava-timeout", dest="dispatch_timeout", action="store", type=int, default=3600, help="Time in seconds to wait for all jobs"
313 )
314 cmdargs.add_argument(
315 "--artifacts-path", dest="artifacts_path", action="store", help="Download LAVA artifacts to this directory"
316 )
317 return parser.parse_args()
318
319
320if __name__ == "__main__":
321 main(get_cmd_args())