Skip to content

Commit

Permalink
api: support MOID conversion in Finder methods
Browse files Browse the repository at this point in the history
Signed-off-by: Doug MacEachern <[email protected]>
  • Loading branch information
dougm committed Nov 4, 2024
1 parent 8f3b0a3 commit a9d5985
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 25 deletions.
7 changes: 5 additions & 2 deletions find/doc.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2022 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -32,6 +32,9 @@ otherwise "find" mode is used.
The exception is to use a "..." wildcard with a path to find all objects recursively underneath any root object.
For example: VirtualMachineList("/DC1/...")
Finder methods can also convert a managed object reference (aka MOID) to an object instance.
For example: VirtualMachine("VirtualMachine:vm-123") or VirtualMachine("vm-123")
See also: https://github.com/vmware/govmomi/blob/main/govc/README.md#usage
*/
package find
23 changes: 16 additions & 7 deletions find/finder.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Copyright (c) 2014-2023 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2024 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -22,6 +22,7 @@ import (
"path"
"strings"

"github.com/vmware/govmomi/fault"
"github.com/vmware/govmomi/internal"
"github.com/vmware/govmomi/list"
"github.com/vmware/govmomi/object"
Expand Down Expand Up @@ -116,6 +117,19 @@ func (f *Finder) findRoot(ctx context.Context, root *list.Element, parts []strin
func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element, error) {
isPath := strings.Contains(arg, "/")

if !isPath {
if ref := object.ReferenceFromString(arg); ref != nil {
p, err := InventoryPath(ctx, f.client, *ref)
if err == nil {
if t, ok := mo.Value(*ref); ok {
return []list.Element{{Object: t, Path: p}}, nil
}
} else if !fault.Is(err, &types.ManagedObjectNotFound{}) {
return nil, err
} // else fall through to name based lookup
}
}

root := list.Element{
Object: object.NewRootFolder(f.client),
Path: "/",
Expand Down Expand Up @@ -821,11 +835,6 @@ func (f *Finder) Network(ctx context.Context, path string) (object.NetworkRefere
}

func (f *Finder) networkByID(ctx context.Context, path string) (object.NetworkReference, error) {
if ref := object.ReferenceFromString(path); ref != nil {
// This is a MOID
return object.NewReference(f.client, *ref).(object.NetworkReference), nil
}

kind := []string{"DistributedVirtualPortgroup"}

m := view.NewManager(f.client)
Expand Down
47 changes: 46 additions & 1 deletion find/finder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand Down Expand Up @@ -105,3 +105,48 @@ func TestFindNetwork(t *testing.T) {
}
}, model)
}

func TestFindByID(t *testing.T) {
simulator.Test(func(ctx context.Context, c *vim25.Client) {
find := find.NewFinder(c)

vms, err := find.VirtualMachineList(ctx, "*")
if err != nil {
t.Fatal(err)
}

for _, vm := range vms {
ref := vm.Reference()
byRef, err := find.VirtualMachine(ctx, ref.String())
if err != nil {
t.Fatal(err)
}
if byRef.InventoryPath != vm.InventoryPath {
t.Errorf("InventoryPath=%q", byRef.InventoryPath)
}
if byRef.Reference() != ref {
t.Error(byRef.Reference())
}
_, err = find.VirtualMachine(ctx, ref.String()+"invalid")
if err == nil {
t.Error("expected error")
}

byID, err := find.VirtualMachine(ctx, ref.Value)
if err != nil {
t.Error(err)
}
if byID.InventoryPath != vm.InventoryPath {
t.Errorf("InventoryPath=%q", byID.InventoryPath)
}
if byID.Reference() != ref {
t.Error(byID.Reference())
}
_, err = find.VirtualMachine(ctx, ref.Value+"invalid")
if err == nil {
t.Error("expected error")
}

}
})
}
18 changes: 6 additions & 12 deletions govc/flags/datacenter.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2024 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand Down Expand Up @@ -197,16 +197,6 @@ func (flag *DatacenterFlag) ManagedObjects(ctx context.Context, args []string) (
}

for _, arg := range args {
if ref := object.ReferenceFromString(arg); ref != nil {
// e.g. output from object.collect
refs = append(refs, *ref)
continue
}

if !strings.Contains(arg, "/") {
return nil, fmt.Errorf("%q must be qualified with a path", arg)
}

elements, err := finder.ManagedObjectList(ctx, arg)
if err != nil {
return nil, err
Expand All @@ -216,6 +206,10 @@ func (flag *DatacenterFlag) ManagedObjects(ctx context.Context, args []string) (
return nil, fmt.Errorf("object '%s' not found", arg)
}

if len(elements) > 1 && !strings.Contains(arg, "/") {
return nil, fmt.Errorf("%q must be qualified with a path", arg)
}

for _, e := range elements {
refs = append(refs, e.Object.Reference())
}
Expand Down
36 changes: 36 additions & 0 deletions govc/test/object.bats
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,42 @@ EOF
assert_success 2
}

@test "collect by id" {
vcsim_env -standalone-host 0 -pod 1 -app 1

run govc find -i /
assert_success

for ref in "${lines[@]}" ; do
run govc collect "$ref" name
assert_success
done

run govc find -I /
assert_success

for id in "${lines[@]}" ; do
run govc collect "$id" name
assert_success
done

run govc find -I -type m /
assert_success

for id in "${lines[@]}" ; do
run govc vm.info "$id"
assert_success
done

run govc find -I -type h /
assert_success

for id in "${lines[@]}" ; do
run govc host.info "$id"
assert_success
done
}

@test "object.find" {
vcsim_env -ds 2

Expand Down
52 changes: 49 additions & 3 deletions object/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"path"
"strings"

"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25"
Expand Down Expand Up @@ -136,13 +137,58 @@ func (c Common) SetCustomValue(ctx context.Context, key string, value string) er
return err
}

var refTypeMap = map[string]string{
"datacenter": "Datacenter",
"datastore": "Datastore",
"domain": "ComputeResource",
"dvportgroup": "DistributedVirtualPortgroup",
"dvs": "DistributedVirtualSwitch",
"group": "Folder",
"host": "HostSystem",
"network": "Network",
"resgroup": "ResourcePool",
"vm": "VirtualMachine",
}

// sub types
var prefixTypeMap = map[string]struct{ prefix, kind string }{
"domain": {"c", "ClusterComputeResource"}, // extends ComputeResource
"group": {"p", "StoragePod"}, // extends Folder
"resgroup": {"v", "VirtualApp"}, // extends ResourcePool
}

// ReferenceFromString converts a string to ManagedObjectReference.
// First checks for ManagedObjectReference (MOR), in the format of:
// "$Type:$ID", e.g. "Datacenter:datacenter-3"
// Next checks for Managed Object ID (MOID), where type is derived from the ID.
// For example, "datacenter-3" is converted to a MOR "Datacenter:datacenter-3"
// Returns nil if string is not in either format.
func ReferenceFromString(s string) *types.ManagedObjectReference {
var ref types.ManagedObjectReference
if !ref.FromString(s) {
if ref.FromString(s) && mo.IsManagedObjectType(ref.Type) {
return &ref
}

id := strings.SplitN(s, "-", 2)
if len(id) != 2 {
return nil
}
if mo.IsManagedObjectType(ref.Type) {
return &ref

if kind, ok := refTypeMap[id[0]]; ok {
if p, ok := prefixTypeMap[id[0]]; ok {
if strings.HasPrefix(id[1], p.prefix) {
return &types.ManagedObjectReference{
Type: p.kind,
Value: s,
}
}
}

return &types.ManagedObjectReference{
Type: kind,
Value: s,
}
}

return nil
}
28 changes: 28 additions & 0 deletions object/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ package object_test

import (
"context"
"reflect"
"testing"

"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/types"
)

func TestCommonName(t *testing.T) {
Expand Down Expand Up @@ -63,3 +65,29 @@ func TestObjectName(t *testing.T) {
}
})
}

func TestReferenceFromString(t *testing.T) {
tests := []struct {
in string
out *types.ManagedObjectReference
}{
{"no:no", nil},
{"Datacenter:yes", &types.ManagedObjectReference{Type: "Datacenter", Value: "yes"}},
{"datacenter-yes", &types.ManagedObjectReference{Type: "Datacenter", Value: "datacenter-yes"}},
{"VirtualMachine:vm-2", &types.ManagedObjectReference{Type: "VirtualMachine", Value: "vm-2"}},
{"vm-2", &types.ManagedObjectReference{Type: "VirtualMachine", Value: "vm-2"}},
{"domain-s2", &types.ManagedObjectReference{Type: "ComputeResource", Value: "domain-s2"}},
{"domain-c2", &types.ManagedObjectReference{Type: "ClusterComputeResource", Value: "domain-c2"}},
{"group-d1", &types.ManagedObjectReference{Type: "Folder", Value: "group-d1"}},
{"group-p2", &types.ManagedObjectReference{Type: "StoragePod", Value: "group-p2"}},
{"resgroup-42", &types.ManagedObjectReference{Type: "ResourcePool", Value: "resgroup-42"}},
{"resgroup-v32", &types.ManagedObjectReference{Type: "VirtualApp", Value: "resgroup-v32"}},
}

for _, test := range tests {
ref := object.ReferenceFromString(test.in)
if !reflect.DeepEqual(test.out, ref) {
t.Errorf("%s: expected %v, got %v", test.in, test.out, ref)
}
}
}
2 changes: 2 additions & 0 deletions simulator/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ var refValueMap = map[string]string{
"EnvironmentBrowser": "envbrowser",
"HostSystem": "host",
"ResourcePool": "resgroup",
"VirtualApp": "resgroup-v",
"VirtualMachine": "vm",
"VirtualMachineSnapshot": "snapshot",
"VmwareDistributedVirtualSwitch": "dvs",
"DistributedVirtualSwitch": "dvs",
"ClusterComputeResource": "domain-c",
"ComputeResource": "domain-s",
"Folder": "group",
"StoragePod": "group-p",
}
Expand Down
10 changes: 10 additions & 0 deletions vim25/mo/type_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@ func IsManagedObjectType(kind string) bool {
return ok
}

// Value returns a new mo instance of the given ref Type.
func Value(ref types.ManagedObjectReference) (Reference, bool) {
if rt, ok := t[ref.Type]; ok {
val := reflect.New(rt)
val.Interface().(Entity).Entity().Self = ref
return val.Elem().Interface().(Reference), true
}
return nil, false
}

// Field of a ManagedObject in string form.
type Field struct {
Path string
Expand Down

0 comments on commit a9d5985

Please sign in to comment.