2018年12月8日土曜日

govmomi で QueryChangedDiskAreas をコールする方法

概要

QueryChangedDiskAreas は CBT の機能を使って VM のスナップショット間でディスクの差分情報を取得する API です
CBT に関してはここでは紹介しないので CBT について知りたい、試してみたいかたはこちらで紹介しています
今回は特定の VM のスナップショットの作成から QueryChangedDiskAreas API をコールするまでの一連の流れをすべてコードにしたので紹介します

環境

  • CentOS 7.5.1804
  • golang 1.11.2
  • govmomi 6712f991d8852a25ae4304a720463301c1ac4c64

コード全体

まずコード全体です
かなり長いです
あとでコードの説明をします

package main

import (
    "context"
    "flag"
    "fmt"
    "net/url"
    "os"
    "time"

    "github.com/vmware/govmomi"
    "github.com/vmware/govmomi/find"
    "github.com/vmware/govmomi/object"
    "github.com/vmware/govmomi/vim25/methods"
    "github.com/vmware/govmomi/vim25/types"
)

var envURL = "https://192.168.100.20/sdk"
var user = "administrator@vsphere.local"
var pass = "xxxxxxxxxxx"
var vmname = "dist"
var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", envURL, urlDescription)

var envInsecure = true
var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", envInsecure, insecureDescription)

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    flag.Parse()
    u, err := url.Parse(*urlFlag)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    // (1)
    u.User = url.UserPassword(user, pass)
    c, err := govmomi.NewClient(ctx, u, *insecureFlag)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(c.Client.Client.Version)
    // (2)
    f := find.NewFinder(c.Client, true)
    dc, err := f.DefaultDatacenter(ctx)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    f.SetDatacenter(dc)
    fmt.Println(dc)
    // vm list (3)
    vss, err := f.VirtualMachineList(ctx, "*")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    // virtualdevice from vm (4, 5)
    devices := object.VirtualDeviceList{}
    device := &types.VirtualDevice{}
    ref := types.ManagedObjectReference{}
    tvm := &object.VirtualMachine{}
    for _, vm := range vss {
        name := vm.Name()
        if name == vmname {
            fmt.Println(name)
            devices, _ = vm.Device(ctx)
            for _, d := range devices {
                vd := d.GetVirtualDevice()
                // fmt.Println(vd.Key)
                // fmt.Println(vd.DeviceInfo)
                // fmt.Println(vd.Backing)
                // fmt.Println(vd.Connectable)
                // fmt.Println(vd.SlotInfo)
                // fmt.Println(vd.ControllerKey)
                // fmt.Println(vd.UnitNumber)
                if vd.DeviceInfo.GetDescription().Label == "Hard disk 1" {
                    device = vd
                }
            }
            ref = vm.Common.Reference()
            tvm = vm
            fmt.Printf("%v\n", ref)
            break
        }
    }
    fmt.Println(device.Key)
    // snapshot from vm (6)
    ssreq := new(types.CreateSnapshot_Task)
    ssreq.Description = "Snapshot created by govmomi"
    ssreq.Name = vmname + "ss"
    ssreq.Memory = false
    ssreq.This.Type = ref.Type
    ssreq.This.Value = ref.Value
    _, err = methods.CreateSnapshot_Task(ctx, c.RoundTripper, ssreq)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    time.Sleep(5 * time.Second)
    ssref, err := tvm.FindSnapshot(ctx, vmname + "ss")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(ssref.Type)
    fmt.Println(ssref.Value)
    // change disk areas (7)
    query := new(types.QueryChangedDiskAreas)
    query.ChangeId = "*"
    query.DeviceKey = device.Key
    query.Snapshot = ssref
    query.StartOffset = 0
    query.This.Type = ref.Type
    query.This.Value = ref.Value
    res, err := methods.QueryChangedDiskAreas(ctx, c.RoundTripper, query)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Printf("%v\n", res) // DiskChangeInfo
    fmt.Println(res.Returnval.StartOffset)
    fmt.Println(res.Returnval.Length)
    for i, area := range res.Returnval.ChangedArea {
        fmt.Println(i)
        fmt.Println(area.Start)
        fmt.Println(area.Length)
    }
}

流れとしては

  1. vCenter 接続
  2. Datacenter 取得
  3. VirtualMachine のリストの取得
  4. リストから対象の VirtualMachine の取得
  5. 対象の VirtualMachine から HardDisk デバイスの情報を取得
  6. 対象の VM に対してスナップショットの作成
  7. QueryChangedDiskAreas を実行して作成したスナップショットと元 VM の差分を取得

という流れになります
この流れのなかで QueryChangedDiskAreas を実行するために実行していないのは対象の VM に対しての CBT の有効化です
VM の設定で changeTrackingEnabled を true にすれば OK なので手動で実施してください

ポイント説明

まず QueryChangedDiskAreasを実行するのに必須なのはスナップショットの情報とスナップショットが持つディスクの Key 番号と ChangeID と呼ばれる変更履歴を管理するタイムスタンプ情報です
今回のサンプルでは ChangeID は * を指定しており * を指定した場合は元 VM の初めから差分をチェックします

スナップショットは新規で作成し、そのスナップショットの情報と元 VM の情報を比較します
スナップショットの作成と作成したスナップショットの moref の取得は 6 でやっています

あと必要なハードディスクの Key 番号は 5 でやっています
vm.Device(ctx) で各デバイスの情報を取得しそこから「Hard disk 1」を取得します
今回は VM が持つハードディスクは 1 つしかないので「Hard disk 1」を指定していますがハードディスクが複数ある場合は差分を取りたいハードディスクを取得するようにしてください
あとは device.Key で参照するだけです
StartOffset は探索する先頭を指定できますが基本は 0 でいいと思います

コードでやることを減らすには

今回はスナップショットも作成もコードでやっています
QueryChangedDiskAreas API は単純に差分を取得するだけなのでスナップショットは事前に作成しておいても OK です
moref の情報をハードコードで設定してあげても良いです
あとは device.Key もハードディスクが決まっているのであれば決め打ちでも OK です
単純な整数番号なので mob などで取得して設定すれば OK です

要するに query := new(types.QueryChangedDiskAreas) に設定する値をあらかじめ取得してハードコードすればいいだけです
3, 4, 5, 6 の流れをごっそり削除することが可能になります

最後に

govmomi で QueryChangedDiskAreas をコールしてみました
govmomi じゃなくても QueryChangedDiskAreas 自体はコールできます
CBT の概念を理解しないと QueryChangedDiskAreas 自体も何をやっているかよくわからないのでまずは CBT を理解したほうが良いかなと思います

0 件のコメント:

コメントを投稿