blob: 5db23b144cc38768a240525165a31d49f1c65759 [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):
121 lava_res = self.results.get_testsuite_results_yaml(job_id, 'lava')
122 results = yaml.load(lava_res)
123 for test in results:
124 if test['name'] == 'job':
125 return(test.get('metadata', {}).get('error_type', ''))
126
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100127 def get_job_state(self, job_id):
128 return self.scheduler.job_state(job_id)["job_state"]
129
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100130 def cancel_job(self, job_id):
131 """ Cancell job with id=job_id. Returns True if successfull """
132
133 return self.scheduler.jobs.cancel(job_id)
134
135 def validate_job_yaml(self, job_definition, print_err=False):
136 """ Validate a job definition syntax. Returns true is server considers
137 the syntax valid """
138
139 try:
140 with open(job_definition) as F:
141 input_yaml = F.read()
142 self.scheduler.validate_yaml(input_yaml)
143 return True
144 except Exception as E:
145 if print_err:
146 print(E)
147 return False
148
149 def submit_job(self, job_definition):
150 """ Will submit a yaml definition pointed by job_definition after
151 validating it againist the remote backend. Returns resulting job id,
152 and server url for job"""
153
154 try:
155 if not self.validate_job_yaml(job_definition):
156 print("Served rejected job's syntax")
157 raise Exception("Invalid job")
158 with open(job_definition, "r") as F:
159 job_data = F.read()
160 except Exception as e:
161 print("Cannot submit invalid job. Check %s's content" %
162 job_definition)
163 print(e)
164 return None, None
Dean Bircha6ede7e2020-03-13 14:00:33 +0000165 try:
166 job_id = self.scheduler.submit_job(job_data)
167 job_url = self.server_job_prefix % job_id
168 return(job_id, job_url)
169 except Exception as e:
170 print(e)
171 return(None, None)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100172
173 def resubmit_job(self, job_id):
174 """ Re-submit job with provided id. Returns resulting job id,
175 and server url for job"""
176
177 job_id = self.scheduler.resubmit_job(job_id)
178 job_url = self.server_job_prefix % job_id
179 return(job_id, job_url)
180
181 def block_wait_for_job(self, job_id, timeout, poll_freq=1):
182 """ Will block code execution and wait for the job to submit.
183 Returns job status on completion """
184
185 start_t = int(time.time())
186 while(True):
187 cur_t = int(time.time())
188 if cur_t - start_t >= timeout:
189 print("Breaking because of timeout")
190 break
191 # Check if the job is not running
Dean Arnoldf1169b92020-03-11 10:14:14 +0000192 cur_status = self.get_job_state(job_id)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100193 # If in queue or running wait
Dean Arnoldc1d81b42020-03-11 15:56:36 +0000194 if cur_status not in ["Canceling","Finished"]:
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100195 time.sleep(poll_freq)
196 else:
197 break
Dean Arnoldc1d81b42020-03-11 15:56:36 +0000198 return self.scheduler.job_health(job_id)["job_health"]
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100199
Matthew Hartfb6fd362020-03-04 21:03:59 +0000200 def block_wait_for_jobs(self, job_ids, timeout, poll_freq=10):
201 """ Wait for multiple LAVA job ids to finish and return finished list """
202
203 start_t = int(time.time())
204 finished_jobs = {}
205 while(True):
206 cur_t = int(time.time())
207 if cur_t - start_t >= timeout:
208 print("Breaking because of timeout")
209 break
210 for job_id in job_ids:
211 # Check if the job is not running
212 cur_status = self.get_job_info(job_id)
213 # If in queue or running wait
214 if cur_status['state'] in ["Canceling","Finished"]:
215 cur_status['error_reason'] = self.get_error_reason(job_id)
216 finished_jobs[job_id] = cur_status
217 if len(job_ids) == len(finished_jobs):
218 break
219 else:
220 time.sleep(poll_freq)
221 if len(job_ids) == len(finished_jobs):
222 break
223 return finished_jobs
224
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100225 def test_credentials(self):
226 """ Attempt to querry the back-end and verify that the user provided
227 authentication is valid """
228
229 try:
230 self._rpc_cmd_raw("system.listMethods")
231 return True
232 except Exception as e:
233 print(e)
234 print("Credential validation failed")
235 return False
236
237
238if __name__ == "__main__":
239 pass