こりずにvcsimネタを擦ります。今日はかなりマニアック。

TL; DR

  • vcsimでの各リソースのIDは何度起動しても変わらない
  • vcsimがバージョンアップした際には
    • MOID: 変わるかも
    • UUID: パスが同じ限り変わらない

はじめに

vcsimの各リソースには、実際のvCenter同様一意なIDが割り振られます。 このIDは何度起動しても同じ1なので、ツールのテストに使用する際には固定値で試せてありがたいです。

# DC0_H0_VM0 の moid (vcsim v0.27.4)
$ govc vm.info -json DC0_H0_VM0 | jq .VirtualMachines[].Self
{
  "Type": "VirtualMachine",
  "Value": "vm-54"
}

ただし、vcsimのバージョンが変わるとIDがずれることもよくあり、その際はテストを書き直して回る必要があります。 今回は、vcsimがどのように各種IDを生成しているのか内部実装を紹介したいと思います。

バージョン

  • vcsim v0.27.4+ (記事投稿時点のmasterブランチ)

MOID

各リソースのMOIDはオートインクリメントで払い出されています。 vcsim全体でロックをとっているため、常に一意性が担保されます。

実装としては、作成されたManaged Objectが親フォルダの子エンティティ一覧に追加される際に設定されます。

例として、vm作成のコードを見ていきます。

https://github.com/vmware/govmomi/blob/81c26b4b6522eca2af3ec9f35df669cb2d51f9fb/simulator/virtual_machine.go#L170

func NewVirtualMachine(ctx *Context, parent types.ManagedObjectReference, spec *types.VirtualMachineConfigSpec) (*VirtualMachine, types.BaseMethodFault) {
	vm := &VirtualMachine{}

	// ...

	folder := ctx.Map.Get(parent)
	f, _ := asFolderMO(folder)
	// ここで親folderに新規作成したvmをセット
	folderPutChild(ctx, f, vm)
}

folderPutChild はフォルダに子エンティティを追加する関数です。

ctx.Map.reference(o) はoのMOIDを参照するメソッドですが、副作用としてMOIDを持っていない場合に新規生成する役目も担っています。

https://github.com/vmware/govmomi/blob/81c26b4b6522eca2af3ec9f35df669cb2d51f9fb/simulator/folder.go#L89

func folderPutChild(ctx *Context, f *mo.Folder, o mo.Entity) {
	ctx.WithLock(f, func() {
		// Need to update ChildEntity before Map.Put for ContainerView updates to work properly
		f.ChildEntity = append(f.ChildEntity, ctx.Map.reference(o)) // ★
		ctx.Map.PutEntity(f, o)

		folderUpdate(ctx, f, o, ctx.Map.AddReference)

		ctx.WithLock(o, func() {
			switch e := o.(type) {
			case *mo.Network:
				e.Summary = networkSummary(e)
			case *mo.OpaqueNetwork:
				e.Summary = networkSummary(&e.Network)
			case *DistributedVirtualPortgroup:
				e.Summary = networkSummary(&e.Network)
			}
		})
	})
}

https://github.com/vmware/govmomi/blob/81c26b4b6522eca2af3ec9f35df669cb2d51f9fb/simulator/registry.go#L261

reference の実装です。typeかvalueが空文字の場合に新規生成しセットしています。

func (r *Registry) reference(item mo.Reference) types.ManagedObjectReference {
	ref := item.Reference()
	if ref.Type == "" || ref.Value == "" {
		ref = r.newReference(item)
		r.setReference(item, ref)
	}
	return ref
}

func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference {
	ref := item.Reference()

	if ref.Type == "" {
		ref.Type = typeName(item)
	}

	if ref.Value == "" {
		n := atomic.AddInt64(&r.counter, 1)
		ref.Value = fmt.Sprintf("%s%d", valuePrefix(ref.Type), n)
	}

	return ref
}

実装からもわかるように

  • Type: エンティティの型名(ここでは VirtualMachine)
  • Value: 型名に応じたprefix + オートインクリメントの整数(例: vm-123

が生成され、晴れて一意なMOIDとして利用可能になります。

MOIDのずれ

上記のように、MOIDの整数部分はオートインクリメントなので、デフォルトのManaged Objectが新規追加されるとその分だけずれます。

DC0_H0_VM0 は現在54番目に作られているのでMOIDが vm-54 ですが、仮に次のリリースで1つManaged Objectが増え、その生成タイミングが DC0_H0_VM0 よりも前なら vm-55 に繰り下げになります。

(もちろん、上記以外にもバグ修正によって変な名前だったMOIDがvCenterに準拠する形式に修正されることもあるでしょう)

UUID

UUIDはリソースのパス文字列をもとに生成されます。

生成には github.com/google/uuid.NewSHA1 が使用され、Version 5のUUIDが作られます。

https://github.com/vmware/govmomi/blob/81c26b4b6522eca2af3ec9f35df669cb2d51f9fb/simulator/object.go#L64

func sha1UUID(s string) uuid.UUID {
	return uuid.NewSHA1(uuid.NameSpaceOID, []byte(s))
}

文字列として与えられる内容は以下の通りです。

  • vmのUUID: .Config.Files.VmPathName
  • vmのInstanceUUID .Config.Files.VmPathName を大文字化した文字列
  • hostのUUID: ホスト名
  • diskのUUID: Datacenter:${所属データセンターのMOID} + .Backing.FileName

詳しくは以前書いたGist をご覧ください(v0.26.0当時の内容ですが、v0.27.4でも実装は同じです)。

名前やパスに依存しているため、基本的にvcsimのバージョンアップでは変化しません(ディスクだけはデータセンターのMOIDが入っているので怪しいかも)。

おわりに

以上、vcsimのIDについてでした。変更リスクを上手く見定めて、上手くテストに組み込んでいきたいですね。


  1. データストアを除く。(データストアは実際のディレクトリに紐づいており、衝突を防ぐためデフォルトでは一時ディレクトリに対して作られる。v0.27.4時点ではこのディレクトリ名がIDに使われる実装になっている。) ↩︎