package webui; use strict; use base 'Exporter'; use Exporter; use testapi; use utils; use bugzilla; our @EXPORT = qw/select_disks webui_add_partition custom_blivet_format_partition custom_blivet_resize_partition custom_change_type custom_change_fs custom_change_device custom_delete_part get_full_repo get_mirrorlist_url crash_anaconda_text report_bug_text/; sub select_disks { # Handles disk selection for the new Cockpit based WebUI. # It has one optional argument - the number of disks to select. # Should be run when main WebUI hub is displayed. Enters # disk selection spoke and then ensures that required number # of disks are selected. # Example usage: # after calling `select_disks(2);` from WebUI main hub, # installation destination spoke will be displayed and two # attached disks will be selected for installation. my %args = ( disks => 1, iscsi => {}, @_ ); my %iscsi = %{$args{iscsi}}; # WebUI hub assert_screen "webui_cockpit_installation_method", 300; # this is awkward, but on the install_repository_hd_variation test, # we have two disks but on F39+ anaconda knows we're using one of # them as an install source and 'protects' it (doesn't show it on # INSTALLATION DESTINATION), so we need to go down the single disk # branch in that case. Once F38 is EOL we could potentially tweak # this to use a dedicated var or something my $relnum = get_release_number; if (get_var('NUMDISKS') > 1 && !(get_var('TEST') eq 'install_repository_hd_variation' && $relnum > 38)) { # Multi-disk case. Select however many disks the test needs. If # $disks is 0, this will do nothing, and 0 disks will be selected. for my $n (1 .. $args{disks}) { assert_and_click "webui_cockpit_select_disk_dropdown"; assert_and_click "webui_cockpit_select_disk_$n"; } } else { # Single disk case. # The Cockpit WebUI cannot deselect disks. if ($args{disks} == 0) { # Clicking will *de*-select. # assert_and_click "anaconda_install_destination_select_disk_1"; diag("The test has attempted to deselect the installation disk, however this is not supported."); } elsif ($args{disks} > 1) { die "Only one disk is connected! Cannot select $args{disks} disks."; } # For exactly 1 disk, we don't need to do anything. } # Handle network disks. if (%iscsi) { assert_and_click "webui_cockpit_modify_storage"; foreach my $target (keys %iscsi) { my $ip = $iscsi{$target}->[0]; my $user = $iscsi{$target}->[1]; my $password = $iscsi{$target}->[2]; assert_and_click "webui_cockpit_blue_menu_button"; assert_and_click "webui_cockpit_add_iscsi_portal"; wait_still_screen 2; type_safely $ip; send_key "tab"; type_safely $user; send_key "tab"; type_safely "password"; assert_and_click "webui_cockpit_next_button_blue" # type_safely $target; # # start discovery - three tabs, enter # type_safely "\t\t\t\n"; # if ($user && $password) { # assert_and_click "anaconda_install_destination_target_auth_type"; # assert_and_click "anaconda_install_destination_target_auth_type_chap"; # send_key "tab"; # type_safely $user; # send_key "tab"; # type_safely $password; # } # assert_and_click "anaconda_install_destination_target_login"; # assert_and_click "anaconda_install_destination_select_target"; } #assert_and_click "anaconda_spoke_done"; } } sub modify_storage { # This subroutine opens the Configure storage page. # It should run, when we are in the main webui menu, # on the Installation method tab. assert_and_click("webui_cockpit_modify_storage"); assert_screen("webui_cockpit_modify_storage_active"); } sub create_partition_table { # This will create a partition table, if no partition # table has been created yet. This should run as a # first function before partitions are added. # It takes two arguments, $type (gpt or mbr) to create # a corresponding partition style, and $zero (0 or 1). # If $zero is 1, the created partition will be overwritten # with zeros. my ($type, $zero) = @_; # Normal waittime is 30. my $waittime = 30; # Open the disk submenu. assert_and_click("webui_cockpit_disk_submenu"); # Click on Create partition table assert_and_click("webui_cockpit_create_parttable"); # Check that the dialog has opened. assert_screen("webui_cockpit_initialize_disk_shown"); # Click on the dropdown menu. assert_and_click("webui_cockpit_dropdown_arrow"); # Select partition type assert_and_click("webui_cockpit_parttable_$type"); # Zerowrite, if user wants it. if ($zero) { assert_and_click("webui_cockpit_overwrite_zeros"); # Increase waittime to give space. $waittime = 300; } # Confirm assert_and_click("webui_cockpit_button_initialize"); # Wait until come back to the Configure storage screen assert_screen("webui_cockpit_modify_storage_active", timeout => $waittime); } sub add_partition { # Used to add partition on WebUI partitioning screen. # Should be called when the Configure storage is displayed and disk(s) are selected. # You can pass device type for partition (needle tagged anaconda_blivet_devicetype_$devicetype should exist), # whether partitions should be of RAID1 (devicetype is then automatically handled) - you then # need to have two disks added, size of that partition in MiBs, desired filesystem of that partition # (anaconda_blivet_part_fs_$filesystem should exist) and mountpoint of that partition (e. g. string "/boot"). my %args = ( devicetype => "", raid1 => 0, size => 0, filesystem => "", mountpoint => "", units => "g", name => "", deep => 0, encrypt => 0, @_ ); my $waittime = 30; $args{devicetype} = "raid" if $args{raid1}; # Currently, no partitions can be created when I select two disks # and create a raid device, therefore only add partitions # when we are in a non-raid mode. # Let's check if there is Free space on the disk, if not # we will die. unless (check_screen("webui_cockpit_disk_free_space")) { die("There is no free space on the disk, I cannot create any partitions."); } unless ($args{devicetype} eq "raid") { # Open the submenu assert_and_click("webui_cockpit_partitioning_freespace"); # If no previous partitions have been created, we need to # create the partition table on the empty disk. assert_and_click("webui_cockpit_create_partition"); # We start the dialogue with the cursor being placed # in the name field, so if $name, we'll type it. If nothing # is specified, the defaults are empty strings, so we can type # it anyway. Then press tab to move away. type_very_safely($args{name}); send_key("tab"); # If mountpoint is set, we'll set it here type_very_safely($args{mountpoint}); send_key("tab"); # Select device type if ($args{devicetype}) { # The focus should be placed on that field already, # so we can open the dropdown using the space key. send_key("space"); assert_and_click "webui_cockpit_devicetype_$args{devicetype}"; } # To come to the size field, we will need to press "tab" twice. send_key("tab"); sleep(1); send_key("tab"); type_very_safely($args{size}); # Type in units unless ($args{units} ne "g") { send_key("tab"); send_key($args{units}); } # If user wants to zerowrite the partition if ($deep) { assert_and_click("webui_cockpit_overwrite_zeros"); $waittime = 300; } # If user wants encryption if ($encryption) { assert_and_click("webui_cockpit_no_encryption"); assert_and_click("webui_cockpit_encrypt_$encryption"); } # Create the partition assert_and_click("webui_cockpit_button_create"); # Check that we have come back to the Storage page. assert_screen("webui_cockpit_modify_storage_active"); } } sub custom_blivet_format_partition { # This subroutine formats a selected partition. To use it, you must select the # partition by other means before you format it using this routine. # You have to create a needle for any non-existing filesystem that is # passed via the $type, such as anaconda_blivet_part_fs_ext4. my %args = @_; # Start editing the partition and select the Format option assert_and_click "anaconda_blivet_part_edit"; assert_and_click "anaconda_blivet_part_format"; # Select the appropriate filesystem type. assert_and_click "anaconda_blivet_part_drop_select"; assert_and_click "anaconda_blivet_part_fs_$args{type}"; wait_still_screen 2; # Fill in the label if needed. send_key "tab"; if ($args{label}) { type_very_safely $args{label}; } # Fill in the mountpoint. send_key "tab"; type_very_safely $args{mountpoint}; assert_and_click "anaconda_blivet_part_format_button"; } sub custom_blivet_resize_partition { # This subroutine resizes the selected (active) partition to a given value. Note, that # if the selected value is bigger than the available space, it will only be # resized to fill up the available space no matter the number. # This routine cannot will not be able to select a particular partition!!! my %args = @_; # Start editing the partition and select the Resize option assert_and_click "anaconda_blivet_part_edit"; assert_and_click "anaconda_blivet_part_resize"; # Select the appropriate units. Note, that there must a be needle existing # for each possible unit that you might want to use, such as # "anaconda_blivet_size_unit_gib". assert_and_click "anaconda_blivet_part_drop_select"; assert_and_click "anaconda_blivet_size_unit_$args{units}"; # Move back to the value field. send_key "shift-tab"; # Type in the new size. type_very_safely $args{size}; assert_and_click "anaconda_blivet_part_resize_button"; } sub custom_change_type { # Used to set different device types for specified partition (e.g. # RAID). Should be called when custom partitioning spoke is # displayed. Pass it type of partition and name of partition. # Needles `anaconda_part_select_$part` and # `anaconda_part_device_type_$type` should exist. Example usage: # `custom_change_type("raid", "root");` uses # `anaconda_part_select_root` and `anaconda_part_device_type_raid` # needles to set RAID for root partition. my ($type, $part) = @_; $part ||= "root"; assert_and_click "anaconda_part_select_$part"; assert_and_click "anaconda_part_device_type"; # Move the mouse away from the menu mouse_set(10, 10); assert_and_click "anaconda_part_device_type_$type"; assert_and_click "anaconda_part_update_settings"; wait_still_screen 5; } sub custom_change_fs { # Used to set different file systems for specified partition. # Should be called when custom partitioning spoke is displayed. # Pass filesystem name and name of partition. Needles # `anaconda_part_select_$part` and `anaconda_part_fs_$fs` should # exist. Example usage: # `custom_change_fs("ext4", "root");` uses # `anaconda_part_select_root` and `anaconda_part_fs_ext4` needles # to set ext4 file system for root partition. my ($fs, $part) = @_; $part ||= "root"; assert_and_click "anaconda_part_select_$part"; wait_still_screen 5; # if fs is already set correctly, do nothing return if (check_screen "anaconda_part_fs_${fs}_selected", 5); assert_and_click "anaconda_part_fs"; # Move the mouse away from the menu mouse_set(10, 10); assert_and_click "anaconda_part_fs_$fs"; assert_and_click "anaconda_part_update_settings"; wait_still_screen 5; } sub custom_change_device { my ($part, $devices) = @_; assert_and_click "anaconda_part_select_$part"; assert_and_click "anaconda_part_device_modify"; foreach my $device (split(/ /, $devices)) { assert_and_click "anaconda_part_device_${device}"; } assert_and_click "anaconda_part_device_select"; assert_and_click "anaconda_part_update_settings"; wait_still_screen 5; } sub custom_delete_part { # Used for deletion of previously added partitions in custom # partitioning spoke. Should be called when custom partitioning # spoke is displayed. Pass the partition name. Needle # `anaconda_part_select_$part` should exist. Example usage: # `custom_delete_part('swap');` uses needle # `anaconda_part_select_swap` to delete previously added swap # partition. my ($part) = @_; return if not $part; assert_and_click "anaconda_part_select_$part"; assert_and_click "anaconda_part_delete"; } sub get_full_repo { my ($repourl) = @_; # trivial thing we kept repeating: fill out an HTTP or HTTPS # repo URL with flavor and arch, leave hd & NFS ones alone # (as for those tests we just use a mounted ISO and URL is complete) if ($repourl !~ m/^(nfs|hd:)/) { my $arch = get_var("ARCH"); $repourl .= "/Everything/$arch/os"; } return $repourl; } sub get_mirrorlist_url { return "mirrors.fedoraproject.org/mirrorlist?repo=fedora-" . lc(get_var("VERSION")) . "&arch=" . get_var('ARCH'); } sub crash_anaconda_text { # This routine uses the WebUI crash trigger to break the ongoing WebUI installation to simulate # an WebUI crash and runs a series of steps that results in creating a bug in Bugzilla. # It is used in the `install_text.pm` test and can be switched on by using the CRASH_REPORT # variable set to 1. # # tty3 has a shell on all f31+ installer and live images select_console "tty3-console"; assert_screen("anaconda_text_install_shell"); # We use the trigger command to do the simulated crash. type_string "kill -USR1 `cat /var/run/anaconda.pid`\n"; # And navigate back to the main panel of WebUI. This should require select_console "tty1-console"; assert_screen("anaconda_text_install_main"); # We wait until the crash menu appears. This usually takes some time, # so let's try for 300 seconds, this should be long enough. my $trials = 1; until (check_screen("anaconda_text_crash_menu_ready") || $trials > 30) { sleep 10; ++$trials; } # If the crash menu never appears, let's assert it to fail. if ($trials > 30) { assert_screen("anaconda_text_crash_menu_ready"); } } sub report_bug_text { # This routine handles the Bugzilla reporting after a simulated crash on # a textual console. # We will not create a needle for every menu item, and we will fail, # if there will be no positive Bugzilla confirmation shown at the end # of the process and then we will fail. # # Let us record the time of this test run. Later, we will use it to # limit the Bugzilla search. my $timestamp = time(); # # First, collect the credentials. my $login = get_var("BUGZILLA_LOGIN"); my $password = get_var("_SECRET_BUGZILLA_PASSWORD"); my $apikey = get_var("_SECRET_BUGZILLA_APIKEY"); # Choose item 1 - Report the bug. type_string "1\n"; sleep 2; # Choose item 1 - Report to Bugzilla type_string "1\n"; sleep 5; # Do login. type_string $login; type_string "\n"; sleep 5; # Enter the name of the Zilla. type_password $password; type_string "\n"; sleep 10; # Save the report without changing it. # It would need some more tweaking to actually type into the report, but since # it is reported even if unchanged, we leave it as such. type_string ":wq\n"; # Wait until the Crash menu appears again. # The same screen shows the result of the Bugzilla operation, # so if the needle matches, the bug has been created in Bugzilla. # Bugzilla connection is slow so we need to wait out some time, # therefore let's use a cycle that will check each 10 seconds and # ends if there is no correct answer from Bugzilla in 120 seconds. my $counter = 0; until (check_screen("anaconda_text_bug_reported") || $counter > 12) { sleep 10; ++$counter; } # Sometimes, Bugzilla throws out a communication error although the bug has been # created successfully. If this happens, we will softfail and leave the creation # check to a later step. if ($counter > 12) { record_soft_failure "Warning: Bugzilla has reported an error which could mean that the bug has not been created correctly, but it probably is not a real problem, if the test has not failed completely. "; } # Now, let us check with Bugzilla directly, if the bug has been created. # First, we shall get a Bugzilla format timestamp to use it in the query. # The timestamp will limit the list of bugs to those that have been created since # the then -> resulting with high probability in the one that this test run # has just created. $timestamp = convert_to_bz_timestamp($timestamp); # Then we fetch the latest bug from Bugzilla. my $lastbug = get_newest_bug($timestamp, $login); unless ($lastbug) { die "Bugzilla returned no newly created bug. It seems that the bug has not been created."; } else { print("BUGZILLA: The last bug was found: $lastbug\n"); } # We have found that the bug indeed is in the bugzilla (otherwise # we would have died already) so now we close it to clean up after this test run. my $result = close_notabug($lastbug, $apikey); unless ($result) { record_soft_failure "The bug has not been closed for some reason. Check manually."; } else { print("BUGZILLA: The last bug $lastbug changed status to CLOSED.\n"); } # Quit anaconda type_string "4\n"; }