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..48d7f15ec90b
--- /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://openqa.suse.de/assets/hdd/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..7608327f19a8
--- /dev/null
+++ b/data/virt_autotest/guest_unattended_installation_files/combustion_script_all_round
@@ -0,0 +1,76 @@
+#!/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
+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-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
+if command -v SUSEConnect 2>&1 >/dev/null; then
+ SUSEConnect -r ##Registration-Code## --url ##Registration-Server##
+fi
+
+### 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..40e6693638f4 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,69 @@ 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. 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. 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;
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..5a94ef7db5bc
--- /dev/null
+++ b/tests/virt_autotest/prepare_non_transactional_server.pm
@@ -0,0 +1,100 @@
+# 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->prepare_ground;
+ $self->prepare_extensions;
+ $self->prepare_packages;
+ $self->prepare_bootloader;
+ $self->prepare_services;
+ $self->prepare_reboot;
+ $self->restore_ground;
+}
+
+sub prepare_ground {
+ my $self = shift;
+
+ set_var('_NEEDS_REBOOTING', get_var('NEEDS_REBOOTING', 0));
+ 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 restore_ground {
+ my $self = shift;
+
+ set_var('NEEDS_REBOOTING', get_required_var('_NEEDS_REBOOTING'));
+}
+
+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];
}