Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package os-autoinst for openSUSE:Factory checked in at 2025-01-29 16:10:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/os-autoinst (Old) and /work/SRC/openSUSE:Factory/.os-autoinst.new.2316 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "os-autoinst" Wed Jan 29 16:10:21 2025 rev:492 rq:1240866 version:4.6.1738076220.6b39995 Changes: -------- --- /work/SRC/openSUSE:Factory/os-autoinst/os-autoinst.changes 2025-01-28 15:00:18.828873869 +0100 +++ /work/SRC/openSUSE:Factory/.os-autoinst.new.2316/os-autoinst.changes 2025-01-29 16:10:33.523913679 +0100 @@ -1,0 +2,11 @@ +Tue Jan 28 15:56:09 UTC 2025 - ok...@suse.com + +- Update to version 4.6.1738076220.6b39995: + * Add unit tests for consoles/sshXtermIPMI.pm + * Extend documentation of SSH related timeout parameters + * Allow specifying the timeout on `get_cmd_output` calls + * Avoid endless loop in case of SSH read errors in `check_ssh_serial` + * Add more code coverage for video_stream.pm + * Clone the disk image with nvram for vmware guest + +------------------------------------------------------------------- Old: ---- os-autoinst-4.6.1737980001.69ac906.obscpio New: ---- os-autoinst-4.6.1738076220.6b39995.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ os-autoinst-devel-test.spec ++++++ --- /var/tmp/diff_new_pack.6xtmtg/_old 2025-01-29 16:10:34.663960956 +0100 +++ /var/tmp/diff_new_pack.6xtmtg/_new 2025-01-29 16:10:34.663960956 +0100 @@ -18,7 +18,7 @@ %define short_name os-autoinst-devel Name: %{short_name}-test -Version: 4.6.1737980001.69ac906 +Version: 4.6.1738076220.6b39995 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ os-autoinst-openvswitch-test.spec ++++++ --- /var/tmp/diff_new_pack.6xtmtg/_old 2025-01-29 16:10:34.683961785 +0100 +++ /var/tmp/diff_new_pack.6xtmtg/_new 2025-01-29 16:10:34.687961951 +0100 @@ -19,7 +19,7 @@ %define name_ext -test %define short_name os-autoinst-openvswitch Name: %{short_name}%{?name_ext} -Version: 4.6.1737980001.69ac906 +Version: 4.6.1738076220.6b39995 Release: 0 Summary: test package for %{short_name} License: GPL-2.0-or-later ++++++ os-autoinst-test.spec ++++++ --- /var/tmp/diff_new_pack.6xtmtg/_old 2025-01-29 16:10:34.707962780 +0100 +++ /var/tmp/diff_new_pack.6xtmtg/_new 2025-01-29 16:10:34.707962780 +0100 @@ -19,7 +19,7 @@ %define name_ext -test %define short_name os-autoinst Name: %{short_name}%{?name_ext} -Version: 4.6.1737980001.69ac906 +Version: 4.6.1738076220.6b39995 Release: 0 Summary: test package for os-autoinst License: GPL-2.0-or-later ++++++ os-autoinst.spec ++++++ --- /var/tmp/diff_new_pack.6xtmtg/_old 2025-01-29 16:10:34.731963776 +0100 +++ /var/tmp/diff_new_pack.6xtmtg/_new 2025-01-29 16:10:34.735963942 +0100 @@ -17,7 +17,7 @@ Name: os-autoinst -Version: 4.6.1737980001.69ac906 +Version: 4.6.1738076220.6b39995 Release: 0 Summary: OS-level test automation License: GPL-2.0-or-later ++++++ os-autoinst-4.6.1737980001.69ac906.obscpio -> os-autoinst-4.6.1738076220.6b39995.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-4.6.1737980001.69ac906/OpenQA/Isotovideo/Interface.pm new/os-autoinst-4.6.1738076220.6b39995/OpenQA/Isotovideo/Interface.pm --- old/os-autoinst-4.6.1737980001.69ac906/OpenQA/Isotovideo/Interface.pm 2025-01-27 13:13:21.000000000 +0100 +++ new/os-autoinst-4.6.1738076220.6b39995/OpenQA/Isotovideo/Interface.pm 2025-01-28 15:57:00.000000000 +0100 @@ -9,7 +9,7 @@ # -> increment on every change of such APIs # -> never move that variable to another place (when refactoring) # because it may be accessed by the tests itself -our $version = 42; +our $version = 43; # major version of the (web socket) API relevant to the developer mode # -> increment when making non-backward compatible changes to that API diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-4.6.1737980001.69ac906/backend/baseclass.pm new/os-autoinst-4.6.1738076220.6b39995/backend/baseclass.pm --- old/os-autoinst-4.6.1737980001.69ac906/backend/baseclass.pm 2025-01-27 13:13:21.000000000 +0100 +++ new/os-autoinst-4.6.1738076220.6b39995/backend/baseclass.pm 2025-01-28 15:57:00.000000000 +0100 @@ -28,6 +28,7 @@ use List::MoreUtils 'uniq'; use Scalar::Util 'looks_like_number'; use Mojo::File 'path'; +use Mojo::Util 'scope_guard'; use OpenQA::Exceptions; use Time::Seconds; use English -no_match_vars; @@ -1291,12 +1292,16 @@ =head2 run_ssh_cmd - $ret = run_ssh_cmd($cmd [, username => ?][, password => ?][,host => ?]); - ($ret, $stdout, $stderr) = run_ssh_cmd($cmd [, username => ?][, password => ?][,host => ?], wantarray => 1); + $ret = run_ssh_cmd($cmd [, username => ?][, password => ?][,host => ?][,timeout => undef]); + ($ret, $stdout, $stderr) = run_ssh_cmd($cmd [, username => ?][, password => ?][,host => ?][,timeout => undef], wantarray => 1); + + The timeout is in seconds and defaults to SSH_COMMAND_TIMEOUT_S. The timeout is enforced for each individual + operation, e.g. sending the command and reading its output as it is produced. That means the total runtime + of the command is allowed to be higher as long as the command can be sent in time and produces new output + frequently enough. =cut sub run_ssh_cmd ($self, $cmd, %args) { - my ($stdout, $stderr) = ('', ''); $args{wantarray} //= 0; $args{keep_open} //= 1; @@ -1304,15 +1309,29 @@ my ($ssh, $chan) = $self->run_ssh($cmd, %args); $chan->send_eof; + my ($stdout, $stderr) = ('', ''); + my $log_output = sub () { + bmwqemu::diag("[run_ssh_cmd($cmd)] stdout:$/$stdout") if length $stdout; + bmwqemu::diag("[run_ssh_cmd($cmd)] stderr:$/$stderr") if length $stderr; + }; + my $timeout_guard; + if (defined(my $new_timeout = $args{timeout})) { + my $initial_timeout = $ssh->timeout; + $timeout_guard = scope_guard sub { $ssh->timeout($initial_timeout) }; + $ssh->timeout($new_timeout); + } until ($chan->eof) { if (my ($o, $e) = $chan->read2) { $stdout .= $o; $stderr .= $e; } + else { + $log_output->(); + $ssh->die_with_error; + } } - bmwqemu::diag("[run_ssh_cmd($cmd)] stdout:$/$stdout") if length($stdout); - bmwqemu::diag("[run_ssh_cmd($cmd)] stderr:$/$stderr") if length($stderr); + $log_output->(); my $ret = $chan->exit_status(); bmwqemu::diag("[run_ssh_cmd($cmd)] exit-code: $ret"); $ssh->disconnect() unless $args{keep_open}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-4.6.1737980001.69ac906/consoles/sshVirtsh.pm new/os-autoinst-4.6.1738076220.6b39995/consoles/sshVirtsh.pm --- old/os-autoinst-4.6.1737980001.69ac906/consoles/sshVirtsh.pm 2025-01-27 13:13:21.000000000 +0100 +++ new/os-autoinst-4.6.1738076220.6b39995/consoles/sshVirtsh.pm 2025-01-28 15:57:00.000000000 +0100 @@ -671,14 +671,14 @@ =head2 get_cmd_output - $stdout = $svirt->get_cmd_output($cmd , $args = {domain => 'default', wantarray => 0}); + $stdout = $svirt->get_cmd_output($cmd , $args = {domain => 'default', timeout => undef, wantarray => 0}); -With C<<wantarray => 1>> the function will return a reference to a list which -contains I<stdout> and I<stderr>. +With C<<wantarray => 1>> the function returns an array reference containing I<stdout> and +I<stderr>. This function is B<deprecated>, you should use C<<$svirt->run_cmd()>> instead. =cut sub get_cmd_output ($self, $cmd, $args = {}) { - my (undef, $stdout, $stderr) = $self->backend->run_ssh_cmd($cmd, $self->get_ssh_credentials($args->{domain}), wantarray => 1); + my (undef, $stdout, $stderr) = $self->backend->run_ssh_cmd($cmd, $self->get_ssh_credentials($args->{domain}), timeout => $args->{timeout}, wantarray => 1); return $args->{wantarray} ? [$stdout, $stderr] : $stdout; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-4.6.1737980001.69ac906/doc/backend_vars.asciidoc new/os-autoinst-4.6.1738076220.6b39995/doc/backend_vars.asciidoc --- old/os-autoinst-4.6.1737980001.69ac906/doc/backend_vars.asciidoc 2025-01-27 13:13:21.000000000 +0100 +++ new/os-autoinst-4.6.1738076220.6b39995/doc/backend_vars.asciidoc 2025-01-28 15:57:00.000000000 +0100 @@ -24,7 +24,7 @@ NO_DEBUG_IO;boolean;0;Disable the I/O debug output in case of needle comparison times longer than expected OSUTILS_WAIT_ATTEMPT_INTERVAL;float;1;The interval in seconds between "attempts" in osutils, e.g. used for connections to qemu qmp backend SCREENSHOTINTERVAL;float;0.5;The interval in seconds at which screenshots are taken internally -SSH_COMMAND_TIMEOUT_S;integer;300;Timeout for any SSH based command in SSH based consoles, disabled for a value of 0. Time in seconds. +SSH_COMMAND_TIMEOUT_S;integer;300;Timeout in seconds for any SSH based command in SSH based consoles, disabled for a value of 0. It can be overriden by particular run_ssh_cmd() calls. Checkout the documentation of this function for details. SSH_CONNECT_RETRY;integer;5;Maximum retries to connect to SSH based console targets SSH_CONNECT_RETRY_INTERVAL;float;10;Interval in seconds between retries to connect to SSH based console targets. Related to SSH_CONNECT_RETRY VNC_STALL_THRESHOLD;integer;4;Time after which is VNC considered stalled diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-4.6.1737980001.69ac906/t/23-baseclass.t new/os-autoinst-4.6.1738076220.6b39995/t/23-baseclass.t --- old/os-autoinst-4.6.1737980001.69ac906/t/23-baseclass.t 2025-01-27 13:13:21.000000000 +0100 +++ new/os-autoinst-4.6.1738076220.6b39995/t/23-baseclass.t 2025-01-28 15:57:00.000000000 +0100 @@ -100,11 +100,12 @@ subtest 'SSH utilities' => sub { my $ssh_expect = {username => 'root', password => 'password', hostname => 'foo.bar', port => undef}; - my $fail_on_channel_call = undef; + my ($fail_on_channel_call, $fail_on_read2); my $ssh_auth_ok = 1; my $ssh_obj_data = {}; # used to store Net::SSH2 fake data per object my $ssh_connect_error; my @net_ssh2_error = (); + my @timeouts = (); my $net_ssh2 = Test::MockModule->new('Net::SSH2'); my @agent; $net_ssh2->redefine(new => sub { @@ -174,8 +175,8 @@ } return 1; }); - $mock_channel->mock(read2 => sub { - my ($self) = @_; + $mock_channel->mock(read2 => sub ($self) { + return () if $fail_on_read2; $self->{eof} = 1; return ($self->{stdout}, $self->{stderr}); }); @@ -188,7 +189,8 @@ $mock_channel->mock(close => sub { return 1; }); return $mock_channel; }); - + $self->mock(die_with_error => \&Net::SSH2::die_with_error); + $self->mock(timeout => sub { @_ > 1 ? (push @timeouts, $_[1]) : ($timeouts[-1] // 42) }); return $self; }); sub refaddr ($host) { $host->{my_custom_id} } @@ -262,6 +264,12 @@ my @output = $baseclass->run_ssh_cmd('echo -n "foo"', wantarray => 1, %ssh_creds); is_deeply(\@output, [0, 'foo', ''], 'Command successful exit with output'); + # test handling read errors and timeout parameter of run_ssh_cmd() + ($fail_on_read2, @net_ssh2_error) = (1, -9, 'LIBSSH2_ERROR_TIMEOUT', 'Time out waiting for data'); + throws_ok { $baseclass->run_ssh_cmd('sleep infinity', %ssh_creds, timeout => 100) } qr/waiting for data.*timeout/i, 'read timeout is fatal error'; + is_deeply \@timeouts, [100, 42], 'timeout increased to specified value, then set back to mocked default again'; + ($fail_on_read2, @net_ssh2_error) = (); + # Create a SSH session implecit with `run_ssh_cmd()` $ssh_expect->{password} = '2+3=5'; is($baseclass->run_ssh_cmd('echo -n "foo"', %ssh_creds, password => '2+3=5'), 0, 'Allow SSH credentials per run_ssh_cmd() call'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-4.6.1737980001.69ac906/t/27-consoles-sshXtermIPMI.t new/os-autoinst-4.6.1738076220.6b39995/t/27-consoles-sshXtermIPMI.t --- old/os-autoinst-4.6.1737980001.69ac906/t/27-consoles-sshXtermIPMI.t 1970-01-01 01:00:00.000000000 +0100 +++ new/os-autoinst-4.6.1738076220.6b39995/t/27-consoles-sshXtermIPMI.t 2025-01-28 15:57:00.000000000 +0100 @@ -0,0 +1,93 @@ +#!/usr/bin/perl + +use Test::Most; +use Mojo::Base -strict, -signatures; +use Test::Mock::Time; +use Test::MockModule; +use Test::MockObject; +use Test::Output qw(combined_like stderr_like); +use Test::Warnings qw(:all :report_warnings); +use POSIX qw(waitpid _exit); +use FindBin '$Bin'; +use lib "$Bin/../external/os-autoinst-common/lib"; +use OpenQA::Test::TimeLimit '5'; +use consoles::console; +use backend::ipmi; +use consoles::VNC; +use consoles::sshXtermIPMI; +use cv; + +cv::init; +require tinycv; + +$bmwqemu::topdir = "$Bin/.."; +$bmwqemu::vars{IPMI_HOSTNAME} = 'localhost'; +$bmwqemu::vars{IPMI_USER} = 'root'; +$bmwqemu::vars{IPMI_PASSWORD} = 'root'; + +$bmwqemu::vars{WORKER_HOSTNAME} = 'localhost'; +$bmwqemu::vars{"HARDWARE_CONSOLE_LOG"} = 1; +$bmwqemu::vars{IPMI_SOL_MAX_RECONNECTS} = 5; + +my @printed; +my $testapi_console = 'sshXtermIPMI'; +my $testapi_console_mock = Test::MockModule->new("consoles::$testapi_console"); +ok my $backend = backend::ipmi->new(), 'backend can be created'; +my $backend_mock = Test::MockModule->new('backend::ipmi'); +my $localXvnc_mock = Test::MockModule->new('consoles::localXvnc'); +my $vnc_mock = Test::MockModule->new('consoles::VNC'); +my $inet_mock = Test::MockModule->new('IO::Socket::INET'); +my $s = Test::MockObject->new->set_true(qw(sockopt fileno print connected close blocking)); + +sub _setup_rfb_magic () { $s->set_series('mocked_read', 'RFB 003.006', pack('N', 1)) } +_setup_rfb_magic; + +$s->mock(read => sub { $_[1] = $s->mocked_read; length $_[1] }); +$s->mock($_ => sub { push @printed, $_[1] }) for qw(print write); +$vnc_mock->redefine(_read_socket => sub { substr(${$_[1]}, $_[3], $_[2]) = $s->mocked_read; length ${$_[1]} }); +$inet_mock->redefine(new => $s); +$backend_mock->redefine(ipmi_cmdline => sub { (qw(echo simulating ipmi)) }); +$backend_mock->redefine(do_mc_reset => sub { bmwqemu::diag('IPMI mc reset success'); }); +$testapi_console_mock->redefine(backend => $backend); +$localXvnc_mock->redefine(activate => sub ($self) { $self->{DISPLAY} = "display"; }); +$vnc_mock->noop('_server_initialization'); +$vnc_mock->noop('login'); + +ok my $sol_connection = consoles::sshXtermIPMI->new($testapi_console, undef), 'sol connection can be established'; + +subtest 'sshXtermIPMI activate' => sub { + stderr_like { + $sol_connection->activate(); + } qr/Xterm PID:/, 'VNC connection established'; +}; + +subtest 'sshXtermIPMI current_screen' => sub { + my $sol_mock = Test::MockModule->new('consoles::sshXtermIPMI'); + $sol_mock->redefine(waitpid => -1); + combined_like { + throws_ok { $sol_connection->current_screen() } qr/Too many IPMI SOL errors/, 'dies on reconnect failure'; + } qr/ + !!!\ consoles::sshXtermIPMI::current_screen:\ IPMI\ SOL\ connection\ died.* + Xterm\ PID:\ \d+ + /xs, 'sol current_screen failure logs as expected'; +}; + +subtest 'sshXtermIPMI cold reset' => sub { + $bmwqemu::vars{IPMI_MC_RESET_MAX_TRIES} = $bmwqemu::vars{IPMI_MC_RESET_TIMEOUT} = 3; + $sol_connection->{activated} = 1; + stderr_like { + $sol_connection->do_mc_reset(); + } qr/IPMI mc reset success/, 'cold reset success'; + cmp_ok $sol_connection->{activated}, '==', 0, 'console deactivated after cold reset'; +}; + +subtest 'sshXtermIPMI disable' => sub { + $sol_connection->{activated} = 1; + stderr_like { + $sol_connection->disable(); + } qr/IPMI: simulating/, 'ipmi disable success'; + cmp_ok $sol_connection->{activated}, '==', 0, 'console deactivated after calling disable'; +}; + +done_testing(); + ++++++ os-autoinst.obsinfo ++++++ --- /var/tmp/diff_new_pack.6xtmtg/_old 2025-01-29 16:10:36.304028968 +0100 +++ /var/tmp/diff_new_pack.6xtmtg/_new 2025-01-29 16:10:36.308029134 +0100 @@ -1,5 +1,5 @@ name: os-autoinst -version: 4.6.1737980001.69ac906 -mtime: 1737980001 -commit: 69ac9064b094b1bd57b9f3ec8c964ac80bd41451 +version: 4.6.1738076220.6b39995 +mtime: 1738076220 +commit: 6b399957ca5f802e08fa88e487c6a9af4d61dfaf