Skip to content

Commit

Permalink
Install SLES 16.0 Minimal VM as guest on SLES 16.0 system
Browse files Browse the repository at this point in the history
01.New module prepare_non_transactional_server.pm to perform all
necessary operations on non-transactional system before doing
actual work like virtual machine installation.
02.New subroutine is_reboot_needed in lib/utils.pm to determine
whether system reboot is needed after changes being made. It uses
setting NEEDS_REBOOTING or returned code of zypper needs-rebooting
to judge whether reboot is needed.
03.New guest profile sles_16_64_kvm_hvm_x86_64_qcow_ignition+combustion.xml
to represent SLES 16.0 Minimal VM for x86_64.
04.New combustion script combustion_script_all_round to perform all
necessary tasks, including adding user, setting passwords, adding
ssh public keys, starting services, doing registration and etc, in
order to have a fully up and running system.
05.Introduce new test suite level setting UNIFIED_GUEST_BUILDS for
unified guest installation because SLES 16.0 Minimal VM may have
different build than agama installer.
06.Update virtualization server patterns for SLES 16.0.
07.Use ##Authorized-Keys## and ##FQDN## in combustion_script_all_round.
08.New subroutine install_extra_packages in lib/utils.pm to wrap
up already existing similar functionality in multiple modules.
09.Exclude 127.0.0.0/8 subnet from being scanned in logs collecting
scripts, including:
data/virt_autotest/virt_logs_collector.sh
data/virt_autotest/fetch_logs_from_guest.sh
10.Add saving logs in emergency mode if guest fails to boot up and
enters into emergency/maintenance shell.
  • Loading branch information
waynechen55 authored and alice-suse committed Feb 25, 2025
1 parent a0b0f58 commit 746c094
Show file tree
Hide file tree
Showing 11 changed files with 392 additions and 70 deletions.
4 changes: 2 additions & 2 deletions data/virt_autotest/fetch_logs_from_guest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,8 @@ for single_subnet in ${subnets_in_route[@]};do
mkdir -p "${virt_logs_folder}/nmap_subnets_scan_results"
single_subnet_scan_results=${virt_logs_folder}'/nmap_subnets_scan_results/nmap_scan_'${single_subnet_transformed}'_'${scan_timestamp}
subnets_scan_results[${subnets_scan_index}]=${single_subnet_scan_results}
echo -e "nmap -sn $single_subnet -oX $single_subnet_scan_results" | tee -a ${fetch_logs_from_guest_log}
nmap -T4 -sn $single_subnet -oX $single_subnet_scan_results | tee -a ${fetch_logs_from_guest_log}
echo -e "nmap -T4 -sn --exclude 127.0.0.0/8 $single_subnet -oX $single_subnet_scan_results" | tee -a ${fetch_logs_from_guest_log}
nmap -T4 -sn --exclude 127.0.0.0/8 $single_subnet -oX $single_subnet_scan_results | tee -a ${fetch_logs_from_guest_log}
subnets_scan_index=$(( ${subnets_scan_index} + 1 ))
done

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?xml version="1.0"?>
<guest>
<guest_os_name>sles</guest_os_name>
<guest_version>16.0</guest_version>
<guest_version_major>16</guest_version_major>
<guest_version_minor>0</guest_version_minor>
<guest_os_word_length>64</guest_os_word_length>
<guest_build/>
<host_hypervisor_uri/>
<host_virt_type>kvm</host_virt_type>
<guest_virt_type>hvm</guest_virt_type>
<guest_machine_type>q35</guest_machine_type>
<guest_arch>x86_64</guest_arch>
<guest_name/>
<guest_domain_name/>
<guest_memory>2048,maxmemory=4096</guest_memory>
<guest_vcpus>2,maxvcpus=4</guest_vcpus>
<guest_cpumodel>host-model</guest_cpumodel>
<guest_metadata/>
<guest_boot_settings>loader=/usr/share/qemu/ovmf-x86_64-suse-4m-code.bin,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/qemu/ovmf-x86_64-suse-4m-vars.bin,hd,bootmenu.enable=yes,menu=on</guest_boot_settings>
<guest_xpath/>
<guest_installation_automation_method>ignition+combustion</guest_installation_automation_method>
<guest_installation_automation_platform>metal</guest_installation_automation_platform>
<guest_installation_automation_file>ignition_config_non_encrypted_image.ign#combustion_script_all_round</guest_installation_automation_file>
<guest_installation_method>import</guest_installation_method>
<guest_installation_extra_args/>
<guest_installation_wait/>
<guest_installation_method_others/>
<guest_installation_media>http://openqa.suse.de/assets/hdd/SLES16-Minimal-VM.x86_64-kvm-and-xen-Build12345.qcow2</guest_installation_media>
<guest_installation_fine_grained/>
<guest_os_variant>sle15sp7</guest_os_variant>
<guest_storage_path/>
<guest_storage_type>disk</guest_storage_type>
<guest_storage_format>qcow2</guest_storage_format>
<guest_storage_label>gpt</guest_storage_label>
<guest_storage_size>40</guest_storage_size>
<guest_storage_backing_path/>
<guest_storage_backing_format/>
<guest_storage_others>driver.name=qemu,target.dev=vda,target.bus=virtio,bus=virtio,cache=none</guest_storage_others>
<guest_network_type>bridge</guest_network_type>
<guest_network_mode>bridge</guest_network_mode>
<guest_network_device/>
<guest_network_others/>
<guest_netaddr/>
<guest_ipaddr/>
<guest_ipaddr_static>false</guest_ipaddr_static>
<guest_macaddr/>
<guest_graphics>vnc</guest_graphics>
<guest_controller/>
<guest_input/>
<guest_serial>pty</guest_serial>
<guest_parallel/>
<guest_channel/>
<guest_console/>
<guest_hostdev/>
<guest_filesystem/>
<guest_sound/>
<guest_watchdog/>
<guest_video/>
<guest_smartcard/>
<guest_redirdev/>
<guest_memballoon/>
<guest_tpm/>
<guest_rng/>
<guest_panic/>
<guest_memdev/>
<guest_vsock/>
<guest_iommu/>
<guest_iothreads/>
<guest_seclabel/>
<guest_keywrap/>
<guest_cputune/>
<guest_memtune/>
<guest_blkiotune/>
<guest_memorybacking/>
<guest_features/>
<guest_clock/>
<guest_power_management>suspend_to_mem.enabled=yes,suspend_to_disk.enabled=yes</guest_power_management>
<guest_events/>
<guest_resource/>
<guest_sysinfo/>
<guest_qemu_command/>
<guest_launchSecurity/>
<guest_autostart/>
<guest_transient/>
<guest_destroy_on_exit/>
<guest_autoconsole>text</guest_autoconsole>
<guest_noautoconsole>false</guest_noautoconsole>
<guest_noreboot/>
<guest_default_target>graphical</guest_default_target>
<guest_do_registration>false</guest_do_registration>
<guest_registration_server/>
<guest_registration_username/>
<guest_registration_password/>
<guest_registration_extensions/>
</guest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/bash
# combustion: network
#
# To be provisioned:
# 01) set localization and timezone
# 02) set root password
# 03) add new user qevirt
# 04) add ssh public keys for root and qevirt
# 05) set hostname
# 06) add customized sshd config
# 07) enable and restart sshd
# 08) test networking
# 09) do registration
# 10) refresh repositories
# 11) leave a marker

### set pipefail and enable debug
set -euo pipefail
set -x
set -v

# redirect output to the console
exec > >(exec tee -a /dev/console) 2>&1

### set locale, keyboard and timezone
rm -f /etc/localtime
systemd-firstboot --force --timezone=UTC --locale=en_US.UTF-8 --keymap=us
echo "FONT=eurlatgr.psfu" >> /etc/vconsole.conf

### set password for root
echo 'root:$6$LZQfIH8bS4JYwAQq$VIdGS2fnED6CSySnb5jJm8O6FUXWgjG3keN2I0c6Td4nLrwxUxratkJq0cKMuo1OMTwUYpQ7EyP2GnZ2pL.ut.' | chpasswd -e

### add new user qevirt
useradd --create-home --uid 1001 --comment "QE Virtualization Functional Test" --no-user-group --gid users qevirt
echo 'qevirt:$6$0Tcx/pXefxOSvZEi$ukUmR.j7/sTbv10LwbesHD8CurSkr/2pkstXeWuErA7TBxeB2nLQwOKFKQJnlqJuVzNWg1E6ovKl6ajAZRtKt.' | chpasswd -e

### add ssh public keys
mkdir -p /root/.ssh
echo "##Authorized-Keys##" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
mkdir -p /home/qevirt/.ssh
echo "##Authorized-Keys##" >> /home/qevirt/.ssh/authorized_keys
chmod 600 /home/qevirt/.ssh/authorized_keys

### set hostname
echo "##FQDN##" > /etc/hostname

### add customized sshd config
cat << EOF > /etc/ssh/sshd_config.d/01-qe-virtualization-functional.conf
PermitRootLogin yes
PubkeyAuthentication yes
PasswordAuthentication yes
PermitEmptyPasswords no
EOF

### enable and restart sshd service
systemctl enable sshd.service
systemctl stop sshd.service
systemctl start sshd.service

### test networking
curl conncheck.opensuse.org

### do registration
if command -v SUSEConnect 2>&1 >/dev/null; then
SUSEConnect -r ##Registration-Code## --url ##Registration-Server##
fi

### refresh and list respositories
if zypper repos 2>&1 >/dev/null; then
zypper --non-interactive --gpg-auto-import-keys refresh
zypper repos --details
fi

### leave a marker
echo "Configured with combustion" > /etc/issue.d/combustion

### close outputs and wait for tee to finish
exec 1>&- 2>&-; wait;
4 changes: 2 additions & 2 deletions data/virt_autotest/virt_logs_collector.sh
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,8 @@ for single_subnet in ${subnets_in_route[@]};do
mkdir -p "${virt_logs_folder}/nmap_subnets_scan_results"
single_subnet_scan_results=${virt_logs_folder}'/nmap_subnets_scan_results/nmap_scan_'${single_subnet_transformed}'_'${scan_timestamp}
subnets_scan_results[${subnets_scan_index}]=${single_subnet_scan_results}
echo -e "nmap -sn $single_subnet -oX $single_subnet_scan_results" | tee -a ${virt_logs_collecor_log}
nmap -T4 -sn $single_subnet -oX $single_subnet_scan_results | tee -a ${virt_logs_collecor_log}
echo -e "nmap -T4 -sn --exclude 127.0.0.0/8 $single_subnet -oX $single_subnet_scan_results" | tee -a ${virt_logs_collecor_log}
nmap -T4 -sn --exclude 127.0.0.0/8 $single_subnet -oX $single_subnet_scan_results | tee -a ${virt_logs_collecor_log}
subnets_scan_index=$(( ${subnets_scan_index} + 1 ))
done

Expand Down
1 change: 1 addition & 0 deletions lib/concurrent_guest_installations.pm
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ sub instantiate_guests_and_profiles {
my $_guest_profile = (XML::Simple->new)->XMLin($_res->content, SuppressEmpty => '');
$_guest_profile->{guest_name} = $_element;
$_guest_profile->{guest_installation_media} = $_store_of_guests{$_element}{INSTALL_MEDIA} if ($_store_of_guests{$_element}{INSTALL_MEDIA} ne '');
$_guest_profile->{guest_build} = $_store_of_guests{$_element}{INSTALL_BUILD} if ($_store_of_guests{$_element}{INSTALL_BUILD} ne '');
$_guest_profile->{guest_registration_code} = $_store_of_guests{$_element}{REG_CODE};
$_guest_profile->{guest_registration_extensions_codes} = $_store_of_guests{$_element}{REG_EXTS_CODES};
$guest_instances_profiles{$_element} = $_guest_profile;
Expand Down
70 changes: 37 additions & 33 deletions lib/guest_installation_and_configuration_base.pm
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,13 @@ sub prepare_non_transactional_environment {

$self->reveal_myself;
if (!is_transactional) {
virt_autotest::utils::setup_rsyslog_host($common_log_folder);
my $_packages_to_check = 'wget curl screen dnsmasq xmlstarlet yast2-schema python3 nmap';
virt_autotest::utils::setup_rsyslog_host($common_log_folder) if (is_sle('<16'));
my $_packages_to_check = 'wget curl screen dnsmasq xmlstarlet python3 nmap';
$_packages_to_check .= ' yast2-schema' if (is_sle('<16'));
zypper_call("install -y $_packages_to_check");
# There is already the highest version for kvm/xen packages on TW
if (is_sle) {
my $_patterns_to_check = 'kvm_server kvm_tools';
$_patterns_to_check = 'xen_server xen_tools' if ($self->{host_virt_type} eq 'xen');
my $_patterns_to_check = is_sle('<16') ? "$self->{host_virt_type}_server $self->{host_virt_type}_tools" : "$self->{host_virt_type}_host";
zypper_call("install -y -t pattern $_patterns_to_check");
}
}
Expand Down Expand Up @@ -1853,34 +1853,8 @@ sub config_guest_installation_media {

$self->reveal_myself;
$self->{guest_installation_media} =~ s/12345/$self->{guest_build}/g if ($self->{guest_build} ne 'gm');
#This is just auxiliary functionality to help correct and set correct installation media major and minor version if it mismatches with guest_version.It is not mandatory
#necessary and can be skipped without causing any issue.The end user should always pay attention and use meaningful and correct guest parameters and profiles.
if ($self->{guest_os_name} =~ /sles|oraclelinux/im) {
if (!($self->{guest_installation_media} =~ /-$self->{guest_version}-/im)) {
record_info("Guest $self->{guest_name} installation media $self->{guest_installation_media} does not match with version $self->{guest_version}", "Going to correct it !");
my $_guest_version_major_indicator = ($self->{guest_os_name} =~ /sles/im ? '' : 'R');
my $_guest_version_minor_indicator = ($self->{guest_os_name} =~ /sles/im ? 'SP' : 'U');
$self->{guest_installation_media} =~ /-((r)?(\d*))-((sp|u)?(\d*))?/im;
if ($self->{guest_version_minor} ne 0) {
if ($4 ne '') {
$self->{guest_installation_media} =~ s/-$1-$4/-${_guest_version_major_indicator}$self->{guest_version_major}-${_guest_version_minor_indicator}$self->{guest_version_minor}/im;
}
else {
$self->{guest_installation_media} =~ s/-$1/-${_guest_version_major_indicator}$self->{guest_version_major}-${_guest_version_minor_indicator}$self->{guest_version_minor}/im;
}
}
else {
if ($4 ne '') {
$self->{guest_installation_media} =~ s/-$1-$4/-${_guest_version_major_indicator}$self->{guest_version_major}/im;
}
else {
$self->{guest_installation_media} =~ s/-$1/-${_guest_version_major_indicator}$self->{guest_version_major}/im;
}
}
}
}

#If guest chooses to use iso installation media, then this iso media should be available on INSTALLATION_MEDIA_NFS_SHARE and mounted locally at INSTALLATION_MEDIA_LOCAL_SHARE.
# If guest chooses to use iso installation media, then this iso media should be available on INSTALLATION_MEDIA_NFS_SHARE and mounted locally at INSTALLATION_MEDIA_LOCAL_SHARE.
if ($self->{guest_installation_media} =~ /^.*\.iso$/im) {
my $_installation_media_nfs_share = get_var('INSTALLATION_MEDIA_NFS_SHARE', '');
my $_installation_media_local_share = get_var('INSTALLATION_MEDIA_LOCAL_SHARE', '');
Expand Down Expand Up @@ -2168,6 +2142,10 @@ sub config_guest_provision_combustion {
}
assert_script_run("curl -s -o $self->{guest_log_folder}/script " . data_url("virt_autotest/guest_unattended_installation_files/$_combustion_config"));
$_combustion_config = "$self->{guest_log_folder}/script";
my $_ssh_public_key = $guest_installation_and_configuration_metadata::host_params{ssh_public_key};
$_ssh_public_key =~ s/\//PLACEHOLDER/img;
assert_script_run("sed -i \'s/##Authorized-Keys##/$_ssh_public_key/g\' $_combustion_config");
assert_script_run("sed -i \'s/##FQDN##/$self->{guest_name}\\.$self->{guest_domain_name}/g\' $_combustion_config");
my $_scc_regcode = get_required_var('SCC_REGCODE');
$_scc_regcode =~ s/\//PLACEHOLDER/img;
assert_script_run("sed -i \'s/##Registration-Code##/$_scc_regcode/g\' $_combustion_config");
Expand Down Expand Up @@ -2715,13 +2693,39 @@ sub monitor_guest_installation {

$self->reveal_myself;
save_screenshot;
if (!(check_screen([qw(text-logged-in-root guest-installation-in-progress guest-installation-failures grub2 linux-login text-login guest-console-text-login)], 180 / get_var('TIMEOUT_SCALE', 1)))) {
if (!(check_screen([qw(text-logged-in-root guest-installation-in-progress guest-installation-failures grub2 linux-login text-login guest-console-text-login emergency-mode)], 180 / get_var('TIMEOUT_SCALE', 1)))) {
save_screenshot;
record_info("Can not detect any interested screens on guest $self->{guest_name} installation process", "Going to detach current screen anyway");
$self->detach_guest_installation_screen;
my $_detect_installation_result = $self->check_guest_installation_result_via_ssh;
record_info("Not able to determine guest $self->{guest_name} installation progress or result at the moment", "Installation is still in progress, guest reboot/shutoff, broken ssh connection or unknown") if ($_detect_installation_result eq '');
}
elsif (match_has_tag('emergency-mode')) {
wait_still_screen;
send_key('ret') for (0 .. 2);
wait_still_screen;
enter_cmd("echo -e \"\\n########## Beginning of journalctl ##########\\n\"");
enter_cmd("journalctl --dmesg --all --no-pager");
wait_still_screen;
enter_cmd("echo -e \"\\n########## End of journalctl ##########\\n\"");
enter_cmd("echo -e \"\\n########## Beginning of /run/initramfs/rdsosreport.txt ##########\\n\"");
enter_cmd("cat /run/initramfs/rdsosreport.txt");
wait_still_screen;
enter_cmd("echo -e \"\\n########## End of /run/initramfs/rdsosreport.txt ##########\\n\"");
enter_cmd("mkdir /sysroot/emergency_mode");
enter_cmd("journalctl --dmesg --all --no-pager > /sysroot/emergency_mode/journalctl");
enter_cmd("cp /run/initramfs/rdsosreport.txt /sysroot/emergency_mode/rdsosreport.txt");
enter_cmd("chroot /sysroot");
enter_cmd("sync");
wait_still_screen;
enter_cmd('exit');
enter_cmd('exit');
wait_still_screen;
$self->detach_guest_installation_screen;
$self->record_guest_installation_result('FAILED');
record_info("Installation failed for guest $self->{guest_name}", "Guest $self->{guest_name} in emergency/maintenance mode", result => 'fail');
$self->get_guest_ipaddr if ($self->{guest_ipaddr_static} ne 'true');
}
elsif (match_has_tag('guest-installation-failures')) {
save_screenshot;
$self->detach_guest_installation_screen;
Expand Down Expand Up @@ -3304,7 +3308,7 @@ sub post_fail_hook {
$self->reveal_myself;
$self->upload_guest_installation_logs;
save_screenshot;
virt_utils::collect_host_and_guest_logs("", "", "/root /var/log");
virt_utils::collect_host_and_guest_logs("", "", "/root /var/log /emergency_mode");
save_screenshot;
$self->upload_coredumps;
save_screenshot;
Expand Down
66 changes: 66 additions & 0 deletions lib/utils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ our @EXPORT = qw(
ensure_testuser_present
is_disk_image
is_ipxe_with_disk_image
is_reboot_needed
install_extra_packages
);

our @EXPORT_OK = qw(
Expand Down Expand Up @@ -3215,4 +3217,68 @@ sub is_ipxe_with_disk_image {
return 0;
}

=head2 is_reboot_needed
is_reboot_needed(username => 'name', address => 'address');
Identify whether rebooting needed after system being changed. Arguments username
and address can be used to specify remote user and host if operation is not local.
=cut

sub is_reboot_needed {
my %args = @_;
$args{username} //= 'root';
$args{address} //= 'localhost';

my $check_reboot_needed = "zypper needs-rebooting";
$check_reboot_needed = "ssh $args{username}\@$args{address} \"$check_reboot_needed\"" if ($args{address} ne 'localhost');
return 1 if (script_run("$check_reboot_needed") == 102 or get_var('NEEDS_REBOOTING'));
return 0;
}

=head2 install_extra_packages
install_extra_packages(repos => 'repositories', packages => 'packages');
Install extra packages that are only available in extra repositories. User may
need to install some useful utilities from other repositories to facilitate test
run. At the same time, it also needs to ensure such operations will not alter
existing system. Althought user should not be prevented from installing legitimate
tools and utilities, it is expected that use of additional packages should be
limited to the minimum and their impact should be paid attention to. User can
specify required repositories and pacakges via arguments, repos and packages or
settings INSTALL_OTHER_REPOS and INSTALL_OTHER_PACKAGES.
=cut

sub install_extra_packages {
my %args = @_;
$args{repos} //= get_var('INSTALL_OTHER_REPOS', '');
$args{packages} //= get_var('INSTALL_OTHER_PACKAGES', '');

if (!$args{repos} or !$args{packages}) {
record_info("No repositories/packags to be installed", "Specify arguments repos/packages or settings INSTALL_OTHER_REPOS/INSTALL_OTHER_PACKAGES");
return;
}

my @repos_to_install = split(/,/, $args{repos});
my @repos_names = ();
my $repo_name = "";
foreach (@repos_to_install) {
$repo_name = (split(/\//, $_))[-1] . "-" . bmwqemu::random_string(8);
push(@repos_names, $repo_name);
zypper_call("--gpg-auto-import-keys ar --enable --refresh $_ $repo_name");
save_screenshot;
}
zypper_call("--gpg-auto-import-keys refresh");
save_screenshot;
my $cmd = "install --no-allow-downgrade --no-allow-name-change --no-allow-vendor-change";
$cmd = $cmd . " $_" foreach (split(/,/, $args{packages}));
zypper_call($cmd);
save_screenshot;
$cmd = "rr";
$cmd = $cmd . " $_" foreach (@repos_names);
zypper_call($cmd);
save_screenshot;
}

1;
Loading

0 comments on commit 746c094

Please sign in to comment.