#!/usr/bin/python3## Copyright (C) 2025 Red Hat, Inc.# SPDX-License-Identifier: LGPL-2.1-or-laterfrom anacondalib import INSTALLER_VM_MEMORY_MB, VirtInstallMachineCase, disk_images, pixel_tests_ignorefrom installer import Installerfrom language import Languagefrom payload_dnf import PayloadDNFfrom progress import Progressfrom review import Reviewfrom storage import EFI_PARTITION_DEFAULT_SIZE_MB, Storagefrom testlib import test_main, timeout # pylint: disable=import-errorfrom timezone import DateAndTimefrom users import Users, create_user, override_user_interface_conf_keysclass TestKickstartAutomatedPause(VirtInstallMachineCase):"""Destructive tests for kickstart-driven automated installations."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomatedPause-testBasic.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,"pause_at_summary": True,},}def testBasic(self):"""Description:Boot with ``inst.ks=TestKickstartAutomatedPause-testBasic.ks``with inst.pauseatsummary option.Expected results:- Automated install pause on the review step with the install action enabled.- Users specified in kickstart are correctly picked up on the Accounts stepand cannot be changed.- Root settings cannot be changed when rootpw is in kickstart.- Timezone and timesource lines from kickstart appear on the date & time screen;NTP servers, pool, and NTS options are visible in the Configure NTP servers modal.- Scenario is preset to 'Use configured storage' and the partitioning appears on the Review storage section.- Environment and groups from the ``%packages`` section in the kickstart appear on the Software selection step."""b = self.browserm = self.machinei = Installer(b, m, scenario="use-configured-storage-kickstart")u = Users(b, m)dt = DateAndTime(b, m)r = Review(b, m)s = Storage(b, m)b.open("/cockpit/@localhost/anaconda-webui/index.html")b.wait_visible("#installation-next-btn:not([aria-disabled=true])")i.reach_on_sidebar(i.steps.LANGUAGE)i.reach(i.steps.DATE_TIME)tz = dt.dbus_get_timezone()region, city = tz.split("/", 1)dt.check_region(region)dt.check_city(city)dt.check_auto_date_time(dt.dbus_get_ntp_enabled())dt.check_ntp_modal_rows([{"hostname": "ntp.cesnet.cz", "pool": False, "nts": False},{"hostname": "nts-test.strangled.net", "pool": False, "nts": True},{"hostname": "0.pool.ntp.org", "pool": True, "nts": False},])i.reach(i.steps.SOFTWARE_SELECTION)dnf = PayloadDNF(b)dnf.check_selected_environment("workstation-product-environment")dnf.check_group_selected("domain-client")i.reach(i.steps.INSTALLATION_METHOD)s.check_scenario_selected("use-configured-storage-kickstart")i.reach(i.steps.ACCOUNTS)# Verify kickstart users (read-only) and locked rootu.check_readonly_users_section_visible()u.check_readonly_user_displayed("alice", full_name="Alice Admin")u.check_readonly_user_displayed("bob", full_name="Bob User")u.check_readonly_user_displayed("carol", full_name="Carol Dev")b.wait_visible("#anaconda-screen-accounts-root-account-enable-root-account:disabled")b.assert_pixels(".anaconda-wizard-step-anaconda-screen-accounts", "users-kickstarted", ignore=pixel_tests_ignore)i.reach(i.steps.REVIEW)r.check_storage_config("Use configured storage")dev1 = "vda"r.check_disk(dev1, "16.1 GB vda (Virtio Block Device)")r.check_disk_row(dev1, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, True, "efi")r.check_disk_row(dev1, "/", "vda5", "11.2 GB", True, "xfs")r.check_disk_row(dev1, "/boot", "vda2", "1.15 GB", True, "xfs")r.check_disk_row(dev1, "/home", "vda3", "2.15 GB", True, "xfs")r.check_disk_row(dev1, "swap", "vda4", "1.07 GB", True, "swap")i.check_next_disabled(disabled=False)b.assert_pixels(".anaconda-wizard-step-anaconda-screen-review", "review-kickstarted", ignore=pixel_tests_ignore)def testUserInterfaceCanChangeAccountsConf(self):"""Description:Non-default ``can_change_root`` and ``can_change_users`` in ``anaconda.conf``(both ``True``): root must stay interactive and the user section must show theeditable create-account UI instead of the kickstart read-only summary.Expected results:- Read-only kickstart user summary is not shown; create-account controls are present- Root enable checkbox is not disabled"""b = self.browserm = self.machinei = Installer(b, m, scenario="use-configured-storage-kickstart")override_user_interface_conf_keys(self, can_change_root=True, can_change_users=True)b.open("/cockpit/@localhost/anaconda-webui/index.html")b.wait_visible("#installation-next-btn:not([aria-disabled=true])")i.reach_on_sidebar(i.steps.ACCOUNTS)b.wait_visible("#anaconda-screen-accounts-root-account-enable-root-account:not(:disabled)")b.wait_visible("#anaconda-screen-accounts-create-account-set-user-account:not(:disabled)")class TestKickstartAutomatedWarning(VirtInstallMachineCase):"""Destructive tests for kickstart-driven automated installations."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomatedWarning-testBasic.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,},}def testBasic(self):"""Description:Boot with ``inst.ks=TestKickstartAutomatedWarning-testBasic.ks``Expected results:- Automated installation starts despite storage warnings.- Storage warnings do not block auto-proceed (matching GTK/TUI behavior)."""b = self.browserb.open("/cockpit/@localhost/anaconda-webui/index.html")b.wait_in_text("h2", "Installing")class TestKickstartAutomated(VirtInstallMachineCase):"""Destructive tests for kickstart-driven automated installations."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomated-testBasic.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,},}@timeout(1200)def testBasic(self):"""Description:Boot with ``inst.ks=TestKickstartAutomated-testBasic.ks``Expected results:- Automated installation starts."""b = self.browserp = Progress(b)b.open("/cockpit/@localhost/anaconda-webui/index.html")p.wait_done()self.handleReboot()class TestKickstartAutomatedNonCriticalError(VirtInstallMachineCase):"""Kickstart install with a missing package triggers a non-critical error dialog."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomated-testNonCriticalError.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,},}def testMissingPackageAbort(self):"""Description:Boot with kickstart that requests a non-existent package during``%packages``.Expected results:- During installation the UI must show a continue/abort dialog.- The dialog mentions the missing package.- Choosing abort ends installation with a failure state."""b = self.browserp = Progress(b)b.open("/cockpit/@localhost/anaconda-webui/index.html")p.wait_installation_error_dialog("domain-client-nonexisting")b.wait_visible("#anaconda-screen-progress-installation-error-continue-btn")# Simulate remote reconnectionb.reload()b.wait_visible(".pf-v6-c-page")p.wait_installation_error_dialog("domain-client-nonexisting")b.wait_visible("#anaconda-screen-progress-installation-error-continue-btn")p.respond_installation_error_abort()p.wait_installation_failed()class TestKickstartAutomatedLangRuntime(VirtInstallMachineCase):"""Kickstart install with language set is applied during runtime."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomated-testLang.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,"pause_at_summary": True,},}def testRuntimeLangSet(self):"""Description:Boot with kickstart setting language with lang command.Expected results:- The UI is translated into language set in kickstart."""b = self.browserr = Review(b, self.machine)b.open("/cockpit/@localhost/anaconda-webui/index.html")r.check_language("Italiano (Italia)")class TestKickstartAutomatedFatalError(VirtInstallMachineCase):"""Kickstart install with a fatal error shows the critical report dialog."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomated-testFatalError.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,},}def testPreInstallScriptFailure(self):"""Description:Boot with kickstart whose ``%pre-install --erroronfail`` script fails.Expected results:- Installation does not show the non-critical continue/abort dialog.- The critical error report dialog appears with script failure details."""b = self.browserp = Progress(b)b.open("/cockpit/@localhost/anaconda-webui/index.html")p.wait_installation_failed("This is a fatal error", timeout=1200)b.wait_not_present("#anaconda-screen-progress-installation-error-continue-btn")# Simulate remote reconnection — error should persistb.reload()b.wait_visible(".pf-v6-c-page")p.wait_installation_failed("This is a fatal error", timeout=10)class TestKickstartAutomatedIncomplete(VirtInstallMachineCase):"""Incomplete kickstart (missing storage, accounts, timezone, and lang) blocks install until filled in."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomatedIncomplete-testBasic.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,},}def testBasic(self):"""Description:Boot with ``inst.ks=TestKickstartAutomatedIncomplete-testBasic.ks``which has no partitioning, ``timezone``, ``lang``, or account commands.Expected results:- The review screen shows an incomplete-configuration warning,with storage, language, timezone, and account rows marked incomplete,and the install button is disabled.- After setting language, timezone, and accounts (navigating forwardthrough the storage screen configures storage), all steps are complete."""b = self.browserm = self.machinei = Installer(b, m)r = Review(b, m)l = Language(b, m)dt = DateAndTime(b, m)u = Users(b, m)b.open("/cockpit/@localhost/anaconda-webui/index.html")b.wait_in_text(f"#{i.steps.REVIEW}-incomplete-configuration","Some installation steps still need to be completed before you can continue.")r.check_storage_incomplete()r.check_language_incomplete()r.check_timezone_incomplete()r.check_account_incomplete()i.check_next_disabled()# Navigate back to language page via sidebar and set a locale.i.reach_on_sidebar(i.steps.LANGUAGE)l.select_locale("en_US")# Navigate forward step by step via Nexti.reach(i.steps.DATE_TIME)dt.set_auto_timezone(False)dt.select_region("Europe")dt.select_city("Prague")i.reach(i.steps.ACCOUNTS)create_user(b, m)# Workaround to stabilize the button state before calling the reach# to prevent race condition on state flippingu.enable_user_account(False)u.enable_user_account(True)i.reach(i.steps.REVIEW)b.wait_in_text(f"#{i.steps.REVIEW}-target-system-account","The following user will be created")r.check_timezone("Europe/Prague")class TestKickstartAutomatedRAID(VirtInstallMachineCase):"""Kickstart automated install with RAID layout from kickstart-tests raid-1.ks."""provision = {"machine1": {"kickstart_file_name": "TestKickstartAutomatedRAID-testRAID1.ks","payload_type": "dnf","memory_mb": INSTALLER_VM_MEMORY_MB,"pause_at_summary": True,},}def setUp(self):super().setUp()s = Storage(self.browser, self.machine)s.dbus_reset_selected_disks()s.dbus_scan_devices()@disk_images([("", 15), ("", 15)])def testRAID1(self):"""Description:Boot with ``inst.ks=TestKickstartAutomatedRAID-testRAID1.ks`` on two disksExpected results:- Automated install pauses on review without bootloader warnings;/boot on vda, RAID1 swap, and RAID0 /."""b = self.browserr = Review(b, self.machine)b.open("/cockpit/@localhost/anaconda-webui/index.html")r.check_disk_row("vda", "/boot", "vda1", "1.15 GB", True, "xfs")r.check_disk_row("vda", "/", "vda3, vdb3, RAID", "27.7 GB", True, "xfs")if __name__ == "__main__":test_main()