blob: 262eec23c8c72c7acaa67ceed632148aa13bcce8 [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 *
Matthew Hartfb6fd362020-03-04 21:03:59 +000029
30try:
31 from tfm_ci_pylib.utils import save_json, load_json, sort_dict,\
32 load_yaml, test, print_test
33 from tfm_ci_pylib.lava_rpc_connector import LAVA_RPC_connector
34except ImportError:
35 dir_path = os.path.dirname(os.path.realpath(__file__))
36 sys.path.append(os.path.join(dir_path, "../"))
37 from tfm_ci_pylib.utils import save_json, load_json, sort_dict,\
38 load_yaml, test, print_test
39 from tfm_ci_pylib.lava_rpc_connector import LAVA_RPC_connector
40
Xinyu Zhang1b8f5152020-11-13 16:10:58 +080041cfgs = ["Default", "CoreIPC", "CoreIPCTfmLevel2", "CoreIPCTfmLevel3",
42 "Regression", "RegressionIPC",
43 "RegressionIPCTfmLevel2", "RegressionIPCTfmLevel3",
44 "DefaultProfileS", "RegressionProfileS",
45 "DefaultProfileM", "RegressionProfileM", "RegressionProfileM PSOFF",
Xinyu Zhang9b1aef92021-03-12 15:36:44 +080046 "DefaultProfileL", "RegressionProfileL",
Xinyu Zhang1b8f5152020-11-13 16:10:58 +080047 "PsaApiTest (Attest)", "PsaApiTestIPC (Attest)",
48 "PsaApiTestIPCTfmLevel2 (Attest)",
49 "PsaApiTest (Crypto)", "PsaApiTestIPC (Crypto)",
50 "PsaApiTestIPCTfmLevel2 (Crypto)",
51 "PsaApiTest (PS)", "PsaApiTestIPC (PS)",
52 "PsaApiTestIPCTfmLevel2 (PS)",
53 "PsaApiTest (ITS)", "PsaApiTestIPC (ITS)",
54 "PsaApiTestIPCTfmLevel2 (ITS)",
55 "PsaApiTestIPC (FF)",
56 "PsaApiTestIPCTfmLevel2 (FF)",
Paul Sokolovskyb6003112022-02-04 00:36:14 +030057 "PsaApiTest (STORAGE)",
58 "PsaApiTestIPC (STORAGE)",
59 "PsaApiTestIPCTfmLevel2 (STORAGE)",
60 "PsaApiTestIPCTfmLevel3 (STORAGE)",
Xinyu Zhang1b8f5152020-11-13 16:10:58 +080061 "PsaApiTestIPCTfmLevel3 (ITS)", "PsaApiTestIPCTfmLevel3 (PS)",
62 "PsaApiTestIPCTfmLevel3 (Crypto)", "PsaApiTestIPCTfmLevel3 (Attest)",
63 "PsaApiTestIPCTfmLevel3 (FF)"]
64
Paul Sokolovskyf1ff6c12022-02-02 21:42:41 +030065# Convert test config identifiers to LAVA naming convention.
66cfgs = [x.replace(" (", "_").replace(")", "") for x in cfgs]
67
Matthew Hartfb6fd362020-03-04 21:03:59 +000068def wait_for_jobs(user_args):
69 job_list = user_args.job_ids.split(",")
70 job_list = [int(x) for x in job_list if x != '']
71 lava = test_lava_dispatch_credentials(user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080072 finished_jobs = get_finished_jobs(job_list, user_args, lava)
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080073 resubmit_jobs = resubmit_failed_jobs(finished_jobs, user_args)
74 finished_resubmit_jobs = get_finished_jobs(resubmit_jobs, user_args, lava)
75 finished_jobs.update(finished_resubmit_jobs)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030076 return finished_jobs
77
78
79def process_finished_jobs(finished_jobs, user_args):
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080080 print_lava_urls(finished_jobs, user_args)
81 job_links(finished_jobs, user_args)
82 boot_report(finished_jobs, user_args)
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030083 test_report(finished_jobs, user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080084 failure_report(finished_jobs, user_args)
85 csv_report(finished_jobs)
86
Paul Sokolovsky451f67b2022-03-08 19:44:41 +030087
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080088def get_finished_jobs(job_list, user_args, lava):
Matthew Hartfb6fd362020-03-04 21:03:59 +000089 finished_jobs = lava.block_wait_for_jobs(job_list, user_args.dispatch_timeout, 0.5)
90 unfinished_jobs = [item for item in job_list if item not in finished_jobs]
91 for job in unfinished_jobs:
92 info_print("Cancelling unfinished job: {}".format(job))
93 lava.cancel_job(job)
94 if user_args.artifacts_path:
95 for job, info in finished_jobs.items():
96 info['job_dir'] = os.path.join(user_args.artifacts_path, "{}_{}".format(str(job), info['description']))
97 finished_jobs[job] = info
98 finished_jobs = fetch_artifacts(finished_jobs, user_args, lava)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080099 return finished_jobs
Matthew Hartfb6fd362020-03-04 21:03:59 +0000100
Xinyu Zhangc8a670c2021-05-18 20:20:53 +0800101def resubmit_failed_jobs(jobs, user_args):
102 if not jobs:
103 return []
Xinyu Zhang4aca6d02021-05-31 11:43:32 +0800104 time.sleep(2) # be friendly to LAVA
Xinyu Zhangc8a670c2021-05-18 20:20:53 +0800105 failed_job = []
106 os.makedirs('failed_jobs', exist_ok=True)
107 for job_id, info in jobs.items():
108 if not (info['health'] == "Complete" and info['state'] == "Finished"):
109 job_dir = info['job_dir']
110 def_path = os.path.join(job_dir, 'definition.yaml')
111 os.rename(def_path, 'failed_jobs/{}_definition.yaml'.format(job_id))
112 shutil.rmtree(job_dir)
113 failed_job.append(job_id)
114 for failed_job_id in failed_job:
115 jobs.pop(failed_job_id)
116 resubmitted_jobs = lava_dispatch(user_args, job_dir='failed_jobs')
117 resubmitted_jobs = [int(x) for x in resubmitted_jobs if x != '']
118 return resubmitted_jobs
119
Matthew Hartfb6fd362020-03-04 21:03:59 +0000120def fetch_artifacts(jobs, user_args, lava):
121 if not user_args.artifacts_path:
122 return
123 for job_id, info in jobs.items():
124 job_dir = info['job_dir']
125 info_print("Fetching artifacts for JOB: {} to {}".format(job_id, job_dir))
126 os.makedirs(job_dir, exist_ok=True)
127 def_path = os.path.join(job_dir, 'definition.yaml')
128 target_log = os.path.join(job_dir, 'target_log.txt')
Matthew Hart4a4f1202020-06-12 15:52:46 +0100129 config = os.path.join(job_dir, 'config.tar.bz2')
130 results_file = os.path.join(job_dir, 'results.yaml')
Matthew Hartfb6fd362020-03-04 21:03:59 +0000131 definition, metadata = lava.get_job_definition(job_id, def_path)
132 jobs[job_id]['metadata'] = metadata
133 time.sleep(0.2) # be friendly to LAVA
Matthew Hart4a4f1202020-06-12 15:52:46 +0100134 lava.get_job_log(job_id, target_log)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000135 time.sleep(0.2)
136 lava.get_job_config(job_id, config)
137 time.sleep(0.2)
Matthew Hart4a4f1202020-06-12 15:52:46 +0100138 lava.get_job_results(job_id, results_file)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000139 return(jobs)
140
141
142def lava_id_to_url(id, user_args):
143 return "{}/scheduler/job/{}".format(user_args.lava_url, id)
144
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800145def generateTestResult(info):
146 if info['health'] == "Complete" and info['state'] == "Finished":
147 return "PASS"
148 else:
149 return "FAIL"
150
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800151def job_links(jobs, user_args):
152 job_links = ""
153 for job, info in jobs.items():
154 job_links += "Build Config: {} ".format(info['metadata']['build_name'])
155 job_links += "LAVA link: {} ".format(lava_id_to_url(job, user_args))
156 job_links += "Build link: {}\n".format(info['metadata']['build_job_url'])
157 print(job_links)
158
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800159def csv_report(jobs):
160 lava_jobs = []
161 for job, info in jobs.items():
162 exist = False
163 for record in lava_jobs:
164 if info['metadata']['platform'] == record["Platform"] and \
165 info['metadata']['compiler'] == record["Compiler"] and \
166 info['metadata']['build_type'] == record["Build Type"]:
167 if record[info['metadata']['name']] != "FAIL":
168 record[info['metadata']['name']] = generateTestResult(info)
169 exist = True
170 break
171 if not exist:
172 record = {}
173 record["Platform"] = info['metadata']['platform']
174 record["Compiler"] = info['metadata']['compiler']
175 record["Build Type"] = info['metadata']['build_type']
176 record["Config Name"] = info['metadata']['name']
177 for cfg in cfgs:
178 record[cfg] = "N.A."
179 record[info['metadata']['name']] = generateTestResult(info)
180 lava_jobs.append(record)
181 lava_jobs.sort(key=lambda x: x["Platform"] + x["Compiler"] + x["Build Type"])
182 with open("test_results.csv", "w", newline="") as csvfile:
183 fieldnames = ["Platform", "Compiler", "Build Type"] + cfgs
184 writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore')
185
186 writer.writeheader()
187 writer.writerows(lava_jobs)
188
Matthew Hartfb6fd362020-03-04 21:03:59 +0000189def boot_report(jobs, user_args):
190 incomplete_jobs = []
191 for job, info in jobs.items():
192 if info['health'] != 'Complete':
193 if info['error_reason'] == 'Infrastructure':
194 info_print("Job {} failed with Infrastructure error".format(job))
195 incomplete_jobs.append(job)
196 incomplete_output = [lava_id_to_url(x, user_args) for x in incomplete_jobs];
197 if len(incomplete_jobs) > 0:
198 print("BOOT_RESULT: -1 Failed: {}".format(incomplete_output))
199 else:
200 print("BOOT_RESULT: +1")
201
Xinyu Zhang38a18872020-11-23 16:45:28 +0800202def failure_report(jobs, user_args):
203 failed_report = "FAILURE_TESTS:"
204 for job, info in jobs.items():
205 if info['health'] != "Complete" or info['state'] != "Finished":
206 failed_report += " {}:{}".format(info['metadata']['build_name'],
207 lava_id_to_url(job, user_args))
208 print(failed_report)
209
Matthew Hartfb6fd362020-03-04 21:03:59 +0000210def remove_lava_dupes(results):
211 for result in results:
212 if result['result'] != 'pass':
213 if result['suite'] == "lava":
214 for other in [x for x in results if x != result]:
215 if other['name'] == result['name']:
216 if other['result'] == 'pass':
217 results.remove(result)
218 return(results)
219
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300220def test_report(jobs, user_args):
Matthew Hartfb6fd362020-03-04 21:03:59 +0000221 # parsing of test results is WIP
222 fail_j = []
223 jinja_data = []
224 for job, info in jobs.items():
Matthew Hart4a4f1202020-06-12 15:52:46 +0100225 results_file = os.path.join(info['job_dir'], 'results.yaml')
226 if not os.path.exists(results_file) or (os.path.getsize(results_file) == 0):
227 fail_j.append(job)
228 continue
229 with open(results_file, "r") as F:
230 res_data = F.read()
Paul Sokolovskyf2f385d2022-01-11 00:36:31 +0300231 results = yaml.safe_load(res_data)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000232 non_lava_results = [x for x in results if x['suite'] != 'lava']
233 info['lava_url'] = lava_id_to_url(job, user_args)
234 info['artifacts_dir'] = "tf-m-ci-scripts/{}".format(info['job_dir'])
235 jinja_data.append({job: [info, non_lava_results]})
236 for result in non_lava_results:
Paul Sokolovsky58f00de2022-02-01 00:26:32 +0300237 if result['result'] == 'fail':
Matthew Hartfb6fd362020-03-04 21:03:59 +0000238 fail_j.append(job) if job not in fail_j else fail_j
239 time.sleep(0.5) # be friendly to LAVA
240 fail_output = [lava_id_to_url(x, user_args) for x in fail_j]
241 if len(fail_j) > 0:
242 print("TEST_RESULT: -1 Failed: {}".format(fail_output))
243 else:
244 print("TEST_RESULT: +1")
245 data = {}
246 data['jobs'] = jinja_data
247 render_jinja(data)
248
249def render_jinja(data):
250 work_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "jinja2_templates")
251 template_loader = FileSystemLoader(searchpath=work_dir)
252 template_env = Environment(loader=template_loader)
253 html = template_env.get_template("test_summary.jinja2").render(data)
254 csv = template_env.get_template("test_summary_csv.jinja2").render(data)
255 with open('test_summary.html', "w") as F:
256 F.write(html)
257 with open('test_summary.csv', "w") as F:
258 F.write(csv)
259
260def print_lava_urls(jobs, user_args):
261 output = [lava_id_to_url(x, user_args) for x in jobs]
262 print("LAVA jobs triggered for this build: {}".format(output))
263
264
265def info_print(line):
266 print("INFO: {}".format(line))
267
268def main(user_args):
269 """ Main logic """
270 user_args.lava_rpc = "RPC2"
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800271 for try_time in range(3):
272 try:
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300273 finished_jobs = wait_for_jobs(user_args)
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800274 break
275 except Exception as e:
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800276 if try_time < 2:
Paul Sokolovskycc51ea92022-02-02 19:34:02 +0300277 print("Exception in wait_for_jobs: {!r}".format(e))
278 print("Trying to get LAVA jobs again...")
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800279 else:
280 raise e
Paul Sokolovsky451f67b2022-03-08 19:44:41 +0300281 process_finished_jobs(finished_jobs, user_args)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000282
283def get_cmd_args():
284 """ Parse command line arguments """
285
286 # Parse command line arguments to override config
287 parser = argparse.ArgumentParser(description="Lava Wait Jobs")
288 cmdargs = parser.add_argument_group("Lava Wait Jobs")
289
290 # Configuration control
291 cmdargs.add_argument(
292 "--lava-url", dest="lava_url", action="store", help="LAVA lab URL (without RPC2)"
293 )
294 cmdargs.add_argument(
295 "--job-ids", dest="job_ids", action="store", required=True, help="Comma separated list of job IDS"
296 )
297 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800298 "--lava-token", dest="lava_token", action="store", help="LAVA auth token"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000299 )
300 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800301 "--lava-user", dest="lava_user", action="store", help="LAVA username"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000302 )
303 cmdargs.add_argument(
304 "--use-env", dest="token_from_env", action="store_true", default=False, help="Use LAVA auth info from environment"
305 )
306 cmdargs.add_argument(
307 "--lava-timeout", dest="dispatch_timeout", action="store", type=int, default=3600, help="Time in seconds to wait for all jobs"
308 )
309 cmdargs.add_argument(
310 "--artifacts-path", dest="artifacts_path", action="store", help="Download LAVA artifacts to this directory"
311 )
312 return parser.parse_args()
313
314
315if __name__ == "__main__":
316 main(get_cmd_args())