virt-managerで仮想環境作成時に選択するOS Typeは何に使っているのか

この記事はLinux Advent Calendar 2014の17日目です。

virt-managerGUIを使って仮想環境を作るときにOS Typeというのを選択していると思いますが、これの設定はどのように使われているのか見てようと思います。 特に最近のvirt-managerはやたら選択可能なOSが多いように思えるんだけど、LinuxディストリビューションのところではArch LinuxとかGentooが無いんですね。その割にRedhat Linux 1.0なんてかなり昔のディストリビューションも入ってたりする謎さ加減です。
今回使っているvirt-managerのバージョンは1.1.0です。

Windowsだとこうだったり、

f:id:masami256:20141217203110p:plain

Linuxだとこうだったりです。

f:id:masami256:20141217203126p:plain

まず、これら選択項目がどこから来ているのか?というのが最初の疑問です。これはvirt-managerではなくてlibosinfoというライブラリが使われているようです。軽く確認してみるとvirt-managerは確かにlibosinfoに依存しています。

masami@saga:~$ pactree  -r libosinfo
libosinfo
└─virt-manager

このlibosinfoのgit treeはgit.fedorahosted.orgにてホスティングされています。data/osesを見てみるとxmlファイルのベースになるものがあり、windowslinuxディストリビューション等の設定が各ファイルにあることがわかります。

CentOSのファイルを見てみるとCentOS 6系、7系の設定があります。CentOS 7.0の設定を見てみますと名前、バージョンと言ったものの他に、vmlinuz、initrd.imgのパス(これはネットワークからインストールする場合用かな)がありますね。多分この中で重要なのはresourcesの部分でしょう。cpu数、cpuのタグはcpuの種類かな、ramのサイズ、ストレージサイズなど初期設定値らしきものが入ってますね。

  <os id="http://centos.org/centos/7.0">
    <short-id>centos7.0</short-id>
    <_name>CentOS 7.0</_name>
    <version>7.0</version>
    <_vendor>CentOS</_vendor>
    <family>linux</family>
    <distro>centos</distro>
    <upgrades id="http://centos.org/centos/6.5"/>
    <clones id="http://redhat.com/rhel/7.0"/>

    <media arch="x86_64">
      <iso>
        <system-id>LINUX</system-id>
        <volume-id>.*CentOS 7 x86_64.*</volume-id>
      </iso>
      <kernel>isolinux/vmlinuz</kernel>
      <initrd>isolinux/initrd.img</initrd>
    </media>

    <resources arch="all">
      <minimum>
        <n-cpus>1</n-cpus>
        <cpu>1000000000</cpu>
        <ram>1073741824</ram>
        <storage>10737418240</storage>
      </minimum>

      <recommended>
        <cpu>400000000</cpu>
        <ram>1073741824</ram>
        <storage>9663676416</storage>
      </recommended>
    </resources>

    <installer>
      <script id='http://redhat.com/scripts/rhel/jeos'/>
      <script id='http://redhat.com/scripts/rhel/desktop'/>
    </installer>
  </os>

では、今度はvirt-managerを見てみましょう。ソースはlibisoinfoと同じくgit.fedorahosted.orgにてホスティングされています。virt-manager内ではいくつかモジュールがありますが、仮想環境の作成に使われるのはvirtinstです。その中でosdict.pyがos typeによる設定に絡んでいます。細かく見ていってもしょうがないので、どんな使い方をしているかを見て行きましょう。

このファイルには_OSVariant_OsVariantOsInfo``の2個の主要なクラスがあります。使い方としては先に_OSVariantのインスタンスを作ってから_OsVariantOsInfoを使う感じだと思います。 _OSVariantのinit```では以下のように _get_default()を使って、ハードウェア的な機能を設定したりしています。

        self.acpi = _get_default("acpi", acpi)
        self.apic = _get_default("apic", apic)
        self.clock = _get_default("clock", clock)
〜略〜
       self.virtiodisk = _get_default("virtiodisk", virtiodisk)

_get_default()はこのような関数です。

        def _get_default(name, val, default=_SENTINEL):
            if val == _SENTINEL:
                if not parent:
                    return default
                return getattr(parent, name)
            return val

parentはコメントにはこう書いてあります。コメントだとFedoraFooが親としてfedoraFOO-1を持つだろうなんて書いているので細かいバージョンは無視してparent側の設定を使えるというところですかね。

   @parent: Name of a pre-created variant that we want to extend. So
        fedoraFOO would have parent fedoraFOO-1. It's used for inheriting
        values.

次に_OsVariantOsInfoクラスを見ていきます。先ほど_get_default()で機能を設定した内容を__init__()で見ていきます。

        acpi = self._is_acpi()
        apic = self._is_apic()
        clock = self._get_clock()

↑の関数がこの辺です。osによってその機能をどうするかということをやっています。

    def _get_clock(self):
        if _OsVariantOsInfo.is_windows(self._os) or \
           self._os.get_family() in ['solaris']:
            return "localtime"
        return _SENTINEL

    def _is_acpi(self):
        if self._os.get_family() in ['msdos']:
            return False
        return _SENTINEL

    def _is_apic(self):
        if self._os.get_family() in ['msdos']:
            return False
        return _SENTINEL

nic周りでは_is_virtionet()があり、fedoraはバージョンにより、その他のOSはnicの選択でvirtio-netを選んだ場合にtrueを返すようになっています。

    def _is_virtionet(self):
        if self._os.get_distro() == "fedora":
            if self._os.get_version() == "unknown":
                return _SENTINEL
            return int(self._os.get_version() >= 9) or _SENTINEL

        fltr = libosinfo.Filter()
        fltr.add_constraint("class", "net")
        devs = self._os.get_all_devices(fltr)
        for dev in range(devs.get_length()):
            d = devs.get_nth(dev)
            if d.get_name() == "virtio-net":
                return True
        return _SENTINEL

次はget_recommended_resources()。ここではOS Type選択された(もしくは.isoのファイル名から自動でOSを決めているはず)libosinfoのxmlに設定されていたcpuモデルとかramサイズなどを読み込んでます。この関数が定義されているのは__init__()の中です。

    def get_recommended_resources(self, arch):
        ret = {}
        def read_resource(resources, arch):
            for i in range(resources.get_length()):
                r = resources.get_nth(i)
                if r.get_architecture() == arch:
                    ret["ram"] = r.get_ram()
                    ret["cpu"] = r.get_cpu()
                    ret["n-cpus"] = r.get_n_cpus()
                    ret["storage"] = r.get_storage()
                    break

        read_resource(self._os.get_recommended_resources(), "all")
        read_resource(self._os.get_recommended_resources(), arch)

さて、サクッと見てきましたが最初のOS Typeの設定でその後の設定内容を適宜変えていることがわかりました。この辺の設定は仮想環境作成時だと以下の画面にあるCustomize configuration before installのチェックをつけるとOSのインストール前に変更できます。例えばnicにvirtioを使いたい場合などはチェックボックスにチェックしてnicの設定を変えたうえでOSをインストールする感じですね。

f:id:masami256:20141217212729p:plain