Python で KVM 用のスクリプトを作成する: 第 1 回 libvirt

現在市場にあるサーバー・オペレーティング・システムの大部分は仮想化機能を標準的に備えています。Linux の世界では、サーバーを仮想化するための主要な選択肢は KVM (Kernel-based Virtual Machine) と Xen の 2 つです。KVM は、Red Hat やその他の Linux が使用している主要な技術です。Xen は Citrix が所有していますが、コア機能の大部分は公開されています。

VMM (仮想マシン・マネージャー、つまり virt-manager) プロジェクトは、KVM と Xen 両方の仮想マシン (VM) インスタンスの作成および実行を管理するためのツールを提供しています。VMM は Python で作成されており、グラフィカル・ユーザー・インターフェースを作成するために GTK+ ライブラリーを使用しています。実際の処理は libvirt ライブラリーによって行われており、この libvirt ライブラリーをこの連載記事で取り上げます。libvirt は Red Hat がスポンサーとなって開発されているライブラリーですが、GNU Lesser General Public License の下でオープンソースのプロジェクトとして利用することができます。

libvirt は、API (Application Programming Interface) ライブラリー、デーモン (libvirtd)、デフォルトのコマンドライン・ユーティリティー (virsh) など、いくつかの異なる部分で構成されています。記事では、すべてのテストに Ubuntu Server 11.04 を使用します。この第 1 回の「インストールとセットアップ」セクションでは、記事で紹介するスクリプトを開発するために行った、サーバーの構成作業をすべて紹介します。それに続くセクションでは、KVM による仮想化と libvirt の基本について説明し、皆さんの興味をそそるためにコマンドライン・スクリプトをいくつか紹介します。第 2 回ではさらに詳細な内容として、libvirt、Python、wxPython を使用して独自の仮想化管理ツールを作成する方法について説明します。

はじめに

実際のコード・サンプルを紹介する前に、KVM による仮想化に関連する用語と概念をいくつか説明しておきましょう。Ubuntu Server 11.04 のようなサーバーに KVM をインストールする場合、仮想化ホスト、つまりハイパーバイザーを構築していることになります。これはつまり、そのサーバーは KVM ホスト上で実行される複数のゲスト・オペレーティング・システムをホストできるということです。それぞれのゲストは「ドメイン」と呼ばれ、その動作は個別のマシン上で単一のサーバー・インスタンスとして動作する場合とほとんど同じです。サーバーへの接続は、あたかも物理マシンと通信しているかのように、SSH (Secure Shell) または VNC (Virtual Network Computing) によって行われます。

KVM は、ハイパーバイザーつまりゲスト・マネージャーとして動作しますが、実際のマシンのエミュレーションは QEMU が行います。つまりQEMU がターゲット・マシンのネイティブ・インストラクション・セットを実行します。ゲストが x86 の場合、QEMU が実行する x86 ネイティブのインストラクション・セットは、実際のハードウェアで直接実行できるネイティブ・インストラクションに変換されます。それ以外のアーキテクチャーの場合 (ARM など) には、必ず変換プロセスが必要です。KVM と QEMU とを組み合わせることで、現在入手可能なオペレーティング・システムを実質的にすべて仮想化することができ、さらにはもはや入手できなくなったいくつかのオペレーティング・システムも仮想化することができます。

1 つのゲスト・ドメインは、1 つ以上のディスク・イメージ・ファイルと 1 つの XML ベースの構成ファイルを含むいくつものファイルで構成されます。この構成のおかげで、ベースとなるシステム・イメージを作成し、要求に合うように構成ファイルを変更することで、極めて簡単に複数の VM を管理することができます。KVM/QEMU を構成したり、KVM/QEMU と通信したりするための 1 つの手段が libvirt ツールキットです。libvirt をベースに管理製品を標準化しているベンダーは数多くあります。

ドメインを構成するための典型的なファイルの内容を見てください。リスト 1libvirt のサンプルに含まれている testdev.xml ファイルを示しています。

リスト 1. デバイスを定義する XML ファイル

<device>   <name>File_test_device</name>   <capability type='system'>         <hardware>               <vendor>Libvirt</vendor>               <version>Test driver</version>               <serial>123456</serial>               <uuid>11111111-2222-3333-4444-555555555555</uuid>         </hardware>         <firmware>               <vendor>Libvirt</vendor>               <version>Test Driver</version>               <release_date>01/22/2007</release_date>         </firmware>   </capability></device>

リスト 2 に示すテスト用の domfv0.xml ファイルを見ると、もう少し詳しく仮想デバイスの構成方法を理解することができます。

リスト 2. domfv0.xml デバイス定義ファイル

<devices>  <emulator>/usr/lib/xen/bin/qemu-dm</emulator>         <interface type='bridge'>               <source bridge='xenbr0'/>               <mac address='00:16:3e:5d:c7:9e'/>               <script path='vif-bridge'/>         </interface>         <disk type='file'>               <source file='/root/fv0'/>               <target dev='hda'/>         </disk>         <disk type='file' device='cdrom'>               <source file='/root/fc5-x86_64-boot.iso'/>               <target dev='hdc'/>               <readonly/>         </disk>         <disk type='file' device='floppy'>               <source file='/root/fd.img'/>               <target dev='fda'/>         </disk>         <graphics type='vnc' port='5904'/></devices>

ここで重要な点は、これらのファイルは比較的容易に内容を理解できること、そのため独自のファイルを作成するのも容易であることです。手作業でいくつも構成ファイルを作成することもできますが、Python のようなスクリプト言語を使用して構成ファイルの作成を自動化することもできます。


インストールとセットアップ

この記事では KVM 用のスクリプトを作成する方法について説明しているため、基本的な前提として読者のサーバーに KVM がインストールされているものとします。Ubuntu Server 11.04 の場合、セットアップ・プロセスの中で仮想化機能をインストールすることができます。そのためには「Software selection (ソフトウェアの選択)」画面で「Virtual Machine host (仮想マシンホスト)」を選択します。またリモートからマシンに接続したい場合には「OpenSSH server (OpenSSH サーバー)」を選択することもできます。

最初に必要なことは、最新バージョンの libvirt をインストールすることです。そのためには少しばかりコマンドラインで作業をする必要があります。Ubuntu Server 11.04 をインストールすると、libvirt バージョン 0.8.8 がインストールされます。libvirt の Web サイトから入手できる最も優れた最新のバージョンは 0.9.5 です。より新しいバージョンの libvirt をインストールするには、そのバージョンの libvirt を含む PPA (Personal Package Archive) リポジトリーをシステムに追加する必要があります。launchpad.net サイトで libvirt を少し検索してみると、いくつかの候補が見つかりますが、更新を試みる前にリポジトリーの詳細ページを調べることが重要です。というのも、一部のリポジトリーのパッケージは破損している可能性があるからです。Ubuntu の仮想化チームは libvirt を含むいくつかの PPA リポジトリーを保持しています。この記事の執筆時点で利用可能な最新バージョンは 0.9.2-4 でした。

以下の手順でその最新バージョンをインストールします。

  1. 以下のようにして python-software-properties パッケージをインストールします。
    sudo apt-get install python-software-properties

    このコマンドにより、サードパーティーのソースを参照するために必要な add-apt-repository コマンドを使用できるようになります。

  2. 以下のコマンドを入力します。
    sudo add-apt-repository ppa:ubuntu-virt/ppasudo apt-get updatesudo apt-get install libvirt-bin

  3. この記事では Python を使用してすべてのスクリプトを作成するため、スクリプトの作成やテストが容易になるように、IDLE シェルをインストールします。

    このステップは Ubuntu サーバーにデスクトップ環境がインストールされていることが前提となります。デスクトップ環境をインストールする最も簡単な方法は、以下のコマンドを使用する方法です。

    sudo apt-get install ubuntu-desktop

この作業を終えると、Ubuntu ソフトウェアのインストーラーに加えて、いくらでもグラフィカル・アプリケーションを利用できるようになります。Python の IDLE ツールをインストールするためには Ubuntu ソフトウェアセンターを使用することができます。


サンプル・スクリプト

ここでは、コードの詳細について説明する前に、libvirt を扱うための基本事項について見て行きましょう。アプリケーションと libvirt ライブラリーとの通信には単純なリモート・プロシージャー・コール・メカニズムを使用します。そのため、リモートのハイパーバイザーと TCP/IP 接続で通信するアプリケーションを作成することができます。接続確立の対象となる具体的なハイパーバイザーを特定するためには URI を使用します (URI (Uniform Resource Identifier) は IETF (Internet Engineering Task Force) の RFC (Request for Comments) 2396 で定義されています)。

通常、ローカル接続に認証は必要ありませんが、一部のリモート接続には認証が必要です。セキュリティーの構成は libvirt.conf ファイルで制御します。固有のドメインとの通信を最も包括的に制御する方法はネットワーク・フィルタリングを使用する方法です。以下の例はネットワーク・トラフィックをフィルターで制御する方法を示しています。

<devices>    <interface type='bridge'>      <mac address='00:16:3e:5d:c7:9e'/>      <filterref filter='clean-traffic'/>    </interface></devices>

このスニペットは clean-traffic というフィルターを定義しており、指定された MAC (Media Access Control) アドレスへのネットワーク・トラフィックすべてに対し、このフィルターが適用されます。この clean-traffic の XML を調べてみると、以下の内容が含まれていることがわかります。

<filter name='clean-traffic' chain='root'>     <uuid>6f145c54-e3de-4c33-544a-70b69c16d9da</uuid>     <filterref filter='no-mac-spoofing'/>     <filterref filter='no-ip-spoofing'/>     <filterref filter='allow-incoming-ipv4'/>     <filterref filter='no-arp-spoofing'/>     <filterref filter='no-other-l2-traffic'/>     <filterref filter='qemu-announce-self'/></filter>

このフィルターの機能は非常に詳細かつ完全に記述されています。libvirt のドキュメントとサンプル・ファイルのローカル・コピーが必要な場合には、たった 1 つのコマンドで入手することができます。そのコマンドは以下のとおりです。

sudo apt-get install libvirt-doc

このコマンドを実行すると、すべてのドキュメントが /usr/share/doc/libvirt-doc ディレクトリーに格納されます。Python の例については少し後で説明します。もっと新しいバージョンの libvirt に更新した場合には、その Python バインディングを明示的にインストールする必要があるかもしれません。そのために必要なコマンドは以下の 1 つのみです。

sudo apt-get install python-libvirt

では、ローカルの QEMU インスタンスとの接続を確立するための Python コードについて検討し、そのコードによって定義されたドメインを調べるために、Python の IDLE コンソールを使用します。リスト 3 にその IDLE コンソールに表示される内容を示します。

リスト 3. IDLE コンソールで Python コードを表示する

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) [GCC 4.5.2] on linux2Type "copyright", "credits" or "license()" for more information.==== No Subprocess ====>>> import libvirt>>> conn=libvirt.open("qemu:///system")>>> names = conn.listDefinedDomains()>>> print names['Test1', 'SBSLite', 'UbuntuServer1104', 'Win7_64-bit']]>>>

このコードは定義されたすべてのドメインのリストを取得する方法を示しています。listDefinedDomains() 関数からの戻り値を見ると、ドメインの名前が 4 つ含まれていることがわかります。ハイパーバイザーとの接続を確立できると、多くの関数を利用できるようになります。下記の簡単なスクリプトは conn オブジェクトで使用可能なすべての関数を表示する方法を示しています。

clist = dir(conn)for item in clist:            print item

以下のように同じような方法を使用すると、定義されたフィルターのリストを表示することもできます。

filts = conn.listNWFilters()for item in filts:            print item

IDLE ツールは、さまざまな API 呼び出しを調べたり、それらの呼び出しを実行して返される結果を素早く見たりするための素晴らしい手段です。一部の関数は実行中のドメイン上でのみ動作します。例えば、指定されたオブジェクトの有効な属性のリストを返す Python の dir() 関数は、ある特定のオブジェクトにどんな属性が用意されているのかを簡単に調べるための便利なコマンドライン・ツールです。この関数を上記のように使用すると、ハイパーバイザーとの接続確立後に使用できる関数のリストを取得することができます。

dir() 関数を実際に使用した例を示すために、IDLE コンソールで何行かの Python コードを入力します。これにより、特定のドメインでどのような処理を実行できるかについての情報を得ることができます。リスト 4 は、実行可能な処理についての情報を得る例を示しています。

リスト 4. ドメイン・オブジェクトに対して Python コードを実行した場合の出力

>>> import libvirt>>> import pprint>>> conn=libvirt.open("qemu:///system")>>> p = conn.lookupByName('ubuntu100403')>>> pprint.pprint(dir(p))['ID', 'OSType', 'UUID', 'UUIDString', 'XMLDesc', '__del__', '__doc__', '__init__', '__module__', '_conn', '_o', 'abortJob', 'attachDevice', 'attachDeviceFlags', 'autostart', 'blkioParameters', 'blockInfo', 'blockPeek', 'blockStats', 'connect', 'coreDump', 'create', 'createWithFlags', 'destroy', 'detachDevice', 'detachDeviceFlags', 'hasCurrentSnapshot', 'hasManagedSaveImage', 'info', 'injectNMI', 'interfaceStats', 'isActive', 'isPersistent',

この基本的な方法を基に、実行中のすべてのドメインに関する情報を一覧表示する単純なスクリプトを作成することができます。このスクリプトの処理の大部分は listDomainsID() 関数と lookupByID() 関数を呼び出した結果を使用して行われます (リスト 5)。

リスト 5 ドメインの情報を一覧表示する Python スクリプト

import libvirtconn=libvirt.open("qemu:///system")for id in conn.listDomainsID():   dom = conn.lookupByID(id)   infos = dom.info()   print 'ID = %d' % id   print 'Name =  %s' % dom.name()   print 'State = %d' % infos[0]   print 'Max Memory = %d' % infos[1]   print 'Number of virt CPUs = %d' % infos[3]   print 'CPU Time (in ns) = %d' % infos[2]   print ' '

このスクリプトを実行した結果の出力には、あるドメインがアクティブで別のドメインが停止されている、という内容が以下のように表示されます。

ID = 3Name =  ubuntu100403State = 3Max Memory = 1048576Number of virt CPUs = 1CPU Time (in ns) = 1048576ID = 4Name =  Win7_64-bitState = 1Max Memory = 2097152Number of virt CPUs = 2CPU Time (in ns) = 2097152

また libvirt はすべてのクラスとメソッドに対して Python の docstrings も実装しています。この情報にアクセスするためには、最上位レベルのヘルプで help(libvirt) と入力するか、あるいは特定のクラスで help(libvirt.class) と入力します。help() コマンドを入力するには、その前に libvirt モジュールをインポートしておく必要があります。私がこの記事のためにテストしたバージョンでは、以下の 11 のクラスが実装されています。

  • libvirtError
  • virConnect
  • virDomain
  • virDomainShapshot
  • virInterface
  • virNWFilter
  • virNetwork
  • virSecret
  • virStoragePool
  • virStorageVol
  • virStream

この一覧は Python から libvirt の関数を利用する場合の構文を理解する上で役立つはずです。またこの libvirt モジュールからは、VIR_DOMAIN_RUNNING (値は 1) などの名前付き定数の一覧を取得することもできます。上記で使用されている dom.info() などの関数は整数値を返すため、その定数一覧を使用して戻り値を解読する必要があります。


自動化のためのユーティリティー・スクリプト

libvirt と Python を使用すると、KVM のインストールを管理するユーティリティー・スクリプトをいくらでも作成することができます。ドメインの数が少ない場合にはスクリプトを作成してもそれほど効率的ではないかもしれませんが、ドメインの数が 2 桁になるとスクリプトはたちまち時間の節約につながります。1 つの単純なタスクの例として、すべてのドメイン・イメージの静的 IP アドレスを大きく変更することが考えられます。そのためには、すべての .conf ファイルに対して繰り返し処理を行い、適切な変更を行います。Python には、そうしたタスクのための機能が大量に組み込まれています。

リスト 6 はネットワークを定義する XML の例を示しています。

リスト 6. ネットワークを構成するための XML ファイル

<network>  <name>testnetwork</name>  <bridge name="virbr1" />  <forward/>  <ip address="192.168.100.1" netmask="255.255.255.0">    <dhcp>      <range start="192.168.100.2" end="192.168.100.254" />      <host mac='de:af:de:af:00:02' name='vm-1' ip='192.168.100.2' />      <host mac='de:af:de:af:00:03' name='vm-2' ip='192.168.100.3' />      <host mac='de:af:de:af:00:04' name='vm-3' ip='192.168.100.4' />      <host mac='de:af:de:af:00:05' name='vm-4' ip='192.168.100.5' />      <host mac='de:af:de:af:00:06' name='vm-5' ip='192.168.100.6' />      <host mac='de:af:de:af:00:07' name='vm-6' ip='192.168.100.7' />      <host mac='de:af:de:af:00:08' name='vm-7' ip='192.168.100.8' />      <host mac='de:af:de:af:00:09' name='vm-8' ip='192.168.100.9' />      <host mac='de:af:de:af:00:10' name='vm-9' ip='192.168.100.10' />    </dhcp  </ip></network>

メインのサブネットを 192.168.100 から 192.168.200 に変更したい場合には、エディターで構成ファイルを開き、グローバルな検索と置換を行います。ちょっとした工夫が必要になるのは、何かもう少し複雑なことをしたい場合、例えば 2 で始まるすべての IP アドレスと MAC アドレスに 10 を追加する、というような場合があります。リスト 7 は、20 ラインをほんの少し超える程度の Python コードでそれを行う方法を示しています。

リスト 7. MAC アドレスと IP アドレスを変更する Python スクリプト

#!/usr/bin/env pythonfrom xml.dom.minidom import parseStringimport sysdef main():    target = sys.argv[1]    number = int(sys.argv[2])        xml = open(target, 'r').read()    doc = parseString(xml)    for host in doc.getElementsByTagName('host'):        ip = host.getAttribute('ip')        parts = ip.split('.')        parts[-1] = str(int(parts[-1]) + number)        host.setAttribute('ip', '.'.join(parts))                mac = host.getAttribute('mac')        parts = mac.split(':')        parts[-1] = str(int(parts[-1]) + number)        host.setAttribute('mac', ':'.join(parts))        f = open(target, 'w')    f.write(doc.toxml())    f.close()if __name__ == '__main__':    main()

このスクリプトを見ると、Python の標準ライブラリーを利用した場合に Python がどれほど強力であるかを理解することができます。ここでは XML ファイルの構文解析という大変な処理を xml.dom.minidomparseString を使用して行っています。XML 内の各属性を取得した後、それを Python の string.split 関数を使用して個々の部分へと分割しています。そして計算処理を行った後にストリング同士をつなぎ合わせて復元しています。この方法を拡張すると、libvirt の .conf ファイルを含め、どのような XML ファイルも大々的に変更することができます。

もう 1 つ便利なスクリプトの例として、実行されているすべてのドメインのスナップショットを取るスクリプトが考えられます。このスクリプトはまず、実行中のすべてのドメインのリストを取得し、それから各ドメインを一時停止して各ドメインのスナップショットを取ります。この操作は本番環境では現実的ではないかもしれませんが、CRON ジョブとして夜間に実行させるように設定することができます。ここまでで焦点を当てたコマンドと snapshotCreateXML() の呼び出しを使用すれば、このスクリプトを簡単に実装することができます。


まとめ

この記事は libvirt に含まれる機能について表面的に説明したにすぎません。libvirt と仮想化全般についての詳細な資料は「参考文献」のリンクを参照してください。皆さんの環境の監視や管理のためのコードを実装しようとしてみると、KVM の基本事項を十分に理解できるようになります。この連載の次回の記事では、今回の基礎を基に、実用的な仮想管理ツールをいくつか作成します。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中