Hyper-V の自動バックアップ(エクスポート) スクリプト

次の機能を実装しています。Hyper-vのエクスポート(export)機能を利用した簡易Backupツールです。

  • 指定した任意のバーチャルマシンをエクスポートできます。(複数指定可能)
  • 実行中のバーチャルマシンは状態を保存に遷移させてからエクスポートします。
    エクスポート後は、状態を実行中に戻します。
    (エクスポート中は一時停止状態になりますがバーチャルマシン自体は再起動したりしません。)
  • ログに開始・終了時刻、エクスポート成否、エラーを出力します。
  • タスクスケジューラから実行することができます。


以下のスクリプトを参考にして実装しました。
http://gallery.technet.microsoft.com/ScriptCenter/ja-jp/cb80c006-4602-410c-9871-a9883915e4b3


このツールでバックアップすると、指定したフォルダに仮想マシン名のサブフォルダが作成されバックアップが保存されます。


このツールは、2つのスクリプトファイル(hyper-v_export.vbs,config.txt)で構成されており、hyper-v_export.vbsは実行ファイルで変更の必要はありません。設定はconfig.txtの以下のキーで指定します。

CS_OUTPUT_FOLDER
エクスポートするフォルダを指定します。実行するとこのフォルダ下に仮想マシンの名前でサブフォルダが作成されバーチャルマシンがエクスポートされます。

CS_LOG_PATH
ログファイルのパスを指定します。

BackupVms
バックアップする仮想マシンの名称を設定します。(Hyper-Vマネージャで表示される仮想マシンの名前を指定してください)

以下にツールのダウンロードリンクと、設定ファイルを配置しますので確認してください。

hyper-v_export.zip 直

config.txt


'
' Configuration Section
'
Const CS_OUTPUT_FOLDER = "E:\Export"
Const CS_LOG_PATH = "E:\Export.log"

Dim BackupVms(1)
BackupVms(0) = "Windows7"
BackupVms(1) = "Windows2008R2"

hyper-v_export.vbs


option explicit
dim objfs:set objfs = createobject("scripting.filesystemobject"):sub include(p):executeglobal objfs.OpenTextfile(p).readall:end sub

dim objWScript
Set objWScript = CreateObject("WScript.Shell")

dim strScriptPath
strScriptPath = objfs.GetParentFolderName(WScript.ScriptFullName)

include(objFs.BuildPath(strScriptPath, "config.txt"))

dim managementService

const JobStarting = 3
const JobRunning = 4
const JobCompleted = 7
const wmiStarted = 4096
const wmiSuccessful = 0

const vm_state_pause = 32769
const vm_state_start = 2
const vm_state_stop = 3

const phase_sleep_time = 2000
const phase_wait_limit = 300

dim objWMIService
set objWMIService = GetObject("winmgmts:\\.\root\virtualization")

Main()

Sub Main()

Dim backupItem
Dim VMName
Dim VMList
Dim VM
Dim WaitResult

For Each VMName In BackupVms

Set VMList = objWMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & VMName & "'")

For Each VM In VMList

dim saveState
saveState = VM.EnabledState
if VM.EnabledState = vm_state_pause Or VM.EnabledState = vm_state_start Or VM.EnabledState = vm_state_stop Then

if VM.EnabledState = vm_state_start then
' 一時停止
VM.RequestStateChange(vm_state_pause)
WaitResult = WaitStateChange(VMName, vm_state_pause)
else
WaitResult = True
end if

if WaitResult = True Then

' エクスポート
Call ExportVirtualMachine(VMName)

WScript.Sleep(phase_sleep_time * 5)

if saveState = vm_state_start then
' 再開
VM.RequestStateChange(vm_state_start)
WaitResult = WaitStateChange(VMName, vm_state_start)
if WaitResult = False Then
WriteLog Format1("Name:{0} 仮想マシンを再開できませんでした。", VMName)
End If
end if

Else
WriteLog Format1("Name:{0} 仮想マシンを一時停止できませんでした。", VMName)
End If
else
WriteLog Format1("Name:{0} 仮想マシンの状態がオン、オフ、保存以外の場合はエクスポートできません。", VMName)
end if

Next

Next

End Sub

Function WaitStateChange(vmName, state)

dim waitIndex
dim VMList
waitIndex = 0
Do While waitIndex < phase_wait_limit

Set VMList = objWMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & VMName & "'")

if VMList.ItemIndex(0).EnabledState = state then
exit do
end if

WScript.Sleep(phase_sleep_time)
waitIndex = waitIndex + 1
Loop

WaitStateChange = Not (waitIndex = phase_wait_limit)

End Function

'-----------------------------------------------------------------
' Main
'-----------------------------------------------------------------
Sub ExportVirtualMachine(vmName)

WriteLog Format1("Name:{0} Start Export Virtual Machine", vmName)

dim objArgs, vm, exportDirectory
set objfs = Wscript.CreateObject("Scripting.FileSystemObject")
set managementService = objWMIService.ExecQuery("select * from Msvm_VirtualSystemManagementService").ItemIndex(0)

exportDirectory = CS_OUTPUT_FOLDER

set vm = GetComputerSystem(vmName)

if ExportVirtualSystem(vmName, vm, exportDirectory) then
WriteLog Format1("Name:{0} Done Success", vmName)
else
WriteLog Format1("Name:{0} ExportVirtualSystem Failed.", vmName)
end if

End Sub

'-----------------------------------------------------------------
' Retrieve Msvm_VirtualComputerSystem from base on its ElementName
'-----------------------------------------------------------------
Function GetComputerSystem(vmName)
On Error Resume Next
dim query
query = Format1("select * from Msvm_ComputerSystem where ElementName = '{0}'", vmName)
set GetComputerSystem = objWMIService.ExecQuery(query).ItemIndex(0)
if (Err.Number <> 0) then
WriteLog Format2("Name:{0} Err.Number: {1}", Err.Number, vmName)
WriteLog Format2("Name:{0} Err.Description:{1}",Err.Description, vmName)
WScript.Quit(1)
end if
End Function

'-----------------------------------------------------------------
' Define a virtual system
'-----------------------------------------------------------------
Function ExportVirtualSystem(vmName, computerSystem, exportDirectory)

dim objInParam, objOutParams
ExportVirtualSystem = false

dim vmPath
vmPath = objFs.BuildPath(exportDirectory, vmName)

if objfs.FolderExists(vmPath) then
objfs.DeleteFolder(vmPath)
end if

WScript.Sleep(5000)

if Not objfs.FolderExists(exportDirectory) then
objfs.CreateFolder(exportDirectory)
end if

set objInParam = managementService.Methods_("ExportVirtualSystem").InParameters.SpawnInstance_()

objInParam.ComputerSystem = computerSystem.Path_.Path
objInParam.CopyVmState = true
objInParam.ExportDirectory = exportDirectory

set objOutParams = managementService.ExecMethod_("ExportVirtualSystem", objInParam)

if objOutParams.ReturnValue = wmiStarted then
if (WMIJobCompleted(vmName, objOutParams)) then
ExportVirtualSystem = true
end if
elseif (objOutParams.ReturnValue = wmiSuccessful) then
ExportVirtualSystem = true
else
WriteLog Format2("Name:{0} DefineVirtualSystem failed with ReturnValue {1}", vmName, objOutParams.ReturnValue)
end if

End Function

'-----------------------------------------------------------------
' Handle wmi Job object
'-----------------------------------------------------------------
Function WMIJobCompleted(vmName, outParam)

dim WMIJob, jobState

set WMIJob = objWMIService.Get(outParam.Job)

WMIJobCompleted = true

jobState = WMIJob.JobState

while jobState = JobRunning or jobState = JobStarting
WScript.Sleep(1000)
set WMIJob = objWMIService.Get(outParam.Job)
jobState = WMIJob.JobState
wend

if (jobState <> JobCompleted) then
WriteLog Format2("Name:{0} ErrorCode:{1}", vmName, WMIJob.ErrorCode)
WriteLog Format2("Name:{0} ErrorDescription:{1}", vmName, WMIJob.ErrorDescription)
WMIJobCompleted = false
end if

End Function

'-----------------------------------------------------------------
' Create the console log files.
'-----------------------------------------------------------------
Sub WriteLog(line)
dim fileStream
set fileStream = objfs.OpenTextFile(CS_LOG_PATH , 8, true)
fileStream.WriteLine CStr(Now()) & vbTab & line
fileStream.Close
End Sub

'------------------------------------------------------------------------------
' The string formating functions to avoid string concatenation.
'------------------------------------------------------------------------------
Function Format2(myString, arg0, arg1)
Format2 = Format1(myString, arg0)
Format2 = Replace(Format2, "{1}", arg1)
End Function

'------------------------------------------------------------------------------
' The string formating functions to avoid string concatenation.
'------------------------------------------------------------------------------
Function Format1(myString, arg0)
Format1 = Replace(myString, "{0}", arg0)
End Function