File Coverage

mmapi.pm
Criterion Covered Total %
statement 140 140 100.0
total 140 140 100.0


line stmt code
1   # Copyright 2015-2021 SUSE LLC
2   # SPDX-License-Identifier: GPL-2.0-or-later
3    
4   ## Multi-Machine API
5    
6   use Mojo::Base 'Exporter', -signatures;
7 1 our @EXPORT = qw(get_children_by_state get_children get_parents
  1  
  1  
8   get_job_info get_job_autoinst_url get_job_autoinst_vars
9   wait_for_children wait_for_children_to_start api_call
10   api_call_2 handle_api_error get_current_job_id
11   );
12    
13   require bmwqemu;
14    
15   use Mojo::UserAgent;
16 1 use Mojo::URL;
  1  
  1  
17 1  
  1  
  1  
18   our $retry_count = $ENV{OS_AUTOINST_MMAPI_RETRY_COUNT} // 30;
19   our $retry_interval = $ENV{OS_AUTOINST_MMAPI_RETRY_INTERVAL} // 10;
20   our $poll_interval = $ENV{OS_AUTOINST_MMAPI_POLL_INTERVAL} // 1;
21    
22   our $url;
23    
24   # private ua
25   my $ua;
26   my $app;
27    
28   # define HTTP return codes which are not treated as errors by api_call/api_call_2/handle_api_error
29   my $CODES_EXPECTED_BY_DEFAULT = {200 => 1, 409 => 1};
30    
31   # define HTTP return codes which are not treated as errors by functions of mmapi itself
32   my $CODES_EXPECTED_BY_MMAPI = {200 => 1};
33    
34   # init $ua and $url
35 2 my $host = $bmwqemu::vars{OPENQA_URL};
  2  
36   my $secret = $bmwqemu::vars{JOBTOKEN};
37 2 return unless $host && $secret;
38 2 $url = Mojo::URL->new($host =~ '/' ? $host : "http://$host");
39 2  
40 2 # Relative paths are appended to the existing one
41   $url->path('/api/v1/');
42    
43 2 $ua = Mojo::UserAgent->new;
44    
45 2 # add JOBTOKEN header secret
46   $ua->on(start => sub ($ua, $tx) { $tx->req->headers->add('X-API-JobToken' => $secret) });
47   }
48 2  
  39  
  39  
  39  
  39  
  39  
49   _init;
50   $ua->server->app($app = $app_arg);
51 1 }
  1  
  1  
52 1  
53 1 =head2 api_call_2
54    
55   Queries openQA's multi-machine API and returns the resulting Mojo::Transaction::HTTP object.
56    
57   =cut
58    
59   _init unless $ua;
60   bmwqemu::mydie('Missing mandatory options') unless $method && $action && $ua;
61    
62 39 my $ua_url = $url->clone;
  39  
  39  
  39  
  39  
  39  
63 39 $ua_url->path($action);
64 39 $ua_url->query($params) if $params;
65    
66 39 my $tries = $retry_count;
67 39 my ($tx, $res);
68 39 while ($tries--) {
69   $tx = $ua->$method($ua_url);
70 39 $res = $tx->res;
71 39 last if $res->code && ($expected_codes // $CODES_EXPECTED_BY_DEFAULT)->{$res->code};
72 39 bmwqemu::diag("api_call_2 failed, retries left: $tries of $retry_count");
73 39 sleep $retry_interval;
74 39 }
75 39 return $tx;
76 14 }
77 14  
78   =head2 api_call
79 39  
80   Queries openQA's multi-machine API and returns the result as Mojo::Message::Response object.
81    
82   =cut
83    
84    
85   =head2 handle_api_error
86    
87   Returns a truthy value if the specified Mojo::Transaction::HTTP object has an error.
88 1 Logs the errors if a log context is specified.
  1  
  1  
  1  
89    
90   =cut
91    
92   my $err = $tx->error;
93   return 0 unless $err;
94    
95   my $url = $tx->req->url;
96   my $code = $err->{code};
97 39 return 0 if $code && ($expected_codes // $CODES_EXPECTED_BY_DEFAULT)->{$code};
  39  
  39  
  39  
  39  
98 39 $err->{message} .= "; URL was $url" if $url;
99 39 bmwqemu::diag($code
100   ? "$log_ctx: $code response: $err->{message}"
101 18 : "$log_ctx: Connection error: $err->{message}") if $log_ctx;
102 18 return 1;
103 18 }
104 14  
105 14 =head2 get_children_by_state
106    
107   my $children = get_children_by_state('done');
108 14 print $children->[0]
109    
110   Returns an array ref containing ids of children in given state.
111    
112   =cut
113    
114   my $tx = api_call_2(get => "mm/children/$state", undef, $CODES_EXPECTED_BY_MMAPI);
115   return undef if handle_api_error($tx, 'get_children_by_state', $CODES_EXPECTED_BY_MMAPI);
116   return $tx->res->json('/jobs');
117   }
118    
119   =head2 get_children
120 2  
  2  
  2  
121 2 my $children = get_children();
122 2 print keys %$children;
123 1  
124   Returns a hash ref containing { id => state } pair for each child job.
125    
126   =cut
127    
128   my $tx = api_call_2(get => 'mm/children', undef, $CODES_EXPECTED_BY_MMAPI);
129   return undef if handle_api_error($tx, 'get_children', $CODES_EXPECTED_BY_MMAPI);
130   return $tx->res->json('/jobs');
131   }
132    
133   =head2 get_parents
134    
135 6 my $parents = get_parents
  6  
136 6 print $parents->[0]
137 6  
138 5 Returns an array ref containing ids of parent jobs.
139    
140   =cut
141    
142   my $tx = api_call_2(get => 'mm/parents', undef, $CODES_EXPECTED_BY_MMAPI);
143   return undef if handle_api_error($tx, 'get_parents', $CODES_EXPECTED_BY_MMAPI);
144   return $tx->res->json('/jobs');
145   }
146    
147   =head2 get_job_info
148    
149   my $info = get_job_info($target_id);
150 1 print $info->{settings}->{DESKTOP}
  1  
151 1  
152 1 Returns a hash containin job information provided by openQA server.
153 1  
154   =cut
155    
156   my $tx = api_call_2(get => "jobs/$target_id", undef, $CODES_EXPECTED_BY_MMAPI);
157   return undef if handle_api_error($tx, 'get_job_info', $CODES_EXPECTED_BY_MMAPI);
158   return $tx->res->json('/job');
159   }
160    
161   =head2 get_job_autoinst_url
162    
163   my $url = get_job_autoinst_url($target_id);
164    
165 2 Returns url of os-autoinst webserver for job $target_id or C<undef> on failure.
  2  
  2  
166 2  
167 2 =cut
168 1  
169   my $tx = api_call_2(get => 'workers', undef, $CODES_EXPECTED_BY_MMAPI);
170   return undef if handle_api_error($tx, 'get_job_autoinst_url', $CODES_EXPECTED_BY_MMAPI);
171    
172   my $workers = $tx->res->json('/workers') // [];
173   for my $worker (@$workers) {
174   if ($worker->{jobid} && $target_id == $worker->{jobid} && $worker->{host} && $worker->{instance} && $worker->{properties}{JOBTOKEN}) {
175   my $hostname = $worker->{host};
176   my $token = $worker->{properties}{JOBTOKEN};
177   my $workerport = $worker->{instance} * 10 + 20002 + 1; # the same as in openqa/script/worker
178   my $url = "http://$hostname:$workerport/$token";
179 2 return $url;
  2  
  2  
180 2 }
181 2 }
182   bmwqemu::diag("get_job_autoinst_url: No worker info for job $target_id available.");
183 2 return undef;
184 2 }
185 2  
186 1 =head2 get_job_autoinst_vars
187 1  
188 1 my $vars = get_job_autoinst_vars($target_id);
189 1 print $vars->{WORKER_ID};
190 1  
191   Returns hash reference containing variables of job $target_id or C<undef> on failure. The variables
192   are taken from vars.json file of the corresponding worker.
193 1  
194 1 =cut
195    
196   my $url = get_job_autoinst_url($target_id);
197   return undef unless $url;
198    
199   # query the os-autoinst webserver of the job specified by $target_id
200   $url .= '/vars';
201    
202   my $ua = Mojo::UserAgent->new;
203   $ua->server->app($app) if $app;
204   my $tx = $ua->get($url);
205   return undef if handle_api_error($tx, 'get_job_autoinst_vars', $CODES_EXPECTED_BY_MMAPI);
206   return $tx->res->json('/vars');
207 2 }
  2  
  2  
208 2  
209 2 =head2 wait_for_children
210    
211   wait_for_children();
212 1  
213   Wait while any running or scheduled children exist.
214 1  
215 1 =cut
216 1  
217 1 while (1) {
218 1 my $children = get_children() // die 'Failed to wait for children';
219   my $n = 0;
220   for my $state (values %$children) {
221   next if $state eq 'done' or $state eq 'cancelled';
222   $n++;
223   }
224    
225   bmwqemu::diag("Waiting for $n jobs to finish");
226   last unless $n;
227   sleep $poll_interval;
228   }
229 2 }
  2  
230 2  
231 3 =head2 wait_for_children_to_start
232 2  
233 2 wait_for_children_to_start();
234 2  
235 1 Wait while any scheduled children exist.
236    
237   =cut
238 2 while (1) {
239 2 my $children = get_children() // die 'Failed to wait for children to start';
240 1 my $n = 0;
241   for my $state (values %$children) {
242   next if $state eq 'done' or $state eq 'cancelled' or $state eq 'running';
243   $n++;
244   }
245    
246   bmwqemu::diag("Waiting for $n jobs to start");
247   last unless $n;
248   sleep $poll_interval;
249   }
250   }
251 1  
  1  
252 1 =head2 get_current_job_id
253 2  
254 2 get_current_job_id();
255 2  
256 2 Query openQA's API to retrieve the current job ID
257 1 =cut
258   my $tx = api_call_2(get => 'whoami', undef, $CODES_EXPECTED_BY_MMAPI);
259   return undef if handle_api_error($tx, 'whoami', $CODES_EXPECTED_BY_MMAPI);
260 2 return $tx->res->json('/id');
261 2 }
262 1  
263   1;