Skip to content

Commit d99e995

Browse files
authored
Merge pull request #2626 from dougm/network-find
Support finding Portgroups by ID in Finder.Network
2 parents b0d1bbd + bb4f739 commit d99e995

File tree

8 files changed

+143
-7
lines changed

8 files changed

+143
-7
lines changed

find/finder.go

+53
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/vmware/govmomi/list"
2727
"github.com/vmware/govmomi/object"
2828
"github.com/vmware/govmomi/property"
29+
"github.com/vmware/govmomi/view"
2930
"github.com/vmware/govmomi/vim25"
3031
"github.com/vmware/govmomi/vim25/mo"
3132
"github.com/vmware/govmomi/vim25/types"
@@ -789,9 +790,26 @@ func (f *Finder) NetworkList(ctx context.Context, path string) ([]object.Network
789790
return ns, nil
790791
}
791792

793+
// Network finds a NetworkReference using a Name, Inventory Path, ManagedObject ID, Logical Switch UUID or Segment ID.
794+
// With standard vSphere networking, Portgroups cannot have the same name within the same network folder.
795+
// With NSX, Portgroups can have the same name, even within the same Switch. In this case, using an inventory path
796+
// results in a MultipleFoundError. A MOID, switch UUID or segment ID can be used instead, as both are unique.
797+
// See also: https://kb.vmware.com/s/article/79872#Duplicate_names
798+
// Examples:
799+
// - Name: "dvpg-1"
800+
// - Inventory Path: "vds-1/dvpg-1"
801+
// - ManagedObject ID: "DistributedVirtualPortgroup:dvportgroup-53"
802+
// - Logical Switch UUID: "da2a59b8-2450-4cb2-b5cc-79c4c1d2144c"
803+
// - Segment ID: "/infra/segments/vnet_ce50e69b-1784-4a14-9206-ffd7f1f146f7"
792804
func (f *Finder) Network(ctx context.Context, path string) (object.NetworkReference, error) {
793805
networks, err := f.NetworkList(ctx, path)
794806
if err != nil {
807+
if _, ok := err.(*NotFoundError); ok {
808+
net, nerr := f.networkByID(ctx, path)
809+
if nerr == nil {
810+
return net, nil
811+
}
812+
}
795813
return nil, err
796814
}
797815

@@ -802,6 +820,41 @@ func (f *Finder) Network(ctx context.Context, path string) (object.NetworkRefere
802820
return networks[0], nil
803821
}
804822

823+
func (f *Finder) networkByID(ctx context.Context, path string) (object.NetworkReference, error) {
824+
if ref := object.ReferenceFromString(path); ref != nil {
825+
// This is a MOID
826+
return object.NewReference(f.client, *ref).(object.NetworkReference), nil
827+
}
828+
829+
kind := []string{"DistributedVirtualPortgroup"}
830+
831+
m := view.NewManager(f.client)
832+
v, err := m.CreateContainerView(ctx, f.client.ServiceContent.RootFolder, kind, true)
833+
if err != nil {
834+
return nil, err
835+
}
836+
defer v.Destroy(ctx)
837+
838+
filter := property.Filter{
839+
"config.logicalSwitchUuid": path,
840+
"config.segmentId": path,
841+
}
842+
843+
refs, err := v.FindAny(ctx, kind, filter)
844+
if err != nil {
845+
return nil, err
846+
}
847+
848+
if len(refs) == 0 {
849+
return nil, &NotFoundError{"network", path}
850+
}
851+
if len(refs) > 1 {
852+
return nil, &MultipleFoundError{"network", path}
853+
}
854+
855+
return object.NewReference(f.client, refs[0]).(object.NetworkReference), nil
856+
}
857+
805858
func (f *Finder) DefaultNetwork(ctx context.Context) (object.NetworkReference, error) {
806859
network, err := f.Network(ctx, "*")
807860
if err != nil {

govc/test/network.bats

+23
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ load test_helper
4646
run govc dvs.portgroup.add -dvs DVS0 -type ephemeral NSX-dvpg
4747
assert_success
4848

49+
uuid=$(govc object.collect -s network/NSX-dvpg config.logicalSwitchUuid)
50+
sid=$(govc object.collect -s network/NSX-dvpg config.segmentId)
51+
moid=$(govc ls -i network/NSX-dvpg)
52+
4953
run govc dvs.portgroup.add -dvs DVS1 -type ephemeral NSX-dvpg
5054
assert_success
5155

@@ -54,6 +58,25 @@ load test_helper
5458

5559
run govc vm.network.add -vm $vm -net DVS0/NSX-dvpg
5660
assert_success # switch_name/portgroup_name is unique
61+
62+
# Add a 2nd PG to the same switch, with the same name
63+
run govc dvs.portgroup.add -dvs DVS0 -type ephemeral NSX-dvpg
64+
assert_success
65+
66+
run govc vm.network.add -vm $vm -net NSX-dvpg
67+
assert_failure # resolves to multiple networks
68+
69+
run govc vm.network.add -vm $vm -net DVS0/NSX-dvpg
70+
assert_failure # switch_name/portgroup_name not is unique
71+
72+
run govc vm.network.add -vm $vm -net "$uuid"
73+
assert_success # switch uuid is unique
74+
75+
run govc vm.network.add -vm $vm -net "$sid"
76+
assert_success # segment id is unique
77+
78+
run govc vm.network.add -vm $vm -net "$moid"
79+
assert_success # moid is unique
5780
}
5881

5982
@test "network change backing" {

property/filter.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ func (f Filter) MatchPropertyList(props []types.DynamicProperty) bool {
130130
return len(f) == len(props) // false if a property such as VM "guest" is unset
131131
}
132132

133-
// MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches the Filter.
133+
// MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches all properties the Filter.
134134
func (f Filter) MatchObjectContent(objects []types.ObjectContent) []types.ManagedObjectReference {
135135
var refs []types.ManagedObjectReference
136136

@@ -142,3 +142,27 @@ func (f Filter) MatchObjectContent(objects []types.ObjectContent) []types.Manage
142142

143143
return refs
144144
}
145+
146+
// MatchAnyPropertyList returns true if any given props match the Filter.
147+
func (f Filter) MatchAnyPropertyList(props []types.DynamicProperty) bool {
148+
for _, p := range props {
149+
if f.MatchProperty(p) {
150+
return true
151+
}
152+
}
153+
154+
return false
155+
}
156+
157+
// MatchAnyObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches any property in the Filter.
158+
func (f Filter) MatchAnyObjectContent(objects []types.ObjectContent) []types.ManagedObjectReference {
159+
var refs []types.ManagedObjectReference
160+
161+
for _, o := range objects {
162+
if f.MatchAnyPropertyList(o.PropSet) {
163+
refs = append(refs, o.Obj)
164+
}
165+
}
166+
167+
return refs
168+
}

simulator/dvs.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ limitations under the License.
1717
package simulator
1818

1919
import (
20+
"fmt"
2021
"strconv"
2122
"strings"
2223

24+
"github.com/google/uuid"
25+
2326
"github.com/vmware/govmomi/vim25/methods"
2427
"github.com/vmware/govmomi/vim25/mo"
2528
"github.com/vmware/govmomi/vim25/soap"
@@ -46,7 +49,15 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(ctx *Context, c *types.Add
4649

4750
// Standard AddDVPortgroupTask() doesn't allow duplicate names, but NSX 3.0 does create some DVPGs with the same name.
4851
// Allow duplicate names using this prefix so we can reproduce and test this condition.
49-
if !strings.HasPrefix(pg.Name, "NSX-") {
52+
if strings.HasPrefix(pg.Name, "NSX-") || spec.BackingType == string(types.DistributedVirtualPortgroupBackingTypeNsx) {
53+
if spec.LogicalSwitchUuid == "" {
54+
spec.LogicalSwitchUuid = uuid.New().String()
55+
}
56+
if spec.SegmentId == "" {
57+
spec.SegmentId = fmt.Sprintf("/infra/segments/vnet_%s", uuid.New().String())
58+
}
59+
60+
} else {
5061
if obj := Map.FindByName(pg.Name, f.ChildEntity); obj != nil {
5162
return nil, &types.DuplicateName{
5263
Name: pg.Name,
@@ -74,6 +85,7 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(ctx *Context, c *types.Add
7485
AutoExpand: spec.AutoExpand,
7586
VmVnicNetworkResourcePoolKey: spec.VmVnicNetworkResourcePoolKey,
7687
LogicalSwitchUuid: spec.LogicalSwitchUuid,
88+
SegmentId: spec.SegmentId,
7789
BackingType: spec.BackingType,
7890
}
7991

simulator/entity.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ import (
2323
"github.com/vmware/govmomi/vim25/types"
2424
)
2525

26-
func RenameTask(ctx *Context, e mo.Entity, r *types.Rename_Task) soap.HasFault {
26+
func RenameTask(ctx *Context, e mo.Entity, r *types.Rename_Task, dup ...bool) soap.HasFault {
2727
task := CreateTask(e, "rename", func(t *Task) (types.AnyType, types.BaseMethodFault) {
2828
obj := Map.Get(r.This).(mo.Entity).Entity()
2929

30-
if parent, ok := asFolderMO(Map.Get(*obj.Parent)); ok {
30+
canDup := len(dup) == 1 && dup[0]
31+
if parent, ok := asFolderMO(Map.Get(*obj.Parent)); ok && !canDup {
3132
if Map.FindByName(r.NewName, parent.ChildEntity) != nil {
3233
return nil, &types.InvalidArgument{InvalidProperty: "name"}
3334
}

simulator/model.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -669,9 +669,9 @@ func (m *Model) Create() error {
669669
for npg := 0; npg < m.PortgroupNSX; npg++ {
670670
name := m.fmtName(dcName+"_NSXPG", npg)
671671
spec := types.DVPortgroupConfigSpec{
672-
Name: name,
673-
LogicalSwitchUuid: uuid.New().String(),
674-
Type: string(types.DistributedVirtualPortgroupPortgroupTypeEarlyBinding),
672+
Name: name,
673+
Type: string(types.DistributedVirtualPortgroupPortgroupTypeEarlyBinding),
674+
BackingType: string(types.DistributedVirtualPortgroupBackingTypeNsx),
675675
}
676676

677677
task, err := dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{spec})

simulator/portgroup.go

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ type DistributedVirtualPortgroup struct {
2727
mo.DistributedVirtualPortgroup
2828
}
2929

30+
func (s *DistributedVirtualPortgroup) RenameTask(ctx *Context, req *types.Rename_Task) soap.HasFault {
31+
canDup := s.DistributedVirtualPortgroup.Config.BackingType == string(types.DistributedVirtualPortgroupBackingTypeNsx)
32+
33+
return RenameTask(ctx, s, req, canDup)
34+
}
35+
3036
func (s *DistributedVirtualPortgroup) ReconfigureDVPortgroupTask(ctx *Context, req *types.ReconfigureDVPortgroup_Task) soap.HasFault {
3137
task := CreateTask(s, "reconfigureDvPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) {
3238
s.Config.DefaultPortConfig = req.Spec.DefaultPortConfig

view/container_view.go

+17
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,20 @@ func (v ContainerView) Find(ctx context.Context, kind []string, filter property.
126126

127127
return filter.MatchObjectContent(content), nil
128128
}
129+
130+
// FindAny returns object references for entities of type kind, matching any property the given filter.
131+
func (v ContainerView) FindAny(ctx context.Context, kind []string, filter property.Filter) ([]types.ManagedObjectReference, error) {
132+
if len(filter) == 0 {
133+
// Ensure we have at least 1 filter to avoid retrieving all properties.
134+
filter = property.Filter{"name": "*"}
135+
}
136+
137+
var content []types.ObjectContent
138+
139+
err := v.Retrieve(ctx, kind, filter.Keys(), &content)
140+
if err != nil {
141+
return nil, err
142+
}
143+
144+
return filter.MatchAnyObjectContent(content), nil
145+
}

0 commit comments

Comments
 (0)