OpenStack(Mitaka)で、EFI起動させてみる


■自分の環境
・ホストサーバ:ubuntu 16.04
・libvertのバージョン:1.3.1
・OpenStack:mitaka

■必須環境
・libvertのバージョン:1.2.9以上
・OpenStack:mitaka以上であること

■対象とする読者
OpenStack環境はすでにできているが、仮想サーバをEFI起動させたいという方に向けて書いています。
自分はなかなかハマってしまったので、備忘として。
やることがわかっていれば、簡単なのでしょうが、仮想化の初心者なので。。。

大きく分けて、3つの作業を行います。
1.KVMで、EFI起動できるようにする。
2.Glanceへのイメージ登録時に、EFI起動の設定を加える。
3.起動ファイルの再作成(バグ?対応。本当はいらない作業)

■参考にしたURL
また、今回の調査にあたり、以下のサイトや記事を参考にさせていただきました。
みなさま、ありがとうございます。
OpenStackドキュメント - Boot From UEFI image
uefi_bootsupport_techtip_final.pdf
OpenStackドキュメント - Images and instances
QEMU の為の UEFI ファームウェアのビルド手順
libvirt の UEFI 設定
第441回 QEMU/KVMでUEFIファームウェアを使う

それでは、始めていきます。
(EFI起動は、OpenStackのMitakaからの対応らしいので、ご注意を)

1.KVMで、EFI起動できるようにする。

自分はOpenStackの仮想サーバを、KVMで実行しているのですが、
KVMは、デフォルトではEFI起動はせず、BIOSでの起動となってしまうようです。
Horizonで作成したインスタンスを起動しようとすると、
エラーメッセージが出てきて、なんだか切ない気持ちになります。

そこで、まずはKVMでもEFI起動できるようにしていきます。
その前にlibvertのバージョンを調べます。

・libvirtのバージョン確認

$ virsh version
コンパイル時に使用したライブラリ: libvirt 1.3.1
使用中のライブラリ: libvirt 1.3.1
使用中の API: QEMU 1.3.1
実行中のハイパーバイザー: QEMU 2.5.0

libvirtのバージョンが1.2.9以上であることを確認してください。

バージョンに問題がなければ、KVMでEFI起動できるようにするための「OVMF」をインストールします。

・OVMFをインストール

$ sudo apt install ovmf

・何かできている
以下のコマンドを打って、それぞれ2つのファイルができていることを確認します。

$ ls /usr/share/OVMF/
OVMF_CODE.fd
OVMF_VARS.fd

これで、KVM側の作業は終了です。

2.Glanceへのイメージ登録時に、EFI起動の設定を加える。

Glanceへのイメージの登録の際に、EFI起動するための指定が必要です。
Horizonでは、設定項目がなさそうなので、コマンドラインから、Glanceの登録を行います。

・glanceイメージを登録
(筆者はvdiファイルを取り込んだので、file、disk-formatなどは環境に合わせた値に変換してください。)

$ openstack image create "NAME" --file XXXXXXXXXX.vdi --disk-format vdi --container-format bare --property hw_firmware_type=uefi --public

「--property hw_firmware_type=uefi」がポイント!
このpropertyを指定しないと、OpenStack側は、EFI起動せず、BIOSで起動させようとしてしまうので、注意してください!

MySqlでglanceユーザでログインし、「image_properties」テーブルを参照してみてください。
先ほど登録したGlanceイメージのpropertyが、name:hw_firmware_type、 value:uefiで登録されていることを確認します。

$ sudo mysql glance

MariaDB [glance]> select * from image_properties;
+----+--------------------------------------+-------------- ---+-------+---------------------+---------------
| id | image_id | name | value | created_at | updated_at >| deleted_at | deleted |
+----+--------------------------------------+------------- ----+-------+---------------------+---------------
| 1 | xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | hw_firmware_type | uefi | 2017-01-01 04:57:31 | 2017-01-01 04:57:31 | NULL | 0 |
+----+--------------------------------------+-------------- ---+-------+---------------------+---------------

これで、準備はOK。

その前に。。。

・driver.pyをちょっと見てみる
先ほど設定した「--property hw_firmware_type=uefi」はどのように使用されるのでしょうか。
そのヒントは、以下のpyファイルに書かれているようです。

$ view /usr/lib/python2.7/dist-packages/nova/virt/libvirt/driver.py

driver.py

(前略)
DEFAULT_UEFI_LOADER_PATH = {                          ←①
    "x86_64": "/usr/share/OVMF/OVMF_CODE.fd",
    "aarch64": "/usr/share/AAVMF/AAVMF_CODE.fd"
}

(中略)

            if hw_firmware_type == fields.FirmwareType.UEFI:    ←②
                if self._has_uefi_support():
                    global uefi_logged
                    if not uefi_logged:
                        LOG.warn(_LW("uefi support is without some kind of "
                                    "functional testing and therefore "
                                    "considered experimental."))
                        uefi_logged = True
                    guest.os_loader = DEFAULT_UEFI_LOADER_PATH[    ←③
                        caps.host.cpu.arch]
                    guest.os_loader_type = "pflash"
                else:
                    raise exception.UEFINotSupported()
            guest.os_mach_type = self._get_machine_type(image_meta, caps)
(後略)

①の部分にて、「DEFAULT_UEFI_LOADER_PATH」の指定が、
OVMFをインストールされた際にできた「OVMF_CODE.fd」に指定されていることがわかります。
これが、EFI起動するためのローダーとなるのでしょう。

さらに、いつこのPATHを使うかというと、
②の「if hw_firmware_type == fields.FirmwareType.UEFI」ブロックの中。
hw_firmware_typeや、UEFIなどの記載があります。

はい、先ほど指定したpropertyの値ですね。
おそらく、fields.FirmwareType.UEFIは、固定文字でuefiとなっているのでしょう。
このif文に入った際に、③でguest.os_loader = DEFAULT_UEFI_LOADER_PATHとあります。

きっとこれで、EFI起動するためのお膳立てはできているはず!

3.起動ファイルの再作成

・まだダメだった
これで普通にインスタンス作成すれば、起動すると思いきや!!

謎のエラーメッセージが出て起動しない!!

ここで数時間試行錯誤。

で、ふと思い、KVM側の設定を確認してみる。

$ virsh list
Id |名前 |状態
----------------------------------------------------
20 |instance-00000001 |実行中

・設定を確認
以下のコマンドで、VMの起動設定のXMLファイルを、確認/編集することができます。

$ virsh edit <インスタンス名>

virshコマンド実行
(前略)
  <os>
    <type arch='x86_64' machine='pc-i440fx-xenial'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
    <nvram template='/usr/share/OVMF/OVMF_CODE.fd'>/var/lib/libvirt/qemu/nvram/インスタンス名_VARS.fd</nvram>
    <boot dev='hd'/>
    <smbios mode='sysinfo'/>
  </os>
(後略)

あれれ? 参考にしたサイトと、表示が異なっている!!
「<nvram template='/usr/share/OVMF/OVMF_CODE.fd'>」となっている!?
「<nvram template='/usr/share/OVMF/OVMF_VARS.fd'>」になると思っていたのに!!

こちらのサイトでは、
OVMF_CODE.fdは、ファームウェアの指定で、
OVMF_VARS.fdは、nvramの指定、と説明されていました。
上記の設定は、nvramのテンプレートにOVMF_CODE.fdが設定されている。
うーん。もしかして設定が変なことになってしまっている???
ここは、OpenStack側が、自動で設定する項目のはずなのに。

・仕方ないので、起動ファイルを再作成
すでにOVMF_CODE.dfをテンプレートとして起動ファイルが作成されてしまっているため、
OVMF_VARS.dfをテンプレートとして作成するようにします。

まずは、すでに作成されているファイルがあると、再作成されないため、リネームする。

$ sudo mv /var/lib/libvirt/qemu/nvram/instance-00000001_VARS.fd /var/lib/libvirt/qemu/nvram/instance-00000001_VARS.fd_bk

以下のコマンドで、<os>部分のパラメータを書き換えます。

$ virsh edit <インスタンス名>

virshコマンド実行
(前略)
  <os>
    <type arch='x86_64' machine='pc-i440fx-xenial'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'></nvram>
    <boot dev='hd'/>
    <smbios mode='sysinfo'/>
  </os>
(後略)

・再作成

$ virsh start <インスタンス名>

・設定を確認

$ virsh edit <インスタンス名>

virshコマンド実行
(前略)
  <os>
    <type arch='x86_64' machine='pc-i440fx-xenial'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/var/lib/libvirt/qemu/nvram/インスタンス名_VARS.fd</nvram>  ←値が変更されていることを確認
    <boot dev='hd'/>
    <smbios mode='sysinfo'/>
  </os>
(後略)

・いけんじゃねーか
ということで、あとは、コマンドラインでも、Horizonからでも、普通にインスタンスを作成すれば、EFI起動がなされます。

本当は、XMLファイルを作成するスクリプトを編集すればスマートなのでしょうが、そこまでは追っていません。
また、今回はmitakaで試しましたが、最新版では解消されているかも??