blob: 6bd1493917d581a76a293f8bf8a12d42527453ce [file] [log] [blame]
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +01001#!/usr/bin/env python3
2
3""" lava_rpc_connector.py:
4
5 class that extends xmlrpc in order to add LAVA specific functionality.
6 Used in managing communication with the back-end. """
7
8from __future__ import print_function
9
10__copyright__ = """
11/*
Dean Arnoldf1169b92020-03-11 10:14:14 +000012 * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010013 *
14 * SPDX-License-Identifier: BSD-3-Clause
15 *
16 */
17 """
18__author__ = "Minos Galanakis"
19__email__ = "minos.galanakis@linaro.org"
20__project__ = "Trusted Firmware-M Open CI"
21__status__ = "stable"
Minos Galanakisea421232019-06-20 17:11:28 +010022__version__ = "1.1"
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010023
24import xmlrpc.client
25import time
Matthew Hartfb6fd362020-03-04 21:03:59 +000026import yaml
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010027
28
29class LAVA_RPC_connector(xmlrpc.client.ServerProxy, object):
30
31 def __init__(self,
32 username,
33 token,
34 hostname,
35 rest_prefix="RPC2",
36 https=False):
37
38 # If user provides hostname with http/s prefix
39 if "://" in hostname:
40 htp_pre, hostname = hostname.split("://")
41 server_addr = "%s://%s:%s@%s/%s" % (htp_pre,
42 username,
43 token,
44 hostname,
45 rest_prefix)
46 self.server_url = "%s://%s" % (htp_pre, hostname)
47 else:
48 server_addr = "%s://%s:%s@%s/%s" % ("https" if https else "http",
49 username,
50 token,
51 hostname,
52 rest_prefix)
53 self.server_url = "%s://%s" % ("https" if https else "http",
54 hostname)
55
56 self.server_job_prefix = "%s/scheduler/job/%%s" % self.server_url
57 super(LAVA_RPC_connector, self).__init__(server_addr)
58
59 def _rpc_cmd_raw(self, cmd, params=None):
60 """ Run a remote comand and return the result. There is no constrain
61 check on the syntax of the command. """
62
63 cmd = "self.%s(%s)" % (cmd, params if params else "")
64 return eval(cmd)
65
66 def ls_cmd(self):
67 """ Return a list of supported commands """
68
69 print("\n".join(self.system.listMethods()))
70
71 def get_job_results(self, job_id, yaml_out_file=None):
72 results = self.results.get_testjob_results_yaml(job_id)
73 if yaml_out_file:
74 with open(yaml_out_file, "w") as F:
75 F.write(results)
76 return results
77
Matthew Hartfb6fd362020-03-04 21:03:59 +000078 def get_job_definition(self, job_id, yaml_out_file=None):
79 job_def = self.scheduler.jobs.definition(job_id)
80 if yaml_out_file:
81 with open(yaml_out_file, "w") as F:
82 F.write(str(job_def))
83 def_o = yaml.load(job_def)
84 return job_def, def_o.get('metadata', [])
85
86 def write_target_lines(self, target_out_file, log):
87 log = yaml.load(str(log))
88 with open(target_out_file, "w+") as F:
89 for line in log:
90 if line['lvl'] in ['target', 'feedback']:
91 F.write("{}\n".format(line['msg']))
92
93 def get_job_log(self, job_id, yaml_out_file=None, target_out_file=None):
94 job_res, job_log = self.scheduler.jobs.logs(job_id)
95 if yaml_out_file:
96 with open(yaml_out_file, "w") as F:
97 F.write(str(job_log))
98 if target_out_file:
99 self.write_target_lines(target_out_file, job_log)
100 return job_log
101
102 def get_job_config(self, job_id, yaml_out_file=None):
103 job_config = self.scheduler.jobs.configuration(job_id)
104 if yaml_out_file:
105 with open(yaml_out_file, "w") as F:
106 for data in job_config:
107 if data:
108 F.write(str(data))
109 return job_config
110
111 def get_job_info(self, job_id, yaml_out_file=None):
112 job_info = self.scheduler.jobs.show(job_id)
113 if yaml_out_file:
114 with open(yaml_out_file, "w") as F:
115 F.write(str(job_info))
116 return job_info
117
118 def get_error_reason(self, job_id):
119 lava_res = self.results.get_testsuite_results_yaml(job_id, 'lava')
120 results = yaml.load(lava_res)
121 for test in results:
122 if test['name'] == 'job':
123 return(test.get('metadata', {}).get('error_type', ''))
124
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100125 def get_job_state(self, job_id):
126 return self.scheduler.job_state(job_id)["job_state"]
127
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100128 def cancel_job(self, job_id):
129 """ Cancell job with id=job_id. Returns True if successfull """
130
131 return self.scheduler.jobs.cancel(job_id)
132
133 def validate_job_yaml(self, job_definition, print_err=False):
134 """ Validate a job definition syntax. Returns true is server considers
135 the syntax valid """
136
137 try:
138 with open(job_definition) as F:
139 input_yaml = F.read()
140 self.scheduler.validate_yaml(input_yaml)
141 return True
142 except Exception as E:
143 if print_err:
144 print(E)
145 return False
146
147 def submit_job(self, job_definition):
148 """ Will submit a yaml definition pointed by job_definition after
149 validating it againist the remote backend. Returns resulting job id,
150 and server url for job"""
151
152 try:
153 if not self.validate_job_yaml(job_definition):
154 print("Served rejected job's syntax")
155 raise Exception("Invalid job")
156 with open(job_definition, "r") as F:
157 job_data = F.read()
158 except Exception as e:
159 print("Cannot submit invalid job. Check %s's content" %
160 job_definition)
161 print(e)
162 return None, None
Dean Bircha6ede7e2020-03-13 14:00:33 +0000163 try:
164 job_id = self.scheduler.submit_job(job_data)
165 job_url = self.server_job_prefix % job_id
166 return(job_id, job_url)
167 except Exception as e:
168 print(e)
169 return(None, None)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100170
171 def resubmit_job(self, job_id):
172 """ Re-submit job with provided id. Returns resulting job id,
173 and server url for job"""
174
175 job_id = self.scheduler.resubmit_job(job_id)
176 job_url = self.server_job_prefix % job_id
177 return(job_id, job_url)
178
179 def block_wait_for_job(self, job_id, timeout, poll_freq=1):
180 """ Will block code execution and wait for the job to submit.
181 Returns job status on completion """
182
183 start_t = int(time.time())
184 while(True):
185 cur_t = int(time.time())
186 if cur_t - start_t >= timeout:
187 print("Breaking because of timeout")
188 break
189 # Check if the job is not running
Dean Arnoldf1169b92020-03-11 10:14:14 +0000190 cur_status = self.get_job_state(job_id)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100191 # If in queue or running wait
Dean Arnoldc1d81b42020-03-11 15:56:36 +0000192 if cur_status not in ["Canceling","Finished"]:
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100193 time.sleep(poll_freq)
194 else:
195 break
Dean Arnoldc1d81b42020-03-11 15:56:36 +0000196 return self.scheduler.job_health(job_id)["job_health"]
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100197
Matthew Hartfb6fd362020-03-04 21:03:59 +0000198 def block_wait_for_jobs(self, job_ids, timeout, poll_freq=10):
199 """ Wait for multiple LAVA job ids to finish and return finished list """
200
201 start_t = int(time.time())
202 finished_jobs = {}
203 while(True):
204 cur_t = int(time.time())
205 if cur_t - start_t >= timeout:
206 print("Breaking because of timeout")
207 break
208 for job_id in job_ids:
209 # Check if the job is not running
210 cur_status = self.get_job_info(job_id)
211 # If in queue or running wait
212 if cur_status['state'] in ["Canceling","Finished"]:
213 cur_status['error_reason'] = self.get_error_reason(job_id)
214 finished_jobs[job_id] = cur_status
215 if len(job_ids) == len(finished_jobs):
216 break
217 else:
218 time.sleep(poll_freq)
219 if len(job_ids) == len(finished_jobs):
220 break
221 return finished_jobs
222
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100223 def test_credentials(self):
224 """ Attempt to querry the back-end and verify that the user provided
225 authentication is valid """
226
227 try:
228 self._rpc_cmd_raw("system.listMethods")
229 return True
230 except Exception as e:
231 print(e)
232 print("Credential validation failed")
233 return False
234
235
236if __name__ == "__main__":
237 pass