From 3d289a98f7cca2f3c3744149e94cf947864aef0a Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Fri, 24 Jan 2025 13:51:59 +0800 Subject: [PATCH] Install SLES 16.0 Minimal VM as guest on SLES 16.0 system 1.New module prepare_non_transactional_server.pm to perform all necessary operations on non-transactional system before doing actual work like virtual machine installation. 2.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. 3.New guest profile sles_16_64_kvm_hvm_x86_64_qcow_ignition+combustion.xml to represent SLES 16.0 Minimal VM for x86_64. 4.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. 5.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. 6.Update virtualization server patterns for SLES 16.0. 7.Use ##Authorized-Keys## and ##FQDN## in combustion_script_all_round. 8.New subroutine install_extra_packages in lib/utils.pm to wrap up already existing similar functionality in multiple modules. --- ...vm_hvm_x86_64_qcow_ignition+combustion.xml | 96 +++++++++++++++++++ .../combustion_script_all_round | 72 ++++++++++++++ lib/concurrent_guest_installations.pm | 1 + ...est_installation_and_configuration_base.pm | 35 ++----- lib/utils.pm | 63 ++++++++++++ .../sle16_guest_installation.yaml | 2 + .../prepare_non_transactional_server.pm | 92 ++++++++++++++++++ .../prepare_transactional_server.pm | 33 +------ .../unified_guest_installation.pm | 4 +- 9 files changed, 336 insertions(+), 62 deletions(-) create mode 100755 data/virt_autotest/guest_params_xml_files/sles_16_64_kvm_hvm_x86_64_qcow_ignition+combustion.xml create mode 100644 data/virt_autotest/guest_unattended_installation_files/combustion_script_all_round create mode 100644 tests/virt_autotest/prepare_non_transactional_server.pm diff --git a/data/virt_autotest/guest_params_xml_files/sles_16_64_kvm_hvm_x86_64_qcow_ignition+combustion.xml b/data/virt_autotest/guest_params_xml_files/sles_16_64_kvm_hvm_x86_64_qcow_ignition+combustion.xml new file mode 100755 index 000000000000..5fb97a95f009 --- /dev/null +++ b/data/virt_autotest/guest_params_xml_files/sles_16_64_kvm_hvm_x86_64_qcow_ignition+combustion.xml @@ -0,0 +1,96 @@ + + + sles + 16.0 + 16 + 0 + 64 + + + kvm + hvm + q35 + x86_64 + + + 2048,maxmemory=4096 + 2,maxvcpus=4 + host-model + + 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,bootmenu.enable=yes + + ignition+combustion + metal + ignition_config_non_encrypted_image.ign#combustion_script_all_round + import + + + + http://mirror.suse.asia/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/images/SLES16-Minimal-VM.x86_64-kvm-and-xen-Build12345.qcow2 + + sle15sp7 + + disk + qcow2 + gpt + 40 + + + driver.name=qemu,target.dev=vda,target.bus=virtio,bus=virtio,cache=none + bridge + bridge + + + + + false + + vnc + + + pty + + + + + + + + + + + + + + + + + + + + + + + + + + + suspend_to_mem.enabled=yes,suspend_to_disk.enabled=yes + + + + + + + + + text + false + + graphical + false + + + + + diff --git a/data/virt_autotest/guest_unattended_installation_files/combustion_script_all_round b/data/virt_autotest/guest_unattended_installation_files/combustion_script_all_round new file mode 100644 index 000000000000..868f56ca9f21 --- /dev/null +++ b/data/virt_autotest/guest_unattended_installation_files/combustion_script_all_round @@ -0,0 +1,72 @@ +#!/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 -euxo pipefail + +# 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-virt.conf +PermitRootLogin yes +PubkeyAuthentication yes +PasswordAuthentication yes +PermitEmptyPasswords no +EOF + +### restart sshd service +systemctl enable sshd.service +systemctl stop sshd.service +systemctl start sshd.service + +### test networking +curl conncheck.opensuse.org + +### do registration +SUSEConnect -r ##Registration-Code## --url ##Registration-Server## + +### refresh and list respositories +zypper --non-interactive --gpg-auto-import-keys refresh +zypper repos --details + +### leave a marker +echo "Configured with combustion" > /etc/issue.d/combustion + +# close outputs and wait for tee to finish +exec 1>&- 2>&-; wait; diff --git a/lib/concurrent_guest_installations.pm b/lib/concurrent_guest_installations.pm index d260e02832c7..65a23b7aa0e4 100755 --- a/lib/concurrent_guest_installations.pm +++ b/lib/concurrent_guest_installations.pm @@ -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; diff --git a/lib/guest_installation_and_configuration_base.pm b/lib/guest_installation_and_configuration_base.pm index db24ba27ee3f..ebd0bf75897b 100755 --- a/lib/guest_installation_and_configuration_base.pm +++ b/lib/guest_installation_and_configuration_base.pm @@ -325,8 +325,7 @@ sub prepare_non_transactional_environment { 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"); } } @@ -1853,34 +1852,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', ''); @@ -2168,6 +2141,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"); diff --git a/lib/utils.pm b/lib/utils.pm index 41b21dbcaf86..cf71616bef31 100644 --- a/lib/utils.pm +++ b/lib/utils.pm @@ -128,6 +128,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( @@ -3211,4 +3213,65 @@ sub is_ipxe_with_disk_image { return 0; } +=head2 is_reboot_needed + + is_reboot_needed; + +Identify whether test runs boots from ipxe and deploy linux disk image built by kiwi or similar programs +=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; + +Install extra packages that are only available in extra repositories. These extra +packages and repositories are specified in test suite settings INSTALL_OTHER_REPOS +and INSTALL_OTHER_PACKAGES. It might be necessary 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 their impact should be paid +attention to. +=cut + +sub install_extra_packages { + if (get_var("INSTALL_OTHER_REPOS")) { + my @repos_to_install = split(/,/, get_var("INSTALL_OTHER_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(/,/, get_required_var("INSTALL_OTHER_PACKAGES"))); + zypper_call($cmd); + save_screenshot; + + $cmd = "rr"; + $cmd = $cmd . " $_" foreach (@repos_names); + zypper_call($cmd); + save_screenshot; + } + else { + record_info("No other packages to be installed from empty INSTALL_OTHER_REPOS"); + } +} + 1; diff --git a/schedule/virt_autotest/sle16_guest_installation.yaml b/schedule/virt_autotest/sle16_guest_installation.yaml index 1168eb50afa0..25651bb8e38c 100644 --- a/schedule/virt_autotest/sle16_guest_installation.yaml +++ b/schedule/virt_autotest/sle16_guest_installation.yaml @@ -6,3 +6,5 @@ schedule: - installation/ipxe_install - installation/agama_reboot - virt_autotest/login_console + - virt_autotest/prepare_non_transactional_server + - virt_autotest/unified_guest_installation diff --git a/tests/virt_autotest/prepare_non_transactional_server.pm b/tests/virt_autotest/prepare_non_transactional_server.pm new file mode 100644 index 000000000000..9ebb6c7cc51c --- /dev/null +++ b/tests/virt_autotest/prepare_non_transactional_server.pm @@ -0,0 +1,92 @@ +# PREPARE NON-TRANSACTIONAL SERVER MODULE +# +# Copyright 2025 SUSE LLC +# SPDX-License-Identifier: FSFAP +# +# Summary: This module do preparation work for virtual machine installation on non +# -transactional server, including extensions registration, packages installation, +# services toggle and other operations being pertinent to non-transactonal server +# or which need to to performed in advance for various purposes, including better +# test flow, clear functional division, placeholder for future extension and etc. +# +# Maintainer: Wayne Chen qe-virt@suse.de +package prepare_non_transactional_server; + +use base "opensusebasetest"; +use strict; +use warnings; +use testapi; +use transactional; +use utils; +use version_utils; +use Utils::Systemd; +use Utils::Backends qw(get_serial_console); +use ipmi_backend_utils; +use virt_autotest::utils; + +sub run { + my $self = shift; + + $self->initialize; + $self->prepare_extensions; + $self->prepare_packages; + $self->prepare_bootloader; + $self->prepare_services; + $self->prepare_reboot; +} + +sub initialize { + my $self = shift; + + set_var('NEEDS_REBOOTING', 0); +} + +sub prepare_extensions { + my $self = shift; + + zypper_call("install --no-allow-downgrade --no-allow-name-change --no-allow-vendor-change suseconnect-ng"); + virt_autotest::utils::subscribe_extensions_and_modules; +} + +sub prepare_packages { + my $self = shift; + + my $zypper_install_package = "install --no-allow-downgrade --no-allow-name-change --no-allow-vendor-change libspice-server1 qemu-audio-spice qemu-chardev-spice qemu-spice qemu-ui-spice-core spice-vdagent"; + zypper_call("$zypper_install_package"); + install_extra_packages; +} + +sub prepare_bootloader { + my $self = shift; + + my $serialconsole = get_serial_console(); + if (script_run("grep -E \"\\s+linux\\s+/boot/.*console=ttyS1,115200\" /boot/grub2/grub.cfg") != 0) { + ipmi_backend_utils::add_kernel_options(kernel_opts => "console=tty console=$serialconsole,115200"); + set_var('NEEDS_REBOOTING', 1); + } +} + +sub prepare_services { + my $self = shift; + + #Disable rebootmgr service to prevent scheduled maitenance reboot. + disable_and_stop_service('rebootmgr.service'); + systemctl('status rebootmgr.service', ignore_failure => 1); +} + +sub prepare_reboot { + my $self = shift; + + if (is_reboot_needed) { + process_reboot(trigger => 1); + } + else { + record_info("No reboot needed", "No core libraries changed or no changes need to be refreshed"); + } +} + +sub test_flags { + return {fatal => 1}; +} + +1; diff --git a/tests/virt_autotest/prepare_transactional_server.pm b/tests/virt_autotest/prepare_transactional_server.pm index 1f64b0f47f73..ea8c336e8137 100644 --- a/tests/virt_autotest/prepare_transactional_server.pm +++ b/tests/virt_autotest/prepare_transactional_server.pm @@ -65,38 +65,7 @@ sub prepare_packages { sub install_additional_pkgs { my $self = shift; - if (get_var("INSTALL_OTHER_REPOS")) { - # SLE Micro is a lightweight operating system purpose built for containerized - # and virtualized workloads. It does not provide equally abundant functionality - # compared with SLES, so it becomes necessary to install some useful utilities - # from SLES repos to facilitate test run. At the same time, ensure it will not - # alter SLEM and its features and characteristics. Althought operating system - # should not prevent user 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 analyzed beforehand. - my @repos_to_install = split(/,/, get_var("INSTALL_OTHER_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(/,/, get_required_var("INSTALL_OTHER_PACKAGES"))); - zypper_call($cmd); - save_screenshot; - - # Remove additional repos from SLEM after packages installation finishes. - $cmd = "rr"; - $cmd = $cmd . " $_" foreach (@repos_names); - zypper_call($cmd); - save_screenshot; - } + install_extra_packages; } sub prepare_bootloader { diff --git a/tests/virt_autotest/unified_guest_installation.pm b/tests/virt_autotest/unified_guest_installation.pm index c6f8709712b4..0912178deb0d 100644 --- a/tests/virt_autotest/unified_guest_installation.pm +++ b/tests/virt_autotest/unified_guest_installation.pm @@ -66,13 +66,15 @@ sub run { my @guest_profiles = split(/,/, get_required_var('UNIFIED_GUEST_PROFILES')); croak("Guest names and profiles must be given to create, configure and install guests.") if ((scalar(@guest_names) eq 0) or (scalar(@guest_profiles) eq 0)); my %store_of_guests; - my @guest_installation_media = my @guest_registration_codes = my @guest_registration_extensions_codes = ('') x scalar @guest_names; + my @guest_installation_media = my @guest_installation_builds = my @guest_registration_codes = my @guest_registration_extensions_codes = ('') x scalar @guest_names; @guest_installation_media = split(/,/, get_var('UNIFIED_GUEST_INSTALLATION_MEDIA', '')) if (get_var('UNIFIED_GUEST_INSTALLATION_MEDIA', '') ne ''); + @guest_installation_builds = split(/,/, get_var('UNIFIED_GUEST_INSTALLATION_BUILDS', '')) if (get_var('UNIFIED_GUEST_INSTALLATION_BUILDS', '') ne ''); @guest_registration_codes = split(/,/, get_var('UNIFIED_GUEST_REG_CODES', '')) if (get_var('UNIFIED_GUEST_REG_CODES', '') ne ''); @guest_registration_extensions_codes = split(/,/, get_var('UNIFIED_GUEST_REG_EXTS_CODES', '')) if (get_var('UNIFIED_GUEST_REG_EXTS_CODES', '') ne ''); while (my ($index, $element) = each @guest_names) { $store_of_guests{$element}{PROFILE} = $guest_profiles[$index]; $store_of_guests{$element}{INSTALL_MEDIA} = $guest_installation_media[$index]; + $store_of_guests{$element}{INSTALL_BUILD} = $guest_installation_builds[$index]; $store_of_guests{$element}{REG_CODE} = $guest_registration_codes[$index]; $store_of_guests{$element}{REG_EXTS_CODES} = $guest_registration_extensions_codes[$index]; }