Enable Anaconda Text install via serial console.

This adds the Anaconda text installation test over
serial console and FIXES #115.
This commit is contained in:
Lukáš Růžička 2019-11-06 13:55:27 +01:00 committed by Adam Williamson
parent 0722cc5bcf
commit b93a197c22
8 changed files with 344 additions and 139 deletions

View File

@ -120,6 +120,14 @@ sub root_console {
if (get_var("LIVE") && get_var("DESKTOP") eq "gnome") { if (get_var("LIVE") && get_var("DESKTOP") eq "gnome") {
send_key "ctrl-alt-f3"; send_key "ctrl-alt-f3";
} }
elsif (get_var("SERIAL_CONSOLE")) {
# select first virtio terminal, we rely on anaconda having run
# a root shell on it for us
select_console("root-virtio-terminal");
# as we don't have any live image serial install tests, we
# know we don't need to login
return;
}
else { else {
send_key "ctrl-alt-f2"; send_key "ctrl-alt-f2";
} }

View File

@ -1,5 +1,6 @@
package fedoradistribution; package fedoradistribution;
use base 'distribution'; use base 'distribution';
use Cwd;
# Fedora distribution class # Fedora distribution class
@ -15,12 +16,19 @@ use base 'distribution';
# importing whole testapi creates circular dependency, so import only # importing whole testapi creates circular dependency, so import only
# necessary functions from testapi # necessary functions from testapi
use testapi qw(send_key type_string wait_idle assert_screen); use testapi qw(check_var get_var send_key type_string wait_idle assert_screen);
sub init() { sub init() {
my ($self) = @_; my ($self) = @_;
$self->SUPER::init(); $self->SUPER::init();
# This initiates the serial console as "root-virtio-terminal".
if (check_var('BACKEND', 'qemu')) {
$self->add_console('root-virtio-terminal', 'virtio-terminal', {});
for (my $num = 1; $num < get_var('VIRTIO_CONSOLE_NUM', 1); $num++) {
$self->add_console('root-virtio-terminal' . $num, 'virtio-terminal', {socked_path => cwd() . '/virtio_console' . $num});
}
}
} }
sub x11_start_program { sub x11_start_program {

View File

@ -15,9 +15,16 @@ sub root_console {
my %args = ( my %args = (
tty => 1, # what TTY to login to tty => 1, # what TTY to login to
@_); @_);
if (get_var("SERIAL_CONSOLE")) {
send_key "ctrl-alt-f$args{tty}"; # select the first virtio terminal, for now we assume we can
console_login; # always use that (we may have to make this smarter in future)
select_console("root-virtio-terminal");
}
else {
# For normal terminal emulation, use key combo to reach a tty
send_key "ctrl-alt-f$args{tty}";
}
console_login; # Do the login.
} }
sub post_fail_hook { sub post_fail_hook {

View File

@ -15,9 +15,21 @@ our @application_list;
sub run_with_error_check { sub run_with_error_check {
my ($func, $error_screen) = @_; my ($func, $error_screen) = @_;
die "Error screen appeared" if (check_screen $error_screen, 5); # Check screen does not work for serial console, so we need to use
$func->(); # different checking mechanism for it.
die "Error screen appeared" if (check_screen $error_screen, 5); if (testapi::is_serial_terminal) {
# by using 'unless' and 'expect_not_found=>1' here we avoid
# the web UI showing each failure to see the error message as
# a 'failed match'
die "Error screen appeared" unless (wait_serial($error_screen, timeout=>5, expect_not_found=>1));
$func->();
die "Error screen appeared" unless (wait_serial($error_screen, timeout=>5, expect_not_found=>1));
}
else {
die "Error screen appeared" if (check_screen $error_screen, 5);
$func->();
die "Error screen appeared" if (check_screen $error_screen, 5);
}
} }
# high-level 'type this string quite safely but reasonably fast' # high-level 'type this string quite safely but reasonably fast'
@ -78,18 +90,27 @@ sub desktop_vt {
sub boot_to_login_screen { sub boot_to_login_screen {
my %args = @_; my %args = @_;
$args{timeout} //= 300; $args{timeout} //= 300;
# we may start at a screen that matches one of the needles; if so, if (testapi::is_serial_terminal) {
# wait till we don't (e.g. when rebooting at end of live install, # For serial console, just wait for the login prompt
# we match text_console_login until the console disappears) unless (wait_serial "login:", timeout=>$args{timeout}) {
my $count = 5; die "No login prompt shown on serial console.";
while (check_screen("login_screen", 3) && $count > 0) { }
sleep 5;
$count -= 1;
} }
assert_screen "login_screen", $args{timeout}; else {
if (match_has_tag "graphical_login") { # we may start at a screen that matches one of the needles; if so,
wait_still_screen 10, 30; # wait till we don't (e.g. when rebooting at end of live install,
assert_screen "login_screen"; # we match text_console_login until the console disappears).
# The following is true for non-serial console.
my $count = 5;
while (check_screen("login_screen", 3) && $count > 0) {
sleep 5;
$count -= 1;
}
assert_screen "login_screen", $args{timeout};
if (match_has_tag "graphical_login") {
wait_still_screen 10, 30;
assert_screen "login_screen";
}
} }
} }
@ -128,8 +149,16 @@ sub desktop_switch_layout {
# failed (the prompt looks different in this case). We treat this as # failed (the prompt looks different in this case). We treat this as
# a soft failure. # a soft failure.
sub _console_login_finish { sub _console_login_finish {
if (match_has_tag "bash_noprofile") { # The check differs according to the console used.
record_soft_failure "It looks like profile sourcing failed"; if (testapi::is_serial_terminal) {
unless (wait_serial("-bash-.*[#\$]", timeout=>5, expect_not_found=>1)) {
record_soft_failure "It looks like profile sourcing failed";
}
}
else {
if (match_has_tag "bash_noprofile") {
record_soft_failure "It looks like profile sourcing failed";
}
} }
} }
@ -145,66 +174,94 @@ sub console_login {
@_); @_);
$args{timeout} ||= 10; $args{timeout} ||= 10;
# There's a timing problem when we switch from a logged-in console # Since we do not test many serial console tests, and we probably
# to a non-logged in console and immediately call this function; # only want to test serial console on a minimal installation only,
# if the switch lags a bit, this function will match one of the # let us not do all the magic to handle different console logins
# logged-in needles for the console we switched from, and get out # and let us simplify the process.
# of sync (e.g. https://openqa.stg.fedoraproject.org/tests/1664 ) # We will check if we are logged in, and if so, we will log out to
# To avoid this, we'll sleep a few seconds before starting # enable a new proper login based on the user variable.
sleep 4; if (get_var("SERIAL_CONSOLE")) {
# Check for the usual prompt.
my $good = ""; if (wait_serial("~\][#\$]", timeout=>5, quiet=>1)) {
my $bad = ""; type_string "logout\n";
if ($args{user} eq "root") { # Wait a bit to let the logout properly finish.
$good = "root_console"; sleep 10;
$bad = "user_console"; }
# Do the new login.
type_string $args{user};
type_string "\n";
sleep 2;
type_string $args{password};
type_string "\n";
# Let's perform a simple login test. This is the same as
# whoami, but has the advantage of existing in installer env
assert_script_run "id -un";
unless (wait_serial $args{user}, timeout=>5) {
die "Logging onto the serial console has failed.";
}
} }
else { else {
$good = "user_console"; # There's a timing problem when we switch from a logged-in console
$bad = "root_console"; # to a non-logged in console and immediately call this function;
} # if the switch lags a bit, this function will match one of the
# logged-in needles for the console we switched from, and get out
# of sync (e.g. https://openqa.stg.fedoraproject.org/tests/1664 )
# To avoid this, we'll sleep a few seconds before starting
sleep 4;
if (check_screen $bad, 0) { my $good = "";
# we don't want to 'wait' for this as it won't return my $bad = "";
script_run "exit", 0; if ($args{user} eq "root") {
sleep 2; $good = "root_console";
} $bad = "user_console";
}
else {
$good = "user_console";
$bad = "root_console";
}
assert_screen [$good, 'text_console_login'], $args{timeout}; if (check_screen $bad, 0) {
# if we're already logged in, all is good # we don't want to 'wait' for this as it won't return
if (match_has_tag $good) { script_run "exit", 0;
_console_login_finish(); sleep 2;
return; }
}
# otherwise, we saw the login prompt, type the username assert_screen [$good, 'text_console_login'], $args{timeout};
type_string("$args{user}\n"); # if we're already logged in, all is good
assert_screen [$good, 'console_password_required'], 30; if (match_has_tag $good) {
# on a live image, just the user name will be enough _console_login_finish();
if (match_has_tag $good) { return;
_console_login_finish(); }
return; # otherwise, we saw the login prompt, type the username
} type_string("$args{user}\n");
# otherwise, type the password assert_screen [$good, 'console_password_required'], 30;
type_string "$args{password}"; # on a live image, just the user name will be enough
if (get_var("SWITCHED_LAYOUT") and $args{user} ne "root") { if (match_has_tag $good) {
# see _do_install_and_reboot; when layout is switched _console_login_finish();
# user password is doubled to contain both US and native return;
# chars }
console_switch_layout; # otherwise, type the password
type_string "$args{password}"; type_string "$args{password}";
console_switch_layout; if (get_var("SWITCHED_LAYOUT") and $args{user} ne "root") {
} # see _do_install_and_reboot; when layout is switched
send_key "ret"; # user password is doubled to contain both US and native
# make sure we reached the console # chars
unless (check_screen($good, 30)) { console_switch_layout;
# as of 2018-10 we have a bug in sssd which makes this take type_string "$args{password}";
# unusually long in the FreeIPA tests, let's allow longer, console_switch_layout;
# with a soft fail - RHBZ #1644919 }
record_soft_failure "Console login is taking a long time - #1644919?"; send_key "ret";
my $timeout = 30; # make sure we reached the console
# even an extra 30 secs isn't long enough on aarch64... unless (check_screen($good, 30)) {
$timeout = 90 if (get_var("ARCH") eq "aarch64"); # as of 2018-10 we have a bug in sssd which makes this take
assert_screen($good, $timeout); # unusually long in the FreeIPA tests, let's allow longer,
# with a soft fail - RHBZ #1644919
record_soft_failure "Console login is taking a long time - #1644919?";
my $timeout = 30;
# even an extra 30 secs isn't long enough on aarch64...
$timeout = 90 if (get_var("ARCH") eq "aarch64");
assert_screen($good, $timeout);
}
} }
_console_login_finish(); _console_login_finish();
} }

View File

@ -700,6 +700,17 @@
}, },
test_suite => { name => "install_anaconda_text" }, test_suite => { name => "install_anaconda_text" },
}, },
{
machine => { name => "64bit" },
prio => 30,
product => {
arch => "x86_64",
distri => "fedora",
flavor => "universal",
version => "*",
},
test_suite => { name => "install_anaconda_serial_console" },
},
{ {
machine => { name => "64bit" }, machine => { name => "64bit" },
prio => 31, prio => 31,
@ -2386,6 +2397,18 @@
}, },
test_suite => { name => "install_anaconda_text" }, test_suite => { name => "install_anaconda_text" },
}, },
{
group_name => "Fedora PowerPC",
machine => { name => "ppc64le" },
prio => 30,
product => {
arch => "ppc64le",
distri => "fedora",
flavor => "universal",
version => "*",
},
test_suite => { name => "install_anaconda_serial_console" },
},
{ {
group_name => "Fedora PowerPC", group_name => "Fedora PowerPC",
machine => { name => "ppc64le" }, machine => { name => "ppc64le" },
@ -3474,6 +3497,18 @@
}, },
test_suite => { name => "install_anaconda_text" }, test_suite => { name => "install_anaconda_text" },
}, },
{
group_name => "Fedora AArch64",
machine => { name => "aarch64" },
prio => 30,
product => {
arch => "aarch64",
distri => "fedora",
flavor => "universal",
version => "*",
},
test_suite => { name => "install_anaconda_serial_console" },
},
{ {
group_name => "Fedora AArch64", group_name => "Fedora AArch64",
machine => { name => "aarch64" }, machine => { name => "aarch64" },
@ -4685,6 +4720,19 @@
{ key => "ANACONDA_TEXT", value => "1" }, { key => "ANACONDA_TEXT", value => "1" },
], ],
}, },
{
name => "install_anaconda_serial_console",
settings => [
{ key => "ANACONDA_TEXT", value => "1" },
{ key => "SERIAL_CONSOLE", value => "1" },
# we want one console for anaconda and one for a root
# terminal
{ key => "VIRTIO_CONSOLE_NUM", value => "2" },
# we don't need to check this here and it doesn't work
# with serial console
{ key => "NO_UEFI_POST", value => "1" },
],
},
{ {
name => "install_rescue_encrypted", name => "install_rescue_encrypted",
settings => [ settings => [

View File

@ -37,8 +37,30 @@ sub run {
} }
if (get_var("ANACONDA_TEXT")) { if (get_var("ANACONDA_TEXT")) {
$params .= "inst.text "; $params .= "inst.text ";
# we need this on aarch64 till #1594402 is resolved # we need this on aarch64 till #1594402 is resolved,
# and we also can utilize this if we want to run this
# over the serial console.
$params .= "console=tty0 " if (get_var("ARCH") eq "aarch64"); $params .= "console=tty0 " if (get_var("ARCH") eq "aarch64");
# when the text installation should run over the serial console,
# we have to add some more parametres to grub. Although, the written
# test case recommends using ttyS0, OpenQA only uses that console for
# displaying information but does not accept key strokes. Therefore,
# let us use a real virtio console here.
if (get_var("SERIAL_CONSOLE")) {
# this is icky. on ppc64 (OFW), virtio-terminal is hvc1 and
# virtio-terminal1 is hvc2, because the 'standard' serial
# terminal is hvc0 (the firmware does this or something).
# On other arches, the 'standard' serial terminal is ttyS0,
# so virtio-terminal becomes hvc0 and virtio-terminal1 is
# hvc1. We want anaconda to wind up on the console that is
# virtio-terminal1 in both cases
if (get_var("OFW")) {
$params .= "console=hvc2 ";
}
else {
$params .= "console=hvc1 ";
}
}
} }
# inst.debug enables memory use tracking # inst.debug enables memory use tracking
$params .= "debug" if get_var("MEMCHECK"); $params .= "debug" if get_var("MEMCHECK");
@ -73,10 +95,20 @@ sub run {
else { else {
if (get_var("ANACONDA_TEXT")) { if (get_var("ANACONDA_TEXT")) {
# select that we don't want to start VNC; we want to run in text mode # select that we don't want to start VNC; we want to run in text mode
assert_screen "anaconda_use_text_mode", 300; if (get_var("SERIAL_CONSOLE")) {
type_string "2\n"; # we direct the installer to virtio-terminal1, and use
# wait for text version of Anaconda main hub # virtio-terminal as a root console
assert_screen "anaconda_main_hub_text", 300; select_console('root-virtio-terminal1');
unless (wait_serial "Use text mode", timeout=>120) { die "Anaconda has not started."; }
type_string "2\n";
unless (wait_serial "Installation") { die "Text version of Anaconda has not started."; }
}
else {
assert_screen "anaconda_use_text_mode", 300;
type_string "2\n";
# wait for text version of Anaconda main hub
assert_screen "anaconda_main_hub_text", 300;
}
} }
else { else {
# on lives, we have to explicitly launch anaconda # on lives, we have to explicitly launch anaconda

View File

@ -20,7 +20,11 @@ sub run {
# do user login unless USER_LOGIN is set to string 'false' # do user login unless USER_LOGIN is set to string 'false'
unless (get_var("USER_LOGIN") eq "false") { unless (get_var("USER_LOGIN") eq "false") {
# this avoids us waiting 90 seconds for a # to show up
my $origprompt = $self->{serial_term_prompt};
$testapi::distri->{serial_term_prompt} = '$ ';
console_login(user=>get_var("USER_LOGIN", "test"), password=>get_var("USER_PASSWORD", "weakpassword")); console_login(user=>get_var("USER_LOGIN", "test"), password=>get_var("USER_PASSWORD", "weakpassword"));
$testapi::distri->{serial_term_prompt} = $origprompt;
} }
if (get_var("ROOT_PASSWORD")) { if (get_var("ROOT_PASSWORD")) {
console_login(user=>"root", password=>get_var("ROOT_PASSWORD")); console_login(user=>"root", password=>get_var("ROOT_PASSWORD"));

View File

@ -3,11 +3,38 @@ use strict;
use testapi; use testapi;
use utils; use utils;
# this enables you to send a command and some post-command wait time
# in one step and also distinguishes between serial console and normal
# VNC based console and handles the wait times differently.
sub console_type_wait {
my ($string, $wait) = @_;
$wait ||= 5;
type_string $string;
if (testapi::is_serial_terminal) {
sleep $wait;
}
else {
wait_still_screen $wait;
}
}
sub run { sub run {
my $self = shift; my $self = shift;
assert_screen "anaconda_main_hub_text";
# IMHO it's better to use sleeps than to have needle for every text screen # First, preset the environment according to the chosen console. This test
wait_still_screen 5; # can run both on a VNC based console, or a serial console.
if (get_var("SERIAL_CONSOLE")) {
select_console('root-virtio-terminal1');
unless (testapi::is_serial_terminal) {
die "The test does not run on a serial console when it should.";
}
}
else {
assert_screen "anaconda_main_hub_text";
# IMHO it's better to use sleeps than to have needle for every text screen
wait_still_screen 5;
}
# prepare for different number of spokes (e. g. as in Atomic DVD) # prepare for different number of spokes (e. g. as in Atomic DVD)
my %spoke_number = ( my %spoke_number = (
@ -21,77 +48,76 @@ sub run {
"user" => 8 "user" => 8
); );
# The error message that we are going to check for in the text installation
# must be different for serial console and a VNC terminal emulator.
my $error = "";
if (testapi::is_serial_terminal) {
$error = "unknown error has occured";
}
else {
$error = "anaconda_text_error";
}
# Set timezone # Set timezone
run_with_error_check(sub {type_string $spoke_number{"timezone"} . "\n"}, "anaconda_text_error"); run_with_error_check(sub {console_type_wait($spoke_number{"timezone"} . "\n")}, $error);
wait_still_screen 5; console_type_wait("1\n"); # Set timezone
type_string "1\n"; # Set timezone console_type_wait("1\n"); # Europe
wait_still_screen 5; console_type_wait("37\n", 7); # Prague
type_string "1\n"; # Europe
wait_still_screen 5;
type_string "37\n"; # Prague
wait_still_screen 7;
# Select disk # Select disk
run_with_error_check(sub {type_string $spoke_number{"destination"} . "\n"}, "anaconda_text_error"); run_with_error_check(sub {console_type_wait($spoke_number{"destination"} . "\n")}, $error);
wait_still_screen 5; console_type_wait("c\n"); # first disk selected, continue
type_string "c\n"; # first disk selected, continue console_type_wait("c\n"); # use all space selected, continue
wait_still_screen 5; console_type_wait("c\n", 7); # LVM selected, continue
type_string "c\n"; # use all space selected, continue
wait_still_screen 5;
type_string "c\n"; # LVM selected, continue
wait_still_screen 7;
# Set root password # Set root password
run_with_error_check(sub {type_string $spoke_number{"rootpwd"} . "\n"}, "anaconda_text_error"); my $rootpwd = get_var("ROOT_PASSWORD", "weakpassword");
wait_still_screen 5; run_with_error_check(sub {console_type_wait($spoke_number{"rootpwd"} . "\n")}, $error);
type_string get_var("ROOT_PASSWORD", "weakpassword"); console_type_wait("$rootpwd\n");
send_key "ret"; console_type_wait("$rootpwd\n");
wait_still_screen 5;
type_string get_var("ROOT_PASSWORD", "weakpassword");
send_key "ret";
wait_still_screen 7;
# Create user # Create user
run_with_error_check(sub {type_string $spoke_number{"user"} . "\n"}, "anaconda_text_error"); my $userpwd = get_var("USER_PASSWORD", "weakpassword");
wait_still_screen 5; my $username = get_var("USER_LOGIN", "test");
type_string "1\n"; # create new run_with_error_check(sub {console_type_wait($spoke_number{"user"} . "\n")}, $error);
wait_still_screen 5; console_type_wait("1\n"); # create new
type_string "3\n"; # set username console_type_wait("3\n"); # set username
wait_still_screen 5; console_type_wait("$username\n");
type_string get_var("USER_LOGIN", "test");
send_key "ret";
wait_still_screen 5;
# from Rawhide-20190503.n.0 (F31) onwards, 'use password' is default # from Rawhide-20190503.n.0 (F31) onwards, 'use password' is default
if (get_release_number() < 31) { if (get_release_number() < 31) {
# typing "4\n" on abrt screen causes system to reboot, so be careful # typing "4\n" on abrt screen causes system to reboot, so be careful
run_with_error_check(sub {type_string "4\n"}, "anaconda_text_error"); # use password run_with_error_check(sub {console_type_wait("4\n")}, $error); # use password
} }
wait_still_screen 5; console_type_wait("5\n"); # set password
type_string "5\n"; # set password console_type_wait("$userpwd\n");
wait_still_screen 5; console_type_wait("$userpwd\n");
type_string get_var("USER_PASSWORD", "weakpassword"); console_type_wait("6\n"); # make him an administrator
send_key "ret"; console_type_wait("c\n", 7);
wait_still_screen 5;
type_string get_var("USER_PASSWORD", "weakpassword");
send_key "ret";
wait_still_screen 5;
type_string "6\n"; # make him an administrator
wait_still_screen 5;
type_string "c\n";
wait_still_screen 7;
my $counter = 0; my $counter = 0;
while (check_screen "anaconda_main_hub_text_unfinished", 2) { if (testapi::is_serial_terminal) {
if ($counter > 10) { while (wait_serial("[!]", timeout=>5, quiet=>1)) {
die "There are unfinished spokes in Anaconda"; if ($counter > 10) {
die "There are unfinished spokes in Anaconda";
}
sleep 10;
$counter++;
console_type_wait("r\n"); # refresh
}
}
else {
while (check_screen "anaconda_main_hub_text_unfinished", 2) {
if ($counter > 10) {
die "There are unfinished spokes in Anaconda";
}
sleep 10;
$counter++;
console_type_wait("r\n"); # refresh
} }
sleep 10;
$counter++;
type_string "r\n"; # refresh
} }
# begin installation # begin installation
type_string "b\n"; console_type_wait("b\n");
# Wait for install to end. Give Rawhide a bit longer, in case # Wait for install to end. Give Rawhide a bit longer, in case
# we're on a debug kernel, debug kernel installs are really slow. # we're on a debug kernel, debug kernel installs are really slow.
@ -99,8 +125,23 @@ sub run {
if (lc(get_var('VERSION')) eq "rawhide") { if (lc(get_var('VERSION')) eq "rawhide") {
$timeout = 2400; $timeout = 2400;
} }
assert_screen "anaconda_install_text_done", $timeout;
type_string "\n"; if (testapi::is_serial_terminal) {
wait_serial("Installation complete", timeout=>$timeout);
if (get_var("SERIAL_CONSOLE") && get_var("OFW")) {
$self->root_console();
# we need to force the system to load a console on both hvc1
# and hvc2 for ppc64 serial console post-install tests
assert_script_run 'chroot /mnt/sysimage systemctl enable serial-getty@hvc1';
assert_script_run 'chroot /mnt/sysimage systemctl enable serial-getty@hvc2';
# back to anaconda ui
select_console("root-virtio-terminal1");
}
}
else {
assert_screen "anaconda_install_text_done", $timeout;
}
console_type_wait("\n");
} }