File Coverage

distribution.pm
Criterion Covered Total %
statement 117 179 65.3
total 117 179 65.3


line stmt code
1   # Copyright 2009-2013 Bernhard M. Wiedemann
2   # Copyright 2012-2015 SUSE LLC
3   # SPDX-License-Identifier: GPL-2.0-or-later
4    
5   use Mojo::Base -strict, -signatures;
6 40  
  40  
  40  
7   use testapi ();
8 40 use Carp 'croak';
  40  
  40  
9 40  
  40  
  40  
10   my $self = bless {}, $class;
11 57 $self->{consoles} = {};
  57  
  57  
12 57 $self->{serial_failures} = [];
13 57 $self->{autoinst_failures} = [];
14 57 $self->{script_run_die_on_timeout} = -1;
15 57  
16 57 =head2 serial_term_prompt
17    
18   wait_serial($serial_term_prompt);
19    
20   A simple undecorated prompt for serial terminals. ANSI escape characters only
21   serve to create log noise in most tests which use the serial terminal, so
22   don't use them here. Also avoid using characters which have special meaning in
23   a regex. Note that our common prompt character '#' denotes a comment in a
24   regex with '/z' on the end, but if you are using /z you will need to wrap the
25   prompt in \Q and \E anyway otherwise the whitespace will be ignored.
26   =cut
27   $self->{serial_term_prompt} = '# ';
28   return $self;
29 57 }
30 57  
31   # no cmds on default distri
32    
33   my %class_names = (
34 23 'tty-console' => 'ttyConsole',
  23  
35   'ssh-serial' => 'sshSerial',
36 56 'ssh-xterm' => 'sshXtermVt',
  56  
  56  
  56  
  56  
  56  
37 56 'ssh-virtsh' => 'sshVirtsh',
38   'ssh-virtsh-serial' => 'sshVirtshSUT',
39   'vnc-base' => 'vnc_base',
40   'local-Xvnc' => 'localXvnc',
41   'ssh-iucvconn' => 'sshIucvconn',
42   'virtio-terminal' => 'virtio_terminal',
43   'amt-sol' => 'amtSol',
44   'ipmi-sol' => 'ipmiSol',
45   'ipmi-xterm' => 'sshXtermIPMI',
46   'video-stream' => 'video_stream',
47   );
48   my $required_type = $class_names{$backend_console} || $backend_console;
49   my $location = "consoles/$required_type.pm";
50   my $class = "consoles::$required_type";
51    
52 56 require $location;
53 56  
54 56 my $ret = $class->new($testapi_console, $backend_args);
55   # now the backend knows which console the testapi means with $testapi_console ("bootloader", "vnc", ...)
56 56 $self->{consoles}->{$testapi_console} = $ret;
57   return $ret;
58 56 }
59    
60 56 die "TODO: implement x11_start_program for your distri " . testapi::get_var('DISTRI');
61 56 }
62    
63   if (testapi::check_var('DISTRI', 'debian')) {
64 0 testapi::x11_start_program("su -c 'aptitude -y install @pkglist'", 4, {terminal => 1});
  0  
65 0 }
66   elsif (testapi::check_var('DISTRI', 'fedora')) {
67   testapi::x11_start_program("su -c 'yum -y install @pkglist'", 4, {terminal => 1});
68 0 }
  0  
  0  
  0  
69 0 else {
70 0 die "TODO: implement 'ensure_installed' for your distri " . testapi::get_var('DISTRI');
71   }
72   if ($testapi::password) { testapi::type_password; testapi::send_key("ret", 1); }
73 0 wait_still_screen(7, 90); # wait for install
74   }
75    
76 0 testapi::script_sudo("bash", 0); # become root
77   testapi::script_run('test $(id -u) -eq 0 && echo "imroot" > /dev/' . $testapi::serialdev, 0);
78 0 testapi::wait_serial("imroot", 5) || die "Root prompt not there";
  0  
  0  
79 0 testapi::script_run("cd /tmp");
80   }
81    
82 0 =head2 script_run
  0  
  0  
83 0  
84 0 script_run($cmd [, timeout => $timeout] [, output => $output] [,quiet => $quiet])
85 0  
86 0 Deprecated mode
87    
88   script_run($program, [$timeout])
89    
90   Run I<$cmd> (by assuming the console prompt and typing the command). After
91   that, echo hashed command to serial line and wait for it in order to detect
92   execution is finished. To avoid waiting, use I<$timeout> 0. The C<script_run>
93   command string must not be terminated with '&' otherwise an exception is
94   thrown.
95    
96   Use C<output> to add a description or a comment of the $cmd.
97    
98   Use C<quiet> to avoid recording serial_results.
99    
100   <Returns> exit code received from I<$cmd>, or C<undef> in case of C<not> waiting for I<$cmd>
101   to return.
102    
103   =cut
104    
105   my %args = testapi::compat_args(
106   {
107   timeout => $bmwqemu::default_timeout,
108   output => '',
109   quiet => undef
110   }, ['timeout'], @args);
111    
112 27 if (testapi::is_serial_terminal) {
  26  
  26  
  26  
  26  
113 26 testapi::wait_serial($self->{serial_term_prompt}, no_regex => 1, quiet => $args{quiet});
114   }
115   testapi::type_string "$cmd";
116   if ($args{timeout} > 0) {
117   die "Terminator '&' found in script_run call. script_run can not check script success. Use 'background_script_run' instead."
118   if $cmd =~ qr/(?<!\\)&$/;
119   my $str = testapi::hashed_string("SR" . $cmd . $args{timeout});
120 26 my $marker = "; echo $str-\$?-" . ($args{output} ? "Comment: $args{output}" : '');
121 4 if (testapi::is_serial_terminal) {
122   testapi::type_string($marker);
123 26 testapi::wait_serial($cmd . $marker, no_regex => 1, quiet => $args{quiet});
124 26 testapi::type_string("\n");
125 23 }
126   else {
127 22 testapi::type_string "$marker > /dev/$testapi::serialdev\n";
128 22 }
129 22 my $res = testapi::wait_serial(qr/$str-\d+-/, timeout => $args{timeout}, quiet => $args{quiet});
130 4 return unless $res;
131 4 return ($res =~ /$str-(\d+)-/)[0];
132 4 }
133   else {
134   testapi::send_key 'ret';
135 18 return;
136   }
137 22 }
138 22  
139 15 =head2 background_script_run
140    
141   background_script_run($cmd [, output => $output] [, quiet => $quiet])
142 3  
143 3 Run I<$cmd> in background without waiting for it to finish. Remember to redirect output,
144   otherwise the PID marker may get corrupted.
145    
146   Use C<output> to add a description or a comment of the $cmd.
147    
148   Use C<quiet> to avoid recording serial_results.
149    
150   <Returns> PID of the I<$cmd> process running in the background.
151    
152   =cut
153    
154   if (testapi::is_serial_terminal) {
155   testapi::wait_serial($self->{serial_term_prompt}, no_regex => 1, quiet => $args{quiet});
156   }
157    
158   $cmd = "( $cmd )";
159   testapi::type_string $cmd;
160   my $str = testapi::hashed_string("SR" . $cmd);
161   my $marker = "& echo $str-\$!-" . ($args{output} ? "Comment: $args{output}" : '');
162 2 if (testapi::is_serial_terminal) {
  2  
  2  
  2  
  2  
163 2 testapi::type_string($marker);
164 0 testapi::wait_serial($cmd . $marker, no_regex => 1, quiet => $args{quiet});
165   testapi::type_string("\n");
166   }
167 2 else {
168 2 testapi::type_string "$marker > /dev/$testapi::serialdev\n";
169 2 }
170 2 my $res = testapi::wait_serial(qr/$str-\d+-/, quiet => $args{quiet});
171 2 die 'PID marker not found' unless ($res =~ m/$str-(\d+)-/);
172 0 return $1;
173 0 }
174 0  
175   =head2 script_sudo
176    
177 2 script_sudo($program, $wait_seconds)
178    
179 2 Run $program. Handle the sudo timeout and send password when appropriate.
180 2  
181 2 $wait_seconds
182    
183   =cut
184    
185   my $str;
186   if ($wait > 0) {
187   $str = testapi::hashed_string("SS$prog$wait");
188   $prog = "$prog; echo $str > /dev/$testapi::serialdev";
189   }
190   testapi::type_string "sudo $prog\n";
191   if (testapi::check_screen "sudo-passwordprompt", 3) {
192   testapi::type_password;
193   testapi::send_key "ret";
194 0 }
  0  
  0  
  0  
  0  
195 0 if ($str) {
196 0 return testapi::wait_serial($str, $wait);
197 0 }
198 0 return;
199   }
200 0  
201 0 =head2 script_output
202 0  
203 0 script_output($script [, timeout => ?] [, type_command => ?] [, proceed_on_failure => ?] [, quiet => ?])
204    
205 0 Deprecated mode
206 0  
207   script_output($script, [ $wait, type_command => ?, proceed_on_failure => ?])
208 0  
209   Execute $script on the SUT and return the data written to STDOUT by
210   $script. See script_output in the testapi.
211    
212   C<proceed_on_failure> - allows to proceed with validation when C<$script> is
213   failing (return non-zero exit code)
214    
215   Use C<quiet> to avoid recording serial_results.
216    
217   You may be able to avoid overriding this function by setting
218   $serial_term_prompt.
219    
220   =cut
221   my %args = testapi::compat_args(
222   {
223   timeout => undef,
224   proceed_on_failure => 0, # fail on error by default
225   quiet => undef,
226   # 80 is approximate quantity of chars typed during 'curl' approach
227   # if script length is lower there is no point to proceed with more complex solution
228   type_command => length($script) < 80,
229   }, ['timeout'], @args);
230    
231 26 my $marker = testapi::hashed_string("SO$script");
  26  
  26  
  26  
  26  
232 26 my $script_path = "/tmp/script$marker.sh";
233    
234   # prevent use of network for offline installations
235   if (testapi::get_var('OFFLINE_SUT')) {
236   testapi::record_info('forced type_cmd', "Forced typing the command as we are offline");
237   $args{type_command} = 1;
238   }
239    
240   if (testapi::is_serial_terminal) {
241   my $heretag = 'EOT_' . $marker;
242 26 my $cat = "cat > $script_path << '$heretag'; echo $marker-\$?-";
243 26 testapi::wait_serial($self->{serial_term_prompt}, no_regex => 1, quiet => $args{quiet});
244   bmwqemu::log_call("Content of $script_path :\n \"$cat\" \n");
245   testapi::type_string($cat . "\n");
246 26 testapi::wait_serial("$cat", no_regex => 1, quiet => $args{quiet});
247 0 # Wait for input prompt of here tag before typing $script. This avoids
248 0 # messy output, like duplicate output of $script. We do this in a second
249   # wait_serial() call, to avoid issues during new line detection.
250   testapi::wait_serial('> ', no_regex => 1, quiet => $args{quiet});
251 26 testapi::type_string("$script\n$heretag\n");
252 14 testapi::wait_serial("> $heretag", no_regex => 1, quiet => $args{quiet});
253 14 testapi::wait_serial("$marker-0-", quiet => $args{quiet});
254 14 }
255 14 elsif ($args{type_command}) {
256 14 my $cat = "cat - > $script_path;";
257 14 testapi::type_string($cat);
258   testapi::type_string("\n", wait_still_screen => testapi::backend_get_wait_still_screen_on_here_doc_input());
259   testapi::type_string($script . "\n", timeout => $args{timeout});
260   testapi::send_key('ctrl-d');
261 14 }
262 14 else {
263 14 open my $fh, ">", 'current_script' or croak("Could not open file. $!");
264 14 print $fh $script;
265   close $fh;
266   testapi::assert_script_run("curl -f -v " . testapi::autoinst_url("/current_script") . " > $script_path");
267 12 testapi::script_run "clear";
268 12 }
269 12  
270 12 # Surround the actual script output with special markers so that we can
271 12 # unambiguously separate the expected output from other content that we
272   # might encounter on the serial device depending on how it is used in the
273   # SUT
274 0 my $shell_cmd = testapi::is_serial_terminal() ? 'bash -oe pipefail' : 'bash -eox pipefail';
275 0 my $run_script = "echo $marker; $shell_cmd $script_path ; echo SCRIPT_FINISHED$marker-\$?-";
276 0 if (testapi::is_serial_terminal) {
277 0 testapi::wait_serial($self->{serial_term_prompt}, no_regex => 1, quiet => $args{quiet});
278 0 testapi::type_string("$run_script\n");
279   testapi::wait_serial($run_script, no_regex => 1, quiet => $args{quiet});
280   }
281   else {
282   testapi::type_string("($run_script) | tee /dev/$testapi::serialdev\n");
283   }
284   my $output = testapi::wait_serial("SCRIPT_FINISHED$marker-\\d+-", timeout => $args{timeout}, record_output => 1, quiet => $args{quiet})
285 26 || croak "script timeout: $script";
286 26  
287 26 if ($output !~ "SCRIPT_FINISHED$marker-0-") {
288 14 my $log_message = 'script failed with : ' . $output;
289 14 if ($args{proceed_on_failure}) {
290 14 bmwqemu::log_call($log_message);
291   }
292   else {
293 12 croak($log_message);
294   }
295   }
296 26  
297   # and the markers including internal exit catcher
298 24 my $out = $output =~ /$marker(?<expected_output>.+)SCRIPT_FINISHED$marker-\d+-/s ? $+ : '';
299 10 # trim whitespaces
300 10 $out =~ s/^\s+|\s+$//g;
301 2 return $out;
302   }
303    
304 8 =head2 set_expected_serial_failures
305    
306   set_expected_serial_failures($failures)
307    
308   Define the patterns to look for in the serial console.
309 16 Each pattern comes along with a type either I<hard> or I<soft> and a message,
310   for instance, to label the match with a bug/ticket
311 16  
312 16 Example:
313   set_expected_serial_failures([
314   { type => 'soft', message => 'Message 1', pattern => qr/Pattern1/ },
315   { type => 'soft', message => 'Message 2', pattern => qr/Pattern2/ },
316   { type => 'hard', message => 'Message 3', pattern => qr/Pattern3/ },]
317   );
318    
319   =cut
320   $self->{serial_failures} = $failures;
321   }
322    
323   =head2 set_expected_autoinst_failures
324    
325   set_expected_autoinst_failures($failures)
326    
327   Define the patterns to look for in the os-autoinst-log.txt
328   Each pattern comes along with a type either I<hard> or I<soft> and a message,
329   for instance, to label the match with a bug/ticket
330    
331 0 Example:
  0  
  0  
  0  
332 0 set_expected_serial_failures([
333   { type => 'soft', message => 'Message 1', pattern => qr/Pattern1/ },
334   { type => 'soft', message => 'Message 2', pattern => qr/Pattern2/ },
335   { type => 'hard', message => 'Message 3', pattern => qr/Pattern3/ },]
336   );
337    
338   =cut
339   $self->{autoinst_failures} = $failures;
340   }
341    
342   # override
343    
344   # override
345    
346   1;