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") {
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 {
send_key "ctrl-alt-f2";
}

View File

@ -1,5 +1,6 @@
package fedoradistribution;
use base 'distribution';
use Cwd;
# Fedora distribution class
@ -15,12 +16,19 @@ use base 'distribution';
# importing whole testapi creates circular dependency, so import only
# 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() {
my ($self) = @_;
$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 {

View File

@ -15,9 +15,16 @@ sub root_console {
my %args = (
tty => 1, # what TTY to login to
@_);
send_key "ctrl-alt-f$args{tty}";
console_login;
if (get_var("SERIAL_CONSOLE")) {
# select the first virtio terminal, for now we assume we can
# 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 {

View File

@ -15,9 +15,21 @@ our @application_list;
sub run_with_error_check {
my ($func, $error_screen) = @_;
die "Error screen appeared" if (check_screen $error_screen, 5);
$func->();
die "Error screen appeared" if (check_screen $error_screen, 5);
# Check screen does not work for serial console, so we need to use
# different checking mechanism for it.
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'
@ -78,18 +90,27 @@ sub desktop_vt {
sub boot_to_login_screen {
my %args = @_;
$args{timeout} //= 300;
# we may start at a screen that matches one of the needles; if so,
# wait till we don't (e.g. when rebooting at end of live install,
# we match text_console_login until the console disappears)
my $count = 5;
while (check_screen("login_screen", 3) && $count > 0) {
sleep 5;
$count -= 1;
if (testapi::is_serial_terminal) {
# For serial console, just wait for the login prompt
unless (wait_serial "login:", timeout=>$args{timeout}) {
die "No login prompt shown on serial console.";
}
}
assert_screen "login_screen", $args{timeout};
if (match_has_tag "graphical_login") {
wait_still_screen 10, 30;
assert_screen "login_screen";
else {
# we may start at a screen that matches one of the needles; if so,
# wait till we don't (e.g. when rebooting at end of live install,
# 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
# a soft failure.
sub _console_login_finish {
if (match_has_tag "bash_noprofile") {
record_soft_failure "It looks like profile sourcing failed";
# The check differs according to the console used.
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;
# There's a timing problem when we switch from a logged-in 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;
my $good = "";
my $bad = "";
if ($args{user} eq "root") {
$good = "root_console";
$bad = "user_console";
# Since we do not test many serial console tests, and we probably
# only want to test serial console on a minimal installation only,
# let us not do all the magic to handle different console logins
# and let us simplify the process.
# We will check if we are logged in, and if so, we will log out to
# enable a new proper login based on the user variable.
if (get_var("SERIAL_CONSOLE")) {
# Check for the usual prompt.
if (wait_serial("~\][#\$]", timeout=>5, quiet=>1)) {
type_string "logout\n";
# Wait a bit to let the logout properly finish.
sleep 10;
}
# 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 {
$good = "user_console";
$bad = "root_console";
}
# There's a timing problem when we switch from a logged-in 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) {
# we don't want to 'wait' for this as it won't return
script_run "exit", 0;
sleep 2;
}
my $good = "";
my $bad = "";
if ($args{user} eq "root") {
$good = "root_console";
$bad = "user_console";
}
else {
$good = "user_console";
$bad = "root_console";
}
assert_screen [$good, 'text_console_login'], $args{timeout};
# if we're already logged in, all is good
if (match_has_tag $good) {
_console_login_finish();
return;
}
# otherwise, we saw the login prompt, type the username
type_string("$args{user}\n");
assert_screen [$good, 'console_password_required'], 30;
# on a live image, just the user name will be enough
if (match_has_tag $good) {
_console_login_finish();
return;
}
# otherwise, type the password
type_string "$args{password}";
if (get_var("SWITCHED_LAYOUT") and $args{user} ne "root") {
# see _do_install_and_reboot; when layout is switched
# user password is doubled to contain both US and native
# chars
console_switch_layout;
if (check_screen $bad, 0) {
# we don't want to 'wait' for this as it won't return
script_run "exit", 0;
sleep 2;
}
assert_screen [$good, 'text_console_login'], $args{timeout};
# if we're already logged in, all is good
if (match_has_tag $good) {
_console_login_finish();
return;
}
# otherwise, we saw the login prompt, type the username
type_string("$args{user}\n");
assert_screen [$good, 'console_password_required'], 30;
# on a live image, just the user name will be enough
if (match_has_tag $good) {
_console_login_finish();
return;
}
# otherwise, type the password
type_string "$args{password}";
console_switch_layout;
}
send_key "ret";
# make sure we reached the console
unless (check_screen($good, 30)) {
# as of 2018-10 we have a bug in sssd which makes this take
# 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);
if (get_var("SWITCHED_LAYOUT") and $args{user} ne "root") {
# see _do_install_and_reboot; when layout is switched
# user password is doubled to contain both US and native
# chars
console_switch_layout;
type_string "$args{password}";
console_switch_layout;
}
send_key "ret";
# make sure we reached the console
unless (check_screen($good, 30)) {
# as of 2018-10 we have a bug in sssd which makes this take
# 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();
}

View File

@ -700,6 +700,17 @@
},
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" },
prio => 31,
@ -2386,6 +2397,18 @@
},
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",
machine => { name => "ppc64le" },
@ -3474,6 +3497,18 @@
},
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",
machine => { name => "aarch64" },
@ -4685,6 +4720,19 @@
{ 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",
settings => [

View File

@ -37,8 +37,30 @@ sub run {
}
if (get_var("ANACONDA_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");
# 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
$params .= "debug" if get_var("MEMCHECK");
@ -73,10 +95,20 @@ sub run {
else {
if (get_var("ANACONDA_TEXT")) {
# select that we don't want to start VNC; we want to run in text mode
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;
if (get_var("SERIAL_CONSOLE")) {
# we direct the installer to virtio-terminal1, and use
# virtio-terminal as a root console
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 {
# 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'
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"));
$testapi::distri->{serial_term_prompt} = $origprompt;
}
if (get_var("ROOT_PASSWORD")) {
console_login(user=>"root", password=>get_var("ROOT_PASSWORD"));

View File

@ -3,11 +3,38 @@ use strict;
use testapi;
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 {
my $self = shift;
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;
# First, preset the environment according to the chosen console. This test
# 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)
my %spoke_number = (
@ -21,77 +48,76 @@ sub run {
"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
run_with_error_check(sub {type_string $spoke_number{"timezone"} . "\n"}, "anaconda_text_error");
wait_still_screen 5;
type_string "1\n"; # Set timezone
wait_still_screen 5;
type_string "1\n"; # Europe
wait_still_screen 5;
type_string "37\n"; # Prague
wait_still_screen 7;
run_with_error_check(sub {console_type_wait($spoke_number{"timezone"} . "\n")}, $error);
console_type_wait("1\n"); # Set timezone
console_type_wait("1\n"); # Europe
console_type_wait("37\n", 7); # Prague
# Select disk
run_with_error_check(sub {type_string $spoke_number{"destination"} . "\n"}, "anaconda_text_error");
wait_still_screen 5;
type_string "c\n"; # first disk selected, continue
wait_still_screen 5;
type_string "c\n"; # use all space selected, continue
wait_still_screen 5;
type_string "c\n"; # LVM selected, continue
wait_still_screen 7;
run_with_error_check(sub {console_type_wait($spoke_number{"destination"} . "\n")}, $error);
console_type_wait("c\n"); # first disk selected, continue
console_type_wait("c\n"); # use all space selected, continue
console_type_wait("c\n", 7); # LVM selected, continue
# Set root password
run_with_error_check(sub {type_string $spoke_number{"rootpwd"} . "\n"}, "anaconda_text_error");
wait_still_screen 5;
type_string get_var("ROOT_PASSWORD", "weakpassword");
send_key "ret";
wait_still_screen 5;
type_string get_var("ROOT_PASSWORD", "weakpassword");
send_key "ret";
wait_still_screen 7;
my $rootpwd = get_var("ROOT_PASSWORD", "weakpassword");
run_with_error_check(sub {console_type_wait($spoke_number{"rootpwd"} . "\n")}, $error);
console_type_wait("$rootpwd\n");
console_type_wait("$rootpwd\n");
# Create user
run_with_error_check(sub {type_string $spoke_number{"user"} . "\n"}, "anaconda_text_error");
wait_still_screen 5;
type_string "1\n"; # create new
wait_still_screen 5;
type_string "3\n"; # set username
wait_still_screen 5;
type_string get_var("USER_LOGIN", "test");
send_key "ret";
wait_still_screen 5;
my $userpwd = get_var("USER_PASSWORD", "weakpassword");
my $username = get_var("USER_LOGIN", "test");
run_with_error_check(sub {console_type_wait($spoke_number{"user"} . "\n")}, $error);
console_type_wait("1\n"); # create new
console_type_wait("3\n"); # set username
console_type_wait("$username\n");
# from Rawhide-20190503.n.0 (F31) onwards, 'use password' is default
if (get_release_number() < 31) {
# 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;
type_string "5\n"; # set password
wait_still_screen 5;
type_string get_var("USER_PASSWORD", "weakpassword");
send_key "ret";
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;
console_type_wait("5\n"); # set password
console_type_wait("$userpwd\n");
console_type_wait("$userpwd\n");
console_type_wait("6\n"); # make him an administrator
console_type_wait("c\n", 7);
my $counter = 0;
while (check_screen "anaconda_main_hub_text_unfinished", 2) {
if ($counter > 10) {
die "There are unfinished spokes in Anaconda";
if (testapi::is_serial_terminal) {
while (wait_serial("[!]", timeout=>5, quiet=>1)) {
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
type_string "b\n";
console_type_wait("b\n");
# Wait for install to end. Give Rawhide a bit longer, in case
# 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") {
$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");
}