From 859ec17a22f39a55c7795aa6a00597e49336cb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ka=C5=88kovsk=C3=BD?= Date: Thu, 16 Nov 2023 10:58:13 +0100 Subject: [PATCH] Creating categories dbus API for installation phases --- pyanaconda/core/constants.py | 9 +++ pyanaconda/installation.py | 64 +++++++++++++------ pyanaconda/installation_tasks.py | 17 ++++- .../installation_category_interface.py | 44 +++++++++++++ .../modules/common/constants/interfaces.py | 5 ++ pyanaconda/modules/common/task/progress.py | 20 ++++++ 6 files changed, 138 insertions(+), 21 deletions(-) create mode 100644 pyanaconda/modules/boss/install_manager/installation_category_interface.py diff --git a/pyanaconda/core/constants.py b/pyanaconda/core/constants.py index 40e9adf171ba..9ba9f3e5259f 100644 --- a/pyanaconda/core/constants.py +++ b/pyanaconda/core/constants.py @@ -506,3 +506,12 @@ class DisplayModes(Enum): # FIPS mode minimum LUKS passphrase length FIPS_PASSPHRASE_MIN_LENGTH = 8 + + +# Installation categories +CATEGORY_UNDEFINED = "UNDEFINED" +CATEGORY_ENVIRONMENT = "ENVIRONMENT_CONFIGURATION" +CATEGORY_STORAGE = "STORAGE_CONFIGURATION" +CATEGORY_SOFTWARE = "SOFTWARE_INSTALLATION" +CATEGORY_BOOTLOADER = "BOOTLOADER_INSTALLATION" +CATEGORY_SYSTEM = "SYSTEM_CONFIGURATION" diff --git a/pyanaconda/installation.py b/pyanaconda/installation.py index 117301839593..24750250d47b 100644 --- a/pyanaconda/installation.py +++ b/pyanaconda/installation.py @@ -19,7 +19,9 @@ # from pyanaconda.core.dbus import DBus from pyanaconda.core.configuration.anaconda import conf -from pyanaconda.core.constants import PAYLOAD_LIVE_TYPES, PAYLOAD_TYPE_DNF +from pyanaconda.core.constants import PAYLOAD_LIVE_TYPES, PAYLOAD_TYPE_DNF, CATEGORY_SYSTEM, CATEGORY_BOOTLOADER, \ + CATEGORY_ENVIRONMENT, CATEGORY_STORAGE, CATEGORY_SOFTWARE +from pyanaconda.modules.boss.install_manager.installation_category_interface import CategoryReportTaskInterface from pyanaconda.modules.common.constants.objects import BOOTLOADER, SNAPSHOT, FIREWALL from pyanaconda.modules.common.constants.services import STORAGE, USERS, SERVICES, NETWORK, \ SECURITY, LOCALIZATION, TIMEZONE, BOSS, SUBSCRIPTION @@ -50,7 +52,6 @@ def _writeKS(ksdata): f.write("# Generated by Anaconda {}\n".format(util.get_anaconda_version_string())) f.write(str(ksdata)) - class RunInstallationTask(InstallationTask): """Task to run the installation queue.""" @@ -82,8 +83,13 @@ def run(self): ksdata=self._ksdata, ) + def for_publication(self): + """Return a DBus representation.""" + return CategoryReportTaskInterface(self) + def _queue_started_cb(self, task): """The installation queue was started.""" + self.report_category(task.task_category) self.report_progress(task.status_message) def _task_completed_cb(self, task): @@ -107,7 +113,8 @@ def _prepare_configuration(self, payload, ksdata): # we only run the tasks if the Subscription module is available subscription_config = TaskQueue( "Subscription configuration", - _("Configuring Red Hat subscription") + _("Configuring Red Hat subscription"), + CATEGORY_SYSTEM ) subscription_proxy = SUBSCRIPTION.get_proxy() subscription_dbus_tasks = subscription_proxy.InstallWithTasks() @@ -117,7 +124,8 @@ def _prepare_configuration(self, payload, ksdata): # schedule the execute methods of ksdata that require an installed system to be present os_config = TaskQueue( "Installed system configuration", - _("Configuring installed system") + _("Configuring installed system"), + CATEGORY_SYSTEM ) # add installation tasks for the Security DBus module @@ -158,7 +166,8 @@ def _prepare_configuration(self, payload, ksdata): overwrite = payload.type in PAYLOAD_LIVE_TYPES network_config = TaskQueue( "Network configuration", - _("Writing network configuration") + _("Writing network configuration"), + CATEGORY_SYSTEM ) network_config.append(Task( "Network configuration", @@ -171,7 +180,8 @@ def _prepare_configuration(self, payload, ksdata): if is_module_available(USERS): user_config = TaskQueue( "User creation", - _("Creating users") + _("Creating users"), + CATEGORY_SYSTEM ) users_proxy = USERS.get_proxy() users_dbus_tasks = users_proxy.InstallWithTasks() @@ -181,7 +191,8 @@ def _prepare_configuration(self, payload, ksdata): # Anaconda addon configuration addon_config = TaskQueue( "Anaconda addon configuration", - _("Configuring addons") + _("Configuring addons"), + CATEGORY_SYSTEM ) boss_proxy = BOSS.get_proxy() @@ -194,7 +205,8 @@ def _prepare_configuration(self, payload, ksdata): # Initramfs generation generate_initramfs = TaskQueue( "Initramfs generation", - _("Generating initramfs") + _("Generating initramfs"), + CATEGORY_BOOTLOADER ) bootloader_proxy = STORAGE.get_proxy(BOOTLOADER) @@ -236,7 +248,8 @@ def run_generate_initramfs(): if flags.flags.kexec: kexec_setup = TaskQueue( "Kexec setup", - _("Setting up kexec") + _("Setting up kexec"), + CATEGORY_BOOTLOADER ) kexec_setup.append(Task( "Setup kexec", @@ -247,7 +260,8 @@ def run_generate_initramfs(): # write anaconda related configs & kickstarts write_configs = TaskQueue( "Write configs and kickstarts", - _("Storing configuration files and kickstarts") + _("Storing configuration files and kickstarts"), + CATEGORY_SYSTEM ) # Write the kickstart file to the installed system (or, copy the input @@ -267,7 +281,8 @@ def run_generate_initramfs(): post_scripts = TaskQueue( "Post installation scripts", - _("Running post-installation scripts") + _("Running post-installation scripts"), + CATEGORY_SYSTEM ) post_scripts.append(Task( "Run post installation scripts", @@ -315,7 +330,8 @@ def _prepare_installation(self, payload, ksdata): # setup the installation environment setup_environment = TaskQueue( "Installation environment setup", - _("Setting up the installation environment") + _("Setting up the installation environment"), + CATEGORY_ENVIRONMENT ) boss_proxy = BOSS.get_proxy() @@ -341,7 +357,8 @@ def _prepare_installation(self, payload, ksdata): storage_proxy = STORAGE.get_proxy() early_storage = TaskQueue( "Early storage configuration", - _("Configuring storage") + _("Configuring storage"), + CATEGORY_STORAGE ) early_storage.append_dbus_tasks(STORAGE, storage_proxy.InstallWithTasks()) @@ -354,7 +371,8 @@ def _prepare_installation(self, payload, ksdata): # Run %pre-install scripts with the filesystem mounted and no packages pre_install_scripts = TaskQueue( "Pre-install scripts", - _("Running pre-installation scripts") + _("Running pre-installation scripts"), + CATEGORY_ENVIRONMENT ) pre_install_scripts.append(Task( "Run %pre-install scripts", @@ -367,7 +385,8 @@ def _prepare_installation(self, payload, ksdata): # - check for possibly needed additional packages. pre_install = TaskQueue( "Pre install tasks", - _("Running pre-installation tasks") + _("Running pre-installation tasks"), + CATEGORY_SOFTWARE ) if is_module_available(SECURITY): @@ -391,7 +410,8 @@ def _prepare_installation(self, payload, ksdata): payload_install = TaskQueue( "Payload installation", - _("Installing the software") + _("Installing the software"), + CATEGORY_SOFTWARE ) payload_install.append(Task( "Install the payload", @@ -403,7 +423,8 @@ def _prepare_installation(self, payload, ksdata): if payload.type != PAYLOAD_TYPE_DNF: late_storage = TaskQueue( "Late storage configuration", - _("Configuring storage") + _("Configuring storage"), + CATEGORY_STORAGE, ) conf_task = storage_proxy.WriteConfigurationWithTask() late_storage.append_dbus_tasks(STORAGE, [conf_task]) @@ -413,7 +434,8 @@ def _prepare_installation(self, payload, ksdata): bootloader_proxy = STORAGE.get_proxy(BOOTLOADER) bootloader_install = TaskQueue( "Bootloader installation", - _("Installing boot loader") + _("Installing boot loader"), + CATEGORY_BOOTLOADER ) def run_configure_bootloader(): @@ -446,7 +468,8 @@ def run_install_bootloader(): post_install = TaskQueue( "Post-installation setup tasks", - _("Performing post-installation setup tasks") + _("Performing post-installation setup tasks"), + CATEGORY_SYSTEM ) post_install.append(Task( "Run post-installation setup tasks", @@ -460,7 +483,8 @@ def run_install_bootloader(): if snapshot_proxy.IsRequested(SNAPSHOT_WHEN_POST_INSTALL): snapshot_creation = TaskQueue( "Creating post installation snapshots", - _("Creating snapshots") + _("Creating snapshots"), + CATEGORY_STORAGE ) snapshot_task = snapshot_proxy.CreateWithTask(SNAPSHOT_WHEN_POST_INSTALL) snapshot_creation.append_dbus_tasks(STORAGE, [snapshot_task]) diff --git a/pyanaconda/installation_tasks.py b/pyanaconda/installation_tasks.py index 36bb2fbafee3..affb3e3ca943 100644 --- a/pyanaconda/installation_tasks.py +++ b/pyanaconda/installation_tasks.py @@ -91,8 +91,9 @@ class TaskQueue(BaseTask): TaskQueues and Tasks can be mixed in a single TaskQueue. """ - def __init__(self, name, status_message=None): + def __init__(self, name, status_message=None, task_category=None): super().__init__(name) + self._task_category = task_category self._status_message = status_message # the list backing this TaskQueue instance self._queue = [] @@ -103,6 +104,20 @@ def __init__(self, name, status_message=None): self.task_started = Signal() self.task_completed = Signal() + @property + def task_category(self): + """A category describing the Queue is trying to achieve. + + Eq. "Converting all foo into bar." + + The current main usecase is to set the ProgressHub status message when + a TaskQueue is started. + + :returns: a task category + :rtype: str + """ + return self._task_category + @property def status_message(self): """A status message describing the Queue is trying to achieve. diff --git a/pyanaconda/modules/boss/install_manager/installation_category_interface.py b/pyanaconda/modules/boss/install_manager/installation_category_interface.py new file mode 100644 index 000000000000..6bb305ecc83b --- /dev/null +++ b/pyanaconda/modules/boss/install_manager/installation_category_interface.py @@ -0,0 +1,44 @@ +# DBus installation task category interface. +# +# API specification of task category interface. +# +# Copyright (C) 2017 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +from dasbus.server.interface import dbus_interface, dbus_signal +from pyanaconda.modules.common.constants.interfaces import TASK_CATEGORY +from dasbus.typing import * # pylint: disable=wildcard-import +from pyanaconda.modules.common.task import TaskInterface + +__all__ = ['CategoryReportTaskInterface'] + + +@dbus_interface(TASK_CATEGORY.interface_name) +class CategoryReportTaskInterface(TaskInterface): + "DBus interface for a task category report" + + def connect_signals(self): + super().connect_signals() + self.implementation.category_changed_signal.connect(self.CategoryChanged) + + @dbus_signal + def CategoryChanged(self, category: Str): + """Signal making progress for this task. + + :param category: Number of the category. See pyanaconda/core/constants.py + InstallationCategories for info about a category indexes. + """ + pass diff --git a/pyanaconda/modules/common/constants/interfaces.py b/pyanaconda/modules/common/constants/interfaces.py index 1b544826722f..a143b0459c4e 100644 --- a/pyanaconda/modules/common/constants/interfaces.py +++ b/pyanaconda/modules/common/constants/interfaces.py @@ -35,6 +35,11 @@ basename="Task" ) +TASK_CATEGORY = DBusInterfaceIdentifier( + namespace=ANACONDA_NAMESPACE, + basename="TaskCategory" +) + DEVICE_TREE_VIEWER = DBusInterfaceIdentifier( namespace=DEVICE_TREE_NAMESPACE, basename="Viewer" diff --git a/pyanaconda/modules/common/task/progress.py b/pyanaconda/modules/common/task/progress.py index 428678c80d54..d16161908b1c 100644 --- a/pyanaconda/modules/common/task/progress.py +++ b/pyanaconda/modules/common/task/progress.py @@ -30,9 +30,11 @@ class ProgressReporter(ABC): def __init__(self): super().__init__() self._progress_changed_signal = Signal() + self._category_changed_signal = Signal() self.__progress_lock = Lock() self.__progress_step = 0 + self.__progress_category = None self.__progress_msg = "" @property @@ -55,6 +57,24 @@ def progress_changed_signal(self): """Signal emits when the progress of the task changes.""" return self._progress_changed_signal + @property + def category_changed_signal(self): + """Signal emits when the category of the task changes.""" + return self._category_changed_signal + + @async_action_nowait + def report_category(self, category): + current_category = self.__progress_category + if category is None: + return + else: + category_value = category.value + if current_category is None: + self.__progress_category = category_value + current_category = category_value + self._category_changed_signal.emit(current_category) + + @async_action_nowait def report_progress(self, message, step_number=None, step_size=None): """Report a progress change.