-
Notifications
You must be signed in to change notification settings - Fork 283
/
Copy pathutils.pm
3317 lines (2606 loc) · 108 KB
/
utils.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Copyright 2015-2022 SUSE LLC
# SPDX-License-Identifier: GPL-2.0-or-later
package utils;
use base Exporter;
use Exporter;
use strict;
use warnings;
use testapi qw(is_serial_terminal :DEFAULT);
use lockapi 'mutex_wait';
use mm_network;
use version_utils qw(is_sle_micro is_microos is_krypton_argon is_leap is_leap_micro is_public_cloud is_sle is_sle12_hdd_in_upgrade is_storage_ng is_jeos package_version_cmp is_transactional is_bootloader_sdboot);
use Carp qw(croak);
use Utils::Architectures;
use Utils::Systemd qw(systemctl disable_and_stop_service);
use Utils::Backends;
use Mojo::UserAgent;
use zypper qw(wait_quit_zypper);
use Storable qw(dclone);
use Getopt::Long qw(GetOptionsFromString);
use File::Basename;
use XML::LibXML;
use security::config;
our @EXPORT = qw(
generate_results
parse_test_results
check_console_font
clear_console
type_string_slow
type_string_very_slow
type_string_slow_extended
enter_cmd_slow
enter_cmd_very_slow
save_svirt_pty
type_line_svirt
integration_services_check
integration_services_check_ip
unlock_if_encrypted
get_netboot_mirror
zypper_call
zypper_enable_install_dvd
zypper_ar
fully_patch_system
handle_patch_11sp4_zvm
ssh_fully_patch_system
minimal_patch_system
zypper_search
zypper_repos
zypper_patches
zypper_install_available
set_zypper_lock_timeout
unlock_bootloader
is_boot_encrypted
need_unlock_after_bootloader
is_bridged_networking
set_bridged_networking
assert_screen_with_soft_timeout
quit_packagekit
wait_for_purge_kernels
systemctl
addon_decline_license
addon_license
addon_products_is_applicable
noupdatestep_is_applicable
installwithaddonrepos_is_applicable
random_string
handle_emergency
handle_grub_zvm
handle_untrusted_gpg_key
service_action
assert_gui_app
get_root_console_tty
get_x11_console_tty
OPENQA_FTP_URL
OPENQA_HTTP_URL
IN_ZYPPER_CALL
arrays_differ
arrays_subset
ensure_serialdev_permissions
assert_and_click_until_screen_change
exec_and_insert_password
shorten_url
reconnect_mgmt_console
set_hostname
show_tasks_in_blocked_state
show_oom_info
svirt_host_basedir
disable_serial_getty
script_retry
script_run_interactive
create_btrfs_subvolume
create_raid_loop_device
file_content_replace
ensure_ca_certificates_suse_installed
is_efi_boot
install_patterns
common_service_action
ensure_service_disabled
script_output_retry
validate_script_output_retry
get_secureboot_status
assert_secureboot_status
susefirewall2_to_firewalld
permit_root_ssh
permit_root_ssh_in_sol
cleanup_disk_space
package_upgrade_check
test_case
remount_tmp_if_ro
detect_bsc_1063638
script_start_io
script_finish_io
handle_screen
define_secret_variable
write_sut_file
@all_tests_results
ping_size_check
is_ipxe_boot
is_uefi_boot
is_usb_boot
remove_efiboot_entry
empty_usb_disks
upload_y2logs
enable_persistent_kernel_log
enable_console_kernel_log
ensure_testuser_present
is_disk_image
is_ipxe_with_disk_image
is_reboot_needed
install_extra_packages
render_autoinst_url
);
our @EXPORT_OK = qw(
download_script
);
=head1 SYNOPSIS
Main file for all kind of functions
=cut
# USB kbd in raw mode is rather slow and QEMU only buffers 16 bytes, so
# we need to type very slowly to not lose keypresses.
# arbitrary slow typing speed for bootloader prompt when not yet scrolling
use constant SLOW_TYPING_SPEED => 13;
# type even slower towards the end to ensure no keybuffer overflow even
# when scrolling within the boot command line to prevent character
# mangling
use constant VERY_SLOW_TYPING_SPEED => 4;
# openQA internal ftp server url
our $OPENQA_FTP_URL = "ftp://openqa.suse.de";
# openQA internal http server url
our $OPENQA_HTTP_URL = "http://openqa.suse.de/assets/repo";
# set flag IN_ZYPPER_CALL in zypper_call and unset when leaving
our $IN_ZYPPER_CALL = 0;
=head2 save_svirt_pty
save_svirt_pty();
Save the pty device within the svirt shell session so that we can refer to the
correct pty pointing to the first tty, e.g. for password entry for encrypted
partitions and rewriting the network definition of zKVM instances.
Does B<not> work on B<Hyper-V>.
=cut
sub save_svirt_pty {
return if check_var('VIRSH_VMM_FAMILY', 'hyperv');
my $name = console('svirt')->name;
enter_cmd "pty=`virsh dumpxml $name 2>/dev/null | grep \"console type=\" | sed \"s/'/ /g\" | awk '{ print \$5 }'`";
enter_cmd "echo \$pty";
}
=head2 type_line_svirt
type_line_svirt($string [, expect => $expect] [, timeout => $timeout] [, fail_message => $fail_message]);
Sends C<$string> to the svirt terminal, waits up to C<$timeout> seconds
and expects C<$expect> to be returned on the terminal if C<$expect> is set.
If the expected text is not found, it will fail with C<$fail_message>.
=cut
sub type_line_svirt {
my ($string, %args) = @_;
enter_cmd "echo $string > \$pty";
if ($args{expect}) {
wait_serial($args{expect}, $args{timeout}) || die $args{fail_message} // 'expected \'' . $args{expect} . '\' not found';
}
}
=head2 unlock_zvm_disk
unlock_zvm_disk($console);
Unlock the zvm disk if needed.
C<$console> should be set to C<console('x3270')>.
C<$testapi::password> will be used as password.
=cut
sub unlock_zvm_disk {
my ($console) = @_;
my $password = check_var('SYSTEM_ROLE', 'Common_Criteria') ? $security::config::strong_password : $testapi::password;
eval { $console->expect_3270(output_delim => 'Please enter passphrase', timeout => 30) };
if ($@) {
diag 'No passphrase asked, continuing';
}
else {
$console->sequence_3270("String(\"$password\")", "ENTER");
diag 'Passphrase entered';
}
}
=head2 handle_grub_zvm
handle_grub_zvm($console);
Make sure that grub was started and send four enter keys to boot the system.
C<$console> should be set to C<console('x3270')>.
TODO: Add support for GRUB_BOOT_NONDEFAULT, GRUB_SELECT_FIRST_MENU, GRUB_SELECT_SECOND_MENU,
see boot_grub_item()
=cut
sub handle_grub_zvm {
my ($console) = @_;
eval { $console->expect_3270(output_delim => 'GNU GRUB', timeout => 60); };
if ($@) {
diag 'Could not find GRUB screen, continuing nevertheless, trying to boot';
}
else {
$console->sequence_3270("ENTER", "ENTER", "ENTER", "ENTER");
}
}
=head2 handle_untrusted_gpg_key
handle_untrusted_gpg_key();
This function is used during the installation.
Check if a previous needle match included the tag C<import-known-untrusted-gpg-key>.
If yes, import the key, otherwise don't.
=cut
sub handle_untrusted_gpg_key {
if (match_has_tag('import-known-untrusted-gpg-key')) {
record_info('Import', 'Known untrusted gpg key is imported');
wait_screen_change { send_key 'alt-t'; send_key 'alt-y' }; # import/yes, depending on variant
}
else {
record_info('Cancel import', 'Untrusted gpg key is NOT imported');
wait_screen_change { send_key 'alt-c'; send_key 'spc' }; # cancel/no, depending on variant
}
}
=head2 integration_services_check_ip
integration_services_check_ip();
Check that guest IP address that host and guest see is the same.
Die, if this is not the case.
=cut
sub integration_services_check_ip {
# Host-side of Integration Services
my $vmname = console('svirt')->name;
my $ips_host_pov;
if (check_var('VIRSH_VMM_FAMILY', 'hyperv')) {
(undef, $ips_host_pov) = console('svirt')->run_cmd(
'powershell -Command "Get-VM ' . $vmname . ' | Get-VMNetworkAdapter | Format-Table -HideTableHeaders IPAddresses"', wantarray => 1);
}
elsif (check_var('VIRSH_VMM_FAMILY', 'vmware')) {
(undef, $ips_host_pov) = console('svirt')->run_cmd(
"set -x; vmid=\$(vim-cmd vmsvc/getallvms | awk '/$vmname/ { print \$1 }');" .
"if [ \$vmid ]; then vim-cmd vmsvc/get.guest \$vmid | awk '/ipAddress/ {print \$3}' " .
"| head -n1 | sed -e 's/\"//g' | sed -e 's/,//g'; fi", domain => 'sshVMwareServer', wantarray => 1);
}
$ips_host_pov =~ m/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
$ips_host_pov = $1;
# Guest-side of Integration Services
my $ips_guest_pov = script_output("default_iface=\$(awk '\$2 == 00000000 { print \$1 }' /proc/net/route); ip addr show dev \"\$default_iface\" | awk '\$1 == \"inet\" { sub(\"/.*\", \"\", \$2); print \$2 }'");
record_info('IP (host)', $ips_host_pov);
record_info('IP (guest)', $ips_guest_pov);
die "ips_host_pov=<$ips_host_pov> ips_guest_pov=<$ips_guest_pov>" if $ips_host_pov ne $ips_guest_pov;
die 'Client nor host see IP address of the VM' unless $ips_host_pov;
}
=head2 integration_services_check
integration_services_check();
Make sure integration services (e.g. kernel modules, utilities, services)
are present and in working condition.
=cut
sub integration_services_check {
integration_services_check_ip();
if (check_var('VIRSH_VMM_FAMILY', 'hyperv')) {
# Guest-side of Integration Services
assert_script_run('rpmquery hyper-v');
assert_script_run('rpmverify hyper-v');
my $base = is_jeos() ? '-base' : '';
for my $module (qw(utils netvsc storvsc vmbus)) {
assert_script_run("rpmquery -l kernel-default$base | grep hv_${module}.ko");
assert_script_run("modinfo hv_$module");
assert_script_run("lsmod | grep hv_$module");
}
# 'hv_balloon' need not to be loaded
assert_script_run('modinfo hv_balloon');
systemctl('is-active hv_kvp_daemon.service');
systemctl('is-active hv_vss_daemon.service');
# 'Guest Services' are not enabled by default on our VMs
assert_script_run('systemctl list-unit-files | grep hv_fcopy_daemon.service');
}
elsif (check_var('VIRSH_VMM_FAMILY', 'vmware')) {
assert_script_run('rpmquery open-vm-tools');
assert_script_run('rpmquery open-vm-tools-desktop') unless check_var('DESKTOP', 'textmode');
assert_script_run('modinfo vmw_vmci');
systemctl('is-active vmtoolsd');
systemctl('is-active vgauthd');
}
}
=head2 unlock_if_encrypted
unlock_if_encrypted([check_typed_password => $check_typed_password]);
Check whether the system under test has an encrypted partition and attempts to unlock it.
C<$check_typed_password> will default to C<0>.
=cut
sub unlock_if_encrypted {
my (%args) = @_;
$args{check_typed_password} //= 0;
my $password = check_var('SYSTEM_ROLE', 'Common_Criteria') ? $security::config::strong_password : $testapi::password;
return unless get_var("ENCRYPT");
if (get_var('S390_ZKVM')) {
select_console('svirt');
# enter passphrase twice (before grub and after grub) if full disk is encrypted
if (get_var('FULL_LVM_ENCRYPT')) {
wait_serial("Please enter passphrase for disk.*", 100);
type_line_svirt "$password";
}
wait_serial('GNU GRUB') || diag 'Could not find GRUB screen, continuing nevertheless, trying to boot';
type_line_svirt '', expect => "Please enter passphrase for disk.*", timeout => 100, fail_message => 'Could not find "enter passphrase" prompt';
type_line_svirt "$password";
} # Handle zVM scenario
elsif (is_backend_s390x) {
my $console = console('x3270');
# Enter password before GRUB if boot is encrypted
# Boot partition is always encrypted, if not using expert partitioner with
# separate unencrypted boot
unlock_zvm_disk($console) unless get_var('UNENCRYPTED_BOOT');
handle_grub_zvm($console);
unlock_zvm_disk($console);
}
else {
assert_screen("encrypted-disk-password-prompt", 200);
type_password $password;
save_screenshot;
if ($args{check_typed_password}) {
unless (check_screen "encrypted_disk-typed_password", 30) {
record_info("Invalid password", "Not all password characters were typed successfully, retyping");
send_key "backspace" for (0 .. 9);
type_password $password;
assert_screen "encrypted_disk-typed_password";
}
}
send_key "ret";
wait_still_screen 15;
}
}
=head2 clear_console
clear_console();
C<ctrl-l> does not get queued up in buffer.
If this happens to fast, the screen would not be cleared.
So this function will simply type C<clear\n>.
=cut
sub clear_console {
enter_cmd "clear";
}
=head2 assert_gui_app
assert_gui_app($application [, install => $install] [, exec_param => $exec_param] [, remain => $remain]);
assert_gui_app (optionally installs and) starts an application, checks it started
and closes it again. It's the most minimalistic way to test a GUI application
Mandatory parameter: C<application> (the name of the application).
Optional parameters are:
install: boolean
Does the application have to be installed first? Especially
on live images where we want to ensure the disks are complete
the parameter should not be set to true - otherwise we might
mask the fact that the app is not on the media.
exec_param: string
When calling the application, pass this parameter on the command line.
remain: boolean
If set to true, do not close the application when tested it is
running. This can be used if the application shall be tested further.
=cut
sub assert_gui_app {
my ($application, %args) = @_;
ensure_installed($application) if $args{install};
my $params = $args{exec_param} ? " $args{exec_param}" : '';
x11_start_program($application . $params, target_match => "test-$application-started");
send_key "alt-f4" unless $args{remain};
}
=head2 check_console_font
check_console_font();
Check the console font using a needle.
13.2, Leap 42.1, SLE12 GA&SP1 have problems with setting up the
console font, we need to call systemd-vconsole-setup to workaround
that.
=cut
sub check_console_font {
# Does not make sense on ssh-based consoles
return if get_var('BACKEND', '') =~ /ipmi|spvm|pvm_hmc/;
# we do not await the console here, as we have to expect the font to be broken
# for the needle to match, for migration, need wait root console
my $flavor = get_var('FLAVOR');
select_console('root-console', await_console => ($flavor =~ /Migration/) ? 1 : 0);
# if this command failed, we're not in a console (e.g. in a svirt
# ssh connection) and don't see the console font but the local
# xterm font - no reason to change
return if script_run 'showconsolefont';
assert_screen [qw(broken-console-font correct-console-font)];
if (match_has_tag 'broken-console-font') {
assert_script_run("/usr/lib/systemd/systemd-vconsole-setup");
assert_screen 'correct-console-font';
}
}
=head2 type_string_slow_extended
type_string_slow_extended($string);
Enable additional arguments for nested calls of C<wait_still_screen>.
=cut
sub type_string_slow_extended {
my ($string) = @_;
type_string($string, max_interval => SLOW_TYPING_SPEED, wait_still_screen => 0.05, timeout => 5, similarity_level => 38);
}
=head2 type_string_slow
type_string_slow($string);
Typing a string with C<SLOW_TYPING_SPEED> to avoid losing keys.
=cut
sub type_string_slow {
my ($string) = @_;
type_string $string, max_interval => SLOW_TYPING_SPEED;
}
=head2 type_string_very_slow
type_string_very_slow($string);
Typing a string even slower with C<VERY_SLOW_TYPING_SPEED>.
The bootloader prompt line is very delicate with typing especially when
scrolling. We are typing very slow but this could still pose problems
when the worker host is utilized so better wait until the string is
displayed before continuing
For the special winter grub screen with moving penguins
C<wait_still_screen> does not work so we just revert to sleeping a bit
instead of waiting for a still screen which is never happening. Sleeping
for 3 seconds is less waste of time than waiting for the
C<wait_still_screen> to timeout, especially because C<wait_still_screen> is
also scaled by C<TIMEOUT_SCALE> which we do not need here.
=cut
sub type_string_very_slow {
my ($string) = @_;
type_string $string, max_interval => VERY_SLOW_TYPING_SPEED;
if (get_var('WINTER_IS_THERE')) {
sleep 3;
}
else {
wait_still_screen(1, 3);
}
}
=head2 enter_cmd_slow
enter_cmd_slow($cmd);
Enter a command with C<SLOW_TYPING_SPEED> to avoid losing keys.
=cut
sub enter_cmd_slow {
my ($cmd) = @_;
enter_cmd $cmd, max_interval => SLOW_TYPING_SPEED;
}
=head2 enter_cmd_very_slow
enter_cmd_very_slow($cmd);
Enter a command even slower with C<VERY_SLOW_TYPING_SPEED>. Compare to
C<type_string_very_slow>.
=cut
sub enter_cmd_very_slow {
my ($cmd) = @_;
enter_cmd $cmd, max_interval => VERY_SLOW_TYPING_SPEED;
wait_still_screen(1, 3);
}
=head2 get_netboot_mirror
get_netboot_mirror();
Return the mirror URL eg from the C<MIRROR_HTTP> var if C<INSTALL_SOURCE> is set to C<http>.
=cut
sub get_netboot_mirror {
my $m_protocol = get_var('INSTALL_SOURCE', 'http');
return get_var('MIRROR_' . uc($m_protocol));
}
=head2 zypper_call
zypper_call($command [, exitcode => $exitcode] [, timeout => $timeout] [, log => $log] [, dumb_term => $dumb_term]);
Function wrapping 'zypper -n' with allowed return code, timeout and logging facility.
First parammeter is required command, all others are named and provided as hash
for example:
zypper_call("up", exitcode => [0,102,103], log => "zypper.log");
# up --> zypper -n up --> update system
# exitcode --> allowed return code values
# log --> capture log and store it in zypper.log
# dumb_term --> pipes through cat if set to 1 and log is not set. This is a workaround
# to get output without any ANSI characters in zypper before 1.14.1. See boo#1055315.
C<dumb_term> will default to C<is_serial_terminal()>.
=cut
sub zypper_call {
my $command = shift;
my %args = @_;
my $allow_exit_codes = $args{exitcode} || [0];
my $timeout = $args{timeout} || 700;
my $log = $args{log};
my $dumb_term = $args{dumb_term} // is_serial_terminal;
my $printer = $log ? "| tee /tmp/$log" : $dumb_term ? '| cat' : '';
die 'Exit code is from PIPESTATUS[0], not grep' if $command =~ /^((?!`).)*\| ?grep/;
$IN_ZYPPER_CALL = 1;
# Retrying workarounds
my $ret;
my $search_conflicts = 'awk \'BEGIN {print "Processing conflicts - ",NR; group=0}
/Solverrun finished with an ERROR/,/statistics/{ print group"|",
$0; if ($0 ~ /statistics/ ){ print "EOL"; group++ }; }\'\
/var/log/zypper.log
';
for (1 .. 5) {
$ret = script_run("zypper -n $command $printer; ( exit \${PIPESTATUS[0]} )", $timeout);
die "zypper did not finish in $timeout seconds" unless defined($ret);
if ($ret == 4) {
if (script_run('grep "Error code.*502" /var/log/zypper.log') == 0) {
die 'According to bsc#1070851 zypper should automatically retry internally. Bugfix missing for current product?';
}
elsif (get_var('WORKAROUND_PREINSTALL_CONFLICT')) {
record_soft_failure('poo#113033 Workaround maintenance package preinstall conflict, job cloned with WORKAROUND_PREINSTALL_CONFLICT');
script_run q(zypper -n rm $(awk '/conflicts with/ {print$7}' /var/log/zypper.log|uniq));
next;
}
elsif (script_run('grep "Solverrun finished with an ERROR" /var/log/zypper.log') == 0) {
my $conflicts = script_output($search_conflicts);
record_info("Conflict", $conflicts, result => 'fail');
diag "Package conflicts found, not retrying anymore" if $conflicts;
last;
}
next unless get_var('FLAVOR', '') =~ /-(Updates|Incidents)$/;
}
if (get_var('FLAVOR', '') =~ /-(Updates|Incidents)/ && ($ret == 4 || $ret == 8 || $ret == 105 || $ret == 106 || $ret == 139 || $ret == 141)) {
if (script_run('grep "Exiting on SIGPIPE" /var/log/zypper.log') == 0) {
record_soft_failure 'Zypper exiting on SIGPIPE received during package download bsc#1145521';
}
else {
record_soft_failure 'Retry due to network problems poo#52319';
}
next;
}
last;
}
# log all install and remove actions for later use by tests/console/zypper_log_packages.pm
my @packages = split(" ", $command);
my $dry_run = 0;
for (my $i = 0; $i < scalar(@packages); $i++) {
if ($packages[$i] eq "--root" || $packages[$i] eq "-R") {
splice(@packages, $i, 2);
}
elsif ($packages[$i] eq "--name" || $packages[$i] eq "-n") {
splice(@packages, $i, 2);
}
elsif ($packages[$i] eq "--from") {
splice(@packages, $i, 2);
}
elsif ($packages[$i] eq "--repo" || $packages[$i] eq "-r") {
splice(@packages, $i, 2);
}
elsif ($packages[$i] eq "--download") {
splice(@packages, $i, 2);
}
elsif ($packages[$i] eq "--dry-run" || $packages[$i] eq "--download-only" || $packages[$i] eq '-d') {
$dry_run = 1;
}
elsif ($packages[$i] eq "--solver-focus") {
splice(@packages, $i, 2);
}
}
@packages = grep(/^[^-]/, @packages);
my $zypper_action = shift(@packages);
$zypper_action = "install" if ($zypper_action eq "in");
$zypper_action = "remove" if ($zypper_action eq "rm");
if ($zypper_action =~ m/^(install|remove)$/ && !$dry_run) {
push(@{$testapi::distri->{zypper_packages}}, {
raw_command => $command,
action => $zypper_action,
packages => \@packages,
return_code => $ret,
test => {
module => $autotest::current_test->{name},
category => $autotest::current_test->{category}
}
});
}
upload_logs("/tmp/$log") if $log;
unless (grep { $_ == $ret } @$allow_exit_codes) {
upload_logs('/var/log/zypper.log');
my $msg = "'zypper -n $command' failed with code $ret";
if ($ret == 104) {
$msg .= " (ZYPPER_EXIT_INF_CAP_NOT_FOUND)\n\nRelated zypper logs:\n";
script_run('tac /var/log/zypper.log | grep -F -m1 -B100000 "Hi, me zypper" | tac | grep \'\(SolverRequester.cc\|THROW\|CAUGHT\)\' > /tmp/z104.txt');
$msg .= script_output('cat /tmp/z104.txt');
}
elsif ($ret == 107) {
$msg .= " (ZYPPER_EXIT_INF_RPM_SCRIPT_FAILED)\n\nRelated zypper logs:\n";
script_run('tac /var/log/zypper.log | grep -F -m1 -B100000 "Hi, me zypper" | tac | grep \'RpmPostTransCollector.cc(executeScripts):.* scriptlet failed, exit status\' > /tmp/z107.txt');
$msg .= script_output('cat /tmp/z107.txt') . "\n\n";
}
else {
script_run('tac /var/log/zypper.log | grep -F -m1 -B100000 "Hi, me zypper" | tac | grep \'Exception.cc\' > /tmp/zlog.txt');
$msg .= "\n\nRelated zypper logs:\n";
$msg .= script_output('cat /tmp/zlog.txt');
}
die $msg;
}
$IN_ZYPPER_CALL = 0;
return $ret;
}
=head2 zypper_enable_install_dvd
zypper_enable_install_dvd();
Enables the install DVDs if they were used during the installation.
=cut
sub zypper_enable_install_dvd {
# If DVD Packages is used we need to (re-)enable the local repos
# see FATE#325541
zypper_call('mr -e -l') if (is_sle('15+') and (get_var('ISO_1', '') =~ /SLE-.*-Packages-.*\.iso/ || check_var('FLAVOR', 'Full') || ((get_required_var('FLAVOR') =~ /Migration/) && get_var('MEDIA_UPGRADE', '')) || get_var('ISO', '') =~ /SLE-.*-Full-.*\.iso/));
zypper_call 'ref';
}
=head2 zypper_ar
zypper_ar($url, [ name => NAME ], [ priority => N ]);
Add repository (with C<zypper ar>) unless it's already repo C<$name> already added
and refresh repositories.
Options:
C<$name> alias for repository, optional
When used, additional check if repo not yet exists is done, and adding
only if it doesn't exist. Also zypper ref is run only on this repository.
NOTE: if not used, $url must be a URI pointing to a .repo file.
C<$no_gpg_check> pass --no-gpgcheck for repos with not valid GPG key, optional
C<$priority> set repo priority, optional
C<$params> other ar subcommand parameters, optional
Examples:
zypper_ar('http://dist.nue.suse.com/ibs/QA:/Head/SLE-15-SP1', name => 'qa-head);
zypper_ar('https://download.opensuse.org/repositories/devel:/kubic/openSUSE_Tumbleweed/devel:kubic.repo', no_gpg_check => 1, priority => 90);
=cut
sub zypper_ar {
my ($url, %args) = @_;
my $name = $args{name} // '';
my $priority = $args{priority} // undef;
my $params = $args{params} // '';
my $no_gpg_check = $args{no_gpg_check} // '';
$no_gpg_check = $no_gpg_check ? "--no-gpgcheck" : "";
my $prioarg = defined($priority) && !is_sle('<=12') ? "-p $priority" : "";
my $cmd_ar = "--gpg-auto-import-keys ar -f $prioarg $no_gpg_check $params $url";
my $cmd_mr = "mr $prioarg $url";
my $cmd_ref = "--gpg-auto-import-keys ref";
# repo file
if (!$name) {
zypper_call($cmd_ar);
zypper_call($cmd_mr) if defined($priority) && is_sle('<12');
return zypper_call($cmd_ref);
}
# URI alias
my $out = script_output("LC_ALL=C zypper lr $name 2>&1", proceed_on_failure => 1);
if ($out =~ /Repository.*$name.*not found/i) {
zypper_call("$cmd_ar $name");
zypper_call($cmd_mr) if $priority && is_sle('<12');
return zypper_call("$cmd_ref --repo $name");
}
}
=head2 fully_patch_system
fully_patch_system();
Run C<zypper patch> twice. The first run will update the package manager,
the second run will update the system.
=cut
sub fully_patch_system {
my (%args) = @_;
my $trup_call_timeout = $args{trup_call_timeout} // '1800';
# special handle for 11-SP4 s390 install
if (is_sle('=11-SP4') && is_s390x && is_backend_s390x) {
# first run, possible update of packager -- exit code 103
zypper_call('patch --with-interactive -l', exitcode => [0, 102, 103], timeout => 3000);
handle_patch_11sp4_zvm();
return;
}
my $ret = 1;
if (is_transactional) {
# Update package manager first, not possible to detect package manager update bsc#1216504
transactional::trup_call('patch', timeout => $trup_call_timeout);
transactional::reboot_on_changes();
# Continue with patch
transactional::trup_call('patch', timeout => $trup_call_timeout);
transactional::reboot_on_changes();
return;
} else {
# Repeatedly call zypper patch until it returns something other than 103 (package manager updates)
# Add -q to reduce the unnecessary log output.
# Reduce the pressure of serial port when running hyperv test with sle15.
# poo#115454
my $zypp_opt = check_var('VIRSH_VMM_FAMILY', 'hyperv') ? '-q' : '';
for (1 .. 3) {
$ret = zypper_call("$zypp_opt patch --with-interactive -l", exitcode => [0, 4, 102, 103], timeout => 6000);
last if $ret != 103;
}
}
if (($ret == 4) && is_sle('>=12') && is_sle('<15')) {
record_soft_failure 'bsc#1176655 openQA test fails in patch_sle - binutils-devel-2.31-9.29.1.aarch64 requires binutils = 2.31-9.29.1';
my $para = '';
$para = '--force-resolution' if get_var('FORCE_DEPS');
$ret = zypper_call("patch --with-interactive -l $para", exitcode => [0, 102], timeout => 6000);
save_screenshot;
}
die "Zypper failed with $ret" if ($ret != 0 && $ret != 102);
return $ret;
}
=head2 ssh_fully_patch_system
ssh_fully_patch_system($host);
Connect to the remote host C<$host> using ssh and update the system by
running C<zypper patch> twice. The first run will update the package manager,
the second run will update the system.
=cut
sub ssh_fully_patch_system {
my $remote = shift;
my $cmd_time = time();
my $resolver_option = get_var('PUBLIC_CLOUD_GEN_RESOLVER') ? '--debug-solver' : '';
my $cmd = "ssh $remote 'sudo zypper -n patch $resolver_option --with-interactive -l'";
# first run, possible update of packager -- exit code 103
my $ret = script_run($cmd, 1500);
record_info('zypper patch', 'The command zypper patch took ' . (time() - $cmd_time) . ' seconds.');
if ($ret != 0 && $ret != 102 && $ret != 103) {
if ($resolver_option) {
script_run("ssh $remote 'tar -czvf /tmp/solver.tar.gz /var/log/zypper.solverTestCase /var/log/zypper.log'");
script_run("scp $remote:/tmp/solver.tar.gz /tmp/solver.tar.gz");
upload_logs('/tmp/solver.tar.gz', failok => 1);
}
croak("Zypper failed with $ret");
}
$cmd_time = time();
# second run, full system update
$ret = script_run($cmd, 6000);
record_info('zypper patch', 'The second command zypper patch took ' . (time() - $cmd_time) . ' seconds.');
if ($resolver_option) {
script_run("ssh $remote 'tar -czvf /tmp/solver.tar.gz /var/log/zypper.solverTestCase /var/log/zypper.log'");
script_run("scp $remote:/tmp/solver.tar.gz /tmp/solver.tar.gz");
upload_logs('/tmp/solver.tar.gz', failok => 1);
}
croak("Zypper failed with $ret") if ($ret != 0 && $ret != 102);
}
=head2 minimal_patch_system
minimal_patch_system([version_variable => $version_variable]);
zypper doesn't offer --updatestack-only option before 12-SP1, use patch for sp0 to update packager
=cut
sub minimal_patch_system {
my (%args) = @_;
$args{version_variable} //= 'VERSION';
if (is_sle('12-SP1+', get_var($args{version_variable}))) {
zypper_call('patch --with-interactive -l --updatestack-only', exitcode => [0, 102, 103], timeout => 3000, log => 'minimal_patch.log');
}
else {
zypper_call('patch --with-interactive -l', exitcode => [0, 102, 103], timeout => 3000, log => 'minimal_patch.log');
}
}
=head2 parse_zypper_table
parse_zypper_table($table, $fields);
Parse ASCII table generated by commands such as C<zypper search> into an array
of hashes. Pass the ASCII table in C<$table> as a single string. Pass an array
of hash keys in C<$fields>. Keys will be mapped to columns by position.
=cut
sub parse_zypper_table {
my ($table, $fields) = @_;
my @ret;
for my $line (split /\n/, $table) {
$line =~ s/^\s*(.*?)\s*$/$1/;
my @tokens = split /\s*\|\s*/, $line;
next if $#tokens < $#$fields;
my %tmp;
for (my $i = 0; $i < scalar @$fields; $i++) {
$tmp{$fields->[$i]} = $tokens[$i];
}
push @ret, \%tmp;
}
# Remove header from row list
shift @ret;
return \@ret;
}
=head2 zypper_search
zypper_search($search_params);
Run C<zypper search> with given command line arguments and parse the output
into an array of hashes.
=cut
sub zypper_search {
my $params = shift;
my %opts;
my @fields = ('status', 'name', 'summary', 'type');
# Set Getopt to ignore any unrecognized options
Getopt::Long::Configure('bundling', 'pass_through', 'permute');
# Call in array context to silence warnings about extra options and args
my @tmp = GetOptionsFromString($params, \%opts, 'details|s', 'verbose|v');
if (exists($opts{details}) || exists($opts{verbose})) {
@fields = ('status', 'name', 'type', 'version', 'arch', 'repository');
}
my $output = script_output("zypper -n se $params");
return parse_zypper_table($output, \@fields);
}
=head2 zypper_repos
zypper_repos($params);
Run C<zypper repos> with given command line arguments and parse the output into
an array of hashes. Only table output is supported.
=cut
sub zypper_repos {
my $params = shift // '';
my %opts;
my @fields = ('order', 'alias', 'name', 'enabled');
push @fields, 'gpgcheck' if is_sle('12+');
push @fields, 'autorefresh';
# Set Getopt to ignore any unrecognized options
Getopt::Long::Configure('bundling', 'pass_through', 'permute');
# Call in array context to silence warnings about extra options and args
my @tmp = GetOptionsFromString($params, \%opts, 'priority|p', 'uri|u',
'details|d');
if (exists($opts{details})) {
push @fields, 'priority', 'type', 'uri';
}
else {
push @fields, 'priority' if exists($opts{priority});
push @fields, 'uri' if exists($opts{uri});
}
my $output = script_output("zypper lr $params");
return parse_zypper_table($output, \@fields);
}
=head2 zypper_patches
zypper_patches($params);
Run C<zypper patches> with given command line arguments and parse the output
into an array of hashes.
=cut
sub zypper_patches {
my $params = shift // '';