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](https://specs.openstack.org/openstack/nova-specs/specs/mitaka/implemented/boot-from-uefi.html)
・[uefi_bootsupport_techtip_final.pdf](https://01.org/sites/default/files/page/uefi_bootsupport_techtip_final.pdf)
・[OpenStackドキュメント - Images and instances](https://docs.openstack.org/admin-guide/compute-images-instances.html)
・[QEMU の為の UEFI ファームウェアのビルド手順](http://blog.usaturn.net/build_uefi_for_qemu/)
・[libvirt の UEFI 設定](http://blog.usaturn.net/setting_libvirt_for_uefi/)
・[第441回　QEMU/KVMでUEFIファームウェアを使う](http://gihyo.jp/admin/serial/01/ubuntu-recipe/0441)


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

##１．KVMで、EFI起動できるようにする。##
自分はOpenStackの仮想サーバを、KVMで実行しているのですが、
KVMは、デフォルトではEFI起動はせず、BIOSでの起動となってしまうようです。
Horizonで作成したインスタンスを起動しようとすると、
エラーメッセージが出てきて、なんだか切ない気持ちになります。
<img width="737" alt="スクリーンショット 2017-02-24 19.02.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/167745/bb614af5-566a-0f60-2ca9-18be8b99a7e4.png">


そこで、まずは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側の作業は終了です。



##２．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

```ruby: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.起動ファイルの再作成##
・まだダメだった
これで普通にインスタンス作成すれば、起動すると思いきや！！

謎のエラーメッセージが出て起動しない！！
<img width="1011" alt="スクリーンショット 2017-02-24 22.49.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/167745/2335a734-5fc1-52b4-7928-695bd501a486.png">

ここで数時間試行錯誤。

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

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

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

```ruby: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>
（後略）
```

あれれ？　[参考にしたサイト](http://blog.usaturn.net/build_uefi_for_qemu/)と、表示が異なっている！！
「\<nvram template='/usr/share/OVMF/OVMF_CODE.fd'>」となっている！？
「\<nvram template='/usr/share/OVMF/OVMF_VARS.fd'>」になると思っていたのに！!

[こちらのサイト](http://blog.usaturn.net/build_uefi_for_qemu/)では、
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 <インスタンス名>

```ruby: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 <インスタンス名>

```ruby: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起動がなされます。
<img width="868" alt="スクリーンショット 2017-02-26 16.43.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/167745/c79a6275-8278-83c6-bf6d-06757204ca61.png">


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