読者です 読者をやめる 読者になる 読者になる

IronPython による VirtualBox のインポート・エクスポート - COM API 使用

.NET Python JavaScript

VirtualBox のインポート・エクスポート処理を IronPython から COM API を使って試してみました。

環境は以下の通りです。

ソースは http://github.com/fits/try_samples/tree/master/blog/20130707/

事前準備

まずは、VirtualBox の COM を tlbimp コマンドを使って .NET アセンブリへ変換しておきます。

tlbimp 実行例
>tlbimp %VBOX_INSTALL_PATH%\VBoxC.dll
・・・
TlbImp : Type library imported to VirtualBox.dll

多少 warning は出ますが、VirtualBox.dll の作成に成功します。

エクスポート処理

エクスポート処理は概ね下記のようになります。

  • (1) 空の Appliance 作成
  • (2) Appliance へ Export
  • (3) Appliance をファイル出力

(2) で Export メソッドへ渡す第2引数の値が .vmdk ファイルの一部(xxx--disk1.vmdk の xxx)に使用されます。 (3) の Write メソッドで出力フォーマットと出力ファイル(.ovf か .ova)を指定するようになっています。なお、出力ファイルに相対パス指定すると %USERPROFILE% ディレクトリからの相対パスとなるようです。

IProgress の WaitForCompletion() に -1 を指定すると処理が完了するまで待機します。

vbox_export.py
# coding: utf-8
import sys

if len(sys.argv) < 3:
    print "%s <machine name> <output file>" % sys.argv[0]
    sys.exit()

import os

import clr
clr.AddReference("VirtualBox")

from VirtualBox import *

vb = VirtualBoxClass()

# 仮想マシンの取得
mc = vb.FindMachine(sys.argv[1])

filePath = sys.argv[2]
fileName = os.path.basename(filePath)
fileNameBase, ext = os.path.splitext(fileName)

# (1) 空の Appliance 作成
ap = vb.CreateAppliance()

# (2) Appliance へ Export
# "<第2引数の値>-disk1.vmdk" が作成される事になる
des = mc.Export(ap, fileNameBase)

print "export start"

# (3) Appliance をファイル出力
pr = ap.Write("ovf-2.0", 0, filePath)

# 処理の完了待ち
pr.WaitForCompletion(-1)

print "export end"
実行例1
> ipy64 vbox_export.py centos6.4 c:\temp\sample1.ova
export start
export end

sample1.ova が作成されます。

実行例2
> ipy64 vbox_export.py centos6.4 c:\temp\sample2.ovf
export start
export end

sample2.ovf と sample2-disk1.vmdk が作成されます。

インポート処理

インポート処理は概ね下記のようになります。

  • (1) 空の Appliance 作成
  • (2) OVF ファイルの読み込み
  • (3) OVF の解釈とセットアップ
  • (4) 仮想マシンの作成

(2) で相対パスを指定すると export と同様に %USERPROFILE% ディレクトリからの相対パスとなるようです。 (3) と (4) の間で IVirtualSystemDescription を使った設定情報(仮想マシン名など)の変更が可能なようですが(getDescription() と setFinalValues() を利用する模様)、IronPython 上で getDescription() を適切に使う方法が分からなかったので今回は断念しました。

なお、今回は importMachines の引数に空の配列を指定しています。型に Int32 を指定している点にご注意ください。(ImportOptions とするとエラーになります)

vbox_import.py
# coding: utf-8
import sys

if len(sys.argv) < 2:
    print "%s <input file>" % sys.argv[0]
    sys.exit()

import clr
clr.AddReference("VirtualBox")

from System import *
from VirtualBox import *

vb = VirtualBoxClass()

# (1) 空の Appliance 作成
ap = vb.CreateAppliance()

print "import start"

# (2) OVF ファイルの読み込み
pr = ap.read(sys.argv[1])
# 処理の完了待ち
pr.WaitForCompletion(-1)

# (3) OVF の解釈とセットアップ
ap.interpret()

# 空の配列作成
opts = Array.CreateInstance(Int32, 0)

# (4) 仮想マシンの作成
pr2 = ap.importMachines(opts)
# 処理の完了待ち
pr2.WaitForCompletion(-1)

print "import end"
実行例1
> ipy64 vbox_import.py c:\temp\sample1.ova
import start
import end
実行例2
> ipy64 vbox_import.py c:\temp\sample2.ovf
import start
・・・
EnvironmentError: System.Runtime.InteropServices.COMException (0x800706BE): 
  リモート プロシージャ コールに失敗しました。 (HRESULT からの例外: 0x800706BE)
・・・

.ovf ファイルのインポートは Windows用 VirtualBox-4.2.14 に問題があるため失敗します。( 前回参照

WSH によるエクスポート処理

最後に、JScript で同様のエクスポート処理を実装してみると下記のようになります。

vbox_export.js
var args = WScript.Arguments;

if (args.Count() < 2) {
    WScript.Echo("<machine name> <output file>");
    WScript.Quit();
}

var vb = WScript.CreateObject("VirtualBox.VirtualBox");
var fs = WScript.CreateObject("Scripting.FileSystemObject");

var mc = vb.findMachine(args.Item(0));

var filePath = args.Item(1);

var ap = vb.createAppliance();

var des = mc.Export(ap, fs.GetBaseName(filePath));

WScript.Echo("export start");

var prog = ap.write("ovf-2.0", false, filePath);

prog.waitForCompletion(-1);

WScript.Echo("export end");
実行例
> cscript vbox_export.js centos6.4 c:\temp\sample3.ova
・・・
export start
export end