blob: a0255d8c4eda51b49d0516aa9d7cfd4d30fe95ed [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):
Matthew Hart83366052020-05-15 12:48:36 +010087 log = yaml.load(log)
Matthew Hartfb6fd362020-03-04 21:03:59 +000088 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)
Matthew Hart83366052020-05-15 12:48:36 +010095 job_log = job_log.data.decode('utf-8')
Matthew Hartfb6fd362020-03-04 21:03:59 +000096 if yaml_out_file:
97 with open(yaml_out_file, "w") as F:
Matthew Hart83366052020-05-15 12:48:36 +010098 F.write(job_log)
Matthew Hartfb6fd362020-03-04 21:03:59 +000099 if target_out_file:
100 self.write_target_lines(target_out_file, job_log)
101 return job_log
102
103 def get_job_config(self, job_id, yaml_out_file=None):
104 job_config = self.scheduler.jobs.configuration(job_id)
105 if yaml_out_file:
106 with open(yaml_out_file, "w") as F:
107 for data in job_config:
108 if data:
Matthew Hart83366052020-05-15 12:48:36 +0100109 line = data.data.decode('utf-8')
110 F.write(line)
Matthew Hartfb6fd362020-03-04 21:03:59 +0000111 return job_config
112
113 def get_job_info(self, job_id, yaml_out_file=None):
114 job_info = self.scheduler.jobs.show(job_id)
115 if yaml_out_file:
116 with open(yaml_out_file, "w") as F:
117 F.write(str(job_info))
118 return job_info
119
120 def get_error_reason(self, job_id):
Matthew Hart2c2688f2020-05-26 13:09:20 +0100121 try:
122 lava_res = self.results.get_testsuite_results_yaml(job_id, 'lava')
123 results = yaml.load(lava_res)
124 for test in results:
125 if test['name'] == 'job':
126 return(test.get('metadata', {}).get('error_type', ''))
127 except Exception:
128 return("Unknown")
Matthew Hartfb6fd362020-03-04 21:03:59 +0000129
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100130 def get_job_state(self, job_id):
131 return self.scheduler.job_state(job_id)["job_state"]
132
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100133 def cancel_job(self, job_id):
134 """ Cancell job with id=job_id. Returns True if successfull """
135
136 return self.scheduler.jobs.cancel(job_id)
137
138 def validate_job_yaml(self, job_definition, print_err=False):
139 """ Validate a job definition syntax. Returns true is server considers
140 the syntax valid """
141
142 try:
143 with open(job_definition) as F:
144 input_yaml = F.read()
145 self.scheduler.validate_yaml(input_yaml)
146 return True
147 except Exception as E:
148 if print_err:
149 print(E)
150 return False
151
152 def submit_job(self, job_definition):
153 """ Will submit a yaml definition pointed by job_definition after
154 validating it againist the remote backend. Returns resulting job id,
155 and server url for job"""
156
157 try:
158 if not self.validate_job_yaml(job_definition):
159 print("Served rejected job's syntax")
160 raise Exception("Invalid job")
161 with open(job_definition, "r") as F:
162 job_data = F.read()
163 except Exception as e:
164 print("Cannot submit invalid job. Check %s's content" %
165 job_definition)
166 print(e)
167 return None, None
Dean Bircha6ede7e2020-03-13 14:00:33 +0000168 try:
169 job_id = self.scheduler.submit_job(job_data)
170 job_url = self.server_job_prefix % job_id
171 return(job_id, job_url)
172 except Exception as e:
173 print(e)
174 return(None, None)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100175
176 def resubmit_job(self, job_id):
177 """ Re-submit job with provided id. Returns resulting job id,
178 and server url for job"""
179
180 job_id = self.scheduler.resubmit_job(job_id)
181 job_url = self.server_job_prefix % job_id
182 return(job_id, job_url)
183
184 def block_wait_for_job(self, job_id, timeout, poll_freq=1):
185 """ Will block code execution and wait for the job to submit.
186 Returns job status on completion """
187
188 start_t = int(time.time())
189 while(True):
190 cur_t = int(time.time())
191 if cur_t - start_t >= timeout:
192 print("Breaking because of timeout")
193 break
194 # Check if the job is not running
Dean Arnoldf1169b92020-03-11 10:14:14 +0000195 cur_status = self.get_job_state(job_id)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100196 # If in queue or running wait
Dean Arnoldc1d81b42020-03-11 15:56:36 +0000197 if cur_status not in ["Canceling","Finished"]:
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100198 time.sleep(poll_freq)
199 else:
200 break
Dean Arnoldc1d81b42020-03-11 15:56:36 +0000201 return self.scheduler.job_health(job_id)["job_health"]
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100202
Matthew Hartfb6fd362020-03-04 21:03:59 +0000203 def block_wait_for_jobs(self, job_ids, timeout, poll_freq=10):
204 """ Wait for multiple LAVA job ids to finish and return finished list """
205
206 start_t = int(time.time())
207 finished_jobs = {}
208 while(True):
209 cur_t = int(time.time())
210 if cur_t - start_t >= timeout:
211 print("Breaking because of timeout")
212 break
213 for job_id in job_ids:
214 # Check if the job is not running
215 cur_status = self.get_job_info(job_id)
216 # If in queue or running wait
217 if cur_status['state'] in ["Canceling","Finished"]:
218 cur_status['error_reason'] = self.get_error_reason(job_id)
219 finished_jobs[job_id] = cur_status
220 if len(job_ids) == len(finished_jobs):
221 break
222 else:
223 time.sleep(poll_freq)
224 if len(job_ids) == len(finished_jobs):
225 break
226 return finished_jobs
227
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100228 def test_credentials(self):
229 """ Attempt to querry the back-end and verify that the user provided
230 authentication is valid """
231
232 try:
233 self._rpc_cmd_raw("system.listMethods")
234 return True
235 except Exception as e:
236 print(e)
237 print("Credential validation failed")
238 return False
239
240
241if __name__ == "__main__":
242 pass