ラベル govmomi の投稿を表示しています。 すべての投稿を表示
ラベル govmomi の投稿を表示しています。 すべての投稿を表示

2018年12月17日月曜日

govmomi で Task の Wait を行う方法

概要

govmomi の methods で API を呼び出した場合、レスポンスが object.Task でないためいきなり Wait が呼べません
レスポンスから object.Task を作成して Wait する必要があります

環境

  • CentOS 7.5.1804
  • golang 1.11.2
  • govmomi 6712f991d8852a25ae4304a720463301c1ac4c64

サンプルコード

今回はスナップショットの作成 (methods.CreateSnapshot_Task) を Wait する方法を紹介します

  • mkdir go/src/github.com/hawksnowlog/h
  • vim go/src/github.com/hawksnowlog/h/main.go
package main

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

    "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 = "xxxxxxxxxx"
var vmname = "vm01"
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)
    }
    u.User = url.UserPassword(user, pass)
    c, err := govmomi.NewClient(ctx, u, *insecureFlag)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    f := find.NewFinder(c.Client, true)
    dc, err := f.DefaultDatacenter(ctx)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    f.SetDatacenter(dc)
    // vm list
    vss, err := f.VirtualMachineList(ctx, "*")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    ref := types.ManagedObjectReference{}
    for _, vm := range vss {
        name := vm.Name()
        if name == vmname {
            ref = vm.Common.Reference()
            break
        }
    }
    // snapshot from vm
    fmt.Println(ref.Type)
    fmt.Println(ref.Value)
    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
    res, err := methods.CreateSnapshot_Task(ctx, c.RoundTripper, ssreq)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    task := object.NewTask(c.Client, res.Returnval)
    err = task.Wait(ctx)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println("created snapshot")
}
  • go build github.com/hawksnowlog/h
  • go install github.com/hawksnowlog/h
  • $GOPATH/bin/h

解説

res, err := methods.CreateSnapshot_Task(ctx, c.RoundTripper, ssreq) で取得できる res は types.CreateSnapshot_TaskResponse になっています
これだと .Wait(ctx) 関数を呼び出せません

なので res を元に task := object.NewTask(c.Client, res.Returnval)object.Task を作成してから .Wait(ctx) を呼び出してあげます

ssreq.This.Type = ref.Typessreq.This.Value = ref.Value はただの文字列になっています
例えばそれぞれ「VirtualMachine」「vm-20」という文字列です
今回のサンプルでは VM を取得してそこから moref を参照していますが決め打ちで決まっている場合は VM を検索するロジックは削除できます

最後に

govmomi で Task を Wait する方法を紹介しました
govmomi は基本は非同期なので、この Wait のやり方を覚えておかないとまだリソースが作成されていない状態で次に行ってしまったりするので注意が必要です
簡単な方法なので覚えておくと良いと思います

参考サイト

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 を理解したほうが良いかなと思います

2017年9月5日火曜日

govmomi を使って VirtualMachine の moid を取得してみた

概要

govmomi を使って VM の情報を取得します
その際に ManagedObjectReference の moid を取得してみます

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

事前準備

ライブラリのインストールなどは過去の記事を参考にしてください
https://hawksnowlog.blogspot.jp/2017/04/try-vmware-govmomi.html

ソースコード

  • vim get_vm_moid.go
package main

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

    "github.com/vmware/govmomi"
    "github.com/vmware/govmomi/find"
    "github.com/vmware/govmomi/vim25/types"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
)

var envURL = "https://192.168.100.101/sdk"
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() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        u.User = url.UserPassword("vcenter-user", "vcenter-password")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        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 の検索
        pc := property.DefaultCollector(c.Client)
        refs := []types.ManagedObjectReference{}
        vss, err := f.VirtualMachineList(ctx, "/dc/vm/folder/folder2/target_vm")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, vs := range vss {
                fmt.Println(vs.Common.InventoryPath)
                refs = append(refs, vs.Reference())
        }
        var vdst []mo.VirtualMachine
        err = pc.Retrieve(ctx, refs, nil, &vdst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, vs := range vdst {
        fmt.Println(vs.Self.Value) // <- これで moid を取得できる
                fmt.Println(vs.Summary.Guest.GuestId)
                fmt.Println(vs.Summary.Guest.GuestFullName)
                fmt.Println(vs.Summary.Guest.HostName)
                fmt.Println(vs.Summary.Guest.IpAddress)
                fmt.Println(vs.Summary.Config.Name)
                fmt.Println(vs.Summary.Config.VmPathName)
                fmt.Println(vs.Summary.Config.MemorySizeMB)
                fmt.Println(vs.Summary.Config.NumCpu)
        }
}

ポイントは最後の vs.Self.Value です
これで moid が取得できます
フィールド名が Value なので moid だと名前から推測できないのでわかりづらいです

参考サイト

2017年6月9日金曜日

govmomi で指定の VApp をフォルダ配下に移動させる

概要

VApp をフォルダ配下に移動する方法を紹介します
1 つの VApp をあるフォルダに移動します

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim main.go
package main

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

        "github.com/vmware/govmomi"
        "github.com/vmware/govmomi/find"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
        "github.com/vmware/govmomi/vim25/types"
)

var envURL = "https://192.168.100.101/sdk"
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() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        u.User = url.UserPassword("vcenter_user", "vcenter_pass")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        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)

        // フォルダの取得
        folder, err := f.Folder(ctx, "/dc/vm/folder/")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(folder.Common.InventoryPath)
        // VirtualApp の取得
        varefs := []types.ManagedObjectReference{}
        va, err := f.VirtualApp(ctx, "/dc/vm/vapp")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(va.Common.InventoryPath)
        varefs = append(varefs, va.Reference())
        // フォルダへ移動
        t, err := folder.MoveInto(ctx, varefs)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        info, err := t.WaitForResult(context.Background(), nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(info.State)
}

ポイントはフォルダのオブジェクトに対して実行している MoveInto メソッドになります
この引数に moref を指定することで指定した moref をフォルダに移動するという処理を行っています
対象の VApp に対して移動せよという命令をするわけではなく、フォルダに対してこのフォルダに入れるという処理をしているのがポイントです

実行後に WaitForResult を使って処理が完了するのを待っています
完了後 info.State で結果を表示して終了です

今回のコードの場合、指定するフォルダと VApp のパスが存在しない場合はエラーになります
なので、一度実行したあと再度実行したい場合は対象の VApp を元のパスに戻す必要があります

最後に

govmomi を使って VApp を特定のフォルダに移動してみました
参照系よりか簡潔に書くことができました

参考サイト

2017年6月8日木曜日

govmomi で VApp の情報を取得する方法

概要

vCenter 配下で管理されている VApp の情報を govmomi を使って取得してみます

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim main.go
package main

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

        "github.com/vmware/govmomi"
        "github.com/vmware/govmomi/find"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
        "github.com/vmware/govmomi/vim25/types"
)

var envURL = "https://192.168.100.101/sdk"
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() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        u.User = url.UserPassword("vcenter_user", "vcenter_pass")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        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)

        // 全 VirtualApp の取得
        pc := property.DefaultCollector(c.Client)
        varefs := []types.ManagedObjectReference{}
        vas, err := f.VirtualAppList(ctx, "*")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, va := range vas {
                fmt.Println(va.Common.InventoryPath)
                varefs = append(varefs, va.Reference())
        }
        var vadst []mo.VirtualApp
        err = pc.Retrieve(ctx, varefs, nil, &vadst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, va := range vadst {
                fmt.Println(va.VAppConfig.Annotation)
                fmt.Println(va.VAppConfig.EntityConfig) // VApp 内の VM の情報
                fmt.Println(va.Name)
        }
}

まず vCenter に接続しデータセンターの情報を取得します
その後 f.VirtualAppList を使ってすべての VApp の情報を取得します
取得後は types.ManagedObjectReference に突っ込んで pc.Retrieve することで VApp の情報を取得しています
あとは types.ManagedObjectReference に設定された値をループで回して参照しているだけです

VApp の場合 VApp 内に VM が存在することがあると思います
VApp 内の子の要素を参照するには EntityConfig を使えば参照できそうだったので、そこから VM の情報を取得できると思います

VApp の別の情報を参照したい場合は以下を参照してください
https://godoc.org/github.com/vmware/govmomi/vim25/mo#VirtualApp

最後に

govmomi を使って VApp の情報を参照していました
これまで紹介したフォルダや VM を取得する方法とほぼ同じなので特に躓くところはありませんでした

参考サイト

2017年6月7日水曜日

govmomi でフォルダの情報を取得する方法

概要

vCenter 配下で管理されているフォルダ情報を govmomi を使って取得してみます
すべてのフォルダを取得する方法と単体で取得する方法を紹介します

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim main.go
package main

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

        "github.com/vmware/govmomi"
        "github.com/vmware/govmomi/find"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
        "github.com/vmware/govmomi/vim25/types"
)

var envURL = "https://192.168.100.101/sdk"
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() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        u.User = url.UserPassword("vcenter_user", "vcenter_pass")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        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)

        // フォルダの取得
        pc := property.DefaultCollector(c.Client)
        frefs := []types.ManagedObjectReference{}
        folder, err := f.Folder(ctx, "/dc/vm/folder_name/")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(folder.Common.InventoryPath)
        frefs = append(frefs, folder.Reference())
        var fdst []mo.Folder
        err = pc.Retrieve(ctx, frefs, nil, &fdst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, f := range fdst {
                fmt.Println(f.Name)
                fmt.Println(f.Tag)
        }
        // 全フォルダの取得
        fsrefs := []types.ManagedObjectReference{}
        folders, err := f.FolderList(ctx, "*")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, folder := range folders {
                fmt.Println(folder.Common.InventoryPath)
                fsrefs = append(fsrefs, folder.Reference())
        }
        var fsdst []mo.Folder
        err = pc.Retrieve(ctx, fsrefs, nil, &fsdst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, f := range fsdst {
                fmt.Println(f.Name)
                fmt.Println(f.Tag)
        }
}

まず vCenter に接続しデータセンターの情報を取得します
その後 f.Folder にフォルダのパスを指定することでフォルダ単体を取得しています
取得後は types.ManagedObjectReference に突っ込んで pc.Retrieve することでフォルダの情報を取得しています
あとは types.ManagedObjectReference に設定された値をループで回して参照しているだけです

全件取得の場合には f.FolderList(ctx, "*") とパスの部分をアスタリスクにすることで実現できます
あとは同じ流れで moref に突っ込んで pc.Retrieve で設定し、moref をループで回して参照するだけです

今回は Name と Tag を参照していますが、フォルダの他の情報を参照したい場合は godoc を見てください
https://godoc.org/github.com/vmware/govmomi/vim25/mo#Folder

最後に

govmomi を使ってフォルダを参照する方法を紹介しました
フォルダのオブジェクトが取得できるようになればフォルダに対する操作もできるようになります
例えば削除や名前の変更 VM をフォルダに移動することなどができるようになります

その辺の操作周りに関しても別記事で紹介できればなと思います

参考サイト

2017年4月18日火曜日

govmomi で VM を削除する方法

概要

govmomi で指定の VM を 1 台削除する方法を紹介します

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim destroy_vm.go
package main

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

    "github.com/vmware/govmomi"
    "github.com/vmware/govmomi/find"
    "github.com/vmware/govmomi/object"
)

type VCenterAPI struct {
    URL           string
    User          string
    Pass          string
    Insecure      bool
    URLFlag       *string
    InsecureFlag  *bool
    Network       string
    GovmomiClient *govmomi.Client
    Finder        *find.Finder
    DC            *object.Datacenter
}

func NewVCenterAPI() (api *VCenterAPI) {
    api = new(VCenterAPI)
    api.URL = "https://192.168.100.101/sdk"
    api.User = "vcenter-username"
    api.Pass = "vcenter-password"
    api.Insecure = true
    api.URLFlag = flag.String("url", api.URL, "")
    api.InsecureFlag = flag.Bool("insecure", api.Insecure, "")
    api.Network = "VM Network"
    flag.Parse()
    return api
}

func (api *VCenterAPI) Init() (err error) {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    u, err := url.Parse(*api.URLFlag)
    if err != nil {
        return err
    }
    u.User = url.UserPassword(api.User, api.Pass)
    c, err := govmomi.NewClient(ctx, u, *api.InsecureFlag)
    if err != nil {
        return err
    }
    f := find.NewFinder(c.Client, true)
    dc, err := f.DefaultDatacenter(ctx)
    if err != nil {
        return err
    }
    f.SetDatacenter(dc)
    api.GovmomiClient = c
    api.Finder = f
    api.DC = dc
    return nil
}

func (api *VCenterAPI) DestroyVM(path string) (err error) {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    vm, err := api.Finder.VirtualMachine(ctx, path)
    if err != nil {
        return err
    }
    t, err := vm.Destroy(ctx)
    if err != nil {
        return err
    }
    err = t.Wait(ctx)
    if err != nil {
        return err
    }
    return nil
}

func (api *VCenterAPI) PowerOffVM(path string) (err error) {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    vm, err := api.Finder.VirtualMachine(ctx, path)
    if err != nil {
        return err
    }
    t, err := vm.PowerOff(ctx)
    if err != nil {
        return err
    }
    err = t.Wait(ctx)
    if err != nil {
        return err
    }
    return nil
}

func main() {
    api := NewVCenterAPI()
    err := api.Init()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    path := "/dc/vm/folder/vm1"
    err = api.PowerOffVM(path)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    err = api.DestroyVM(path)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}
  • go fmt destroy_vm.go
  • go run destroy_vm.go

解説

削除する前に VM が停止していなければいけません
なので、PowerOffVM という関数を用意しています

DestroyVM も PowerOffVM もロジックはだいたい同じです
ポイントは find.VirtualMachine を使って単一の VM だけ取得している点です
find.Finder.VirtualMachineList を使って VM のインベントリパスを指定しても問題ないのですが、例えばアスタリスクなどを使って複数の VM を指定した場合複数の VM が取得できてしまいます
自分で VM の名前などでフィルタを掛けてもいいですが、そんなことをしないでも良いように準備されている find.VirtualMachine を使っています

あとはそこで取得できた object.VirtualMachine の構造体が用意している Destroy(context) を呼び出すだけで OK です
関数の戻り値にタスクが戻ってくるので Wait することでパワーオフと削除を待つことができます

実行する際に VM へのインベントリパスを指定しています
パスが存在しない場合にはエラーになります
主な発生するエラーは以下の通りです

  • find.VirtualMachine で複数指定すると以下のエラーになる
    • path '/dc/vm/folder/*' resolves to multiple vms
  • Destroy 対象の VM がパワー ON だと以下のエラーになる
    • The attempted operation cannot be performed in the current state (Powered on).
  • 指定した VM のインベントリパスがない場合
    • vm ‘/dc/vm/folder/non-vm’ not found

最後に

govmomi で VM を削除する方法を紹介しました
サンプルコードが見つからなかったのでリファレンスとソースコードを見ながら実装しましたが、慣れれば簡単にできるようになると思います

参考サイト

2017年4月17日月曜日

govmomi で指定のフォルダ配下の VM を取得する方法

概要

vSphpere 上のあるデータセンター配下に作成されたフォルダ配下に存在する VM の情報を取得してみました
今回は VM の名前と IP アドレスを取得するサンプルを紹介します

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim find_vm_sample.go
package main

import (
        "context"
        "flag"
        "fmt"
        "net/url"
        "os"
        "github.com/vmware/govmomi"
        "github.com/vmware/govmomi/find"
        "github.com/vmware/govmomi/object"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
        "github.com/vmware/govmomi/vim25/types"
)

type MyVCenter struct {
        URL           string
        User          string
        Pass          string
        Insecure      bool
        URLFlag       *string
        InsecureFlag  *bool
        Network       string
        GovmomiClient *govmomi.Client
        Finder        *find.Finder
        DC            *object.Datacenter
}

func NewMyVC() (myvc *MyVCenter) {
        myvc = new(MyVCenter)
        myvc.URL = "https://192.168.100.101/sdk"
        myvc.User = "vcenter-username"
        myvc.Pass = "vcenter-password"
        myvc.Insecure = true
        myvc.URLFlag = flag.String("url", myvc.URL, "")
        myvc.InsecureFlag = flag.Bool("insecure", myvc.Insecure, "")
        myvc.Network = "VM Network"
        flag.Parse()
        return myvc
}

func (myvc *MyVCenter) Init() (err error) {
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        u, err := url.Parse(*myvc.URLFlag)
        if err != nil {
                return err
        }
        u.User = url.UserPassword(myvc.User, myvc.Pass)
        c, err := govmomi.NewClient(ctx, u, *myvc.InsecureFlag)
        if err != nil {
                return err
        }
        f := find.NewFinder(c.Client, true) // true にするとディレクトリの場合も再帰的に検索してくれる
        dc, err := f.DefaultDatacenter(ctx)
        if err != nil {
                return err
        }
        f.SetDatacenter(dc)
        myvc.GovmomiClient = c
        myvc.Finder = f
        myvc.DC = dc
        return nil
}

func (myvc *MyVCenter) FindVMs(folder string) (err error) {
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        dci := myvc.DC.Common.InventoryPath
        path := fmt.Sprintf("%s/vm/%s/*", dci, folder) // フォルダは /dc/vm/folder という構造になっている
        vss, err := myvc.Finder.VirtualMachineList(ctx, path)
        if err != nil {
                return err
        }
        refs := []types.ManagedObjectReference{}
        for _, vs := range vss {
                refs = append(refs, vs.Reference())
        }
        vdst := []mo.VirtualMachine{}
        pc := property.DefaultCollector(myvc.GovmomiClient.Client)
        err = pc.Retrieve(ctx, refs, nil, &vdst)
        if err != nil {
                return err
        }
        for _, vs := range vdst {
                fmt.Println(vs.Summary.Config.Name)
                for _, n := range vs.Guest.Net {
                        if n.Network == myvc.Network {
                                fmt.Println(n.IpAddress)
                        }
                }
        }
        return nil
}

func main() {
        myvc := NewMyVC()
        err := myvc.Init()
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        err = myvc.FindVMs("your-folder-name")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
}
  • go fmt find_vm_sample.go
  • go run find_vm_sample.go

解説

ちょっと長いですが、流れは簡単です
まず NewMyVC で必要なデータをセットします
変更する箇所は VCenter の IP とユーザ名/パスワードかなと思います

次に Init してます
Init では VCenter への接続確認と検索するための必要な変数を MyVCenter 構造体に保存しています

そして取得したい VM の一覧があるフォルダを指定して FindVM を実行します
今回は VM 名と VM が接続しているネットワーク (VM Network) に割り振られた IP アドレスを表示するだけになります
ネットワークに接続されていない VM は VM だけ表示されると思います

最後に

govmomi を使ってフォルダ指定のフォルダ配下の VM を取得してみました
pager や limit のような機能が現状ないので、必要に応じて実装する必要があります
詳しく調べないと何ともですが、VirtualMachineList の引数にそういった指定ができなさそうなので、基本は全件取得かもしれません

VirtualMachine から取得できるプロパティは govmomi の godoc と types.go のコードを直接見るのがわかりやすいと思います

2017年4月13日木曜日

govmomi を試してみた

概要

vSphere の API を golang でコールするのに govmomi というライブラリがあります
今回はこれを使って vCenter から情報を取得したりしてみました

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ライブラリインストール

  • go get github.com/vmware/govmomi

とりあえず vCenter に接続する

  • vim connect.go
package main

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

        "github.com/vmware/govmomi"
)

var envURL = "https://192.168.100.101/sdk"
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)
        }
        u.User = url.UserPassword("vcenter_user", "vcenter_pass")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)
}

設定する箇所は envURL の部分と url.UserPassword の部分です
それぞれ vCenter の IP とユーザ名、パスワードを設定してください

とりあえずこれでエラーにならなければ vCenter には接続できています

データセンターの情報を取得する

import に以下を追加してください

"github.com/vmware/govmomi/find"

main の部分に以下を追記してください

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)

これでデータセンターの情報を取得できます

データストアのリストを取得する

import に以下を追加してください

"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types'

main の部分に以下を追記します

dss, err := f.DatastoreList(ctx, "*")
if err != nil {
        fmt.Println(err)
        os.Exit(1)
}
pc := property.DefaultCollector(c.Client)
var refs []types.ManagedObjectReference
for _, ds := range dss {
        fmt.Println(ds.Common.InventoryPath)
        refs = append(refs, ds.Reference())
}
var dst []mo.Datastore
err = pc.Retrieve(ctx, refs, nil, &dst)
if err != nil {
        fmt.Println(err)
        os.Exit(1)
}
for _, ds := range dst {
        fmt.Println(ds.Summary.Name)
        fmt.Println(ds.Summary.Type)
        fmt.Println(ds.Summary.Capacity)
        fmt.Println(ds.Summary.FreeSpace)
}

いきなりややこしくなった感があります
まず f.DatastoreList でデータストアのリストを取得します
このままだと InventoryPath しか取得できないので一旦ループさせて types.ManagedObjectReference の配列を作成します

さらにその配列をループさせて mo.Datastore の配列を作成します
この配列の中にデータストアのいろいろな情報が詰め込まれています
その詰め込むための関数が用意されていて property.DefaultCollector -> pc.Retrieve をコールします
これの 3 番目の引数に今回は nil を指定していますが、ここに ManagedObjectReference で設定したい値だけ設定することができます
例えば []string{"summary"} と指定すると summary の情報だけ設定することができます

設定した値はポインタで渡した dst に設定されるので、これを参照することでデータストアの詳細なデータにアクセスできます
dst から参照できるフィールドは mo の godoc を見ながらやるとわかりやすいと思います
更にその配下の構造体にさわる場合は godoc がなかったのどんなフィールドがあるかは直接ソースを見るしかなさそうです

VM のリストを取得する

main の部分に以下を追記します

refs = []types.ManagedObjectReference{}
vss, err := f.VirtualMachineList(ctx, "*")
if err != nil {
        fmt.Println(err)
        os.Exit(1)
}
for _, vs := range vss {
        fmt.Println(vs.Common.InventoryPath)
        refs = append(refs, vs.Reference())
}
var vdst []mo.VirtualMachine
err = pc.Retrieve(ctx, refs, nil, &vdst)
if err != nil {
        fmt.Println(err)
        os.Exit(1)
}
for _, vs := range vdst {
        fmt.Println(vs.Summary.Guest.GuestId)
        fmt.Println(vs.Summary.Guest.GuestFullName)
        fmt.Println(vs.Summary.Guest.HostName)
        fmt.Println(vs.Summary.Guest.IpAddress)
        fmt.Println(vs.Summary.Config.Name)
        fmt.Println(vs.Summary.Config.VmPathName)
        fmt.Println(vs.Summary.Config.MemorySizeMB)
        fmt.Println(vs.Summary.Config.NumCpu)
}

refs は設定し直します
f.VirtualMachineList を代わりにコールします
あとは pc.Retrieve を使って []mo.VirtualMachine に値を設定します

設定した dst のフィールドの値を参照することで VM の値を取得します

最後に

とりあえず取得系だけ試してみました
結構クセがある感じはしましたが、慣れればそれほど難しくはなさそうです

あとは update や post 系の処理がどんな感じかも試してみたいなと思います

参考サイト