blob: 6847ee438d36dd998ffa34903cbf597cbfbf832f [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)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080076 print_lava_urls(finished_jobs, user_args)
77 job_links(finished_jobs, user_args)
78 boot_report(finished_jobs, user_args)
79 test_report(finished_jobs, user_args, lava)
80 failure_report(finished_jobs, user_args)
81 csv_report(finished_jobs)
82
83def get_finished_jobs(job_list, user_args, lava):
Matthew Hartfb6fd362020-03-04 21:03:59 +000084 finished_jobs = lava.block_wait_for_jobs(job_list, user_args.dispatch_timeout, 0.5)
85 unfinished_jobs = [item for item in job_list if item not in finished_jobs]
86 for job in unfinished_jobs:
87 info_print("Cancelling unfinished job: {}".format(job))
88 lava.cancel_job(job)
89 if user_args.artifacts_path:
90 for job, info in finished_jobs.items():
91 info['job_dir'] = os.path.join(user_args.artifacts_path, "{}_{}".format(str(job), info['description']))
92 finished_jobs[job] = info
93 finished_jobs = fetch_artifacts(finished_jobs, user_args, lava)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080094 return finished_jobs
Matthew Hartfb6fd362020-03-04 21:03:59 +000095
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080096def resubmit_failed_jobs(jobs, user_args):
97 if not jobs:
98 return []
Xinyu Zhang4aca6d02021-05-31 11:43:32 +080099 time.sleep(2) # be friendly to LAVA
Xinyu Zhangc8a670c2021-05-18 20:20:53 +0800100 failed_job = []
101 os.makedirs('failed_jobs', exist_ok=True)
102 for job_id, info in jobs.items():
103 if not (info['health'] == "Complete" and info['state'] == "Finished"):
104 job_dir = info['job_dir']
105 def_path = os.path.join(job_dir, 'definition.yaml')
106 os.rename(def_path, 'failed_jobs/{}_definition.yaml'.format(job_id))
107 shutil.rmtree(job_dir)
108 failed_job.append(job_id)
109 for failed_job_id in failed_job:
110 jobs.pop(failed_job_id)
111 resubmitted_jobs = lava_dispatch(user_args, job_dir='failed_jobs')
112 resubmitted_jobs = [int(x) for x in resubmitted_jobs if x != '']
113 return resubmitted_jobs
114
Matthew Hartfb6fd362020-03-04 21:03:59 +0000115def fetch_artifacts(jobs, user_args, lava):
116 if not user_args.artifacts_path:
117 return
118 for job_id, info in jobs.items():
119 job_dir = info['job_dir']
120 info_print("Fetching artifacts for JOB: {} to {}".format(job_id, job_dir))
121 os.makedirs(job_dir, exist_ok=True)
122 def_path = os.path.join(job_dir, 'definition.yaml')
123 target_log = os.path.join(job_dir, 'target_log.txt')
Matthew Hart4a4f1202020-06-12 15:52:46 +0100124 config = os.path.join(job_dir, 'config.tar.bz2')
125 results_file = os.path.join(job_dir, 'results.yaml')
Matthew Hartfb6fd362020-03-04 21:03:59 +0000126 definition, metadata = lava.get_job_definition(job_id, def_path)
127 jobs[job_id]['metadata'] = metadata
128 time.sleep(0.2) # be friendly to LAVA
Matthew Hart4a4f1202020-06-12 15:52:46 +0100129 lava.get_job_log(job_id, target_log)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000130 time.sleep(0.2)
131 lava.get_job_config(job_id, config)
132 time.sleep(0.2)
Matthew Hart4a4f1202020-06-12 15:52:46 +0100133 lava.get_job_results(job_id, results_file)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000134 return(jobs)
135
136
137def lava_id_to_url(id, user_args):
138 return "{}/scheduler/job/{}".format(user_args.lava_url, id)
139
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800140def generateTestResult(info):
141 if info['health'] == "Complete" and info['state'] == "Finished":
142 return "PASS"
143 else:
144 return "FAIL"
145
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800146def job_links(jobs, user_args):
147 job_links = ""
148 for job, info in jobs.items():
149 job_links += "Build Config: {} ".format(info['metadata']['build_name'])
150 job_links += "LAVA link: {} ".format(lava_id_to_url(job, user_args))
151 job_links += "Build link: {}\n".format(info['metadata']['build_job_url'])
152 print(job_links)
153
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800154def csv_report(jobs):
155 lava_jobs = []
156 for job, info in jobs.items():
157 exist = False
158 for record in lava_jobs:
159 if info['metadata']['platform'] == record["Platform"] and \
160 info['metadata']['compiler'] == record["Compiler"] and \
161 info['metadata']['build_type'] == record["Build Type"]:
162 if record[info['metadata']['name']] != "FAIL":
163 record[info['metadata']['name']] = generateTestResult(info)
164 exist = True
165 break
166 if not exist:
167 record = {}
168 record["Platform"] = info['metadata']['platform']
169 record["Compiler"] = info['metadata']['compiler']
170 record["Build Type"] = info['metadata']['build_type']
171 record["Config Name"] = info['metadata']['name']
172 for cfg in cfgs:
173 record[cfg] = "N.A."
174 record[info['metadata']['name']] = generateTestResult(info)
175 lava_jobs.append(record)
176 lava_jobs.sort(key=lambda x: x["Platform"] + x["Compiler"] + x["Build Type"])
177 with open("test_results.csv", "w", newline="") as csvfile:
178 fieldnames = ["Platform", "Compiler", "Build Type"] + cfgs
179 writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore')
180
181 writer.writeheader()
182 writer.writerows(lava_jobs)
183
Matthew Hartfb6fd362020-03-04 21:03:59 +0000184def boot_report(jobs, user_args):
185 incomplete_jobs = []
186 for job, info in jobs.items():
187 if info['health'] != 'Complete':
188 if info['error_reason'] == 'Infrastructure':
189 info_print("Job {} failed with Infrastructure error".format(job))
190 incomplete_jobs.append(job)
191 incomplete_output = [lava_id_to_url(x, user_args) for x in incomplete_jobs];
192 if len(incomplete_jobs) > 0:
193 print("BOOT_RESULT: -1 Failed: {}".format(incomplete_output))
194 else:
195 print("BOOT_RESULT: +1")
196
Xinyu Zhang38a18872020-11-23 16:45:28 +0800197def failure_report(jobs, user_args):
198 failed_report = "FAILURE_TESTS:"
199 for job, info in jobs.items():
200 if info['health'] != "Complete" or info['state'] != "Finished":
201 failed_report += " {}:{}".format(info['metadata']['build_name'],
202 lava_id_to_url(job, user_args))
203 print(failed_report)
204
Matthew Hartfb6fd362020-03-04 21:03:59 +0000205def remove_lava_dupes(results):
206 for result in results:
207 if result['result'] != 'pass':
208 if result['suite'] == "lava":
209 for other in [x for x in results if x != result]:
210 if other['name'] == result['name']:
211 if other['result'] == 'pass':
212 results.remove(result)
213 return(results)
214
215def test_report(jobs, user_args, lava):
216 # parsing of test results is WIP
217 fail_j = []
218 jinja_data = []
219 for job, info in jobs.items():
Matthew Hart4a4f1202020-06-12 15:52:46 +0100220 results_file = os.path.join(info['job_dir'], 'results.yaml')
221 if not os.path.exists(results_file) or (os.path.getsize(results_file) == 0):
222 fail_j.append(job)
223 continue
224 with open(results_file, "r") as F:
225 res_data = F.read()
Paul Sokolovskyf2f385d2022-01-11 00:36:31 +0300226 results = yaml.safe_load(res_data)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000227 non_lava_results = [x for x in results if x['suite'] != 'lava']
228 info['lava_url'] = lava_id_to_url(job, user_args)
229 info['artifacts_dir'] = "tf-m-ci-scripts/{}".format(info['job_dir'])
230 jinja_data.append({job: [info, non_lava_results]})
231 for result in non_lava_results:
Paul Sokolovsky58f00de2022-02-01 00:26:32 +0300232 if result['result'] == 'fail':
Matthew Hartfb6fd362020-03-04 21:03:59 +0000233 fail_j.append(job) if job not in fail_j else fail_j
234 time.sleep(0.5) # be friendly to LAVA
235 fail_output = [lava_id_to_url(x, user_args) for x in fail_j]
236 if len(fail_j) > 0:
237 print("TEST_RESULT: -1 Failed: {}".format(fail_output))
238 else:
239 print("TEST_RESULT: +1")
240 data = {}
241 data['jobs'] = jinja_data
242 render_jinja(data)
243
244def render_jinja(data):
245 work_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "jinja2_templates")
246 template_loader = FileSystemLoader(searchpath=work_dir)
247 template_env = Environment(loader=template_loader)
248 html = template_env.get_template("test_summary.jinja2").render(data)
249 csv = template_env.get_template("test_summary_csv.jinja2").render(data)
250 with open('test_summary.html', "w") as F:
251 F.write(html)
252 with open('test_summary.csv', "w") as F:
253 F.write(csv)
254
255def print_lava_urls(jobs, user_args):
256 output = [lava_id_to_url(x, user_args) for x in jobs]
257 print("LAVA jobs triggered for this build: {}".format(output))
258
259
260def info_print(line):
261 print("INFO: {}".format(line))
262
263def main(user_args):
264 """ Main logic """
265 user_args.lava_rpc = "RPC2"
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800266 for try_time in range(3):
267 try:
268 wait_for_jobs(user_args)
269 break
270 except Exception as e:
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800271 if try_time < 2:
Paul Sokolovskycc51ea92022-02-02 19:34:02 +0300272 print("Exception in wait_for_jobs: {!r}".format(e))
273 print("Trying to get LAVA jobs again...")
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800274 else:
275 raise e
Matthew Hartfb6fd362020-03-04 21:03:59 +0000276
277def get_cmd_args():
278 """ Parse command line arguments """
279
280 # Parse command line arguments to override config
281 parser = argparse.ArgumentParser(description="Lava Wait Jobs")
282 cmdargs = parser.add_argument_group("Lava Wait Jobs")
283
284 # Configuration control
285 cmdargs.add_argument(
286 "--lava-url", dest="lava_url", action="store", help="LAVA lab URL (without RPC2)"
287 )
288 cmdargs.add_argument(
289 "--job-ids", dest="job_ids", action="store", required=True, help="Comma separated list of job IDS"
290 )
291 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800292 "--lava-token", dest="lava_token", action="store", help="LAVA auth token"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000293 )
294 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800295 "--lava-user", dest="lava_user", action="store", help="LAVA username"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000296 )
297 cmdargs.add_argument(
298 "--use-env", dest="token_from_env", action="store_true", default=False, help="Use LAVA auth info from environment"
299 )
300 cmdargs.add_argument(
301 "--lava-timeout", dest="dispatch_timeout", action="store", type=int, default=3600, help="Time in seconds to wait for all jobs"
302 )
303 cmdargs.add_argument(
304 "--artifacts-path", dest="artifacts_path", action="store", help="Download LAVA artifacts to this directory"
305 )
306 return parser.parse_args()
307
308
309if __name__ == "__main__":
310 main(get_cmd_args())