Skip to content

Commit

Permalink
Creating categories dbus API for installation phases
Browse files Browse the repository at this point in the history
  • Loading branch information
adamkankovsky committed Nov 16, 2023
1 parent bed2e8b commit cf30d38
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 40 deletions.
8 changes: 8 additions & 0 deletions pyanaconda/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,11 @@ class DisplayModes(Enum):

# FIPS mode minimum LUKS passphrase length
FIPS_PASSPHRASE_MIN_LENGTH = 8


# Installation categories
class InstallationCategories(Enum):
STORAGE = 0
PAYLOAD = 1
BOOTLOADER = 2
CONFIGURATION = 3
63 changes: 43 additions & 20 deletions pyanaconda/installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
#
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, InstallationCategories
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
from pyanaconda.modules.common.task import sync_run_task, Task as InstallationTask
from pyanaconda.modules.common.task.task_interface import CategoryReportTaskInterface
from pyanaconda.modules.common.util import is_module_available
from pyanaconda import flags
from pyanaconda.core import util
Expand All @@ -50,7 +51,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."""

Expand Down Expand Up @@ -82,8 +82,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):
Expand All @@ -107,7 +112,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"),
InstallationCategories.CONFIGURATION
)
subscription_proxy = SUBSCRIPTION.get_proxy()
subscription_dbus_tasks = subscription_proxy.InstallWithTasks()
Expand All @@ -117,7 +123,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"),
InstallationCategories.CONFIGURATION
)

# add installation tasks for the Security DBus module
Expand Down Expand Up @@ -158,7 +165,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"),
InstallationCategories.CONFIGURATION
)
network_config.append(Task(
"Network configuration",
Expand All @@ -171,7 +179,8 @@ def _prepare_configuration(self, payload, ksdata):
if is_module_available(USERS):
user_config = TaskQueue(
"User creation",
_("Creating users")
_("Creating users"),
InstallationCategories.CONFIGURATION
)
users_proxy = USERS.get_proxy()
users_dbus_tasks = users_proxy.InstallWithTasks()
Expand All @@ -181,7 +190,8 @@ def _prepare_configuration(self, payload, ksdata):
# Anaconda addon configuration
addon_config = TaskQueue(
"Anaconda addon configuration",
_("Configuring addons")
_("Configuring addons"),
InstallationCategories.CONFIGURATION
)

boss_proxy = BOSS.get_proxy()
Expand All @@ -194,7 +204,8 @@ def _prepare_configuration(self, payload, ksdata):
# Initramfs generation
generate_initramfs = TaskQueue(
"Initramfs generation",
_("Generating initramfs")
_("Generating initramfs"),
InstallationCategories.BOOTLOADER
)
bootloader_proxy = STORAGE.get_proxy(BOOTLOADER)

Expand Down Expand Up @@ -236,7 +247,8 @@ def run_generate_initramfs():
if flags.flags.kexec:
kexec_setup = TaskQueue(
"Kexec setup",
_("Setting up kexec")
_("Setting up kexec"),
InstallationCategories.CONFIGURATION
)
kexec_setup.append(Task(
"Setup kexec",
Expand All @@ -247,7 +259,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"),
InstallationCategories.CONFIGURATION
)

# Write the kickstart file to the installed system (or, copy the input
Expand All @@ -267,7 +280,8 @@ def run_generate_initramfs():

post_scripts = TaskQueue(
"Post installation scripts",
_("Running post-installation scripts")
_("Running post-installation scripts"),
InstallationCategories.CONFIGURATION
)
post_scripts.append(Task(
"Run post installation scripts",
Expand Down Expand Up @@ -315,7 +329,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"),
InstallationCategories.STORAGE
)

boss_proxy = BOSS.get_proxy()
Expand All @@ -341,7 +356,8 @@ def _prepare_installation(self, payload, ksdata):
storage_proxy = STORAGE.get_proxy()
early_storage = TaskQueue(
"Early storage configuration",
_("Configuring storage")
_("Configuring storage"),
InstallationCategories.STORAGE
)
early_storage.append_dbus_tasks(STORAGE, storage_proxy.InstallWithTasks())

Expand All @@ -354,7 +370,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"),
InstallationCategories.STORAGE
)
pre_install_scripts.append(Task(
"Run %pre-install scripts",
Expand All @@ -367,7 +384,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"),
InstallationCategories.STORAGE
)

if is_module_available(SECURITY):
Expand All @@ -391,7 +409,8 @@ def _prepare_installation(self, payload, ksdata):

payload_install = TaskQueue(
"Payload installation",
_("Installing the software")
_("Installing the software"),
InstallationCategories.PAYLOAD
)
payload_install.append(Task(
"Install the payload",
Expand All @@ -403,7 +422,8 @@ def _prepare_installation(self, payload, ksdata):
if payload.type != PAYLOAD_TYPE_DNF:
late_storage = TaskQueue(
"Late storage configuration",
_("Configuring storage")
_("Configuring storage"),
InstallationCategories.STORAGE,
)
conf_task = storage_proxy.WriteConfigurationWithTask()
late_storage.append_dbus_tasks(STORAGE, [conf_task])
Expand All @@ -413,7 +433,8 @@ def _prepare_installation(self, payload, ksdata):
bootloader_proxy = STORAGE.get_proxy(BOOTLOADER)
bootloader_install = TaskQueue(
"Bootloader installation",
_("Installing boot loader")
_("Installing boot loader"),
InstallationCategories.BOOTLOADER
)

def run_configure_bootloader():
Expand Down Expand Up @@ -446,7 +467,8 @@ def run_install_bootloader():

post_install = TaskQueue(
"Post-installation setup tasks",
_("Performing post-installation setup tasks")
_("Performing post-installation setup tasks"),
InstallationCategories.CONFIGURATION
)
post_install.append(Task(
"Run post-installation setup tasks",
Expand All @@ -460,7 +482,8 @@ def run_install_bootloader():
if snapshot_proxy.IsRequested(SNAPSHOT_WHEN_POST_INSTALL):
snapshot_creation = TaskQueue(
"Creating post installation snapshots",
_("Creating snapshots")
_("Creating snapshots"),
InstallationCategories.PAYLOAD
)
snapshot_task = snapshot_proxy.CreateWithTask(SNAPSHOT_WHEN_POST_INSTALL)
snapshot_creation.append_dbus_tasks(STORAGE, [snapshot_task])
Expand Down
17 changes: 16 additions & 1 deletion pyanaconda/installation_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand All @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions pyanaconda/modules/common/constants/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
basename="Task"
)

TASK_CATEGORY = DBusInterfaceIdentifier(
namespace=ANACONDA_NAMESPACE,
basename="TaskCategory"
)

DEVICE_TREE_VIEWER = DBusInterfaceIdentifier(
namespace=DEVICE_TREE_NAMESPACE,
basename="Viewer"
Expand Down
20 changes: 20 additions & 0 deletions pyanaconda/modules/common/task/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 or current_category < category_value:
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.
Expand Down
22 changes: 20 additions & 2 deletions pyanaconda/modules/common/task/task_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
# Red Hat, Inc.
#
from dasbus.server.interface import dbus_interface, dbus_signal, dbus_class
from pyanaconda.modules.common.constants.interfaces import TASK
from pyanaconda.modules.common.constants.interfaces import TASK, TASK_CATEGORY
from pyanaconda.modules.common.base.base_template import InterfaceTemplate
from dasbus.typing import * # pylint: disable=wildcard-import
from pyanaconda.modules.common.errors.task import NoResultError
from pyanaconda.modules.common.structures.validation import ValidationReport

__all__ = ['TaskInterface', 'ValidationTaskInterface']
__all__ = ['TaskInterface', 'ValidationTaskInterface', 'CategoryReportTaskInterface']


@dbus_interface(TASK.interface_name)
Expand Down Expand Up @@ -149,3 +149,21 @@ def convert_result(value) -> Variant:
:return: a variant with the structure
"""
return get_variant(Structure, ValidationReport.to_structure(value))


@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: Int):
"""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
24 changes: 7 additions & 17 deletions ui/webui/src/components/installation/InstallationProgress.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export const InstallationProgress = ({ onCritFail }) => {
"org.fedoraproject.Anaconda.Task",
tasks[0]
);
const categoryProxy = new BossClient().client.proxy(
"org.fedoraproject.Anaconda.TaskCategory",
tasks[0]
);

const addEventListeners = () => {
taskProxy.addEventListener("ProgressChanged", (_, step, message) => {
Expand All @@ -69,23 +73,6 @@ export const InstallationProgress = ({ onCritFail }) => {
ret => setSteps(ret.v),
onCritFail()
);
// FIXME: hardcoded progress steps
// - if ProgressStepper turns out to be viable,
// use a proper DBus API for progress tep discovery
// and switching
//
// storage
} else if (step <= 3) {
setCurrentProgressStep(0);
// payload
} else if (step === 4) {
setCurrentProgressStep(1);
// configuration
} else if (step >= 5 && step <= 11) {
setCurrentProgressStep(2);
// bootloader
} else if (step >= 12) {
setCurrentProgressStep(3);
}
if (message) {
setStatusMessage(message);
Expand All @@ -104,6 +91,9 @@ export const InstallationProgress = ({ onCritFail }) => {
setStatus("success");
setCurrentProgressStep(4);
});
categoryProxy.addEventListener("CategoryChanged", (_, category) => {
setCurrentProgressStep(category);
});
};
taskProxy.wait(() => {
addEventListeners();
Expand Down

0 comments on commit cf30d38

Please sign in to comment.