Red Hat Enterprise Linux および Fedora インストールプログラムである Anaconda では、最新バージョンに多くの改良が追加されました。これらの改善の 1 つは、カスタマイズの可能性が強化されました。アドオンを作成して、ベースインストーラー機能を拡張し、グラフィカルユーザーインターフェイスの外観を変更できるようになりました。
このガイドで説明されている手順は、Red Hat Enterprise Linux 10 または同様のシステム用に記述されています。これ以外のシステムでは、(カスタム ISO イメージを作成する xorrisofs などの) 使用するツールやアプリケーションが異なるため、手順を調整する必要がある場合があります。
サポートステートメント
Red Hat は、Red Hat Enterprise Linux Image Builder を使用した Red Hat Enterprise Linux インストールメディアとイメージのカスタマイズのみサポートします。もしくは、キックスタートを使用してインフラストラクチャーに一貫性のあるシステムをデプロイすることもできます。
Red Hat Enterprise Linux のインストールメディアはカスタマイズが可能です。カスタマイズするには、Red Hat カスタマーポータルから RHEL 10 ブートイメージをダウンロードし、その内容を抽出して、独自のデプロイメントニーズに合わせて詳細な変更を加えてからブートイメージを再ビルドします。
ブートメニュー は、インストールイメージを使用してシステムを起動すると表示されるメニューです。通常、このメニューでは、Install Red Hat Enterprise Linux、Boot from local drive、または Rescue an installed system などのオプションを選択できます。
ブートメディアの boot/grub2/grub.cfg 設定ファイルには、事前設定されたメニューエントリーのリストと、外観とブートメニュー機能を制御する他のディレクティブが含まれます。設定ファイルでは、Red Hat Enterprise Linux のデフォルトメニューエントリー (Test this media & install Red Hat Enterprise Linux 10) が以下のブロックで定義されます。
menuentry 'Install Red Hat Enterprise Linux 10.0' --class fedora --class gnu-linux --class gnu --class os {
linux /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 quiet
initrd /images/pxeboot/initrd.img
}
menuentry 'Test this media & install Red Hat Enterprise Linux 10.0' --class fedora --class gnu-linux --class gnu --class os {
linux /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 rd.live.check quiet
initrd /images/pxeboot/initrd.img
}
menuentry 'Install Red Hat Enterprise Linux 10.0' --class fedora --class gnu-linux --class gnu --class os {
linux /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 quiet
initrd /images/pxeboot/initrd.img
}
menuentry 'Test this media & install Red Hat Enterprise Linux 10.0' --class fedora --class gnu-linux --class gnu --class os {
linux /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 rd.live.check quiet
initrd /images/pxeboot/initrd.img
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
ブートメディアの EFI/BOOT/grub.cfg 設定ファイルには、事前設定されたメニューエントリーのリストと、外観とブートメニュー機能を制御する他のディレクティブが含まれます。設定ファイルでは、Red Hat Enterprise Linux のデフォルトメニューエントリー (Test this media & install Red Hat Enterprise Linux 10) が以下のブロックで定義されます。
menuentry 'Test this media & install Red Hat Enterprise Linux 10.0' --class fedora --class gnu-linux --class gnu --class os {
linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 rd.live.check quiet
initrdefi /images/pxeboot/initrd.img
}
menuentry 'Test this media & install Red Hat Enterprise Linux 10.0' --class fedora --class gnu-linux --class gnu --class os {
linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 rd.live.check quiet
initrdefi /images/pxeboot/initrd.img
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
カスタムブランディング資料を作成するには、まずデフォルトのグラフィカル要素ファイルタイプと寸法を参照します。適切なカスタム資料を作成できます。デフォルトのグラフィカル要素の詳細は、Customizing graphical elements セクションに記載されているサンプルファイルを参照してください。
# Anaconda configuration file.
[Anaconda]
# Run Anaconda in the debugging mode.
debug = False
# List of Anaconda DBus modules that can be activated.
# Supported patterns: MODULE.PREFIX., MODULE.NAME activatable_modules = org.fedoraproject.Anaconda.Modules.
org.fedoraproject.Anaconda.Addons.*
# List of Anaconda DBus modules that are not allowed to run.
# Supported patterns: MODULE.PREFIX., MODULE.NAME forbidden_modules = # List of Anaconda DBus modules that can fail to run. # The installation won't be aborted because of them. # Supported patterns: MODULE.PREFIX., MODULE.NAME
optional_modules =
org.fedoraproject.Anaconda.Modules.Subscription
org.fedoraproject.Anaconda.Addons.*
[Installation System]
# Type of the installation system.
# FIXME: This is a temporary solution.
type = UNKNOWN
# Should the installer show a warning about enabled SMT?
can_detect_enabled_smt = False
[Installation Target]
# Type of the installation target.
type = HARDWARE
# A path to the physical root of the target.
physical_root = /mnt/sysimage
# A path to the system root of the target.
system_root = /mnt/sysroot
# Should we install the network configuration?
can_configure_network = True
# Should we copy input kickstart to target system?
can_copy_input_kickstart = True
# Should we save kickstart equivalent to installation settings to the new system?
can_save_output_kickstart = True
# Should we save logs from the installation to the new system?
can_save_installation_logs = True
[Network]
# Network device to be activated on boot if none was configured so.
# Valid values:
#
# NONE No device
# DEFAULT_ROUTE_DEVICE A default route device
# FIRST_WIRED_WITH_LINK The first wired device with link
#
default_on_boot = NONE
[Payload]
# Default package environment.
default_environment =
# List of ignored packages.
ignored_packages =
# Names of repositories that provide latest updates.
updates_repositories =
# Names of repositories disabled by default.
# Supported patterns: REPO-NAME, PREFIX*, SUFFIX, *INFIX
disabled_repositories =
source
debuginfo
updates-testing
updates-testing-modular
# List of .treeinfo variant types to enable.
# Valid items:
#
addon
optional
variant
#
enabled_repositories_from_treeinfo = addon optional variant
# Enable installation from the closest mirror.
enable_closest_mirror = True
# Default installation source.
# Valid values:
#
# CLOSEST_MIRROR Use closest public repository mirror.
# CDN Use Content Delivery Network (CDN).
#
default_source = CLOSEST_MIRROR
# Enable ssl verification for all HTTP connection
verify_ssl = True
# GPG keys to import to RPM database by default.
# Specify paths on the installed system, each on a line.
# Substitutions for $releasever and $basearch happen automatically.
default_rpm_gpg_keys =
[Security]
# Enable SELinux usage in the installed system.
# Valid values:
#
# -1 The value is not set.
# 0 SELinux is disabled.
# 1 SELinux is enabled.
#
selinux = -1
[Bootloader]
# Type of the bootloader.
# Supported values:
#
# DEFAULT Choose the type by platform.
# EXTLINUX Use extlinux as the bootloader.
# SDBOOT Use systemd-boot as the bootloader.
#
type = DEFAULT
# Name of the EFI directory.
efi_dir = default
# Hide the GRUB menu.
menu_auto_hide = False
# Are non-iBFT iSCSI disks allowed?
nonibft_iscsi_boot = False
# Arguments preserved from the installation system.
preserved_arguments =
cio_ignore zfcp.allow_lun_scan
speakup_synth apic noapic apm ide noht acpi video
pci nodmraid nompath nomodeset noiswmd fips selinux
biosdevname ipv6.disable net.ifnames net.ifnames.prefix
nosmt vga
[Storage]
# Enable iBFT usage during the installation.
ibft = True
# Tell multipathd to use user friendly names when naming devices during the installation.
multipath_friendly_names = True
# Create GPT discoverable partition type IDs, if possible
gpt_discoverable_partitions = True
# Do you want to allow imperfect devices (for example, degraded mdraid array devices)?
allow_imperfect_devices = False
# Btrfs compression algorithm and level. e.g. zstd:1
btrfs_compression =
# Default disk label type.
# Valid values:
#
# gpt Prefer creation of GPT disk labels.
# mbr Prefer creation of MBR disk labels.
#
# If not specified, use whatever Blivet uses by default.
#
disk_label_type =
# Default file system type. Use whatever Blivet uses by default.
file_system_type =
# Default partitioning.
# Specify a mount point and its attributes on each line.
#
# Valid attributes:
#
# size <SIZE> The size of the mount point.
# min <MIN_SIZE> The size will grow from MIN_SIZE to MAX_SIZE.
# max <MAX_SIZE> The max size is unlimited by default.
# free <SIZE> The required available space.
btrfs The mount point will be created only for the Btrfs scheme
#
default_partitioning =
/ (min 1 GiB, max 70 GiB)
/home (min 500 MiB, free 50 GiB)
# Default partitioning scheme.
# Valid values:
#
# PLAIN Create standard partitions.
# BTRFS Use the Btrfs scheme.
# LVM Use the LVM scheme.
# LVM_THINP Use LVM Thin Provisioning.
#
default_scheme = LVM
# Default version of LUKS.
# Valid values:
#
# luks1 Use version 1 by default.
# luks2 Use version 2 by default.
#
luks_version = luks2
[Storage Constraints]
# Minimal size of the total memory.
min_ram = 320 MiB
# Minimal size of the available memory for LUKS2.
luks2_min_ram = 128 MiB
# Should we recommend to specify a swap partition?
swap_is_recommended = False
# Recommended minimal sizes of partitions.
# Specify a mount point and a size on each line.
min_partition_sizes =
/ 250 MiB
/usr 250 MiB
/tmp 50 MiB
/var 384 MiB
/home 100 MiB
/boot 512 MiB
# Required minimal sizes of partitions.
# Specify a mount point and a size on each line.
req_partition_sizes =
# Allowed device types of the / partition if any.
# Valid values:
#
# LVM Allow LVM.
# MD Allow RAID.
# PARTITION Allow standard partitions.
# BTRFS Allow Btrfs.
# DISK Allow disks.
# LVM_THINP Allow LVM Thin Provisioning.
#
root_device_types =
# Mount points that must be on a linux file system.
# Specify a list of mount points.
must_be_on_linuxfs = / /var /tmp /usr /home /usr/share /usr/lib
# Paths that must be directories on the / file system.
# Specify a list of paths.
must_be_on_root = /bin /dev /sbin /etc /lib /root /mnt lost+found /proc
# Paths that must NOT be directories on the / file system.
# Specify a list of paths.
must_not_be_on_root =
# Mount points that are recommended to be reformatted.
#
# It will be recommended to create a new file system on a mount point
# that has an allowed prefix, but doesn't have a blocked one.
# Specify lists of mount points.
reformat_allowlist = /boot /var /tmp /usr
reformat_blocklist = /home /usr/local /opt /var/www
[User Interface]
# The path to a custom stylesheet.
custom_stylesheet =
# A list of spokes to hide in UI.
# FIXME: Use other identification then names of the spokes.
hidden_spokes =
# Should the UI allow to change the configured root account?
can_change_root = False
# Should the UI allow to change the configured user accounts?
can_change_users = False
# Define the default password policies.
# Specify a policy name and its attributes on each line.
#
# Valid attributes:
#
# quality <NUMBER> The minimum quality score (see libpwquality).
# length <NUMBER> The minimum length of the password.
# empty Allow an empty password.
# strict Require the minimum quality.
#
password_policies =
root (quality 1, length 6)
user (quality 1, length 6, empty)
luks (quality 1, length 6)
# Should kernel options be shown in the software selection spoke?
show_kernel_options = True
[License]
# A path to EULA (if any)
#
# If the given distribution has an EULA & feels the need to
tell the user about it fill in this variable by a path
# pointing to a file with the EULA on the installed system.
#
# This is currently used just to show the path to the file to
# the user at the end of the installation.
eula =
[Timezone]
# URL for geolocation data provider.
# This is used for automatic language and timezone detection.
#
# Known valid providers:
#
https://geoip.fedoraproject.org/city
https://api.hostip.info/get_json.php
#
# If left empty, geolocation does not run.
#
geolocation_provider = https://geoip.fedoraproject.org/city
[Localization]
# Should geolocation be used when setting the language ?
#
use_geolocation = True
# Anaconda configuration file.
[Anaconda]
# Run Anaconda in the debugging mode.
debug = False
# List of Anaconda DBus modules that can be activated.
# Supported patterns: MODULE.PREFIX., MODULE.NAME activatable_modules = org.fedoraproject.Anaconda.Modules.
org.fedoraproject.Anaconda.Addons.*
# List of Anaconda DBus modules that are not allowed to run.
# Supported patterns: MODULE.PREFIX., MODULE.NAME forbidden_modules = # List of Anaconda DBus modules that can fail to run. # The installation won't be aborted because of them. # Supported patterns: MODULE.PREFIX., MODULE.NAME
optional_modules =
org.fedoraproject.Anaconda.Modules.Subscription
org.fedoraproject.Anaconda.Addons.*
[Installation System]
# Type of the installation system.
# FIXME: This is a temporary solution.
type = UNKNOWN
# Should the installer show a warning about enabled SMT?
can_detect_enabled_smt = False
[Installation Target]
# Type of the installation target.
type = HARDWARE
# A path to the physical root of the target.
physical_root = /mnt/sysimage
# A path to the system root of the target.
system_root = /mnt/sysroot
# Should we install the network configuration?
can_configure_network = True
# Should we copy input kickstart to target system?
can_copy_input_kickstart = True
# Should we save kickstart equivalent to installation settings to the new system?
can_save_output_kickstart = True
# Should we save logs from the installation to the new system?
can_save_installation_logs = True
[Network]
# Network device to be activated on boot if none was configured so.
# Valid values:
#
# NONE No device
# DEFAULT_ROUTE_DEVICE A default route device
# FIRST_WIRED_WITH_LINK The first wired device with link
#
default_on_boot = NONE
[Payload]
# Default package environment.
default_environment =
# List of ignored packages.
ignored_packages =
# Names of repositories that provide latest updates.
updates_repositories =
# Names of repositories disabled by default.
# Supported patterns: REPO-NAME, PREFIX*, SUFFIX, *INFIX
disabled_repositories =
sourcedebuginfo
updates-testing
updates-testing-modular
# List of .treeinfo variant types to enable.
# Valid items:
#
# addon
# optional
# variant
#
enabled_repositories_from_treeinfo = addon optional variant
# Enable installation from the closest mirror.
enable_closest_mirror = True
# Default installation source.
# Valid values:
#
# CLOSEST_MIRROR Use closest public repository mirror.
# CDN Use Content Delivery Network (CDN).
#
default_source = CLOSEST_MIRROR
# Enable ssl verification for all HTTP connection
verify_ssl = True
# GPG keys to import to RPM database by default.
# Specify paths on the installed system, each on a line.
# Substitutions for $releasever and $basearch happen automatically.
default_rpm_gpg_keys =
[Security]
# Enable SELinux usage in the installed system.
# Valid values:
#
# -1 The value is not set.
# 0 SELinux is disabled.
# 1 SELinux is enabled.
#
selinux = -1
[Bootloader]
# Type of the bootloader.
# Supported values:
#
# DEFAULT Choose the type by platform.
# EXTLINUX Use extlinux as the bootloader.
# SDBOOT Use systemd-boot as the bootloader.
#
type = DEFAULT
# Name of the EFI directory.
efi_dir = default
# Hide the GRUB menu.
menu_auto_hide = False
# Are non-iBFT iSCSI disks allowed?
nonibft_iscsi_boot = False
# Arguments preserved from the installation system.
preserved_arguments =
cio_ignore zfcp.allow_lun_scan
speakup_synth apic noapic apm ide noht acpi video
pci nodmraid nompath nomodeset noiswmd fips selinux
biosdevname ipv6.disable net.ifnames net.ifnames.prefix
nosmt vga
[Storage]
# Enable iBFT usage during the installation.
ibft = True
# Tell multipathd to use user friendly names when naming devices during the installation.
multipath_friendly_names = True
# Create GPT discoverable partition type IDs, if possible
gpt_discoverable_partitions = True
# Do you want to allow imperfect devices (for example, degraded mdraid array devices)?
allow_imperfect_devices = False
# Btrfs compression algorithm and level. e.g. zstd:1
btrfs_compression =
# Default disk label type.
# Valid values:
#
# gpt Prefer creation of GPT disk labels.
# mbr Prefer creation of MBR disk labels.
#
# If not specified, use whatever Blivet uses by default.
#
disk_label_type =
# Default file system type. Use whatever Blivet uses by default.
file_system_type =
# Default partitioning.
# Specify a mount point and its attributes on each line.
#
# Valid attributes:
#
# size <SIZE> The size of the mount point.
# min <MIN_SIZE> The size will grow from MIN_SIZE to MAX_SIZE.
# max <MAX_SIZE> The max size is unlimited by default.
# free <SIZE> The required available space.
# btrfs The mount point will be created only for the Btrfs scheme
#
default_partitioning =
/ (min 1 GiB, max 70 GiB)
/home (min 500 MiB, free 50 GiB)
# Default partitioning scheme.
# Valid values:
#
# PLAIN Create standard partitions.
# BTRFS Use the Btrfs scheme.
# LVM Use the LVM scheme.
# LVM_THINP Use LVM Thin Provisioning.
#
default_scheme = LVM
# Default version of LUKS.
# Valid values:
#
# luks1 Use version 1 by default.
# luks2 Use version 2 by default.
#
luks_version = luks2
[Storage Constraints]
# Minimal size of the total memory.
min_ram = 320 MiB
# Minimal size of the available memory for LUKS2.
luks2_min_ram = 128 MiB
# Should we recommend to specify a swap partition?
swap_is_recommended = False
# Recommended minimal sizes of partitions.
# Specify a mount point and a size on each line.
min_partition_sizes =
/ 250 MiB
/usr 250 MiB
/tmp 50 MiB
/var 384 MiB
/home 100 MiB
/boot 512 MiB
# Required minimal sizes of partitions.
# Specify a mount point and a size on each line.
req_partition_sizes =
# Allowed device types of the / partition if any.
# Valid values:
#
# LVM Allow LVM.
# MD Allow RAID.
# PARTITION Allow standard partitions.
# BTRFS Allow Btrfs.
# DISK Allow disks.
# LVM_THINP Allow LVM Thin Provisioning.
#
root_device_types =
# Mount points that must be on a linux file system.
# Specify a list of mount points.
must_be_on_linuxfs = / /var /tmp /usr /home /usr/share /usr/lib
# Paths that must be directories on the / file system.
# Specify a list of paths.
must_be_on_root = /bin /dev /sbin /etc /lib /root /mnt lost+found /proc
# Paths that must NOT be directories on the / file system.
# Specify a list of paths.
must_not_be_on_root =
# Mount points that are recommended to be reformatted.
#
# It will be recommended to create a new file system on a mount point
# that has an allowed prefix, but doesn't have a blocked one.
# Specify lists of mount points.
reformat_allowlist = /boot /var /tmp /usr
reformat_blocklist = /home /usr/local /opt /var/www
[User Interface]
# The path to a custom stylesheet.
custom_stylesheet =
# A list of spokes to hide in UI.
# FIXME: Use other identification then names of the spokes.
hidden_spokes =
# Should the UI allow to change the configured root account?
can_change_root = False
# Should the UI allow to change the configured user accounts?
can_change_users = False
# Define the default password policies.
# Specify a policy name and its attributes on each line.
#
# Valid attributes:
#
# quality <NUMBER> The minimum quality score (see libpwquality).
# length <NUMBER> The minimum length of the password.
# empty Allow an empty password.
# strict Require the minimum quality.
#
password_policies =
root (quality 1, length 6)
user (quality 1, length 6, empty)
luks (quality 1, length 6)
# Should kernel options be shown in the software selection spoke?
show_kernel_options = True
[License]
# A path to EULA (if any)
#
# If the given distribution has an EULA & feels the need to
# tell the user about it fill in this variable by a path
# pointing to a file with the EULA on the installed system.
#
# This is currently used just to show the path to the file to
# the user at the end of the installation.
eula =
[Timezone]
# URL for geolocation data provider.
# This is used for automatic language and timezone detection.
#
# Known valid providers:
#
# https://geoip.fedoraproject.org/city
# https://api.hostip.info/get_json.php
#
# If left empty, geolocation does not run.
#
geolocation_provider = https://geoip.fedoraproject.org/city
[Localization]
# Should geolocation be used when setting the language ?
#
use_geolocation = True
Copy to ClipboardCopied!Toggle word wrapToggle overflow
will never be translated
_ = lambda x: x
N_ = lambda x: x
the path to addons is in sys.path so we can import things from org_fedora_hello_world
from org_fedora_hello_world.gui.categories.hello_world import HelloWorldCategory
from pyanaconda.ui.gui.spokes import NormalSpoke
export only the spoke, no helper functions, classes or constants
all = ["HelloWorldSpoke"]
class HelloWorldSpoke(FirstbootSpokeMixIn, NormalSpoke):
"""
Class for the Hello world spoke. This spoke will be in the Hello world
category and thus on the Summary hub. It is a very simple example of a unit
for the Anaconda's graphical user interface. Since it is also inherited form
the FirstbootSpokeMixIn, it will also appear in the Initial Setup (successor
of the Firstboot tool).
:see: pyanaconda.ui.common.UIObject
:see: pyanaconda.ui.common.Spoke
:see: pyanaconda.ui.gui.GUIObject
:see: pyanaconda.ui.common.FirstbootSpokeMixIn
:see: pyanaconda.ui.gui.spokes.NormalSpoke
"""
# class attributes defined by API #
# list all top-level objects from the .glade file that should be exposed
# to the spoke or leave empty to extract everything
builderObjects = ["helloWorldSpokeWindow", "buttonImage"]
# the name of the main window widget
mainWidgetName = "helloWorldSpokeWindow"
# name of the .glade file in the same directory as this source
uiFile = "hello_world.glade"
# category this spoke belongs to
category = HelloWorldCategory
# spoke icon (will be displayed on the hub)
# preferred are the -symbolic icons as these are used in Anaconda's spokes
icon = "face-cool-symbolic"
# title of the spoke (will be displayed on the hub)
title = N_("_HELLO WORLD")
# will never be translated
_ = lambda x: x
N_ = lambda x: x
# the path to addons is in sys.path so we can import things from org_fedora_hello_world
from org_fedora_hello_world.gui.categories.hello_world import HelloWorldCategory
from pyanaconda.ui.gui.spokes import NormalSpoke
# export only the spoke, no helper functions, classes or constants
all = ["HelloWorldSpoke"]
class HelloWorldSpoke(FirstbootSpokeMixIn, NormalSpoke):
"""
Class for the Hello world spoke. This spoke will be in the Hello world
category and thus on the Summary hub. It is a very simple example of a unit
for the Anaconda's graphical user interface. Since it is also inherited form
the FirstbootSpokeMixIn, it will also appear in the Initial Setup (successor
of the Firstboot tool).
:see: pyanaconda.ui.common.UIObject
:see: pyanaconda.ui.common.Spoke
:see: pyanaconda.ui.gui.GUIObject
:see: pyanaconda.ui.common.FirstbootSpokeMixIn
:see: pyanaconda.ui.gui.spokes.NormalSpoke
"""
# class attributes defined by API #
# list all top-level objects from the .glade file that should be exposed
# to the spoke or leave empty to extract everything
builderObjects = ["helloWorldSpokeWindow", "buttonImage"]
# the name of the main window widget
mainWidgetName = "helloWorldSpokeWindow"
# name of the .glade file in the same directory as this source
uiFile = "hello_world.glade"
# category this spoke belongs to
category = HelloWorldCategory
# spoke icon (will be displayed on the hub)
# preferred are the -symbolic icons as these are used in Anaconda's spokes
icon = "face-cool-symbolic"
# title of the spoke (will be displayed on the hub)
title = N_("_HELLO WORLD")
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Hello World add-on の例で、以下のようにこの 2 つのメソッドを定義します。__init__ メソッドに渡される引数の数および説明をメモしてください。以下に例を示します。
def __init__(self, data, storage, payload):
"""
:see: pyanaconda.ui.common.Spoke.init
:param data: data object passed to every spoke to load/store data
from/to it
:type data: pykickstart.base.BaseHandler
:param storage: object storing storage-related information
(disks, partitioning, boot loader, etc.)
:type storage: blivet.Blivet
:param payload: object storing packaging-related information
:type payload: pyanaconda.packaging.Payload
"""
NormalSpoke.init(self, data, storage, payload)
self._hello_world_module = HELLO_WORLD.get_proxy()
def initialize(self):
"""
The initialize method that is called after the instance is created.
The difference between init and this method is that this may take
a long time and thus could be called in a separate thread.
:see: pyanaconda.ui.common.UIObject.initialize
"""
NormalSpoke.initialize(self)
self._entry = self.builder.get_object("textLines")
self._reverse = self.builder.get_object("reverseCheckButton")
def __init__(self, data, storage, payload):
"""
:see: pyanaconda.ui.common.Spoke.init
:param data: data object passed to every spoke to load/store data
from/to it
:type data: pykickstart.base.BaseHandler
:param storage: object storing storage-related information
(disks, partitioning, boot loader, etc.)
:type storage: blivet.Blivet
:param payload: object storing packaging-related information
:type payload: pyanaconda.packaging.Payload
"""
NormalSpoke.init(self, data, storage, payload)
self._hello_world_module = HELLO_WORLD.get_proxy()
def initialize(self):
"""
The initialize method that is called after the instance is created.
The difference between init and this method is that this may take
a long time and thus could be called in a separate thread.
:see: pyanaconda.ui.common.UIObject.initialize
"""
NormalSpoke.initialize(self)
self._entry = self.builder.get_object("textLines")
self._reverse = self.builder.get_object("reverseCheckButton")
Copy to ClipboardCopied!Toggle word wrapToggle overflow
def refresh(self):
"""
The refresh method that is called every time the spoke is displayed.
It should update the UI elements according to the contents of
internal data structures.
:see: pyanaconda.ui.common.UIObject.refresh
"""
lines = self._hello_world_module.Lines
self._entry.get_buffer().set_text("".join(lines))
reverse = self._hello_world_module.Reverse
self._reverse.set_active(reverse)
def apply(self):
"""
The apply method that is called when user leaves the spoke. It should
update the D-Bus service with values set in the GUI elements.
"""
buf = self._entry.get_buffer()
text = buf.get_text(buf.get_start_iter(),
buf.get_end_iter(),
True)
lines = text.splitlines(True)
self._hello_world_module.SetLines(lines)
self._hello_world_module.SetReverse(self._reverse.get_active())
def execute(self):
"""
The execute method that is called when the spoke is exited. It is
supposed to do all changes to the runtime environment according to
the values set in the GUI elements.
"""
# nothing to do here
pass
def refresh(self):
"""
The refresh method that is called every time the spoke is displayed.
It should update the UI elements according to the contents of
internal data structures.
:see: pyanaconda.ui.common.UIObject.refresh
"""
lines = self._hello_world_module.Lines
self._entry.get_buffer().set_text("".join(lines))
reverse = self._hello_world_module.Reverse
self._reverse.set_active(reverse)
def apply(self):
"""
The apply method that is called when user leaves the spoke. It should
update the D-Bus service with values set in the GUI elements.
"""
buf = self._entry.get_buffer()
text = buf.get_text(buf.get_start_iter(),
buf.get_end_iter(),
True)
lines = text.splitlines(True)
self._hello_world_module.SetLines(lines)
self._hello_world_module.SetReverse(self._reverse.get_active())
def execute(self):
"""
The execute method that is called when the spoke is exited. It is
supposed to do all changes to the runtime environment according to
the values set in the GUI elements.
"""
# nothing to do here
pass
Copy to ClipboardCopied!Toggle word wrapToggle overflow
これらの属性はすべて、インストールプロセスの現在の状態に基づいて動的に決定する必要があります。以下は、Hello World アドオンでのこれらのメソッドの実装例です。これには、HelloWorldData クラスの text 属性に特定の値を設定する必要があります。
@property
def ready(self):
"""
The ready property reports whether the spoke is ready, that is, can be visited
or not. The spoke is made (in)sensitive based on the returned value of the ready
property.
:rtype: bool
"""
# this spoke is always ready
return True
@property
def mandatory(self):
"""
The mandatory property that tells whether the spoke is mandatory to be
completed to continue in the installation process.
:rtype: bool
"""
# this is an optional spoke that is not mandatory to be completed
return False
@property
def ready(self):
"""
The ready property reports whether the spoke is ready, that is, can be visited
or not. The spoke is made (in)sensitive based on the returned value of the ready
property.
:rtype: bool
"""
# this spoke is always ready
return True
@property
def mandatory(self):
"""
The mandatory property that tells whether the spoke is mandatory to be
completed to continue in the installation process.
:rtype: bool
"""
# this is an optional spoke that is not mandatory to be completed
return False
Copy to ClipboardCopied!Toggle word wrapToggle overflow
@property
def status(self):
"""
The status property that is a brief string describing the state of the
spoke. It should describe whether all values are set and if possible
also the values themselves. The returned value will appear on the hub
below the spoke's title.
:rtype: str
"""
lines = self._hello_world_module.Lines
if not lines:
return _("No text added")
elif self._hello_world_module.Reverse:
return _("Text set with {} lines to reverse").format(len(lines))
else:
return _("Text set with {} lines").format(len(lines))
@property
def status(self):
"""
The status property that is a brief string describing the state of the
spoke. It should describe whether all values are set and if possible
also the values themselves. The returned value will appear on the hub
below the spoke's title.
:rtype: str
"""
lines = self._hello_world_module.Lines
if not lines:
return _("No text added")
elif self._hello_world_module.Reverse:
return _("Text set with {} lines to reverse").format(len(lines))
else:
return _("Text set with {} lines").format(len(lines))
Copy to ClipboardCopied!Toggle word wrapToggle overflow
dialog クラスは、self.window 属性を介してアクセス可能な内部 Gtk ダイアログを実行および破棄する run メソッドを定義します。この属性は、同じ意味の mainWidgetName クラス属性を使用して設定されます。そのため、以下の例のようにダイアログを定義するコードは非常にシンプルです。
例5.3 englightbox Dialog の定義
every GUIObject gets ksdata in init
dialog = HelloWorldDialog(self.data)
# show dialog above the lightbox
with self.main_window.enlightbox(dialog.window):
dialog.run()
# every GUIObject gets ksdata in init
dialog = HelloWorldDialog(self.data)
# show dialog above the lightbox
with self.main_window.enlightbox(dialog.window):
dialog.run()
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Defining an englightbox Dialog のサンプルコードは、ダイアログのインスタンスを作成してから、enlightbox コンテキストマネージャーを使用してライトボックス内でダイアログを実行します。コンテキストマネージャーにはスポークのウィンドウへの参照があり、ダイアログのライトボックスをインスタンス化するためにダイアログのウィンドウだけを必要とします。
以下の例に従って、アドオン Text User Interface (TUI) のサポートを追加するために必要なすべての定義を含むモジュールを作成します。
class HelloWorldSpoke(NormalTUISpoke):
# category this spoke belongs to
category = HelloWorldCategory
def init(self, *args, kwargs): """ Create the representation of the spoke. :see: simpleline.render.screen.UIScreen """ super().init(*args, kwargs)
self.title = N_("Hello World")
self._hello_world_module = HELLO_WORLD.get_proxy()
self._container = None
self._reverse = False
self._lines = ""
def initialize(self):
"""
The initialize method that is called after the instance is created.
The difference between init and this method is that this may take
a long time and thus could be called in a separated thread.
:see: pyanaconda.ui.common.UIObject.initialize
"""
# nothing to do here
super().initialize()
def setup(self, args=None):
"""
The setup method that is called right before the spoke is entered.
It should update its state according to the contents of DBus modules.
:see: simpleline.render.screen.UIScreen.setup
"""
super().setup(args)
self._reverse = self._hello_world_module.Reverse
self._lines = self._hello_world_module.Lines
return True
def refresh(self, args=None):
"""
The refresh method that is called every time the spoke is displayed.
It should generate the UI elements according to its state.
:see: pyanaconda.ui.common.UIObject.refresh
:see: simpleline.render.screen.UIScreen.refresh
"""
super().refresh(args)
self._container = ListColumnContainer(
columns=1
)
self._container.add(
CheckboxWidget(
title="Reverse",
completed=self._reverse
),
callback=self._change_reverse
)
self._container.add(
EntryWidget(
title="Hello world text",
value="".join(self._lines)
),
callback=self._change_lines
)
self.window.add_with_separator(self._container)
def _change_reverse(self, data):
"""
Callback when user wants to switch checkbox.
Flip state of the "reverse" parameter which is boolean.
"""
self._reverse = not self._reverse
def _change_lines(self, data):
"""
Callback when user wants to input new lines.
Show a dialog and save the provided lines.
"""
dialog = Dialog("Lines")
result = dialog.run()
self._lines = result.splitlines(True)
def input(self, args, key):
"""
The input method that is called by the main loop on user's input.
* If the input should not be handled here, return it.
* If the input is invalid, return InputState.DISCARDED.
* If the input is handled and the current screen should be refreshed,
return InputState.PROCESSED_AND_REDRAW.
* If the input is handled and the current screen should be closed,
return InputState.PROCESSED_AND_CLOSE.
:see: simpleline.render.screen.UIScreen.input
"""
if self._container.process_user_input(key):
return InputState.PROCESSED_AND_REDRAW
if key.lower() == Prompt.CONTINUE:
self.apply()
self.execute()
return InputState.PROCESSED_AND_CLOSE
return super().input(args, key)
def apply(self):
"""
The apply method is not called automatically for TUI. It should be called
in input() if required. It should update the contents of internal data
structures with values set in the spoke.
"""
self._hello_world_module.SetReverse(self._reverse)
self._hello_world_module.SetLines(self._lines)
def execute(self):
"""
The execute method is not called automatically for TUI. It should be called
in input() if required. It is supposed to do all changes to the runtime
environment according to the values set in the spoke.
"""
# nothing to do here
pass
class HelloWorldSpoke(NormalTUISpoke):
# category this spoke belongs to
category = HelloWorldCategory
def init(self, *args, kwargs): """ Create the representation of the spoke. :see: simpleline.render.screen.UIScreen """ super().init(*args, kwargs)
self.title = N_("Hello World")
self._hello_world_module = HELLO_WORLD.get_proxy()
self._container = None
self._reverse = False
self._lines = ""
def initialize(self):
"""
The initialize method that is called after the instance is created.
The difference between init and this method is that this may take
a long time and thus could be called in a separated thread.
:see: pyanaconda.ui.common.UIObject.initialize
"""
# nothing to do here
super().initialize()
def setup(self, args=None):
"""
The setup method that is called right before the spoke is entered.
It should update its state according to the contents of DBus modules.
:see: simpleline.render.screen.UIScreen.setup
"""
super().setup(args)
self._reverse = self._hello_world_module.Reverse
self._lines = self._hello_world_module.Lines
return True
def refresh(self, args=None):
"""
The refresh method that is called every time the spoke is displayed.
It should generate the UI elements according to its state.
:see: pyanaconda.ui.common.UIObject.refresh
:see: simpleline.render.screen.UIScreen.refresh
"""
super().refresh(args)
self._container = ListColumnContainer(
columns=1
)
self._container.add(
CheckboxWidget(
title="Reverse",
completed=self._reverse
),
callback=self._change_reverse
)
self._container.add(
EntryWidget(
title="Hello world text",
value="".join(self._lines)
),
callback=self._change_lines
)
self.window.add_with_separator(self._container)
def _change_reverse(self, data):
"""
Callback when user wants to switch checkbox.
Flip state of the "reverse" parameter which is boolean.
"""
self._reverse = not self._reverse
def _change_lines(self, data):
"""
Callback when user wants to input new lines.
Show a dialog and save the provided lines.
"""
dialog = Dialog("Lines")
result = dialog.run()
self._lines = result.splitlines(True)
def input(self, args, key):
"""
The input method that is called by the main loop on user's input.
* If the input should not be handled here, return it.
* If the input is invalid, return InputState.DISCARDED.
* If the input is handled and the current screen should be refreshed,
return InputState.PROCESSED_AND_REDRAW.
* If the input is handled and the current screen should be closed,
return InputState.PROCESSED_AND_CLOSE.
:see: simpleline.render.screen.UIScreen.input
"""
if self._container.process_user_input(key):
return InputState.PROCESSED_AND_REDRAW
if key.lower() == Prompt.CONTINUE:
self.apply()
self.execute()
return InputState.PROCESSED_AND_CLOSE
return super().input(args, key)
def apply(self):
"""
The apply method is not called automatically for TUI. It should be called
in input() if required. It should update the contents of internal data
structures with values set in the spoke.
"""
self._hello_world_module.SetReverse(self._reverse)
self._hello_world_module.SetLines(self._lines)
def execute(self):
"""
The execute method is not called automatically for TUI. It should be called
in input() if required. It is supposed to do all changes to the runtime
environment according to the values set in the spoke.
"""
# nothing to do here
pass
Copy to ClipboardCopied!Toggle word wrapToggle overflow
以下の例に従って、アドオン Text User Interface (TUI) のサポートを追加するために必要なすべての定義を含むモジュールを作成します。
class HelloWorldEditSpoke(NormalTUISpoke):
"""Example class demonstrating usage of editing in TUI"""
category = HelloWorldCategory
def init(self, data, storage, payload):
"""
:see: simpleline.render.screen.UIScreen
:param data: data object passed to every spoke to load/store data
from/to it
:type data: pykickstart.base.BaseHandler
:param storage: object storing storage-related information
(disks, partitioning, boot loader, etc.)
:type storage: blivet.Blivet
:param payload: object storing packaging-related information
:type payload: pyanaconda.packaging.Payload
"""
super().init(self, *args, **Kwargs)
self.title = N_("Hello World Edit")
self._container = None
# values for user to set
self._checked = False
self._unconditional_input = ""
self._conditional_input = ""
def refresh(self, args=None):
"""
The refresh method that is called every time the spoke is displayed.
It should update the UI elements according to the contents of self.data.
:see: pyanaconda.ui.common.UIObject.refresh
:see: simpleline.render.screen.UIScreen.refresh
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
"""
super().refresh(args)
self._container = ListColumnContainer(columns=1)
# add ListColumnContainer to window (main window container)
# this will automatically add numbering and will call callbacks when required
self.window.add(self._container)
self._container.add(CheckboxWidget(title="Simple checkbox", completed=self._checked),
callback=self._checkbox_called)
self._container.add(EntryWidget(title="Unconditional text input",
value=self._unconditional_input),
callback=self._get_unconditional_input)
# show conditional input only if the checkbox is checked
if self._checked:
self._container.add(EntryWidget(title="Conditional password input",
value="Password set" if self._conditional_input
else ""),
callback=self._get_conditional_input)
self.window.add_with_separator(self._container)
def _checkbox_called(self, data): # pylint: disable=unused-argument
"""Callback when user wants to switch checkbox.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
self._checked = not self._checked
def _get_unconditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set unconditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = Dialog(
"Unconditional input",
conditions=[self._check_user_input]
)
self._unconditional_input = dialog.run()
def _get_conditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set conditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = PasswordDialog(
"Unconditional password input",
policy_name=PASSWORD_POLICY_ROOT
)
self._conditional_input = dialog.run()
def _check_user_input(self, user_input, report_func):
"""Check if the user has written a valid value.
:param user_input: user input for validation
:type user_input: str
:param report_func: function for reporting errors on user input
:type report_func: func with one param
"""
if re.match(r'^\w+$', user_input):
return True
else:
report_func("You must set at least one word")
return False
def input(self, args, key):
"""
The input method that is called by the main loop on user's input.
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
:param key: user's input
:type key: unicode
:return: if the input should not be handled here, return it, otherwise
return InputState.PROCESSED or InputState.DISCARDED if the input was
processed successfully or not respectively
:rtype: enum InputState
"""
if self._container.process_user_input(key):
return InputState.PROCESSED_AND_REDRAW
else:
return super().input(args, key)
@property
def completed(self):
# completed if user entered something non-empty to the Conditioned input
return bool(self._conditional_input)
@property
def status(self):
return "Hidden input %s" % ("entered" if self._conditional_input
else "not entered")
def apply(self):
# nothing needed here, values are set in the self.args tree
pass
class HelloWorldEditSpoke(NormalTUISpoke):
"""Example class demonstrating usage of editing in TUI"""
category = HelloWorldCategory
def init(self, data, storage, payload):
"""
:see: simpleline.render.screen.UIScreen
:param data: data object passed to every spoke to load/store data
from/to it
:type data: pykickstart.base.BaseHandler
:param storage: object storing storage-related information
(disks, partitioning, boot loader, etc.)
:type storage: blivet.Blivet
:param payload: object storing packaging-related information
:type payload: pyanaconda.packaging.Payload
"""
super().init(self, *args, **Kwargs)
self.title = N_("Hello World Edit")
self._container = None
# values for user to set
self._checked = False
self._unconditional_input = ""
self._conditional_input = ""
def refresh(self, args=None):
"""
The refresh method that is called every time the spoke is displayed.
It should update the UI elements according to the contents of self.data.
:see: pyanaconda.ui.common.UIObject.refresh
:see: simpleline.render.screen.UIScreen.refresh
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
"""
super().refresh(args)
self._container = ListColumnContainer(columns=1)
# add ListColumnContainer to window (main window container)
# this will automatically add numbering and will call callbacks when required
self.window.add(self._container)
self._container.add(CheckboxWidget(title="Simple checkbox", completed=self._checked),
callback=self._checkbox_called)
self._container.add(EntryWidget(title="Unconditional text input",
value=self._unconditional_input),
callback=self._get_unconditional_input)
# show conditional input only if the checkbox is checked
if self._checked:
self._container.add(EntryWidget(title="Conditional password input",
value="Password set" if self._conditional_input
else ""),
callback=self._get_conditional_input)
self.window.add_with_separator(self._container)
def _checkbox_called(self, data): # pylint: disable=unused-argument
"""Callback when user wants to switch checkbox.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
self._checked = not self._checked
def _get_unconditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set unconditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = Dialog(
"Unconditional input",
conditions=[self._check_user_input]
)
self._unconditional_input = dialog.run()
def _get_conditional_input(self, data): # pylint: disable=unused-argument
"""Callback when the user wants to set conditional input.
:param data: can be passed when adding callback in container (not used here)
:type data: anything
"""
dialog = PasswordDialog(
"Unconditional password input",
policy_name=PASSWORD_POLICY_ROOT
)
self._conditional_input = dialog.run()
def _check_user_input(self, user_input, report_func):
"""Check if the user has written a valid value.
:param user_input: user input for validation
:type user_input: str
:param report_func: function for reporting errors on user input
:type report_func: func with one param
"""
if re.match(r'^\w+$', user_input):
return True
else:
report_func("You must set at least one word")
return False
def input(self, args, key):
"""
The input method that is called by the main loop on user's input.
:param args: optional argument that may be used when the screen is
scheduled
:type args: anything
:param key: user's input
:type key: unicode
:return: if the input should not be handled here, return it, otherwise
return InputState.PROCESSED or InputState.DISCARDED if the input was
processed successfully or not respectively
:rtype: enum InputState
"""
if self._container.process_user_input(key):
return InputState.PROCESSED_AND_REDRAW
else:
return super().input(args, key)
@property
def completed(self):
# completed if user entered something non-empty to the Conditioned input
return bool(self._conditional_input)
@property
def status(self):
return "Hidden input %s" % ("entered" if self._conditional_input
else "not entered")
def apply(self):
# nothing needed here, values are set in the self.args tree
pass
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.