まず、VMに対する管理操作コマンドの一部を紹介します。
| コマンド | 管理操作 | 
|---|---|
| define | VM のハードウェア仕様などを定義するXMLファイルからインスタンスを定義し、登録。この後、VMのXML定義ファイルが所定の場所(例:qemu/kvmの場合標準では/etc/libvirt/qemu/.xml)にインストールされる。 | 
| undefine | VMの登録を外す、つまり削除する。そのVMのXML定義ファイルは削除される。 | 
| start | VMを起動 | 
| destroy | VMを強制停止(通常はこちらではなくVM内のOSを操作して正常停止させる) | 
| dumpxml | VMのXML定義を出力 | 
| edit | VMのXML定義をエディタで編集 | 
| 表5 VMに対する管理操作コマンド | |
$ sudo virsh start rhel-5-cluster-1
Domain rhel-5-cluster-1 started
$ sudo virsh dumpxml rhel-5-cluster-1 | head
<domain type='kvm' id='14'>
  <name>rhel-5-cluster-1</name>
  <uuid>7a85c498-9ae7-990e-0b57-93260a9637f7</uuid>
  <memory>262144</memory>
  <currentMemory>262144</currentMemory>
  <vcpu>1</vcpu>
  <os>
    <type arch='i686' machine='pc'>hvm</type>
    <boot dev='hd'/>
  </os>
(中略)
$ sudo virsh destroy rhel-5-cluster-1
Domain rhel-5-cluster-1 destroyed
$
次に、virshによるVMのためのリソースの管理操作の例として、仮想ネットワークについて一部のコマンドを紹介します。
| コマンド | 仮想ネットワーク管理操作 | 
|---|---|
| net-define <NETWORK_XML_FILE> | 仮想ネットワークのXML定義ファイルからインスタンスを定義し登録。仮想ネットワークのXML定義ファイルは所定の場所(例:qemu/kvmの場合標準では/etc/libvirt/qemu/networks/.xml)にインストールされる。 | 
| net-undefine<NETWORK_NAME> | 仮想ネットワークの登録を外す、つまり削除する。XML定義ファイルは削除される。 | 
| net-start<NETWORK_NAME> | 仮想ネットワークを起動。ネットワークの開始とともに関連するサービス(dnsmasqなど)も開始/起動 | 
| net-destroy<NETWORK_NAME> | 仮想ネットワークを強制停止 | 
| net-dumpxml<NETWORK_NAME> | 仮想ネットワークのXML定義を出力 | 
| net-edit<NETWORK_NAME> | net-edit <NETWORK_NAME> | 
| 表6 仮想ネットワーク操作のためのコマンド | |
$ sudo virsh net-list --all Name State Autostart ----------------------------------------- net-1 active yes net-2 active yes net-3 active yes default inactive no $ sudo virsh net-start default Network default started $ sudo virsh net-list Name State Autostart ----------------------------------------- default active no net-1 active yes net-2 active yes net-3 active yes $ sudo virsh net-destroy default Network default destroyed $ sudo virsh net-list Name State Autostart ----------------------------------------- net-1 active yes net-2 active yes net-3 active yes $
いかがでしょうか? VMの管理操作と仮想ネットワークの管理操作が非常に似通っていることがご理解いただけるかと思います。
libvirt APIはCのライブラリとして提供されていますが、APIを試すだけの目的でCでコードを書くのは、少し気が重い作業作業かもしれません。そこでC APIの代わりに、標準でlibvirtソースツリーにも含まれるPythonバインディングを使ってみます(注8)。
C APIと Python APIでは、言語の違いのため、多少APIの扱い方が違う部分もありますが、大筋では似ています。PythonでAPIを使う場合でも、CのAPIの利用例を見ると非常に参考になります。
またVMの管理用のGUIツールであるvirt-managerや、VMへのOSのインストールを行うツールvirt-installは、いずれもlibvirt APIのPythonバインディングを使っていますので、これらのソースコードも参考になるでしょう。さらに、最近ではApplication Development Guideの内容も充実してきており、非常に参考になります。
ここではvirshのソースコードを参照しながら、pythonでlibvirt APIを利用するコードを書いてみましょう。
APIの利用の手順はおおよそ次のとおりとなります。
接続先を表すURIは“xen:///”や“qemu:///system”というような表現となり、先頭部分の“xen”や“qemu”といった文字列からどのVMMにアクセスするかが選択され、VMMバックエンドが切り替えられます。
 簡単なものとしてまず“virsh domstate 
/*
* "domstate" command
*/
static const vshCmdInfo info_domstate[] = {
    {"help", N_("domain state")},
    {"desc", N_("Returns state about a domain.")},
    {NULL, NULL}
};
static const vshCmdOptDef opts_domstate[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {NULL, 0, 0, NULL}
};
static int
cmdDomstate(vshControl *ctl, const vshCmd *cmd)
{
    virDomainInfo info;
    virDomainPtr dom;
    int ret = TRUE;
    if (!vshConnectionUsability(ctl, ctl->conn))
        return FALSE;
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
        return FALSE;
    if (virDomainGetInfo(dom, &info) == 0)
        vshPrint(ctl, "%s\n",
                _(vshDomainStateToString(info.state)));
    else
        ret = FALSE;
    virDomainFree(dom);
    return ret;
}
要約すると、次のような処理を行っています。
これをPythonで同様に書いてみると、例えば次のように書けます(注9)。
#! /usr/bin/python
import libvirt
import sys
def domainStateToString(state):
  """@see http://libvirt.org/html/libvirt-libvirt.html#virDomainState
  """
  sts = ('no sate', 'running', 'blocked', 'paused', 'shutting down', 'shut off', 'crushed')
  return sts[state]
if len(sys.argv) < 2:
  print >> sys.stderr, "Usage: %s VM_NAME" % sys.argv[0]
  sys.exit(1)
vm_name = sys.argv[1]
conn = libvirt.openReadOnly(None) # Choose it instead of libvirt.openAuth.
if conn is None:
  print >> sys.stderr, "Could not connect to VMM"
  sys.exit(1)
try:
  vm = conn.lookupByName(vm_name)
  (st,_maxmem,_mem,_nrcpus,_cputime) = vm.info()
  print "%s: %s" % (vm_name, domainStateToString(st))
except:
  print >> sys.stderr, "Could not get the info of vm %s" % (vm_name,)
$ sudo python domstate.py rhel-5-4-guest-1 [sudo] password for kvmuser: rhel-5-4-guest-1: running $
次に“virsh list”に相当するPythonコードを書いてみましょう。“list”コマンドに該当するのはvirshのソースコード(tools/virsh.c)のcmdList関数です。こちらを参考に、Pythonでまねをして書いてみると、例えば次のように書けます。
#! /usr/bin/python
import libvirt
import sys
def domainStateToString(state):
  """@see http://libvirt.org/html/libvirt-libvirt.html#virDomainState
  """
  sts = ('no sate', 'running', 'blocked', 'paused', 'shutting down', 'shut off', 'crushed')
  return sts[state]
if len(sys.argv) < 2:
  uri = 'qemu:///system' # default: KVM/qemu
else:
  uri = sys.argv[1]
conn = libvirt.open(uri)
if conn is None:
  print >> sys.stderr, "Could not connect to VMM: %s" % uri
  sys.exit(1)
try:
  vms = [(i, conn.lookupByID(i)) for i in conn.listDomainsID()]
  res = [(i, vm.name(), domainStateToString(vm.info()[0])) for (i,vm) in vms]
  print "%3s %-20s %s" % ('Id', 'Name', 'State')
  print "----------------------------------"
  for r in res:
    print "%3s %-20s %s" % r
except:
  print >> sys.stderr, "Could not get the list" 
お好みのプログラミング言語のlibvirt APIのバインディング実装がない場合、バインディングを実装する以外にも、制限はありますがいくつか方法が考えられます。
前者は機能の制限はありますが、ほとんどのプログラミング言語で比較的容易に実現可能でしょう。例として、筆者が最近試しはじめているHaskellによる実装(注10)をサンプルとして挙げておきます。
後者は、libvirt APIをそれぞれCIM(注11)とQMF(注12)の枠組みの中で扱えるようにするインターフェイスを利用する方法です。利用にはそれぞれ対応するパッケージ(libvirt-cim、libvirt-qpid)の追加インストールと相応のセットアップ作業が必要になります。これらについては筆者には十分な知識がありませんので、以下、参考程度に各種URLを挙げるにとどめます。
libvirt-CIMについてはおそらく次などが参考になるでしょう。
関連リンク
▼Managing KVM with CIM(Kaitlin Rupert, Linux Plumbers Conference 2009 スライド資料)
http://linuxplumbersconf.org/2009/slides/Kaitlin-Rupert-Plumbers_2009_Managing_KVM_with_CIM.pdf
▼libvirt-cim setup instructions
http://wiki.libvirt.org/page/Libvirt-cim_setup
libvirt-qpidについては文書はほとんど見当たりませんが、qpidのsubversionリポジトリにqpid のRubyバインディングによるサンプル実装があるようです。
関連リンク
▼libvirt-qpid(QMF)のRubyによるサンプルコード
https://svn.apache.org/repos/asf/qpid/trunk/qpid/ruby/examples/qmf-libvirt.rb
以上、libvirtの概要とvirshを通したVMの管理方法、libvirt APIの利用方法について解説しました。次回は、libvirt開発最前線の状況をお伝えします。
佐藤 暁
レッドハット株式会社RHELコンサルタント。Linuxカーネルから Python/JavaによるWebアプリケーションまで、ソースコードを探りつつ、システム管理や標準化など コンサルテーション業務をこなす日々。
Copyright © ITmedia, Inc. All Rights Reserved.