てくてくてっく

おそらく技術ブログ

govmomiでVMのオブジェクトを取得する方法 6パターン

govmomiでオブジェクトの取得方法を探している方のために取得方法をいくつか紹介します。

(前提)govmomi検証環境

govmomiを試すためにはgovmomiのパッケージに含まれるvcsimを使えば、実際のvCenterと疎通しない環境でもコードの実行が可能です。vcsimコマンドにより起動することもできますが、簡単なやり方としてコード内で examples.Run() をすることで内部的にvcsimと疎通したクライアントが取得できるため、これを用いると楽です。

package main

import (
    "context"
    "fmt"

    "github.com/vmware/govmomi/examples"
    "github.com/vmware/govmomi/find"
    "github.com/vmware/govmomi/vim25"
)

func main() {
    examples.Run(func(ctx context.Context, c *vim25.Client) error {
        f := find.NewFinder(c)
        vm, err := f.VirtualMachine(ctx, "DC0_H0_VM0")
        if err != nil {
            return err
        }

        fmt.Printf("name: %s, path: %s, mo-id: %v\n", vm.Name(), vm.InventoryPath, vm.Reference())

        return nil
    })
}

実行結果

> go run main.go
name: DC0_H0_VM0, path: /DC0/vm/DC0_H0_VM0, mo-id: VirtualMachine:vm-54

では本題に戻ってVMの取得方法を紹介します。

1. finderでVM名から検索

すでに紹介してしまいましたが、finderで取得する方法です。名前、もしくはパスでVMを検索します。ただし名前で検索する場合は複数一致した場合にfind.MultipleFoundErrorが返ってくるため、複数ある場合を想定するのであれば VirtualMachine 関数ではなく VirtualMachineList 関数の利用が必要です。

examples.Run(func(ctx context.Context, c *vim25.Client) error {
        fmt.Println("from name")

        f := find.NewFinder(c)
        vm, err := f.VirtualMachine(ctx, "DC0_H0_VM0")
        if err != nil {
            return err
        }

        fmt.Printf("name: %s, path: %s, mo-id: %v\n", vm.Name(), vm.InventoryPath, vm.Reference())

        return nil
})

実行結果

from name
name: DC0_H0_VM0, path: /DC0/vm/DC0_H0_VM0, mo-id: VirtualMachine:vm-54

2. finderでフォルダのパスから検索

特定のフォルダ配下のVMを取得したい場合は、finderでのVM取得方法を少し工夫することで実現可能です。実はfinderの引数にはワールドカードが指定可能なので次のようにフォルダ名以降を * とすることでVM名が不明であってもフォルダ配下のVMとして取得可能です。ちなみに内部的にワイルドカードはGo標準の path.Match が利用されてますので、利用可能な文字や仕様はそちらが参考になります。

examples.Run(func(ctx context.Context, c *vim25.Client) error {
        fmt.Println("from folder name")

        f := find.NewFinder(c)
        vms, err := f.VirtualMachineList(ctx, "/DC0/vm/*")
        if err != nil {
            return err
        }

        for _, vm := range vms {
            fmt.Printf("name: %s, path: %s, mo-id: %v\n", vm.Name(), vm.InventoryPath, vm.Reference())
        }

        return nil
})

実行結果

from folder name
name: DC0_H0_VM0, path: /DC0/vm/DC0_H0_VM0, mo-id: VirtualMachine:vm-54
name: DC0_H0_VM1, path: /DC0/vm/DC0_H0_VM1, mo-id: VirtualMachine:vm-57
name: DC0_C0_RP0_VM0, path: /DC0/vm/DC0_C0_RP0_VM0, mo-id: VirtualMachine:vm-60
name: DC0_C0_RP0_VM1, path: /DC0/vm/DC0_C0_RP0_VM1, mo-id: VirtualMachine:vm-63

3. mo-idから取得

逆に名前やパスが不明でも、ManagedObjectのIDがわかっていればIDから直接の取得も可能です。finderのObjectReference関数はManagedObjectが存在する場合のみInventoryPathがセットされた状態でオブジェクトが返却されてきます。ただ、返却値がobject.Reference型のためVMのオブジェクトとして利用するためには型アサーションによる変換が必要です。型アサーションはpanicやnilアクセスを起こす原因にもなり得ますので扱いには注意が必要です。

examples.Run(func(ctx context.Context, c *vim25.Client) error {
        fmt.Println("from mo-id")

        f := find.NewFinder(c)
        ref, err := f.ObjectReference(ctx, types.ManagedObjectReference{
            Type:  "VirtualMachine",
            Value: "vm-54",
        })
        if err != nil {
            return err
        }
        vm, _ := ref.(*object.VirtualMachine)

        fmt.Printf("name: %s, path: %s, mo-id: %v\n", vm.Name(), vm.InventoryPath, vm.Reference())

        return nil
})

実行結果

from mo-id
name: DC0_H0_VM0, path: /DC0/vm/DC0_H0_VM0, mo-id: VirtualMachine:vm-54

4. mo-idから取得(finderを利用しない方法)

先ほどのfinderのObjectReference関数を使った方法は型アサーションが必要であり若干扱いづらそうです。mo-idがわかっているのであれば、object.NewVirtualMachine()関数によりオブジェクト自体を生成してしまう方法も取れます。生成したオブジェクトより、必要な要素をメンバ関数から取得していく方法がとれます。この方法で注意したい点としては、object.NewVirtualMachine関数の返却値にerrorが存在しないことからもわかるように、生成した段階では実際にそのオブジェクトが存在するかは保証されません。そのため生成後に、vm.Name(), vm.InventoryPathなどは空で返却されます。そのためサンプルではvm.ObjectName(ctx)等の関数を利用し、内部的にはプロパティコレクターを利用することで、オブジェクトの存在確認と合わせて情報を収集しています。

examples.Run(func(ctx context.Context, c *vim25.Client) error {
        fmt.Println("from mo-id(without finder)")

        vm := object.NewVirtualMachine(c, types.ManagedObjectReference{
            Type:  "VirtualMachine",
            Value: "vm-54",
        })

        name, err := vm.ObjectName(ctx)
        if err != nil {
            return err
        }
        path, err := find.InventoryPath(ctx, c, vm.Reference())
        if err != nil {
            return err
        }

        fmt.Printf("name: %s, path: %s, mo-id: %v\n", name, path, vm.Reference())

        return nil
})

実行結果

from mo-id(without finder)
name: DC0_H0_VM0, path: /DC0/vm/DC0_H0_VM0, mo-id: VirtualMachine:vm-54

5. VMのUUIDから取得

VMにはUUIDが存在しますのでこれをベースに取得する方法がシンプルです。SearchIndexのFindByUuid関数により取得します。ポイントとしてはFindByUuid関数はVMとホストが取得可能な関数であったり、UUIDとしてもVM UUIDとInstance UUIDが指定可能な点などオプションの要素がありますのでここを設定する必要があります。オプションの詳細は SearchIndexのAPI仕様 が参考になります。

ちなみにこの記事でSearchIndexというものは初めて登場したようにも見えますが、実はfinderはSearchIndexの FindByInventoryPath と FindChild をラップしたものだったりします。(参照: find/docs.go)

   examples.Run(func(ctx context.Context, c *vim25.Client) error {
        fmt.Println("from vm uuid")

        si := object.NewSearchIndex(c)
        ref, err := si.FindByUuid(
            ctx,
            nil, // use default dc
            "265104de-1472-547c-b873-6dc7883fb6cb",
            true,
            nil, // if Instance UUID => types.NewBool(true)
        )
        if err != nil {
            return err
        }

        vm, _ := ref.(*object.VirtualMachine)

        name, err := vm.ObjectName(ctx)
        if err != nil {
            return err
        }
        path, err := find.InventoryPath(ctx, c, vm.Reference())
        if err != nil {
            return err
        }

        fmt.Printf("name: %s, path: %s, mo-id: %v\n", name, path, vm.Reference())

        return nil
})

実行結果

from vm uuid
name: DC0_H0_VM0, path: /DC0/vm/DC0_H0_VM0, mo-id: VirtualMachine:vm-54

6. Hostから取得

最後は少し違った方法としてVMを所有するHostから取得します。HostはVMの参照(ManagedObjectReference)を持つため間接的に取得可能です。host.PropertiesでプロパティコレクターによりVMの参照を取得します。ちなみにプロパティコレクターの引数が "vm" であることを特定するためにはManagedObjectの HostSystemの仕様 により vm という子要素を持っていることが確認できるため、引数は "vm" となります。VMの参照が取得できたらすでに紹介したmo-idからの取得方法によりVMのオブジェクトを取得します。

examples.Run(func(ctx context.Context, c *vim25.Client) error {
        fmt.Println("from host")

        f := find.NewFinder(c)
        host, err := f.HostSystem(ctx, "DC0_H0")
        if err != nil {
            return err
        }

        var m mo.HostSystem
        err = host.Properties(ctx, host.Reference(), []string{"vm"}, &m)
        if err != nil {
            return err
        }

        vmrefs := m.Vm
        for _, vmref := range vmrefs {
            vm := object.NewVirtualMachine(c, vmref)

            name, err := vm.ObjectName(ctx)
            if err != nil {
                return err
            }
            path, err := find.InventoryPath(ctx, c, vm.Reference())
            if err != nil {
                return err
            }

            fmt.Printf("name: %s, path: %s, mo-id: %v\n", name, path, vm.Reference())
        }

        return nil
})

実行結果

from host
name: DC0_H0_VM0, path: /DC0/vm/DC0_H0_VM0, mo-id: VirtualMachine:vm-54
name: DC0_H0_VM1, path: /DC0/vm/DC0_H0_VM1, mo-id: VirtualMachine:vm-57

さいごに

govmomiでVMを見つけるための方法をいくつか紹介しました。vCenter関係の自動化やシステム化に取り組もうとしている方の参考になれば幸いです。
コードはGitHubにも公開しましたのでご参照ください。

github.com