Chapter 4. Developing installer add-ons

This section provides details about Anaconda and it’s architecture, and how to develop your own add-ons. The details about Anaconda and its architecture helps you to understand Anaconda backend and various plug points for the add-ons to work. It also helps to accordingly develop the add-ons.

4.1. Introduction to Anaconda and add-ons

Anaconda is the operating system installer used in Fedora, Red Hat Enterprise Linux, and their derivatives. It is a set of Python modules and scripts together with some additional files like Gtk widgets (written in C), systemd units, and dracut libraries. Together, they form a tool that allows users to set parameters of the resulting (target) system and then set up this system on a machine. The installation process has four major steps:

  1. Prepare installation destination (usually disk partitioning)
  2. Install package and data
  3. Install and configure boot loader
  4. Configure newly installed system

Using Anaconda enables you to install Fedora, Red Hat Enterprise Linux, and their derivatives, in the following three ways:

Using graphical user interface (GUI):

This is the most common installation method. The interface allows users to install the system interactively with little or no configuration required before starting the installation. This method covers all common use cases, including setting up complicated partitioning layouts.

The graphical interface supports remote access over VNC, which allows you to use the GUI even on systems with no graphics cards or attached monitor.

Using text user interface (TUI):

The TUI works similar to a monochrome line printer, which allows it to work on serial consoles that do not support cursor movement, colors and other advanced features. The text mode is limited and allows you to customize only the most common options, such as network settings, language options or installation (package) source; advanced features such as manual partitioning are not available in this interface.

Using Kickstart file:

A Kickstart file is a plain text file with shell-like syntax that can contain data to drive the installation process. A Kickstart file allows you to partially or completely automate the installation. A set of commands which configures all required areas is necessary to completely automate the installation. If one or more commands are missed, the installation requires interaction.

Apart from automation of the installer itself, Kickstart files can contain custom scripts that are run at specific moments during the installation process.

4.2. Anaconda Architecture

Anaconda is a set of Python modules and scripts. It also uses several external packages and libraries. The major components of this toolset include the following packages:

  • pykickstart - parses and validates the Kickstart files. Also, provides data structure that stores values that drives the installation.
  • yum - the package manager that installs packages and resolves dependencies
  • blivet - handles all activities related to storage management
  • pyanaconda - contains the user interface and modules for Anaconda, such as keyboard and timezone selection, network configuration, and user creation. Also provides various utilities to perform system-oriented functions
  • python-meh - contains an exception handler that gathers and stores additional system information in case of a crash and passes this information to the libreport library, which itself is a part of the ABRT Project
  • dasbus - enables communication between the D-Bus library with modules of anaconda and with external components
  • python-simpleline - text UI framework library to manage user interaction in the Anaconda text mode
  • gtk - the Gnome toolkit library for creating and managing GUI

Apart from the division into packages previously mentioned, Anaconda is internally divided into the user interface and a set of modules that run as separate processes and communicate using the D-Bus library. These modules are:

  • Boss - manages the internal module discovery, lifecycle, and coordination
  • Localization - manages locales
  • Network - handles network
  • Payloads - handles data for installation in different formats, such as rpm, ostree, tar and other installation formats. Payloads manage the sources of data for installation; sources can vary in format such as CD-ROM, HDD, NFS, URLs, and other sources
  • Security - manages security related aspects
  • Services - handles services
  • Storage - manages storage using blivet
  • Subscription - handles the subscription-manager tool and Insights.
  • Timezone - deals with time, date, zones, and time synchronization.
  • Users - creates users and groups.

Each module declares which parts of Kickstart it handles, and has methods to apply the configuration from Kickstart to the installation environment and to the installed system.

The Python code portion of Anaconda (pyanaconda) starts as a “main” process that owns the user interface. Any Kickstart data you provide are parsed using the pykickstart module and the Boss module is started, it discovers all other modules, and starts them. Main process then sends Kickstart data to the modules according to their declared capabilities. Modules process the data, apply the configuration to the installation environment, and the UI validates if all required choices have been made. If not, you must supply the data in an interactive installation mode. Once all required choices have been made, the installation can start - the modules write data to the installed system.

4.3. Anaconda User Interface

The Anaconda user interface (UI) has a non-linear structure, also known as hub and spoke model.

The advantages of Anaconda hub and spoke model are:

  • Flexibility to follow the installer screens.
  • Flexibility to retain the default settings.
  • Provides an overview of the configured values.
  • Supports extensibility. You can add hubs without the need to reorder anything and can resolve some complex ordering dependencies.
  • Supports installation in graphical and text mode.

The following diagram shows the installer layout and the possible interactions between hubs and spokes (screens):

Figure 4.1. Hub and spoke model

hub and spoke

In the diagram, screens 2-13 are called normal spokes, and screens 1 and 14 are standalone spokes. Standalone spokes are the screens that can be used before or after the standalone spoke or hub. For example, the Welcome screen at the beginning of the installation which prompts you to choose your language for the rest of the installation.

Note
  • The Installation Summary is the only hub in Anaconda. It shows a summary of configured options before the installation begins

Each spoke has the following predefined properties that reflect the hub.

  • ready - states whether or not you can visit a spoke. For example, when the installer is configuring a package source, the spoke is colored in gray, and you cannot access it until the configuration is complete.
  • completed - marks whether or not is the spoke complete (all required values are set).
  • mandatory - determines whether you must visit the spoke before continuing the installation; for example, you must visit the Installation Destination spoke, even if you want to use automatic disk partitioning
  • status - provides a short summary of values configured within the spoke (displayed under the spoke name in the hub)

To make the user interface clearer, spokes are grouped together into categories. For example, the Localization category groups together spokes for keyboard layout selection, language support and time zone settings.

Each spoke contains UI controls which display and allow you to modify values from one or more modules. The same applies to spokes that add-ons provide.

4.4. Communication across Anaconda threads

Some of the actions that you need to perform during the installation process may take a long time. For example, scanning disks for existing partitions or downloading package metadata. To prevent you from waiting and remaining responsive, Anaconda runs these actions in separate threads.

The Gtk toolkit does not support element changes from multiple threads. The main event loop of Gtk runs in the main thread of the Anaconda process. Therefore, all actions pertaining to the GUI must be performed in the main thread. To do so, use GLib.idle_add, which is not always easy or desired. Several helper functions and decorators that are defined in the pyanaconda.ui.gui.utils module may add to the difficulty.

The @gtk_action_wait and @gtk_action_nowait decorators change the decorated function or method in such a way that when this function or method is called, it is automatically queued into Gtk’s main loop that runs in the main thread. The return value is either returned to the caller or dropped, respectively.

In a spoke and hub communication, a spoke announces when it is ready and is not blocked. The hubQ message queue handles this function, and periodically checks the main event loop. When a spoke becomes accessible, it sends a message to the queue announcing the change and that it should no longer be blocked.

The same applies in a situation where a spoke needs to refresh its status or complete a flag. The Configuration and Progress hub has a different queue called progressQ which serves as a medium to transfer installation progress updates.

These mechanisms are also used for the text-based interface. In the text mode, there is no main loop, but the keyboard input takes most of the time.

4.5. Anaconda Modules and D-Bus library

Anaconda’s modules run as independent processes. To communicate with these processes via their D-Bus API, use the dasbus library.

Calls to methods via D-Bus`API are asynchronous, but with the `dasbus library you can convert them to synchronous method calls in Python. You can also write either of the following programs:

  • program with asynchronous calls and return handlers
  • A program with synchronous calls that makes the caller wait until the call is complete.

For more information about threads and communication, see Section 4.4, “Communication across Anaconda threads”.

Additionally, Anaconda uses Task objects running in modules. Tasks have a D-Bus API and methods that are automatically executed in additional threads. To successfully run the tasks, use the sync_run_task and async_run_task helper functions.

4.6. The Hello World addon example

Anaconda developers publish an example addon called “Hello World”, available on GitHub: https://github.com/rhinstaller/hello-world-anaconda-addon/ The descriptions in further sections are reproduced in this.

4.7. Anaconda add-on structure

An Anaconda add-on is a Python package that contains a directory with an __init__.py and other source directories (subpackages). Because Python allows you to import each package name only once, specify a unique name for the package top-level directory. You can use an arbitrary name, because add-ons are loaded regardless of their name - the only requirement is that they must be placed in a specific directory.

The suggested naming convention for add-ons is similar to Java packages or D-Bus service names.

To make the directory name a unique identifier for a Python package, prefix the add-on name with the reversed domain name of your organization, using underscores (_) instead of dots. For example, com_example_hello_world.

Important

Make sure to create an __init__.py file in each directory. Directories missing this file are considered as invalid Python packages.

When writing an add-on, ensure the following:

  • Support for each interface (graphical interface and text interface) is available in a separate subpackage and these subpackages are named gui for the graphical interface and tui for the text-based interface.
  • The gui and tui packages contain a spokes subpackage. [1]
  • Modules contained in the packages have an arbitrary name.
  • The gui/ and tui/ directories contain Python modules with any name.
  • There is a service that performs the actual work of the addon. This service can be written in Python or any other language.
  • The service implements support for D-Bus and Kickstart.
  • The addon contains files that enable automatic startup of the service.

Following is a sample directory structure for an add-on which supports every interface (Kickstart, GUI and TUI):

Example 4.1. Sample Add-on Structure

com_example_hello_world
├─ gui
│  ├─ init.py
│  └─ spokes
│     └─ init.py
└─ tui
   ├─ init.py
   └─ spokes
   └─ init.py

Each package must contain at least one module with an arbitrary name defining the classes that are inherited from one or more classes defined in the API.

Note

For all add-ons, follow Python’s PEP 8 and PEP 257 guidelines for docstring conventions. There is no consensus on the format of the actual content of docstrings in Anaconda; the only requirement is that they are human-readable. If you plan to use auto-generated documentation for your add-on, docstrings should follow the guidelines for the toolkit you use to accomplish this.

You can include a category subpackage if an add-on needs to define a new category, but this is not recommended.

4.8. Anaconda services and configuration files

Anaconda services and configuration files are included in data/ directory. These files are required to start the add-ons service and to configure D-Bus.

Following are some examples of Anaconda Hello World add-on:

Example 4.2. Example of addon-name.conf:

<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
       <policy user="root">
               <allow own="org.fedoraproject.Anaconda.Addons.HelloWorld"/>
               <allow send_destination="org.fedoraproject.Anaconda.Addons.HelloWorld"/>
       </policy>
       <policy context="default">
               <deny own="org.fedoraproject.Anaconda.Addons.HelloWorld"/>
               <allow send_destination="org.fedoraproject.Anaconda.Addons.HelloWorld"/>
       </policy>
</busconfig>

This file must be placed in the /usr/share/anaconda/dbus/confs/ directory in the installation environment. The string org.fedoraproject.Anaconda.Addons.HelloWorld must correspond to the location of addon’s service on D-Bus.

Example 4.3. Example of addon-name.service:

[D-BUS Service]
# Start the org.fedoraproject.Anaconda.Addons.HelloWorld service.
# Runs org_fedora_hello_world/service/main.py
Name=org.fedoraproject.Anaconda.Addons.HelloWorld
Exec=/usr/libexec/anaconda/start-module org_fedora_hello_world.service
User=root

This file must be placed in the /usr/share/anaconda/dbus/services/ directory in the installation environment. The string org.fedoraproject.Anaconda.Addons.HelloWorld must correspond to the location of addon’s service on D-Bus. The value on the line starting with Exec= must be a valid command that starts the service in the installation environment.

4.9. Deploying and testing an Anaconda add-on

You can deploy and test your own Anaconda add-on into the installation environment. To do so, follow the steps:

Prerequisites

  • You created an Add-on.
  • You have access to your D-Bus files.

Procedure

  1. Create a directory DIR at the place of your preference.
  2. Add the Add-on python files into DIR/usr/share/anaconda/addons/.
  3. Copy your D-Bus service file into DIR/usr/share/anaconda/dbus/services/.
  4. Copy your D-Bus service configuration file to /usr/share/anaconda/dbus/confs/.
  5. Create the updates image.

    Access the DIR directory:

    cd DIR

    Locate the updates image.

    find . | cpio -c -o | pigz -9cv > DIR/updates.img
  6. Extract the contents of the ISO boot image.
  7. Use the resulting updates image:

    1. Add the updates.img file into the images directory of your unpacked ISO contents.
    2. Repack the image.
    3. Set up a web server to provide the updates.img file to the Anaconda installer via HTTP.
    4. Load updates.img file at boot time by adding the following specification to the boot options.

      inst.updates=http://your-server/whatever/updates.img to boot options.

For specific instructions on unpacking an existing boot image, creating a product.img file and repackaging the image, see Section 1.2.3, “Extracting Red Hat Enterprise Linux boot images”.



[1] The gui package may also contain a categories subpackage if the add-on needs to define a new category, but this is not recommended.