diff --git a/neon/lib/distribution_neon.pm b/neon/lib/distribution_neon.pm index cb49d80..4423a90 100644 --- a/neon/lib/distribution_neon.pm +++ b/neon/lib/distribution_neon.pm @@ -1,176 +1,176 @@ package distribution_neon; use base 'distribution'; use testapi qw(send_key %cmd assert_screen check_screen check_var get_var match_has_tag set_var type_password type_string wait_serial mouse_hide send_key_until_needlematch record_soft_failure wait_still_screen wait_screen_change diag); sub init() { my ($self) = @_; $self->SUPER::init(); $self->init_consoles(); $self->{console_sudo_cache} = %{()}; } sub x11_start_program($$$) { my ($self, $program, $timeout, $options) = @_; # enable valid option as default $options->{valid} //= 1; send_key "alt-f2"; mouse_hide(1); check_screen('desktop-runner', $timeout); type_string $program; sleep 8; send_key "ret"; } sub ensure_installed { my ($self, $pkgs, %args) = @_; my $pkglist = ref $pkgs eq 'ARRAY' ? join ' ', @$pkgs : $pkgs; $args{timeout} //= 90; testapi::x11_start_program('konsole'); assert_screen('konsole'); testapi::assert_script_sudo("chown $testapi::username /dev/$testapi::serialdev"); testapi::assert_script_sudo("chmod 666 /dev/$testapi::serialdev"); # make sure packagekit service is available testapi::assert_script_sudo('systemctl is-active -q packagekit || (systemctl unmask -q packagekit ; systemctl start -q packagekit)'); $self->script_run( "for i in {1..$retries} ; do pkcon -y install $pkglist && break ; done ; RET=\$?; echo \"\n pkcon finished\n\"; echo \"pkcon-\${RET}-\" > /dev/$testapi::serialdev", 0 ); if (check_screen('polkit-install', $args{timeout})) { type_password; send_key('ret', 1); } wait_serial('pkcon-0-', $args{timeout}) || die "pkcon failed"; send_key('alt-f4'); } # initialize the consoles needed during our tests sub init_consoles { my ($self) = @_; $self->add_console('root-virtio-terminal', 'virtio-terminal', {}); # NB: ubuntu only sets up tty1 to 7 by default. $self->add_console('log-console', 'tty-console', {tty => 6}); # in bionic ubuntu switched to tty1 for default. we adjusted our sddm # accordingly. $self->add_console('x11', 'tty-console', {tty => 1}); # oem-config runs on tty1, later it will drop into tty7 for the final # x11. $self->add_console('oem-config', 'tty-console', {tty => 1}); use Data::Dumper; print Dumper($self->{consoles}); return; } sub script_sudo($$) { # Clear the TTY first, otherwise we may needle match a previous sudo # password query and get confused. Clearing first make sure the TTY is empty # and we'll either get a new password query or none (still in cache). type_string "clear\n"; # NB: this is an adjusted code copy from os-autoinst distribution, we're # caching sudo results as by default sudo has a cache anyway. my ($self, $prog, $wait) = @_; # Iff the current console is a tty and it's been less than 4 minutes since # the last auth we don't expect an auth and skip the auth needle. # The time stamps are reset in activate_console which is only called after # consoles get reset and switched to again, so this should be fairly ok. # !tty never are cached. e.g. on x11 you could have multiple konsoles but # since the sudo cache is per-shell we don't know if there is a cache. use Scalar::Util 'blessed'; - my $class = blessed($self->{consoles}{$testapi::selected_console}); + my $class = blessed($self->{consoles}{$testapi::current_console}); my $is_tty = ($class =~ m/ttyConsole/); - my $last_auth = $self->{console_sudo_cache}{$testapi::selected_console}; + my $last_auth = $self->{console_sudo_cache}{$testapi::current_console}; my $need_auth = (!$is_tty || !$last_auth || (time() - $last_auth >= 4 * 60)); # Debug for now. Can be removed when someone stumbles upon this again. print "sudo cache [tty: $is_tty, last_auth: $last_auth, need auth: $need_auth]:\n"; $wait //= 10; my $str; if ($wait > 0) { $str = testapi::hashed_string("SS$prog$wait"); $prog = "$prog; echo $str > /dev/$testapi::serialdev"; } testapi::type_string "sudo $prog\n"; if ($need_auth) { if (testapi::check_screen "sudo-passwordprompt", 2, no_wait => 1) { testapi::type_password; testapi::send_key "ret"; - $self->{console_sudo_cache}{$testapi::selected_console} = time(); + $self->{console_sudo_cache}{$testapi::current_console} = time(); } } if ($str) { return testapi::wait_serial($str, $wait); } return; } sub activate_console { my ($self, $console) = @_; diag "activating $console"; $self->{console_sudo_cache}{$console} = 0; if ($console eq 'log-console') { assert_screen 'tty6-selected'; type_string $testapi::username; send_key 'ret'; assert_screen 'tty-password'; type_password $testapi::password; send_key 'ret'; assert_screen [qw(tty-logged-in tty-login-incorrect)]; if (match_has_tag('tty-login-incorrect')) { # Let's try again if the login failed. If it fails again give up. # It can happen that due to IO some of the password gets lost. # Not much to be done about that other than retry and hope for the # best. type_string $testapi::username; send_key 'ret'; assert_screen 'tty-password'; type_password $testapi::password; send_key 'ret'; assert_screen 'tty-logged-in'; } # Mostly just a workaround. os-autoinst wants to write to /dev/ttyS0 but # on ubuntu that doesn't fly unless chowned first. testapi::assert_script_sudo("chown $testapi::username /dev/$testapi::serialdev"); testapi::assert_script_sudo("chmod 666 /dev/$testapi::serialdev"); } return; } # Make sure consoles are ready and in a well known state. This prevents # switching between consoles quickly from ending up on a console which isn't # yet ready for use (e.g. typing on TTY before ready and losing chars). sub console_selected { my ($self, $console, %args) = @_; # FIXME: should make sure the session is unlocked if ($console eq 'x11') { # Do not wait on X11 specifically. Desktop state is wildely divergent. # Instead wait a static amount. This is a bit shit. But meh. # We could maybe needle the panel specifically? But then sddm has no # panel. I am really not sure how to best handle this. sleep 2; return; } assert_screen($console, no_wait => 1); } 1; diff --git a/neon/lib/livetest_neon.pm b/neon/lib/livetest_neon.pm index 5a12550..b6cdfdd 100644 --- a/neon/lib/livetest_neon.pm +++ b/neon/lib/livetest_neon.pm @@ -1,318 +1,318 @@ # Copyright (C) 2017-2018 Harald Sitter # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License or (at your option) version 3 or any later version # accepted by the membership of KDE e.V. (or its successor approved # by the membership of KDE e.V.), which shall act as a proxy # defined in Section 14 of version 3 of the license. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . package livetest_neon; use base 'basetest_neon'; use testapi; use strict; sub new { my ($class, $args) = @_; my $self = $class->SUPER::new($args); # OPENQA_IS_OFFLINE is an env var used by online/offline. return $self; } sub post_fail_hook { my ($self, $args) = @_; # Make sure networking is on (we disable it during installation). $self->online; # Base test handles everything we need handling return $self->SUPER::post_fail_hook; } sub login { die 'not implemented' } sub maybe_login { die 'not implemented' } sub boot_to_dm { die 'not possible for live sessions' } sub _archive_iso_artifacts { upload_logs '/cdrom/.disk/info', log_name => 'metadata'; upload_logs '/cdrom/casper/filesystem.manifest', log_name => 'metadata'; } sub _secureboot { if (!get_var('SECUREBOOT')) { return } assert_script_sudo 'apt install -y mokutil', 60; assert_script_sudo 'mokutil --sb-state', 16; assert_screen 'mokutil-sb-on'; } sub offline { my ($self, $args) = @_; if (!get_var('OPENQA_INSTALLATION_OFFLINE')) { return; } if (defined $ENV{'OPENQA_IS_OFFLINE'}) { return; } my $previous_console = current_console; select_console 'log-console'; { assert_script_sudo 'nmcli networking off'; } select_console $previous_console; $ENV{'OPENQA_IS_OFFLINE'} = '1'; } sub online { my ($self, $args) = @_; if (!get_var('OPENQA_INSTALLATION_OFFLINE')) { return; } if (!defined $ENV{'OPENQA_IS_OFFLINE'}) { return; } - my $previous_console = $testapi::selected_console; + my $previous_console = current_console; select_console 'log-console'; assert_script_sudo 'nmcli networking on'; select_console $previous_console; delete $ENV{'OPENQA_IS_OFFLINE'}; } sub maybe_switch_offline { my ($self, $args) = @_; if (!get_var('OPENQA_INSTALLATION_OFFLINE')) { print "staying online!\n"; # Run the early first start script to install coredumpd. # Only when NOT offline! # This runs apt update which would otherwise break the offline testing # with an update apt cache. # FIXME: we should possibly preseed the coredumpd into the ISO repo # so we can install it even without internet in the tests. # The package is fairly small and has no extra deps. select_console 'log-console'; { assert_script_run 'wget ' . data_url('early_first_start.rb'), 16; assert_script_sudo 'ruby early_first_start.rb', 60 * 5; } select_console 'x11'; return 0; } select_console 'log-console'; { print "going offline!\n"; $self->offline; # TODO: This isn't the most reliable assertion. # Ideally we'd have a list of all packages simulate them to make # sure all deps are installed. Or maybe even install them one # by one to make sure they actually work? # Make sure the preinstalled repo is actually being used. assert_script_sudo 'DEBIAN_FRONTEND=noninteractive apt-get install -y bcmwl-kernel-source', 10 * 60; } select_console 'x11'; assert_screen 'plasma-nm-offline'; return 1; } sub bootloader_secureboot { if (!get_var('SECUREBOOT')) { return; } # Enable scureboot first. When in secureboot mode we expect a second # ISO to be attached for uefi fs1 where we can run a efi program to # enroll the default keys to enable secureboot. # In the core.pm we'll then assert that secureboot is on. # In first_start.pm we'll further assert that secureboot is still on. # Use a fairly low timeout for the f2 trigger. The default 1 second # timeout might well cause us to shoot past tianocore and into the ISO. # Checking more often is more expensive, but should prevent this from # failing. Try this for only 10 seconds. If we aren't in OVMF by then # something definitely went wrong. send_key_until_needlematch 'ovmf', 'f2', 10 * 4, 0.25; send_key_until_needlematch 'ovmf-select-bootmgr', 'down'; send_key 'ret'; send_key_until_needlematch 'ovmf-bootmgr-shell', 'up'; # up is faster send_key 'ret'; assert_screen 'uefi-shell', 30; type_string 'fs1:'; send_key 'ret'; assert_screen 'uefi-shell-fs1'; type_string 'EnrollDefaultKeys.efi'; send_key 'ret'; type_string 'reset'; send_key 'ret'; reset_consoles; } sub bootloader { my ($self, $args) = @_; $self->bootloader_secureboot; # Wait for installation bootloader. This is either isolinux for BIOS or # GRUB for UEFI. # When it is grub we need to hit enter to proceed. assert_screen 'bootloader', 60; if (match_has_tag('live-bootloader-uefi')) { if (testapi::get_var("INSTALLATION_OEM")) { send_key 'down'; assert_screen('live-bootloader-uefi-oem'); } # Hack to force kmsg onto ttyS1 to debug shutdown problems. This hack # can be dropped once neon properly shuts down all the time again! # Edits grub entry to add more kernel cmdlines. send_key 'e'; my $counter = 8; while (!check_screen('live-grub-linux', 1)) { if (!$counter--) { last; } send_key 'down'; sleep 1; } send_key 'end'; if (testapi::get_var("INSTALLATION_OEM")) { foreach my $i (0..25) { send_key 'left'; } } else { send_key 'left'; send_key 'left'; send_key 'left'; } # Set the kmsg target to ttyS1. We then also need to force plymouth as # it'd not do anything if console= is set. type_string 'console=ttyS1 plymouth.force-splash plymouth.ignore-show-splash plymouth.ignore-serial-consoles '; send_key 'ctrl-x'; send_key 'ret'; } } # Waits for system to boot to desktop. sub boot { my ($self, $args) = @_; my $user = $testapi::username; my $password = $testapi::password; $testapi::username = 'neon'; $testapi::password = ''; $self->bootloader; # We better be at the desktop now. assert_screen 'live-desktop', 360; select_console 'log-console'; { validate_script_output 'grep -e "Using input driver" /var/log/Xorg.0.log', sub { m/.+evdev.+/ }; $self->_archive_iso_artifacts; $self->_secureboot; $self->_upgrade; assert_script_run 'wget ' . data_url('permissions_check.rb'), 16; assert_script_run 'ruby permissions_check.rb', 16; # This primarily to set up journald console output for /dev/ttyS1. # This script will also be run for the final system on first start and # retained in the image. assert_script_run 'wget ' . data_url('setup_journald_ttyS1.rb'), 16; assert_script_sudo 'ruby setup_journald_ttyS1.rb', 60 * 5; # Make sure the evdev driver is installed. We prefer evdev at this time # instead of libinput since our KCMs aren't particularly awesome for # libinput. if (get_var('OPENQA_SERIES') ne 'xenial') { assert_script_run 'dpkg -s xserver-xorg-input-evdev'; validate_script_output 'grep -e "Using input driver" /var/log/Xorg.0.log', sub { m/.+evdev.+/ }; } # TODO: maybe control via env var? # assert_script_run 'wget ' . data_url('enable_qdebug.rb'), 16; # assert_script_run 'ruby enable_qdebug.rb', 16; } select_console 'x11'; # Leave system as we have found it. assert_screen 'live-desktop', 5 * 60; $testapi::username = $user; $testapi::password = $password; } # TODO: could maybe be renamed to reboot and also grow an impl in basetest, then # use them interchangably. However, this doesn't actually trigger a reboot, # but conducts it, so it's somewhat different from a regular reboot in # basetest. Muse on this a bit. sub live_reboot { assert_screen "live-remove-medium", 60; # The message actually comes up before input is read, make sure to send rets # until the system reboots or we've waited a bit of time. We'll then # continue and would fail on the first start test if the system in fact # never rebooted. my $counter = 20; while (check_screen('live-remove-medium', 1)) { if (!$counter--) { last; } eject_cd; send_key 'ret'; sleep 1; } # There's a bug in the unit ordering which prevents reboot from working # every once in a while. I utterly failed to debug what exactly is wrong, # but it sucks enormously in code that isn't even maintained by us. # So, to mitigate this problem w'll force a reset if the remove medium # screen is still up after having tried to reboot nicely. # - sitter, Sept. 2018 if (check_screen('live-remove-medium', 1)) { eject_cd; sleep 1; power 'reset'; } reset_consoles; } 1;