blob: 12ce57c6d9255a1357455f7c29f3f3a6aa11b421 [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)",
57 "PsaApiTestIPCTfmLevel3 (ITS)", "PsaApiTestIPCTfmLevel3 (PS)",
58 "PsaApiTestIPCTfmLevel3 (Crypto)", "PsaApiTestIPCTfmLevel3 (Attest)",
59 "PsaApiTestIPCTfmLevel3 (FF)"]
60
Paul Sokolovskyf1ff6c12022-02-02 21:42:41 +030061# Convert test config identifiers to LAVA naming convention.
62cfgs = [x.replace(" (", "_").replace(")", "") for x in cfgs]
63
Matthew Hartfb6fd362020-03-04 21:03:59 +000064def wait_for_jobs(user_args):
65 job_list = user_args.job_ids.split(",")
66 job_list = [int(x) for x in job_list if x != '']
67 lava = test_lava_dispatch_credentials(user_args)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080068 finished_jobs = get_finished_jobs(job_list, user_args, lava)
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080069 resubmit_jobs = resubmit_failed_jobs(finished_jobs, user_args)
70 finished_resubmit_jobs = get_finished_jobs(resubmit_jobs, user_args, lava)
71 finished_jobs.update(finished_resubmit_jobs)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080072 print_lava_urls(finished_jobs, user_args)
73 job_links(finished_jobs, user_args)
74 boot_report(finished_jobs, user_args)
75 test_report(finished_jobs, user_args, lava)
76 failure_report(finished_jobs, user_args)
77 csv_report(finished_jobs)
78
79def get_finished_jobs(job_list, user_args, lava):
Matthew Hartfb6fd362020-03-04 21:03:59 +000080 finished_jobs = lava.block_wait_for_jobs(job_list, user_args.dispatch_timeout, 0.5)
81 unfinished_jobs = [item for item in job_list if item not in finished_jobs]
82 for job in unfinished_jobs:
83 info_print("Cancelling unfinished job: {}".format(job))
84 lava.cancel_job(job)
85 if user_args.artifacts_path:
86 for job, info in finished_jobs.items():
87 info['job_dir'] = os.path.join(user_args.artifacts_path, "{}_{}".format(str(job), info['description']))
88 finished_jobs[job] = info
89 finished_jobs = fetch_artifacts(finished_jobs, user_args, lava)
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +080090 return finished_jobs
Matthew Hartfb6fd362020-03-04 21:03:59 +000091
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080092def resubmit_failed_jobs(jobs, user_args):
93 if not jobs:
94 return []
Xinyu Zhang4aca6d02021-05-31 11:43:32 +080095 time.sleep(2) # be friendly to LAVA
Xinyu Zhangc8a670c2021-05-18 20:20:53 +080096 failed_job = []
97 os.makedirs('failed_jobs', exist_ok=True)
98 for job_id, info in jobs.items():
99 if not (info['health'] == "Complete" and info['state'] == "Finished"):
100 job_dir = info['job_dir']
101 def_path = os.path.join(job_dir, 'definition.yaml')
102 os.rename(def_path, 'failed_jobs/{}_definition.yaml'.format(job_id))
103 shutil.rmtree(job_dir)
104 failed_job.append(job_id)
105 for failed_job_id in failed_job:
106 jobs.pop(failed_job_id)
107 resubmitted_jobs = lava_dispatch(user_args, job_dir='failed_jobs')
108 resubmitted_jobs = [int(x) for x in resubmitted_jobs if x != '']
109 return resubmitted_jobs
110
Matthew Hartfb6fd362020-03-04 21:03:59 +0000111def fetch_artifacts(jobs, user_args, lava):
112 if not user_args.artifacts_path:
113 return
114 for job_id, info in jobs.items():
115 job_dir = info['job_dir']
116 info_print("Fetching artifacts for JOB: {} to {}".format(job_id, job_dir))
117 os.makedirs(job_dir, exist_ok=True)
118 def_path = os.path.join(job_dir, 'definition.yaml')
119 target_log = os.path.join(job_dir, 'target_log.txt')
Matthew Hart4a4f1202020-06-12 15:52:46 +0100120 config = os.path.join(job_dir, 'config.tar.bz2')
121 results_file = os.path.join(job_dir, 'results.yaml')
Matthew Hartfb6fd362020-03-04 21:03:59 +0000122 definition, metadata = lava.get_job_definition(job_id, def_path)
123 jobs[job_id]['metadata'] = metadata
124 time.sleep(0.2) # be friendly to LAVA
Matthew Hart4a4f1202020-06-12 15:52:46 +0100125 lava.get_job_log(job_id, target_log)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000126 time.sleep(0.2)
127 lava.get_job_config(job_id, config)
128 time.sleep(0.2)
Matthew Hart4a4f1202020-06-12 15:52:46 +0100129 lava.get_job_results(job_id, results_file)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000130 return(jobs)
131
132
133def lava_id_to_url(id, user_args):
134 return "{}/scheduler/job/{}".format(user_args.lava_url, id)
135
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800136def generateTestResult(info):
137 if info['health'] == "Complete" and info['state'] == "Finished":
138 return "PASS"
139 else:
140 return "FAIL"
141
Xinyu Zhang97ee3fd2020-12-14 14:45:06 +0800142def job_links(jobs, user_args):
143 job_links = ""
144 for job, info in jobs.items():
145 job_links += "Build Config: {} ".format(info['metadata']['build_name'])
146 job_links += "LAVA link: {} ".format(lava_id_to_url(job, user_args))
147 job_links += "Build link: {}\n".format(info['metadata']['build_job_url'])
148 print(job_links)
149
Xinyu Zhang1b8f5152020-11-13 16:10:58 +0800150def csv_report(jobs):
151 lava_jobs = []
152 for job, info in jobs.items():
153 exist = False
154 for record in lava_jobs:
155 if info['metadata']['platform'] == record["Platform"] and \
156 info['metadata']['compiler'] == record["Compiler"] and \
157 info['metadata']['build_type'] == record["Build Type"]:
158 if record[info['metadata']['name']] != "FAIL":
159 record[info['metadata']['name']] = generateTestResult(info)
160 exist = True
161 break
162 if not exist:
163 record = {}
164 record["Platform"] = info['metadata']['platform']
165 record["Compiler"] = info['metadata']['compiler']
166 record["Build Type"] = info['metadata']['build_type']
167 record["Config Name"] = info['metadata']['name']
168 for cfg in cfgs:
169 record[cfg] = "N.A."
170 record[info['metadata']['name']] = generateTestResult(info)
171 lava_jobs.append(record)
172 lava_jobs.sort(key=lambda x: x["Platform"] + x["Compiler"] + x["Build Type"])
173 with open("test_results.csv", "w", newline="") as csvfile:
174 fieldnames = ["Platform", "Compiler", "Build Type"] + cfgs
175 writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore')
176
177 writer.writeheader()
178 writer.writerows(lava_jobs)
179
Matthew Hartfb6fd362020-03-04 21:03:59 +0000180def boot_report(jobs, user_args):
181 incomplete_jobs = []
182 for job, info in jobs.items():
183 if info['health'] != 'Complete':
184 if info['error_reason'] == 'Infrastructure':
185 info_print("Job {} failed with Infrastructure error".format(job))
186 incomplete_jobs.append(job)
187 incomplete_output = [lava_id_to_url(x, user_args) for x in incomplete_jobs];
188 if len(incomplete_jobs) > 0:
189 print("BOOT_RESULT: -1 Failed: {}".format(incomplete_output))
190 else:
191 print("BOOT_RESULT: +1")
192
Xinyu Zhang38a18872020-11-23 16:45:28 +0800193def failure_report(jobs, user_args):
194 failed_report = "FAILURE_TESTS:"
195 for job, info in jobs.items():
196 if info['health'] != "Complete" or info['state'] != "Finished":
197 failed_report += " {}:{}".format(info['metadata']['build_name'],
198 lava_id_to_url(job, user_args))
199 print(failed_report)
200
Matthew Hartfb6fd362020-03-04 21:03:59 +0000201def remove_lava_dupes(results):
202 for result in results:
203 if result['result'] != 'pass':
204 if result['suite'] == "lava":
205 for other in [x for x in results if x != result]:
206 if other['name'] == result['name']:
207 if other['result'] == 'pass':
208 results.remove(result)
209 return(results)
210
211def test_report(jobs, user_args, lava):
212 # parsing of test results is WIP
213 fail_j = []
214 jinja_data = []
215 for job, info in jobs.items():
Matthew Hart4a4f1202020-06-12 15:52:46 +0100216 results_file = os.path.join(info['job_dir'], 'results.yaml')
217 if not os.path.exists(results_file) or (os.path.getsize(results_file) == 0):
218 fail_j.append(job)
219 continue
220 with open(results_file, "r") as F:
221 res_data = F.read()
Paul Sokolovskyf2f385d2022-01-11 00:36:31 +0300222 results = yaml.safe_load(res_data)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000223 non_lava_results = [x for x in results if x['suite'] != 'lava']
224 info['lava_url'] = lava_id_to_url(job, user_args)
225 info['artifacts_dir'] = "tf-m-ci-scripts/{}".format(info['job_dir'])
226 jinja_data.append({job: [info, non_lava_results]})
227 for result in non_lava_results:
Paul Sokolovsky58f00de2022-02-01 00:26:32 +0300228 if result['result'] == 'fail':
Matthew Hartfb6fd362020-03-04 21:03:59 +0000229 fail_j.append(job) if job not in fail_j else fail_j
230 time.sleep(0.5) # be friendly to LAVA
231 fail_output = [lava_id_to_url(x, user_args) for x in fail_j]
232 if len(fail_j) > 0:
233 print("TEST_RESULT: -1 Failed: {}".format(fail_output))
234 else:
235 print("TEST_RESULT: +1")
236 data = {}
237 data['jobs'] = jinja_data
238 render_jinja(data)
239
240def render_jinja(data):
241 work_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "jinja2_templates")
242 template_loader = FileSystemLoader(searchpath=work_dir)
243 template_env = Environment(loader=template_loader)
244 html = template_env.get_template("test_summary.jinja2").render(data)
245 csv = template_env.get_template("test_summary_csv.jinja2").render(data)
246 with open('test_summary.html', "w") as F:
247 F.write(html)
248 with open('test_summary.csv', "w") as F:
249 F.write(csv)
250
251def print_lava_urls(jobs, user_args):
252 output = [lava_id_to_url(x, user_args) for x in jobs]
253 print("LAVA jobs triggered for this build: {}".format(output))
254
255
256def info_print(line):
257 print("INFO: {}".format(line))
258
259def main(user_args):
260 """ Main logic """
261 user_args.lava_rpc = "RPC2"
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800262 for try_time in range(3):
263 try:
264 wait_for_jobs(user_args)
265 break
266 except Exception as e:
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800267 if try_time < 2:
Paul Sokolovskycc51ea92022-02-02 19:34:02 +0300268 print("Exception in wait_for_jobs: {!r}".format(e))
269 print("Trying to get LAVA jobs again...")
Xinyu Zhang3e8f6602021-04-28 10:57:32 +0800270 else:
271 raise e
Matthew Hartfb6fd362020-03-04 21:03:59 +0000272
273def get_cmd_args():
274 """ Parse command line arguments """
275
276 # Parse command line arguments to override config
277 parser = argparse.ArgumentParser(description="Lava Wait Jobs")
278 cmdargs = parser.add_argument_group("Lava Wait Jobs")
279
280 # Configuration control
281 cmdargs.add_argument(
282 "--lava-url", dest="lava_url", action="store", help="LAVA lab URL (without RPC2)"
283 )
284 cmdargs.add_argument(
285 "--job-ids", dest="job_ids", action="store", required=True, help="Comma separated list of job IDS"
286 )
287 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800288 "--lava-token", dest="lava_token", action="store", help="LAVA auth token"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000289 )
290 cmdargs.add_argument(
Xinyu Zhangf2b7cbf2021-05-18 20:17:34 +0800291 "--lava-user", dest="lava_user", action="store", help="LAVA username"
Matthew Hartfb6fd362020-03-04 21:03:59 +0000292 )
293 cmdargs.add_argument(
294 "--use-env", dest="token_from_env", action="store_true", default=False, help="Use LAVA auth info from environment"
295 )
296 cmdargs.add_argument(
297 "--lava-timeout", dest="dispatch_timeout", action="store", type=int, default=3600, help="Time in seconds to wait for all jobs"
298 )
299 cmdargs.add_argument(
300 "--artifacts-path", dest="artifacts_path", action="store", help="Download LAVA artifacts to this directory"
301 )
302 return parser.parse_args()
303
304
305if __name__ == "__main__":
306 main(get_cmd_args())