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:
- Prepare installation destination (usually disk partitioning)
- Install package and data
- Install and configure boot loader
- 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
libreportlibrary, which itself is a part of the ABRT Project
dasbus- enables communication between the
D-Buslibrary 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
tarand 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
Subscription- handles the
subscription-managertool 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
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.
Installation Summaryis 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 Destinationspoke, 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.
@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
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
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,
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
guifor the graphical interface and
tuifor the text-based interface.
tuipackages contain a
- Modules contained in the packages have an arbitrary name.
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.
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:
- You created an Add-on.
You have access to your
Create a directory
DIRat the place of your preference.
Add-onpython files into
D-Busservice file into
D-Busservice configuration file to
Create the updates image.
Locate the updates image.
find . | cpio -c -o | pigz -9cv > DIR/updates.img
- Extract the contents of the ISO boot image.
Use the resulting
updates.imgfile into the images directory of your unpacked ISO contents.
- Repack the image.
Set up a web server to provide the
updates.img file to the Anaconda installer via HTTP.
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.
- Add the
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”.