vboxapiでVirtualBoxをPythonで操作その2

対象: VirtualBox-3.1, Python-2.6

前回の続きです。

昨日は、VM一覧の取得と簡単な内容表示まで書きました。
machinesというのは、リファレンスでいうところの、IMachineインターフェースを実装したオブジェクトのリストになっています。

import vboxapi
vbm = vboxapi.VirtualBoxManager(None, None)
vbox = vbm.vbox
machines = vbm.getArray(vbox, 'machines')

for m in machines:
    print "uuid =", m.id

IMachineが保持している情報のうち、特に重要なものがid属性です。idには各VMwikipedia:UUIDを保持しています。これを使ってVirtualBoxに「このVM」を指定する仕様になっています。

VMの状態変更(失敗編)

では早速VMの状態を変更してみましょう。
とはいえ、いきなりVMを起動したりHDDを取り外したりするのはリスキーなので、影響が小さいVRDP機能の有効無効の切り替えから試してみましょう。
念のためテストは壊れてもいいVMで行います。

import vboxapi
vbm = vboxapi.VirtualBoxManager(None, None)
vbox = vbm.vbox
m = vbox.getMachine('4f2f****-...')
m.VRDPServer.enabled = not m.VRDPServer.enabled
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/usr/lib/virtualbox/sdk/bindings/xpcom/python/xpcom/client/__init__.py", line 397, in __setattr__
    setattr(interface, attr, val)
  File "/usr/lib/virtualbox/sdk/bindings/xpcom/python/xpcom/client/__init__.py", line 488, in __setattr__
    return XPTC_InvokeByIndex(self._comobj_, method_index, real_param_infos)
xpcom.Exception: 0x80bb0002 (The machine is not mutable (state is PoweredOff))

「machineは変更不可能だよ(状態は PoweredOffだよ)」という例外が飛んできました。
原因はVMに対する状態変更操作が競合しないように、状態変更はセッションを開いてから行う仕組みになっているからです。
"state is PoweredOff"は無関係でワナです。私は30分ハマりました。

APIGUI以外のツール

ちなみに、VMの操作にはコマンドラインツールも使用できます。

  • VBoxManage
  • vboxshell.py (/usr/lib/virtualbox/vboxshell.py)

vboxshell.pyは、Pythonのvboxapiのサンプルとして最適です。

VMの状態変更(セッション編)

import vboxapi

mid = '4f2f****-...'

vbm = vboxapi.VirtualBoxManager(None, None)
const = vboxapi.VirtualBox_constants.VirtualBoxReflectionInfo(False)

vbox = vbm.vbox
m = vbox.getMachine(mid)

print "BEFORE:", m.VRDPServer.enabled

sessionManager = vboxapi.SessionManager(vbm)
session = sessionManager.getSessionObject(vbox)
# session = vbm.mgr.getSessionObject(vbox) # これでも良い

if m.sessionState == const.SessionState_Closed:
    vbox.openSession(session, mid)
elif m.sessionState == const.SessionState_Open:
    vbox.openExistingSession(session, mid)
else:
    # その他の状態の場合はとりあえず例外
    raise Exception()

v = session.machine.VRDPServer
v.enabled = not v.enabled

session.machine.saveSettings()
session.close()

print "AFTER:", m.VRDPServer.enabled

変更できました。

BEFORE: True
AFTER: False
VirtualBoxReflectionInfo

VirtualBoxReflectionInfo には、状態を表す定数などが定義されています。

/usr/share/pyshared/vboxapi/VirtualBox_constants.py

ソースを読むだけで、vboxapiでどのような操作や情報取得ができるのかがわかります。
以下は使用例です。

import vboxapi

const = vboxapi.VirtualBox_constants.VirtualBoxReflectionInfo(False)
mid = '4f2f****-...'

state = vboxapi.VirtualBoxManager(None, None).vbox.getMachine(mid).state
print 'state:', state
print 'isPoweredOff:', const.MachineState_PoweredOff == state
state: 1
isPoweredOff: True
Sessionの取得

ISessionはSessionManagerから作ります。
作ったISessionはUUIDでVMを指定して開きます。
開くには、VMのsessionStateの状態によってIVirtualBoxのopenSessionとopenExistingSessionを使い分ける必要があります。

sessionManager = vboxapi.SessionManager(vbm)
session = sessionManager.getSessionObject(vbox)
# session = vbm.mgr.getSessionObject(vbox) # これでも良い 

if m.sessionState == const.SessionState_Closed:
    vbox.openSession(session, mid)
elif m.sessionState == const.SessionState_Open:
    vbox.openExistingSession(session, mid)

APIドキュメントには以下のように記述してあります。

SessionState_Open The machine has an open direct session;

私が試した限りでは、VMが起動している場合にはOpenが帰るようですが、他に条件があるのかは不明です。VMを起動するには一度RemoteSessionを開く必要があるので、その関係でしょうか。
vboxshell.pyは、起動している状態でのみ有効な操作をするときにはopenExistingSession決め打ちでした。

sessionを開いてsession.machineからIMachineを取得して操作すれば、例外が出ることなくVMの状態(設定)を変更できました。
machine.saveSettings() をして状態を保存し、session.close()でISessionを閉じましょう。

ここまでのまとめ

  • IMachine.idは、VMを一意に特定するIDを返します。
  • vbox.getMachineなどで取得できるIMachineオブジェクトは情報の取得にのみ使用できます。
  • vboxshell.pyはPython-APIのサンプルに最適です。
  • VirtualBox_constants.pyを見てみるとなんとなく全容がわかります。
  • VMの状態を変更するにはsessionを作って開いてsession.machineからIMachineを取得して操作します。

今後の予定

VMを起動したり停止したりする。