Anaconda のカスタマイズ


Red Hat Enterprise Linux 10.0 Beta

Red Hat Enterprise Linux でのインストーラーの外観の変更およびカスタムアドオンの作成

Red Hat Customer Content Services

概要

Anaconda は、Red Hat Enterprise Linux で使用されるインストーラーです。RHEL を環境にインストールするときに、Anaconda をカスタマイズして機能を拡張できます。

第1章 Anaconda のカスタマイズの概要

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 インストールメディアとイメージのカスタマイズのみサポートします。もしくは、キックスタートを使用してインフラストラクチャーに一貫性のあるシステムをデプロイすることもできます。

第2章 事前カスタマイズタスクの実行

Red Hat Enterprise Linux のインストールメディアはカスタマイズが可能です。カスタマイズするには、Red Hat カスタマーポータルから RHEL 10 ブートイメージをダウンロードし、その内容を抽出して、独自のデプロイメントニーズに合わせて詳細な変更を加えてからブートイメージを再ビルドします。

2.1. ISO イメージの使用

このセクションでは、以下の方法を説明します。

  • Red Hat Enterprise Linux ISO を抽出します。
  • カスタマイズを含む新規ブートイメージを作成します。

2.2. RHEL ブートイメージのダウンロード

インストーラーのカスタマイズを開始する前に、Red Hat が提供するブートイメージをダウンロードします。アカウントにログイン後、Red Hat カスタマーポータル から Red Hat Enterprise Linux 10 ブートメディアを入手できます。

注記
  • アカウントには、Red Hat Enterprise Linux 10 イメージをダウンロードするために十分なエンタイトルメントが必要です。
  • Binary DVD または Boot ISO イメージのいずれかをダウンロードする必要があります。
  • KVM Guest Image や Supplementary DVD などの他の利用可能なダウンロードを使用して、インストーラーをカスタマイズすることはできません。

バイナリー DVD およびブート ISO のダウンロードの詳細は、製品のダウンロード を参照してください。

2.3. Red Hat Enterprise Linux ブートイメージの抽出

ブートイメージのコンテンツを抽出できます。

手順

  1. /mnt/iso ディレクトリーが存在し、現在そこにマウントされていないことを確認してください。
  2. ダウンロードしたイメージをマウントします。

    # mount -t iso9660 -o loop path/to/image.iso /mnt/iso
    Copy to Clipboard Toggle word wrap

    path/to/image.iso は、ダウンロードしたブートイメージのパスです。

  3. ISO イメージのコンテンツを配置する作業ディレクトリーを作成します。

    $ mkdir /tmp/ISO
    Copy to Clipboard Toggle word wrap
  4. マウントされたイメージのすべてのコンテンツを新しい作業ディレクトリーにコピーします。-p オプションを使用して、ファイルおよびディレクトリーのパーミッションと所有権を保持するようにしてください。

    # cp -pRf /mnt/iso /tmp/ISO
    Copy to Clipboard Toggle word wrap
  5. イメージをアンマウントします。

    # umount /mnt/iso
    Copy to Clipboard Toggle word wrap

第3章 ブートメニューのカスタマイズ

ブートメニューオプションをカスタマイズできます。ブートイメージのダウンロードと抽出は、Red Hat Enterprise Linux ブートイメージの抽出 を参照してください。

ブートメニューのカスタマイズには、以下のハイレベルなタスクが必要です。

  1. 前提条件を完了します。
  2. ブートメニューをカスタマイズします。
  3. カスタムブートイメージを作成します。

3.1. ブートメニューのカスタマイズ

ブートメニュー は、インストールイメージを使用してシステムを起動すると表示されるメニューです。通常、このメニューでは、Install Red Hat Enterprise LinuxBoot from local drive、または Rescue an installed system などのオプションを選択できます。

Boot メニューをカスタマイズするには、以下を行います。

  • デフォルトのオプションをカスタマイズします。
  • オプションを追加します。
  • 視覚的スタイル (色と背景) を変更します。

インストールメディアは GRUB2 ブートローダーで構成されます。GRUB2 ブートローダーは、UEFI ファームウェアが搭載されているシステムで使用されます。

起動オプションのカスタマイズは、特にキックスタートで役に立ちます。インストールを開始する前に、キックスタートファイルをインストーラーに提供する必要があります。通常、これは、inst.ks= ブートオプションを追加するために、既存のブートオプションを手動で編集して行います。メディア上のブートローダー設定ファイルを編集する場合は、このオプションを事前設定されたエントリーのいずれかに追加できます。

3.2. レガシー BIOS ファームウェアのシステム

ブートメディアの 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
}
Copy to Clipboard Toggle word wrap

ここでは、以下のようになります。

  • menuentry: エントリーのタイトルを定義します。これは引用符または二重引用符 (' または ") で指定されます。--class オプションを使用して、メニューエントリーを異なるクラスにグループ化できます。これは、GRUB2 テーマを使用してスタイルを変更できます。

    注記

    各メニューエントリーの定義は中括弧 ({}) で囲む必要があります。

  • linux: ブートするカーネル (上記の例では /images/pxeboot/vmlinuz) と、その他の追加オプション (存在する場合) を定義します。これらのオプションをカスタマイズして、ブートエントリーの動作を変更できます。Anaconda に適用可能なオプションの詳細は、RHEL の自動インストール を参照してください。主なオプションは inst.ks= で、キックスタートファイルの場所を指定できます。ブート ISO イメージにキックスタートファイルを配置し、inst.ks= オプションを使用してその場所を指定できます。たとえば、kickstart.ks ファイルをイメージの root ディレクトリーに配置し、inst.ks=hd:LABEL=RHEL-10-0-BaseOS-x86_64:/kickstart.ks を使用できます。また、dracut.cmdline(7) の man ページに記載されている dracut オプションを使用することもできます。

    重要

    ディスクラベルを使用して特定のドライブを参照する場合 (例: inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64)、すべてのスペースを \x20 に置き換えます。

  • initrd: ロードする初期 RAM ディスク (initrd) イメージの場所。grub.cfg 設定ファイルで使用されるその他のオプションは以下のとおりです。

    • set timeout: デフォルトのメニューエントリーが自動的に使用される前に、ブートメニューが表示される時間の長さを決定します。デフォルト値は 60 です。つまり、メニューが 60 秒間表示されます。この値を -1 に設定すると、タイムアウトを完全に無効にします。

      注記

      この設定によりデフォルトのブートエントリーがすぐに有効になるため、ヘッドレスインストールの実行時にタイムアウトを 0 に設定すると便利です。

  • submenu: submenu ブロックを使用すると、メインメニューにエントリーを表示する代わりに、サブメニューを作成してその下にいくつかのエントリーをグループ化できます。デフォルト設定の Troubleshooting サブメニューには、既存のシステムをレスキューするエントリーが含まれます。エントリーのタイトルは、引用符または二重引用符 (' または ") で囲まれています。submenu ブロックには、上記のように 1 つ以上のメニューエントリー定義が含まれており、ブロック全体は中括弧 ({}) で囲まれています。以下に例を示します。

    submenu 'Submenu title' {
      menuentry 'Submenu option 1' {
        linux /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 nomodeset quiet
        initrd /images/pxeboot/initrd.img
      }
      menuentry 'Submenu option 2' {
        linux /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 inst.rescue quiet
        initrd /images/pxeboot/initrd.img
      }
    }
    Copy to Clipboard Toggle word wrap
  • set default: デフォルトエントリーを決定します。エントリー番号は 0 から始まります。3 番目のエントリーをデフォルトにしたい場合は、set default=2 などを使用します。
  • theme: GRUB2 テーマファイルが含まれるディレクトリーを決定します。テーマを使用して、ブートローダーの視覚的な要素 (背景、フォント、および特定の要素の色) をカスタマイズできます。

3.3. UEFI ファームウェアのシステム

ブートメディアの 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
}
Copy to Clipboard Toggle word wrap

ここでは、以下のようになります。

  • menuentry: エントリーのタイトルを定義します。これは引用符または二重引用符 (' または ") で指定されます。--class オプションを使用して、メニューエントリーを異なる クラス にグループ化できます。このクラスは、GRUB2 テーマを使用してスタイルを変更できます。

    注記

    上記の例で示すように、各メニューエントリー定義は中括弧 ({}) で囲む必要があります。

  • linuxefi: ブートするカーネル (例では /images/pxeboot/vmlinuz) と、その他の追加オプション (存在する場合) を定義します。

    これらのオプションをカスタマイズして、ブートエントリーの動作を変更できます。

    主なオプションは inst.ks= で、キックスタートファイルの場所を指定できます。ブート ISO イメージにキックスタートファイルを配置し、inst.ks= オプションを使用してその場所を指定できます。たとえば、kickstart.ks ファイルをイメージの root ディレクトリーに配置し、inst.ks=hd:LABEL=RHEL-10-0-BaseOS-x86_64:/kickstart.ks を使用できます。

    システムの dracut.cmdline(7) man ページに記載されている dracut オプションを使用することもできます。

    重要

    ディスクラベルを使用して特定のドライブを参照する場合 (例: inst.stage2=hd:LABEL=RHEL-10-BaseOS-x86_64)、すべてのスペースを \x20 に置き換えます。

  • initrdefi: ロードする初期 RAM ディスク (initrd) イメージの場所。

grub.cfg 設定ファイルで使用されるその他のオプションは以下のとおりです。

  • set timeout: デフォルトのメニューエントリーが自動的に使用される前に、ブートメニューが表示される時間の長さを決定します。デフォルト値は 60 です。つまり、メニューが 60 秒間表示されます。この値を -1 に設定すると、タイムアウトを完全に無効にします。

    注記

    この設定によりデフォルトのブートエントリーがすぐに有効になるため、ヘッドレスインストールの実行時にタイムアウトを 0 に設定すると便利です。

  • submenu: submenu ブロックを使用すると、メインメニューにエントリーを表示する代わりに、サブメニューを作成してその下にいくつかのエントリーをグループ化できます。デフォルト設定の Troubleshooting サブメニューには、既存のシステムをレスキューするエントリーが含まれます。

    エントリーのタイトルは引用符または二重引用符 (' または ") です。

    submenu ブロックには上記のように 1 つ以上の menuentry 定義が含まれ、ブロック全体が中括弧 ({}) で囲まれています。以下に例を示します。

    submenu 'Submenu title' {
      menuentry 'Submenu option 1' {
        linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 nomodeset quiet
        initrdefi /images/pxeboot/initrd.img
      }
      menuentry 'Submenu option 2' {
        linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-10-0-BaseOS-x86_64 inst.rescue quiet
        initrdefi /images/pxeboot/initrd.img
      }
    }
    Copy to Clipboard Toggle word wrap
  • set default: デフォルトエントリーを決定します。エントリー番号は 0 から始まります。3 番目 のエントリーをデフォルトのエントリーに設定する場合は、set default=2 などを使用します。
  • theme: GRUB2 テーマファイルを含むディレクトリーを決定します。テーマを使用して、ブートローダーの視覚的な要素 (背景、フォント、および特定の要素の色) をカスタマイズできます。

第4章 グラフィカルユーザーインターフェイスのブランド化と色調節

Anaconda ユーザーインターフェイスのカスタマイズには、グラフィカル要素のカスタマイズや製品名のカスタマイズが含まれます。

ユーザーインターフェイスのカスタマイズには、以下のハイレベルなタスクが必要です。

  1. 前提条件を完了します。
  2. カスタムブランディングマテリアルを作成します (グラフィカル要素をカスタマイズする予定がある場合)。
  3. グラフィカル要素をカスタマイズします (カスタマイズを計画している場合)。
  4. 製品名をカスタマイズします (カスタマイズを計画している場合)。
  5. product.img ファイルを作成します。
  6. カスタムブートイメージを作成します。

    注記

    カスタムブランディング資料を作成するには、まずデフォルトのグラフィカル要素ファイルタイプと寸法を参照します。適切なカスタム資料を作成できます。デフォルトのグラフィカル要素の詳細は、Customizing graphical elements セクションに記載されているサンプルファイルを参照してください。

4.1. 前提条件

  • ISO イメージをダウンロードしてデプロイメントしている。
  • 独自のブランディングマネージメントを作成している。

    ブートイメージのダウンロードと抽出は、Red Hat Enterprise Linux ブートイメージの抽出 を参照してください。

4.2. グラフィカル要素のカスタマイズ

グラフィカル要素をカスタマイズするには、カスタマイズ可能な要素をカスタムブランドの資料に変更または置き換え、コンテナーファイルを更新します。

インストーラーのカスタマイズ可能なグラフィカル要素は、インストーラーランタイムファイルシステムの /usr/share/anaconda/pixmaps/ ディレクトリーに保存されます。このディレクトリーには、以下のカスタマイズ可能なファイルが含まれます。

pixmaps
├─ anaconda-password-show-off.svg
├─ anaconda-password-show-on.svg
├─ right-arrow-icon.png
├─ sidebar-bg.png
├─ sidebar-logo.png
└─ topbar-bg.png
Copy to Clipboard Toggle word wrap

さらに、/usr/share/anaconda/ ディレクトリーには、anaconda-gtk.css という名前のベースの CSS スタイルシートが含まれており、メインの UI 要素のファイル名とパラメーター (サイドバーとトップバーのロゴおよび背景) を決定します。製品固有のスタイルシートのカスタマイズは別のファイル (/usr/share/anaconda/pixmaps/redhat.css) にあり、anaconda-gtk.css ファイルのデフォルトをオーバーライドします。CSS カスタマイズには製品固有のファイルを使用します。これは、必要に応じてスタイルシートの特定の要素のみをオーバーライドするためです。

製品固有の redhat.css ファイルには、要件に応じてカスタマイズできる次のコンテンツが含まれています (完全なスタイルシート仕様は、anaconda-gtk.css ファイルのコンテンツを参照してください)。

/* theme colors/images */

@define-color product_bg_color @redhat;

/* logo and sidebar classes */

.logo-sidebar {
   background-image: url('/usr/share/anaconda/pixmaps/sidebar-bg.png');
   background-color: @product_bg_color;
   background-repeat: no-repeat;
}

/* Add a logo to the sidebar */

.logo {
   background-image: url('/usr/share/anaconda/pixmaps/sidebar-logo.png');
   background-position: 50% 20px;
   background-repeat: no-repeat;
   background-color: transparent;
}

/* This is a placeholder to be filled by a product-specific logo. */

.product-logo {
   background-image: none;
   background-color: transparent;
}

AnacondaSpokeWindow #nav-box {
   background-color: @product_bg_color;
   background-image: url('/usr/share/anaconda/pixmaps/topbar-bg.png');
   background-repeat: no-repeat;
   color: white;
}
Copy to Clipboard Toggle word wrap

CSS ファイルの最も重要な部分は、解決に基づいてスケーリングを処理する方法です。PNG イメージの背景はスケーリングされず、常に実際の画面に表示されます。代わりに、バックグラウンドには透過的な背景があり、スタイルシートは @define-color 行に一致する背景色を定義します。そのため、バックグラウンド イメージ は背景の に "フェード" します。これは、イメージのスケーリングを必要とせずに、すべての解像度でバックグラウンドが機能することを意味します。

また、background-repeat パラメーターをバックグラウンドのタイル配置に変更することもできます。インストールするすべてのシステムが同じディスプレイの解像度を持つことを保証している場合は、バー全体を埋めるバックグラウンドイメージを使用できます。

上記のファイルはどれでもカスタマイズできます。その後、product.img ファイルの作成 の手順に従ってカスタムグラフィックを含む独自の product.img を作成し、カスタムブートイメージの作成 の手順に従って変更を加えた新しいブート可能な ISO イメージを作成します。

4.3. 製品名のカスタマイズ

プロダクト名をカスタマイズするには、カスタム .buildstamp ファイルを作成する必要があります。そのためには、次のコンテンツを含む新しい .buildstamp ファイルを作成します。

[Main]
Product=My Distribution
Version=10
BugURL=https://bugzilla.redhat.com/
IsFinal=True
Copy to Clipboard Toggle word wrap

My Distribution を、インストーラーで表示する名前に変更します。

カスタム .buildstamp ファイルを作成したら、product.img ファイルの作成 セクションに従ってカスタマイズを含む新しい product.img ファイルを作成し、カスタムブートイメージの作成 セクションに従って変更が含まれた新しいブート可能な ISO ファイルを作成します。

4.4. デフォルトの設定ファイルの設定

Anaconda 設定ファイルは、.ini ファイル形式で記述できます。Anaconda 設定ファイルは、セクション、オプション、およびコメントで構成されています。各セクションは、section ヘッダー、# 文字で始まるコメント、および options を定義するための key = value ペアで定義されます。生成される設定ファイルは、configparser Python 設定ファイルパーサーで処理されます。

/etc/anaconda/anaconda.conf にあるデフォルトの設定ファイルには、サポート対象のセクションおよびオプションが含まれています。このファイルは、インストーラーの完全なデフォルト設定を提供します。/etc/anaconda/conf.d/ ディレクトリーにカスタム設定ファイルを作成できます。

以下の設定ファイルは、デフォルト設定を説明しています。

# 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
Copy to Clipboard Toggle word wrap

conf.d ディレクトリー内のファイルのコンテンツは、anaconda.conf のデフォルトをオーバーライドします。ファイル名は、<priority>-<config-description>.conf 形式で付けられます (例: 100-my-distribution.conf)。最も優先度の高いファイルが最後に適用され、以前に適用されたすべての設定ファイルがオーバーライドされます。

カスタマイズ設定ファイルのコンテンツの例を次に示します。

# Anaconda configuration file for My Distribution

[Profile]
 Define the profile.
profile_id = my_distribution

[Profile Detection]
 Match os-release values.
os_id = my_distribution

[Network]
default_on_boot = NONE

[Storage]
file_system_type = xfs
default_partitioning =
	/     (min 2 GiB, max 50 GiB)
	/home (min 20 GiB, free 10 GiB)
	/test (size 256 MiB)
	swap

[Storage Constraints]
swap_is_recommended = True

[User Interface]
custom_stylesheet = /usr/share/anaconda/pixmaps/my_distribution.css
Copy to Clipboard Toggle word wrap

第5章 インストーラーアドオンの開発

Anaconda とそのアーキテクチャーの詳細では、そのバックエンドと、アドオンが機能するために必要なさまざまなプラグポイントを説明します。この情報は、特定の要件に合わせてカスタマイズされたアドオンの開発をサポートします。

5.1. Anaconda およびアドオンの概要

Anaconda: Fedora、Red Hat Enterprise Linux、およびその他の派生製品に使用されるオペレーティングシステムインストーラーです。これは、Gtk ウィジェット (C で記述)、systemd ユニット、dracut ライブラリーなどの一部の追加ファイルが含まれる、一連の Python モジュールおよびスクリプトです。同時に、これらは、作成される (ターゲット) システムのパラメーターを設定してから、このシステムをマシンにセットアップできるツールを形成します。インストールプロセスには、主に 4 つのステップがあります。

  1. インストール先の準備 (通常は、ディスクのパーティション設定)
  2. パッケージおよびデータのインストール
  3. ブートローダーのインストールおよび設定
  4. 新規インストールシステムの設定

Anaconda を使用すると、以下の 3 つの方法で Fedora、Red Hat Enterprise Linux、および派生製品をインストールできます。

グラフィカルユーザーインターフェイス (GUI) の使用:

これは、最も一般的なインストール方法です。インターフェイスを使用すると、インストールを開始する前に、設定がほとんどまたは何も必要のない状態でシステムを対話的にインストールできます。この方法は、複雑なパーティションレイアウトの設定など、一般的なすべてのユースケースを取り上げます。

グラフィカルインターフェイスは RDP 経由のリモートアクセスをサポートしており、グラフィックカードや接続されたモニターがないシステムでも GUI を使用できます。

テキストユーザーインターフェイス (TUI) の使用:

TUI はモノクロラインプリンターと同様に動作します。これにより、カーソルの移動、色、その他の高度な機能に対応しないシリアルコンソールで作業できます。テキストモードは限定されており、ネットワーク設定、言語オプション、インストール (0 パッケージ) ソースなど、最も一般的なオプションのみをカスタマイズできます。このインターフェイスでは、手動パーティション設定などの高度な機能は利用できません。

キックスタートファイルの使用:

キックスタートファイルは、シェルのような構文を持つプレーンテキストファイルで、インストールプロセスを実行するためのデータを含めることができます。キックスタートファイルを使用すると、インストールを部分的または完全に自動化できます。インストールを完全に自動化するには、すべての必要なエリアを設定する一連のコマンドが必要になります。1 つ以上のコマンドが見つからない場合は、インストールに対話が必要です。

インストーラー自体の自動化以外に、キックスタートファイルには、インストールプロセス中に特定の時点で実行するカスタムスクリプトを含めることができます。

5.2. Anaconda アーキテクチャー

Anaconda は Python モジュールおよびスクリプトのセットです。また、外部パッケージおよびライブラリーも使用します。

このツールセットの主要コンポーネントには、以下のパッケージが含まれます。

  • pykickstart - キックスタートファイルを解析し、検証します。また、インストールを動作させる値を保存するデータ構造も提供します。
  • dnf - パッケージをインストールして依存関係を解決するパッケージマネージャー
  • blivet - ストレージ管理に関連するすべてのアクティビティーを処理します。
  • pyanaconda - キーボードおよびタイムゾーンの選択、ネットワーク設定、ユーザー作成など、Anaconda のユーザーインターフェイスおよびモジュールが含まれます。システム指向の機能を実行するためのさまざまなユーティリティーも提供します。
  • python-meh - クラッシュ発生時に追加のシステム情報を収集して保存する例外ハンドラーを含みます。また、この情報を libreport ライブラリーに渡します。それ自体は ABRT プロジェクト に含まれます。
  • dasbus - D-Bus ライブラリーと、anaconda のモジュールと外部コンポーネントとの通信を有効にします。
  • python-simpleline - Anaconda テキストモードでユーザーの対話を管理するテキスト UI フレームワークライブラリー
  • gtk - GUI を作成および管理する Gnome ツールキットライブラリー

前述のパッケージへの分割とは別に、Anaconda は内部的にユーザーインターフェイスと、個別のプロセスとして実行され、D-Bus ライブラリーを使用して通信する一連のモジュールに分かれています。これらのモジュールは以下のとおりです。

  • Boss - 内部モジュール検出、ライフサイクル、およびコーディネーションを管理します。
  • Localization - ロケールを管理します。
  • Network - ネットワークを処理します。
  • Payloads - rpmostreetar などの異なる形式でインストール用のデータを処理します。ペイロードはインストール用のデータのソースを管理します。ソースは CD-ROM、HDD、NFS、URL などの形式によって異なる可能性があります。
  • Security - セキュリティー関連機能を管理します。
  • Services - サービスを処理します。
  • Storage - blivet を使用してストレージを管理します。
  • Subscription - subscription-manager ツールと Red Hat Lightspeed を処理します。
  • Timezone - 時間、日付、ゾーン、および時刻の同期を処理します。
  • Users - ユーザーおよびグループを作成します。

各モジュールは、処理するキックスタート部分を宣言し、キックスタートから設定を適用してインストール環境とインストール済みシステムに適用します。

Anaconda の Python コード部分 (pyanaconda) は、ユーザーインターフェイスを所有する "メイン" プロセスとして起動します。提供されたキックスタートデータは pykickstart モジュールを使用して解析され、Boss モジュールが起動され、他のすべてのモジュールが検出されて起動されます。その後、メインプロセスは宣言された機能に応じてキックスタートデータをモジュールに送信します。モジュールはデータを処理し、インストール環境に設定を適用し、必要な選択がすべて行われているかどうかを UI が検証します。インストールされていない場合は、対話式インストールモードでデータを指定する必要があります。必要な選択がすべて行われると、インストールを開始できます。モジュールは、インストール済みシステムにデータを書き込むことができます。

5.3. Anaconda ユーザーインターフェイス

Anaconda ユーザーインターフェイス (UI) は、ハブアンドスポークモデルとも呼ばれる非線形の構造を持っています。

Anaconda のハブアンドスポークモデルには次の利点があります。

  • インストーラー画面をフォローする柔軟性。
  • デフォルト設定を保持する柔軟性。
  • 設定された値の概要を説明します。
  • 拡張性をサポートします。ハブは、何も並べ替えることなく追加でき、複雑な順序の依存関係を解決できます。
  • グラフィカルおよびテキストモードでのインストールをサポートします。

以下の図は、インストーラーレイアウトと ハブスポーク (スクリーン) 間の対話を可能にします。

図5.1 ハブアンドスポークモデル

ハブとスポーク

この図では、画面 2-13 は 通常のスポーク と呼ばれ、スクリーン 1 および 14 は スタンドアロンのスポーク です。スタンドアロンのスリープとは、スタンドアロンのドアまたはハブの前後に使用できる画面です。たとえば、インストール先頭の Welcome 画面では、残りのインストールに言語を選択するように求められます。

注記
  • Installation Summary は Anaconda の唯一のハブです。インストールを開始する前に設定されたオプションの概要が示されます。

各スポークには、ハブを反映する以下の事前定義 プロパティー があります。

  • ready: 到着できるかどうかを示します。たとえば、インストーラーがパッケージソースを設定している場合は、スポークがグレーで色化され、設定が完了するまでアクセスできません。
  • completed - スポークが完了しているかどうかをマークします (必要なすべての値が設定されます)。
  • mandatory - インストールを続行する前にスポークにアクセスする 必要がある かどうかを決定します。たとえば、ディスクの自動パーティション設定を使用する場合でも、Installation Destination スポークにアクセスする必要があります。
  • status: (ハブのスポーク名で表示される) 内で設定された値の短いサマリーを提供します。

ユーザーインターフェイスを明確にするために、スポークは カテゴリー に分類されます。たとえば、Localization カテゴリーには、キーボードレイアウトの選択、言語サポート、タイムゾーン設定のスポークがグループ化されています。

各スポークには、1 つ以上のモジュールの値を表示および変更できる UI コントロールが含まれています。この動作は、アドオンによって提供されるスポークにも適用されます。キックスタートのインストール中、一部のスポークは非表示のままになり、開かなくてもデータが自動的に処理される場合があります。

5.4. Anaconda スレッド間の通信

インストールプロセス中に実行する必要のあるアクションの一部には時間がかかる場合があります。たとえば、既存のパーティションのディスクをスキャンしたり、パッケージメタデータをダウンロードしたりできます。待機して応答しなくなるため、Anaconda はこれらのアクションを別のスレッドで実行します。

Gtk ツールキットは、複数のスレッドからの要素の変更をサポートしません。Gtk の主なイベントループは、Anaconda プロセスのメインスレッドで実行されます。したがって、GUI に関連するすべてのアクションはメインスレッドで実行する必要があります。これを行うには、GLib.idle_add を使用しますが、これは常に簡単でも望ましい訳ではありません。pyanaconda.ui.gui.utils モジュールに定義されているいくつかのヘルパー関数およびデコレーターが難易度に追加される場合があります。

@gtk_action_wait および @gtk_action_nowait デコレーターにより、デコードされた関数またはメソッドが呼び出されたときに、メインスレッドで実行される Gtk のメインループに自動的にキューに置かれるように、デコレートされた関数またはメソッドが変更されます。戻り値はそれぞれ呼び出し元またはドロップされた値に返されます。

スポークやハブの通信では、準備ができ、ブロックされていないときにスポークが通知します。hubQ メッセージキューはこの機能を処理し、メインのイベントループを定期的に確認します。スポークがアクセス可能になると、メッセージをキューに送付し、メッセージをブロックしないようにします。

スポークがステータスを更新するか、フラグを完了する必要がある場合にも、同じことが当てはまります。Configuration and Progress ハブには、progressQ と呼ばれる異なるキューがあり、インストール進捗の更新を転送するためのメディアとして機能します。

これらのメカニズムは、テキストベースのインターフェイスにも使用されます。テキストモードではメインループはありませんが、キーボード入力にかかる時間がほとんどありません。

5.5. Anaconda モジュールおよび D-Bus ライブラリー

Anaconda のモジュールは、独立したプロセスとして実行されます。D-Bus API を使用してこれらのプロセスと通信するには、dasbus ライブラリーを使用します。

D-Bus API を使用したメソッドへの呼び出しは非同期ですが、dasbus ライブラリーでは Python で同期メソッド呼び出しに変換できます。以下のプログラムのいずれかを記述することもできます。

  • 非同期呼び出しおよびリターンハンドラーを使用するプログラム
  • 呼び出しが完了するまで呼び出しを待機する同期呼び出しを使用するプログラム

スレッドと通信の詳細は、Anaconda スレッド間 の通信を参照してください。

また、Anaconda は、モジュールで実行している Task オブジェクトを使用します。タスクには、追加のスレッドで自動的に実行される D-Bus API とメソッドがあります。タスクを正常に実行するには、sync_run_task 関数および async_run_task ヘルパー関数を使用します。

5.6. Hello World アドオンの例

Anaconda の開発者は、"Hello World" というサンプルアドオンを公開しています。これは GitHub で入手できます。これ以降のセクションの説明は、このアドオンの内容をもとに記述されています。

5.7. Anaconda アドオン構造

Anaconda アドオンは、__init__.py とその他のソースディレクトリー (サブパッケージ) のディレクトリーが含まれる Python パッケージです。Python では各パッケージ名を一度だけインポートできるため、パッケージの最上層のディレクトリーに一意の名前を指定します。アドオンは名前に関係なく読み込まれるため、任意の名前を使用できます。唯一の要件は、特定のディレクトリーに配置する必要があることです。

そのため、推奨のアドオン命名規則は Java パッケージまたは D-Bus サービス名に似ています。

ディレクトリー名を Python パッケージの一意の識別子にするには、ドットの代わりにアンダースコア (_) を使用して、アドオン名の前に組織の逆引きドメイン名を付けます。例: com_example_hello_world

重要

各ディレクトリーに __init__.py ファイルを作成してください。このファイルがないディレクトリーは、無効な Python パッケージとみなされます。

アドオンを作成する場合は、以下を確認してください。

  • 各インターフェイス (グラフィカルインターフェイスおよびテキストインターフェイス) のサポートは個別のサブパッケージで利用可能です。このサブパッケージには、グラフィカルインターフェイスには gui、テキストベースのインターフェイスには tui という名前が付けられています。
  • gui および tui パッケージには、spokes サブパッケージが含まれています。[1]
  • パッケージに含まれるモジュールには任意の名前があります。
  • gui/ および tui/ ディレクトリーには、任意の名前の Python モジュールを含めることができます。
  • アドオンの実際の作業を実行するサービスがあります。このサービスは、Python またはその他の言語で記述できます。
  • サービスは、D-Bus およびキックスタートのサポートを実装します。
  • アドオンには、サービスの自動起動を有効にするファイルが含まれます。

以下は、全インターフェイス (Kickstart、GUI、および TUI) をサポートするアドオンのディレクトリー構造の例です。

com_example_hello_world
├─ gui
│  ├─ init.py
│  └─ spokes
│     └─ init.py
└─ tui
   ├─ init.py
   └─ spokes
   └─ init.py
Copy to Clipboard Toggle word wrap

各パッケージには、API で定義される 1 つ以上のクラスから継承されるクラスを定義する任意の名前を持つモジュールが少なくとも 1 つ含まれる必要があります。

注記

すべてのアドオンは、docstring 規則の Python PEP 8 および PEP 257 のガイドラインに従ってください。Anaconda の docstring の実際のコンテンツの形式には合意はありません。唯一の要件は、人間が判読できることです。アドオンに自動生成されるドキュメントを使用する予定の場合には、docstrings はこれを行うために使用するツールキットのガイドラインに従う必要があります。

アドオンが新しいカテゴリーを定義する必要がある場合は、カテゴリーサブパッケージを追加できますが、これは推奨していません。



[1] アドオンが新しい カテゴリー を定義する必要がある場合に、gui パッケージにカテゴリーサブパッケージが含まれる場合もありますが、これは推奨されません。

5.8. Anaconda サービスおよび設定ファイル

Anaconda サービスと設定ファイルは data/ ディレクトリーに含まれます。アドオンサービスを起動し、D-Bus を設定するには、これらのファイルが必要です。

以下は、Anaconda Hello World アドオンの例です。

例5.1 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>
Copy to Clipboard Toggle word wrap

このファイルは、インストール環境の /usr/share/anaconda/dbus/confs/ ディレクトリーに置く必要があります。org.fedoraproject.Anaconda.Addons.HelloWorld は D-Bus 上の addon のサービスの場所に対応する必要があります。

例5.2 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
Copy to Clipboard Toggle word wrap

このファイルは、インストール環境の /usr/share/anaconda/dbus/services/ ディレクトリーに置く必要があります。org.fedoraproject.Anaconda.Addons.HelloWorld は D-Bus 上の addon のサービスの場所に対応する必要があります。Exec= で始まる行の値は、インストール環境でサービスを開始する有効なコマンドである必要があります。

5.9. GUI アドオンの基本機能

アドオンのキックスタートサポートと同様に、GUI サポートでは、アドオンのすべての部分に、API で定義される特定のクラスから継承されたクラスを定義するモジュールが少なくとも 1 つ含まれる必要があります。グラフィカルアドオンサポートの場合、追加すべき唯一のクラスは NormalSpoke クラスで、これはスクリーンの通常スポークタイプのクラスとして pyanaconda.ui.gui.spokes に定義されています。詳細は、Anaconda ユーザーインターフェイス を参照してください。

NormalSpoke から継承された新しいクラスを実装するには、API が必要とする以下のクラス属性を定義する必要があります。

  • builderObjects: スポークの .glade ファイルからすべての最上位オブジェクトをリスト表示します。これは、子オブジェクトでスポークに (再帰的に) 公開される必要があります。すべてをスポークに公開する必要がある場合は、リストを空にする必要があります。
  • mainWidgetName: .glade ファイルで定義されているようにメインウィンドウウィジェット (Add Link) の ID が含まれます。
  • uiFile: .glade ファイルの名前が含まれます。
  • category: スポークが属するカテゴリーのクラスが含まれます。
  • icon: ハブ上のスポークに使用するアイコンの識別子が含まれます。
  • title: ハブ上のスポークに使用するタイトルを定義します。

5.10. アドオンのグラフィカルユーザーインターフェイス (GUI) サポートの追加

作成したアドオンに、グラフィカルユーザーインターフェイス (GUI) のサポートを追加できます。これを行うには、次の主な手順を実行します。

  1. Normalspoke クラスに必要な属性を定義します。
  2. __init__initialize メソッドを定義します。
  3. refreshapply、および execute メソッドを定義します。
  4. status および readycompleted および mandatory のプロパティーを定義します。

前提条件

  • アドオンにキックスタートのサポートが含まれている。Anaconda アドオンの構造 を参照してください。
  • Anaconda 固有の Gtk ウィジェット (SpokeWindow など) が含まれる anaconda-widgets および anaconda-widgets-devel パッケージがインストールされている。

手順

  • アドオンのグラフィカルユーザーインターフェイス (GUI) のサポートを追加するには、次の例に従って、必要なすべての定義を含む次のモジュールを作成します。

    # 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 Clipboard Toggle word wrap

    __all__ 属性は spoke クラスをエクスポートします。この後に、以前 GUI アドオンの基本機能 で言及した、属性の定義を含む定義の最初の行が続きます。これらの属性値は、com_example_hello_world/gui/spokes/hello.glade ファイルで定義されるウィジェットを参照します。この他に、以下の 2 つの重要な属性があります。

  • category。この値は、com_example_hello_world.gui.categories モジュールの HelloWorldCategory クラスからインポートされます。アドオンへのパスが sys.path にある HelloWorldCategory。これにより、com_example_hello_world パッケージから値をインポートできます。category 属性は N_ function 名の一部で、変換用の文字列をマークしますが、変換は後の段階で行われるため、変換されていない文字列のバージョンを返します。
  • title。この属性の定義にはアンダースコアが 1 つ含まれています。title 属性アンダースコアは、タイトル自体の先頭をマークし、Alt+H キーボードショートカットを使用してスポークに到達できるようにします。

    通常、クラス定義のヘッダーとクラス attributes の定義に続くのは、クラスのインスタンスを初期化するコンストラクターです。Anaconda グラフィカルインターフェイスオブジェクトの場合、新しいインスタンスの初期化には __init__ メソッドおよび initialize メソッドの 2 つのメソッドがあります。

    このようなメソッドが 2 つある理由は、spoke の初期化に時間がかかる可能性があるため、あるタイミングで GUI オブジェクトがメモリーに作成され、別のタイミングで完全に初期化される可能性があるためです。したがって、__init__ メソッドは親の __init__ メソッドのみを呼び出し、たとえば、GUI 以外の属性を初期化する必要があります。一方、インストーラーのグラフィカルユーザーインターフェイスの初期化時に呼び出される initialize メソッドは、スポークの完全な初期化を完了する必要があります。

    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")
    Copy to Clipboard Toggle word wrap

    __init__ メソッドに渡されるデータパラメーターは、すべてのデータが保存されるキックスタートファイルのメモリー内ツリーのような表示になります。ancestor の __init__ メソッドのいずれかで、self.data 属性に格納されます。これにより、クラス内の他のすべてのメソッドで構造の読み取りおよび修正が可能になります。

    注記

    RHEL10 以降、storage object は利用できなくなりました。アドオンがストレージ設定と対話する必要がある場合は、Storage DBus モジュールを使用します。

    HelloWorldData クラスは Hello World アドオンの例 ですでに定義されているため、このアドオンの self.data にはすでにサブツリーがあります。クラスのインスタンスである root は self.data.addons.com_example_hello_world として利用できます。

    ancestor の __init__ が実行するもう 1 つのアクションは、スポークの .glade ファイルを使用して GtkBuilder のインスタンスを初期化し、それを self.builder として保存することです。initialize メソッドは GtkBuilder を使用して、キックスタートファイルの %addon セクションにあるテキストを表示および変更するために使用される GtkTextEntry を取得します。

    __init__ および initialize メソッドは両方とも、スポークの作成時に重要となります。ただし、スポークの主なロールは、スポークの値の表示と設定を変更または確認したいユーザーがアクセスすることです。これを有効にするには、その他の 3 つの方法を使用できます。

  • refresh: スポークがアクセスされようとするときに呼び出されます。このメソッドは、スポーク (主に UI 要素) の状態を更新し、表示されるデータが内部データ構造と一致するようにします。これにより、self.data 構造に保存されている現在の値が表示されるようにします。
  • apply: スポークが残っている場合に呼び出され、UI 要素の値を self.data 構造に戻す際に使用されます。
  • execute: ユーザーがスポークを離れる場合に呼び出され、スポークの新しい状態に基づいてランタイムの変更を実行する際に使用されます。

    これらの関数は、以下のように Hello World アドオンのサンプルに実装されます。

    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 Clipboard Toggle word wrap

    複数の追加のメソッドを使用して、スポークの状態を制御できます。

  • ready: スポークへアクセスできるかどうかを判断します。値が "False" の場合は spoke へアクセスできません。たとえば、パッケージソースを設定する前に Package Selection スポークにアクセスできません。
  • completed - スポークが完了しているかどうかを確認します。
  • mandatory: スポークが必須かどうかを判別します。たとえば、自動パーティションを使用する場合でも、常にアクセスする必要がある Installation Destination スポークが必須かどうか判別します。

    これらの属性はすべて、インストールプロセスの現在の状態に基づいて動的に決定する必要があります。以下は、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
    Copy to Clipboard Toggle word wrap

    これらのプロパティーが定義された後、スポークはそのアクセス可能性と完全性を制御できますが、内部で設定された値のサマリーを提供することはできません。スポークにアクセスして、スポークがどのように設定されているかを確認する必要がありますが、これは望ましくない場合があります。このため、status という追加のプロパティーが存在します。このプロパティーには、設定された値の短いサマリーを含む 1 行のテキストが含まれ、スポークタイトルの下のハブに表示することができます。

    status プロパティーは、以下のように Hello World の例のアドオンで定義されます。

    @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 Clipboard Toggle word wrap

    例で説明しているプロパティーをすべて定義した後に、アドオンには、グラフィカルユーザーインターフェイス (GUI) とキックスタートを示す完全なサポートがあります。

    注記

    ここで示した例は非常にシンプルで、制御を含むものはありません。GUI で機能的かつインタラクティブなスポークを開発するには、Python Gtk プログラミングに関する知識が必要です。

    主な制限の 1 つとして、それぞれのスポークに独自のメインウィンドウ (SpokeWindow ウィジェットのインスタンス) が必要である点が挙げられます。このウィジェットは、Anaconda 固有の他のウィジェットとともに、anaconda-widgets パッケージにあります。Glade など、GUI サポートでアドオンの開発に必要な他のファイルは、anaconda-widgets-devel パッケージで見つけることができます。

    グラフィカルインターフェイスのサポートモジュールに必要なメソッドをすべて含めたら、次のセクションに進み、テキストベースのユーザーインターフェイスのサポートを追加できます。

5.11. アドオン GUI の高度な機能

pyanaconda パッケージには、ヘルパー関数やユーティリティー関数が複数含まれ、ハブやスポークで使用されるコンストラクトも含まれます。そのほとんどは、pyanaconda.ui.gui.utils パッケージにあります。

Hello World アドオンの例は、Anaconda も使用する englightbox コンテンツマネージャーの使用方法を示しています。このコンテンツマネージャーはウィンドウをライトボックスに入れ、可視性を高め、ユーザーの基礎となるウィンドウとの対話を防ぐことに重点を置くことができます。この機能を示すために、サンプルアドオンには新しいダイアログウィンドウを開くボタンが含まれています。ダイアログ自体は、pyanaconda.ui.gui.init で定義される GUIObject クラスから継承される特別な HelloWorldDialog です。

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()
Copy to Clipboard Toggle word wrap

Defining an englightbox Dialog のサンプルコードは、ダイアログのインスタンスを作成してから、enlightbox コンテキストマネージャーを使用してライトボックス内でダイアログを実行します。コンテキストマネージャーにはスポークのウィンドウへの参照があり、ダイアログのライトボックスをインスタンス化するためにダイアログのウィンドウだけを必要とします。

5.12. TUI アドオンの基本機能

Anaconda は、テキストベースのインターフェイス (TUI) にも対応しています。このインターフェイスは機能がさらに制限されていますが、一部のシステムでは、インタラクティブインストールの唯一の選択肢となる場合があります。テキストベースのインターフェイスとグラフィカルインターフェイスの違い、および TUI の制限に関する詳細は、Introduction to Anaconda and add-ons を参照してください。

注記

テキストインターフェイスのサポートをアドオンに追加するには、Anaconda add-on structure の説明に従って、tui ディレクトリー内に新しいサブパッケージのセットを作成します。

インストーラーのテキストモードサポートは simpleline ライブラリーに基づいており、非常にシンプルなユーザーの対話のみを許可します。テキストモードインターフェイスは、

  • カーソルの移動には対応していません。代わりに、ラインプリンターのように動作します。
  • 視覚的機能拡張 (異なる色やフォントの使用など) はサポートしません。

内部的には、simpleline ツールキットには、AppUIScreen および Widget の 3 つの主要クラスがあります。ウィジェットは、画面に出力される情報が含まれるユニットです。これらは、App クラスの単一のインスタンスによって切り替えられる UIScreens に配置されます。基本的な要素に加えて、hubsspokesdialogs には、グラフィカルインターフェイスに似たさまざまなウィジェットが含まれています。

アドオンで最も重要なクラスは、NormalTUISpoke および pyanaconda.ui.tui.spokes パッケージで定義される他のさまざまなクラスです。これらのクラスはすべて TUIObject クラスをベースとしています。TUIObject クラス自体は GUIObject クラスと同等です。各 TUI スポークは、NormalTUISpoke クラスを継承する Python クラスであり、API で定義される特別な引数とメソッドをオーバーライドします。テキストインターフェイスは GUI よりも簡単なため、引数は以下の 2 つのみになります。

  • title: GUI の title 引数と同様にスポークのタイトルを決定します。
  • category: スポークのカテゴリーを文字列として判別します。カテゴリー名はどこにも表示されず、グループ化にのみ使用されます。
注記

TUI は GUI とは異なる方法でカテゴリーを処理します。新しいスポークに既存のカテゴリーを割り当てます。新しいカテゴリーを作成するには、Anaconda にパッチを適用する必要があり、ほとんどメリットがありません。

また、各スポークは、initinitializerefreshapplyexecuteinputprompt、およびプロパティー (readycompletedmandatory、および status) などのいくつかのメソッドをオーバーライドすることも想定されています。

5.13. シンプルな TUI Spoke の定義

以下の例は、Hello World サンプルアドオンのシンプルな Text User Interface (TUI) スポークの実装を示しています。

前提条件

  • Anaconda add-on structure の説明に従って、tui ディレクトリーに新しいサブパッケージセットを作成している。

手順

  • 以下の例に従って、アドオン 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
    Copy to Clipboard Toggle word wrap

    詳細と最新のコードは、Hello World Anaconda Addon - GitHub Repository を参照してください。

    注記

    ancestor の init のみを呼び出す場合は init メソッドを上書きする必要はありませんが、この例のコメントでは、一般的な方法でスポーククラスのコンストラクターへ渡された引数を記述します。

    上の例では、以下のようになります。

  • setup メソッドは、各エントリーのスポークの内部属性のデフォルト値を設定します。これは、refresh メソッドによって表示され、input メソッドによって更新され、apply メソッドによって使用されて内部データ構造を更新します。
  • execute メソッドの目的は、GUI の同等のメソッドの目的と同じです。この場合、このメソッドは効果がありません。
  • input メソッドはテキストインターフェイスに固有のものです。キックスタートまたは GUI には同等のものはありません。input メソッドはユーザーの対話を行います。
  • input メソッドは、入力された文字列を処理し、そのタイプと値に応じてアクションを取ります。上記の例は値を要求してから、それを内部属性 (キー) として保存します。より複雑なアドオンでは、通常、文字をアクションとして解析したり、数値を整数に変換したり、追加の画面を表示したり、ブール値を切り替えたりするなど、重要なアクションを実行する必要があります。
  • この入力を別の画面で処理する必要がある場合に備えて、入力クラスの return 値は、InputState enum または input 文字列自体のいずれかである必要があります。グラフィカルモードとは対照的に、apply メソッドと execute メソッドは、スポークを離れるときに自動的に呼び出されません。入力メソッドから明示的に呼び出す必要があります。スポークの画面を閉じる (非表示にする) 場合も同様です。close メソッドから明示的に呼び出す必要があります。

    別のスポークで入力した追加情報が必要な場合など、別の画面を表示するには、別の TUIObject をインスタンス化し、ScreenHandler.push_screen_modal() を使用してそれを表示します。

    テキストベースのインターフェイスの制限により、TUI スポークは非常によく似た構造を持つ傾向があり、ユーザーがチェックまたはチェックを外して入力する必要があるチェックボックスまたはエントリーのリストで構成されます。

5.14. NormalTUISpoke を使用したテキストインターフェイススポークの定義

Defining a Simple TUI Spoke の例では、メソッドが利用可能かつ提供されるデータの出力と処理に対処する TUI スポークを実装する方法が示されました。ただし、pyanaconda.ui.tui.spokes パッケージの NormalTUISpoke クラスを使用してこれを実現する別の方法があります。このクラスを継承することで、設定する必要のあるフィールドと属性を指定するだけで、一般的な TUI スポークを実装できます。以下の例で説明します。

前提条件

  • Anaconda アドオン構造 で説明されているように、TUI ディレクトリーの下に新しいサブパッケージのセットを追加しました。

手順

  • 以下の例に従って、アドオン 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
    Copy to Clipboard Toggle word wrap

    詳細と最新のコードは、Hello World NormalTUISpoke - GitHub Repository を参照してください。

5.15. Anaconda アドオンのデプロイおよびテスト

独自の Anaconda アドオンをインストール環境にデプロイしてテストできます。テストを行う方法は、以下の手順に従います。

前提条件

  • アドオンを作成している。
  • D-Bus ファイルにアクセスできる。
  • lorax パッケージがインストールされている。

手順

  1. 任意の場所に DIR ディレクトリーを作成します。
  2. Add-on python ファイルを DIR/usr/share/anaconda/addons/ に追加します。
  3. D-Bus サービスファイルを DIR/usr/share/anaconda/dbus/services/ にコピーします。
  4. D-Bus サービス設定ファイルを /usr/share/anaconda/dbus/confs/ にコピーします。
  5. 更新 イメージを作成します。

    DIR ディレクトリーにアクセスします。

    cd DIR
    Copy to Clipboard Toggle word wrap

    更新 イメージを見つけます。

    find . | cpio -c -o | pigz -9cv > DIR/updates.img
    Copy to Clipboard Toggle word wrap
  6. mkksiso ユーティリティーを使用して、updates イメージを ISO ブートイメージに含めます。

    sudo mkksiso -u updates.img boot.iso new_boot.iso
    Copy to Clipboard Toggle word wrap
  7. 作成された new_boot.iso を起動します。

    アドオンに埋め込まれた updates イメージが自動的に適用され、インストール中にアドオンが使用されるようになります。

    既存のブートイメージをデプロイメントし、product.img ファイルを作成してイメージを再パッケージ化する方法は、Red Hat Enterprise Linux ブートイメージの抽出 を参照してください。

第6章 ポストカスタマイズタスクの完了

カスタマイズを行うには、以下のタスクを実行します。

  • product.img イメージファイルを作成します (グラフィカルカスタマイズにのみ適用)。
  • カスタムのブートイメージを作成します。

このセクションでは、product.img イメージファイルを作成し、カスタムブートイメージを作成する方法を説明します。

6.1. product.img ファイルの作成

product.img イメージファイルは、実行時に既存のインストーラーファイルを置き換える新しいインストーラーファイルを含むアーカイブです。

システムの起動時に、Anaconda は起動用メディア上の images/ ディレクトリーから product.img ファイルを読み込みます。その後、このディレクトリーにあるファイルを使用して、インストーラーのファイルシステムで同じ名前が付けられたファイルを置き換えます。置き換えると、インストーラーをカスタマイズします (例: デフォルトのイメージをカスタムイメージに置き換えるためなど)。

注記

product.img イメージには、インストーラーと同じディレクトリー構造が含まれている必要があります。インストーラーのディレクトリー構造の詳細は、以下の表を参照してください。

Expand
表6.1 インストーラーのディレクトリー構造およびカスタムコンテンツ
カスタムコンテンツのタイプファイルシステムの場所

pixmaps (ログ、サイドバー、トップバーなど)

/usr/share/anaconda/pixmaps/

GUI スタイルシート

/usr/share/anaconda/anaconda-gtk.css

Anaconda アドオン

/usr/share/anaconda/addons/

プロファイル設定ファイル

/etc/anaconda/profile.d/

カスタム設定ファイル

/etc/anaconda/conf.d/

Anaconda DBus サービスの conf ファイル

/usr/share/anaconda/dbus/confs/

Anaconda DBus サービスファイル

/usr/share/anaconda/dbus/services/

手順

  1. /tmp などの作業ディレクトリーに移動し、product/ という名前のサブディレクトリーを作成します。

    $ cd /tmp
    Copy to Clipboard Toggle word wrap
  2. サブディレクトリー product/ を作成します。

    $ mkdir product/
    Copy to Clipboard Toggle word wrap
  3. 置き換えるファイルの場所と同じディレクトリー構造を作成します。たとえば、インストールシステムの /usr/share/anaconda/addons ディレクトリーにあるアドオンをテストする場合は、作業ディレクトリーに同じ構造を作成します。

    $ mkdir -p product/usr/share/anaconda/addons
    Copy to Clipboard Toggle word wrap
    注記

    インストーラーのランタイムファイルシステムを表示するには、インストールを起動し、仮想コンソール 1 (Ctrl+Alt+F1) に切り替えてから、2 番目の tmux ウィンドウ (Ctrl+b+2) に切り替えます。ファイルシステムの閲覧に使用できるシェルプロンプトが開きます。

  4. カスタマイズしたファイル (この例では、Anaconda のカスタムアドオン) を新しく作成したディレクトリーに配置します。

    $ cp -r ~/path/to/custom/addon/ product/usr/share/anaconda/addons/
    Copy to Clipboard Toggle word wrap
  5. インストーラーに追加するすべてのファイルについて、ステップ 3 および 4 を繰り返します (ディレクトリー構造を作成してカスタムファイルをそこに配置します)。
  6. ディレクトリーのルートに .buildstamp ファイルを作成します。.buildstamp ファイルは、システムバージョン、製品、およびその他のパラメーターを説明します。以下は、Red Hat Enterprise Linux 8.4 からの .buildstamp ファイルの例です。

    [Main]
    Product=Red Hat Enterprise Linux
    Version=8.4
    BugURL=https://bugzilla.redhat.com/
    IsFinal=True
    UUID=202007011344.x86_64
    [Compose]
    Lorax=28.14.49-1
    Copy to Clipboard Toggle word wrap

    IsFinal パラメーターは、イメージが製品のリリース (GA) バージョン (True) か、Alpha、Beta、または内部マイルストーン (False) などのプレリリースであるかを指定します。

  7. product/ ディレクトリーに移動し、product.img アーカイブを作成します。

    $ cd product
    Copy to Clipboard Toggle word wrap
    $ find . | cpio -c -o | gzip -9cv > ../product.img
    Copy to Clipboard Toggle word wrap

    これにより、product/ ディレクトリーの上に product.img ファイルが 1 レベル上に作成されます。

  8. product.img ファイルを、デプロイメントした ISO イメージの images/ ディレクトリーに移動します。

    これで product.img ファイルが作成され、必要なカスタマイズの内容がそれぞれのディレクトリーに配置された状態になりました。

    注記

    ブート用メディアに product.img ファイルを追加する代わりに、別の場所にこのファイルを追加し、ブートメニューで inst.updates= ブートオプションを使用してロードできます。この場合、イメージファイルは任意の名前を指定することができ、インストールシステムからこの場所にアクセス可能な限り、任意の場所 (USB フラッシュドライブ、ハードディスク、HTTP、FTP、または NFS サーバー) に配置できます。

6.2. カスタムブートイメージの作成

ブートイメージと GUI レイアウトをカスタマイズしたら、変更を含む新しいイメージを作成します。

手順

  1. すべての変更が作業ディレクトリーに含まれていることを確認してください。たとえば、アドオンをテストする場合は、images/ ディレクトリーに product.img を配置してください。
  2. 現在の作業ディレクトリーが、抽出された ISO イメージの最上位ディレクトリー (例: /tmp/ISO/iso/) であることを確認します。
  3. 次のパッケージをインストールします: isomd5sumxorrisolorax:

    # dnf install isomd5sum xorriso lorax
    Copy to Clipboard Toggle word wrap
  4. mkefiboot を使用して EFI ブートイメージを作成します。

    # mkefiboot --label=ANACONDA /tmp/ISO/iso/EFI/BOOT/ /tmp/ISO/iso/images/efiboot.img
    Copy to Clipboard Toggle word wrap
  5. xorrisofs を使用して新しい ISO イメージを作成します。

    # xorrisofs -o ../NEWISO.iso -R -J -V RHEL-10-0-BaseOS-x86_64 --grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img -partition_offset 16 -appended_part_as_gpt -append_partition 2 C12A7328-F81F-11D2-BA4B-00A0C93EC93B /tmp/ISO/iso/images/efiboot.img -iso_mbr_part_type EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 -c boot.cat --boot-catalog-hide -b images/eltorito.img -no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info -eltorito-alt-boot -e --interval:appended_partition_2:all:: -no-emul-boot -graft-points .discinfo=/tmp/ISO/iso/.discinfo images/install.img=/tmp/ISO/iso/images/install.img images/pxeboot=/tmp/ISO/iso/images/pxeboot boot/grub2=/tmp/ISO/iso/boot/grub2 boot/grub2/i386-pc=/usr/lib/grub/i386-pc images/eltorito.img=/tmp/ISO/iso/images/eltorito.img EFI/BOOT=/tmp/ISO/iso/EFI/BOOT
    Copy to Clipboard Toggle word wrap

    上記の例では、以下のようになります。

    • 同じディスク上のファイルをロードするための場所を必要とするオプションに LABEL= ディレクティブを使用している場合は、-V オプションの値がイメージのブートローダー設定と一致することを確認します。ブートローダーの設定 (BIOS の場合は boot/grub2/grub.cfg、UEFI の場合は EFI/BOOT/grub.cfg) が inst.stage2=LABEL=disk_label スタンザを使用して同じディスクからインストーラーの 2 番目のステージをロードする場合は、ディスクラベルが一致する必要があります。

      重要

      ブートローダー設定ファイルで、ディスクラベルのすべてのスペースを \x20 に置き換えます。たとえば、RHEL 10.0 ラベルが付いた ISO イメージを作成する場合は、ブートローダー設定に RHEL\x2010.0 を使用する必要があります。

    • -o オプションの値 (-o ../NEWISO.iso) は、新しいイメージのファイル名に置き換えます。この例の値では、現在のディレクトリーの のディレクトリーに NEWISO.iso ファイルを作成します。このコマンドの詳細は、システムの xorrisofs(1) man ページを参照してください。
  6. MD5 チェックサムをイメージに埋め込みます。MD5 checksu を使用しないと、イメージ検証チェック (ブートローダー設定の rd.live.check オプション) が失敗し、インストールがハングする可能性があることに注意してください。

    # implantisomd5 ../NEWISO.iso
    Copy to Clipboard Toggle word wrap

    上記の例では、../NEWISO.iso を、直前の手順で作成したファイル名と ISO イメージの場所に置き換えます。

    これで、新しい ISO イメージを物理メディアまたはネットワークサーバーに書き込んで物理ハードウェアで起動することや、仮想マシンのインストールを開始できるようになりました。

法律上の通知

Copyright © 2025 Red Hat, Inc.
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.
Red Hat logoGithubredditYoutubeTwitter

詳細情報

試用、購入および販売

コミュニティー

Red Hat ドキュメントについて

Red Hat をお使いのお客様が、信頼できるコンテンツが含まれている製品やサービスを活用することで、イノベーションを行い、目標を達成できるようにします。 最新の更新を見る.

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。このような変更は、段階的に実施される予定です。詳細情報: Red Hat ブログ.

会社概要

Red Hat は、企業がコアとなるデータセンターからネットワークエッジに至るまで、各種プラットフォームや環境全体で作業を簡素化できるように、強化されたソリューションを提供しています。

Theme

© 2026 Red Hat
トップに戻る