1/*
2Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package find
18
19import (
20	"context"
21	"errors"
22	"path"
23	"strings"
24
25	"github.com/vmware/govmomi/list"
26	"github.com/vmware/govmomi/object"
27	"github.com/vmware/govmomi/property"
28	"github.com/vmware/govmomi/vim25"
29	"github.com/vmware/govmomi/vim25/mo"
30	"github.com/vmware/govmomi/vim25/types"
31)
32
33type Finder struct {
34	client  *vim25.Client
35	r       recurser
36	dc      *object.Datacenter
37	si      *object.SearchIndex
38	folders *object.DatacenterFolders
39}
40
41func NewFinder(client *vim25.Client, all bool) *Finder {
42	f := &Finder{
43		client: client,
44		si:     object.NewSearchIndex(client),
45		r: recurser{
46			Collector: property.DefaultCollector(client),
47			All:       all,
48		},
49	}
50
51	return f
52}
53
54func (f *Finder) SetDatacenter(dc *object.Datacenter) *Finder {
55	f.dc = dc
56	f.folders = nil
57	return f
58}
59
60// findRoot makes it possible to use "find" mode with a different root path.
61// Example: ResourcePoolList("/dc1/host/cluster1/...")
62func (f *Finder) findRoot(ctx context.Context, root *list.Element, parts []string) bool {
63	if len(parts) == 0 {
64		return false
65	}
66
67	ix := len(parts) - 1
68
69	if parts[ix] != "..." {
70		return false
71	}
72
73	if ix == 0 {
74		return true // We already have the Object for root.Path
75	}
76
77	// Lookup the Object for the new root.Path
78	rootPath := path.Join(root.Path, path.Join(parts[:ix]...))
79
80	ref, err := f.si.FindByInventoryPath(ctx, rootPath)
81	if err != nil || ref == nil {
82		// If we get an error or fail to match, fall through to find() with the original root and path
83		return false
84	}
85
86	root.Path = rootPath
87	root.Object = ref
88
89	return true
90}
91
92func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element, error) {
93	isPath := strings.Contains(arg, "/")
94
95	root := list.Element{
96		Path:   "/",
97		Object: object.NewRootFolder(f.client),
98	}
99
100	parts := list.ToParts(arg)
101
102	if len(parts) > 0 {
103		switch parts[0] {
104		case "..": // Not supported; many edge case, little value
105			return nil, errors.New("cannot traverse up a tree")
106		case ".": // Relative to whatever
107			pivot, err := s.Relative(ctx)
108			if err != nil {
109				return nil, err
110			}
111
112			mes, err := mo.Ancestors(ctx, f.client, f.client.ServiceContent.PropertyCollector, pivot.Reference())
113			if err != nil {
114				return nil, err
115			}
116
117			for _, me := range mes {
118				// Skip root entity in building inventory path.
119				if me.Parent == nil {
120					continue
121				}
122				root.Path = path.Join(root.Path, me.Name)
123			}
124
125			root.Object = pivot
126			parts = parts[1:]
127		}
128	}
129
130	if s.listMode(isPath) {
131		if f.findRoot(ctx, &root, parts) {
132			parts = []string{"*"}
133		} else {
134			return f.r.List(ctx, s, root, parts)
135		}
136	}
137
138	s.Parents = append(s.Parents, s.Nested...)
139
140	return f.r.Find(ctx, s, root, parts)
141}
142
143func (f *Finder) datacenter() (*object.Datacenter, error) {
144	if f.dc == nil {
145		return nil, errors.New("please specify a datacenter")
146	}
147
148	return f.dc, nil
149}
150
151// datacenterPath returns the absolute path to the Datacenter containing the given ref
152func (f *Finder) datacenterPath(ctx context.Context, ref types.ManagedObjectReference) (string, error) {
153	mes, err := mo.Ancestors(ctx, f.client, f.client.ServiceContent.PropertyCollector, ref)
154	if err != nil {
155		return "", err
156	}
157
158	// Chop leaves under the Datacenter
159	for i := len(mes) - 1; i > 0; i-- {
160		if mes[i].Self.Type == "Datacenter" {
161			break
162		}
163		mes = mes[:i]
164	}
165
166	var p string
167
168	for _, me := range mes {
169		// Skip root entity in building inventory path.
170		if me.Parent == nil {
171			continue
172		}
173
174		p = p + "/" + me.Name
175	}
176
177	return p, nil
178}
179
180func (f *Finder) dcFolders(ctx context.Context) (*object.DatacenterFolders, error) {
181	if f.folders != nil {
182		return f.folders, nil
183	}
184
185	dc, err := f.datacenter()
186	if err != nil {
187		return nil, err
188	}
189
190	folders, err := dc.Folders(ctx)
191	if err != nil {
192		return nil, err
193	}
194
195	f.folders = folders
196
197	return f.folders, nil
198}
199
200func (f *Finder) dcReference(_ context.Context) (object.Reference, error) {
201	dc, err := f.datacenter()
202	if err != nil {
203		return nil, err
204	}
205
206	return dc, nil
207}
208
209func (f *Finder) vmFolder(ctx context.Context) (object.Reference, error) {
210	folders, err := f.dcFolders(ctx)
211	if err != nil {
212		return nil, err
213	}
214
215	return folders.VmFolder, nil
216}
217
218func (f *Finder) hostFolder(ctx context.Context) (object.Reference, error) {
219	folders, err := f.dcFolders(ctx)
220	if err != nil {
221		return nil, err
222	}
223
224	return folders.HostFolder, nil
225}
226
227func (f *Finder) datastoreFolder(ctx context.Context) (object.Reference, error) {
228	folders, err := f.dcFolders(ctx)
229	if err != nil {
230		return nil, err
231	}
232
233	return folders.DatastoreFolder, nil
234}
235
236func (f *Finder) networkFolder(ctx context.Context) (object.Reference, error) {
237	folders, err := f.dcFolders(ctx)
238	if err != nil {
239		return nil, err
240	}
241
242	return folders.NetworkFolder, nil
243}
244
245func (f *Finder) rootFolder(_ context.Context) (object.Reference, error) {
246	return object.NewRootFolder(f.client), nil
247}
248
249func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, include []string) ([]list.Element, error) {
250	fn := f.rootFolder
251
252	if f.dc != nil {
253		fn = f.dcReference
254	}
255
256	if len(path) == 0 {
257		path = "."
258	}
259
260	s := &spec{
261		Relative: fn,
262		Parents:  []string{"ComputeResource", "ClusterComputeResource", "HostSystem", "VirtualApp", "StoragePod"},
263		Include:  include,
264	}
265
266	if tl {
267		s.Contents = true
268		s.ListMode = types.NewBool(true)
269	}
270
271	return f.find(ctx, path, s)
272}
273
274// Element returns an Element for the given ManagedObjectReference
275// This method is only useful for looking up the InventoryPath of a ManagedObjectReference.
276func (f *Finder) Element(ctx context.Context, ref types.ManagedObjectReference) (*list.Element, error) {
277	rl := func(_ context.Context) (object.Reference, error) {
278		return ref, nil
279	}
280
281	s := &spec{
282		Relative: rl,
283	}
284
285	e, err := f.find(ctx, "./", s)
286	if err != nil {
287		return nil, err
288	}
289
290	if len(e) == 0 {
291		return nil, &NotFoundError{ref.Type, ref.Value}
292	}
293
294	if len(e) > 1 {
295		panic("ManagedObjectReference must be unique")
296	}
297
298	return &e[0], nil
299}
300
301// ObjectReference converts the given ManagedObjectReference to a type from the object package via object.NewReference
302// with the object.Common.InventoryPath field set.
303func (f *Finder) ObjectReference(ctx context.Context, ref types.ManagedObjectReference) (object.Reference, error) {
304	e, err := f.Element(ctx, ref)
305	if err != nil {
306		return nil, err
307	}
308
309	r := object.NewReference(f.client, ref)
310
311	type common interface {
312		SetInventoryPath(string)
313	}
314
315	r.(common).SetInventoryPath(e.Path)
316
317	if f.dc != nil {
318		if ds, ok := r.(*object.Datastore); ok {
319			ds.DatacenterPath = f.dc.InventoryPath
320		}
321	}
322
323	return r, nil
324}
325
326func (f *Finder) ManagedObjectList(ctx context.Context, path string, include ...string) ([]list.Element, error) {
327	return f.managedObjectList(ctx, path, false, include)
328}
329
330func (f *Finder) ManagedObjectListChildren(ctx context.Context, path string, include ...string) ([]list.Element, error) {
331	return f.managedObjectList(ctx, path, true, include)
332}
333
334func (f *Finder) DatacenterList(ctx context.Context, path string) ([]*object.Datacenter, error) {
335	s := &spec{
336		Relative: f.rootFolder,
337		Include:  []string{"Datacenter"},
338	}
339
340	es, err := f.find(ctx, path, s)
341	if err != nil {
342		return nil, err
343	}
344
345	var dcs []*object.Datacenter
346	for _, e := range es {
347		ref := e.Object.Reference()
348		if ref.Type == "Datacenter" {
349			dc := object.NewDatacenter(f.client, ref)
350			dc.InventoryPath = e.Path
351			dcs = append(dcs, dc)
352		}
353	}
354
355	if len(dcs) == 0 {
356		return nil, &NotFoundError{"datacenter", path}
357	}
358
359	return dcs, nil
360}
361
362func (f *Finder) Datacenter(ctx context.Context, path string) (*object.Datacenter, error) {
363	dcs, err := f.DatacenterList(ctx, path)
364	if err != nil {
365		return nil, err
366	}
367
368	if len(dcs) > 1 {
369		return nil, &MultipleFoundError{"datacenter", path}
370	}
371
372	return dcs[0], nil
373}
374
375func (f *Finder) DefaultDatacenter(ctx context.Context) (*object.Datacenter, error) {
376	dc, err := f.Datacenter(ctx, "*")
377	if err != nil {
378		return nil, toDefaultError(err)
379	}
380
381	return dc, nil
382}
383
384func (f *Finder) DatacenterOrDefault(ctx context.Context, path string) (*object.Datacenter, error) {
385	if path != "" {
386		dc, err := f.Datacenter(ctx, path)
387		if err != nil {
388			return nil, err
389		}
390		return dc, nil
391	}
392
393	return f.DefaultDatacenter(ctx)
394}
395
396func (f *Finder) DatastoreList(ctx context.Context, path string) ([]*object.Datastore, error) {
397	s := &spec{
398		Relative: f.datastoreFolder,
399		Parents:  []string{"StoragePod"},
400	}
401
402	es, err := f.find(ctx, path, s)
403	if err != nil {
404		return nil, err
405	}
406
407	var dss []*object.Datastore
408	for _, e := range es {
409		ref := e.Object.Reference()
410		if ref.Type == "Datastore" {
411			ds := object.NewDatastore(f.client, ref)
412			ds.InventoryPath = e.Path
413
414			if f.dc == nil {
415				// In this case SetDatacenter was not called and path is absolute
416				ds.DatacenterPath, err = f.datacenterPath(ctx, ref)
417				if err != nil {
418					return nil, err
419				}
420			} else {
421				ds.DatacenterPath = f.dc.InventoryPath
422			}
423
424			dss = append(dss, ds)
425		}
426	}
427
428	if len(dss) == 0 {
429		return nil, &NotFoundError{"datastore", path}
430	}
431
432	return dss, nil
433}
434
435func (f *Finder) Datastore(ctx context.Context, path string) (*object.Datastore, error) {
436	dss, err := f.DatastoreList(ctx, path)
437	if err != nil {
438		return nil, err
439	}
440
441	if len(dss) > 1 {
442		return nil, &MultipleFoundError{"datastore", path}
443	}
444
445	return dss[0], nil
446}
447
448func (f *Finder) DefaultDatastore(ctx context.Context) (*object.Datastore, error) {
449	ds, err := f.Datastore(ctx, "*")
450	if err != nil {
451		return nil, toDefaultError(err)
452	}
453
454	return ds, nil
455}
456
457func (f *Finder) DatastoreOrDefault(ctx context.Context, path string) (*object.Datastore, error) {
458	if path != "" {
459		ds, err := f.Datastore(ctx, path)
460		if err != nil {
461			return nil, err
462		}
463		return ds, nil
464	}
465
466	return f.DefaultDatastore(ctx)
467}
468
469func (f *Finder) DatastoreClusterList(ctx context.Context, path string) ([]*object.StoragePod, error) {
470	s := &spec{
471		Relative: f.datastoreFolder,
472	}
473
474	es, err := f.find(ctx, path, s)
475	if err != nil {
476		return nil, err
477	}
478
479	var sps []*object.StoragePod
480	for _, e := range es {
481		ref := e.Object.Reference()
482		if ref.Type == "StoragePod" {
483			sp := object.NewStoragePod(f.client, ref)
484			sp.InventoryPath = e.Path
485			sps = append(sps, sp)
486		}
487	}
488
489	if len(sps) == 0 {
490		return nil, &NotFoundError{"datastore cluster", path}
491	}
492
493	return sps, nil
494}
495
496func (f *Finder) DatastoreCluster(ctx context.Context, path string) (*object.StoragePod, error) {
497	sps, err := f.DatastoreClusterList(ctx, path)
498	if err != nil {
499		return nil, err
500	}
501
502	if len(sps) > 1 {
503		return nil, &MultipleFoundError{"datastore cluster", path}
504	}
505
506	return sps[0], nil
507}
508
509func (f *Finder) DefaultDatastoreCluster(ctx context.Context) (*object.StoragePod, error) {
510	sp, err := f.DatastoreCluster(ctx, "*")
511	if err != nil {
512		return nil, toDefaultError(err)
513	}
514
515	return sp, nil
516}
517
518func (f *Finder) DatastoreClusterOrDefault(ctx context.Context, path string) (*object.StoragePod, error) {
519	if path != "" {
520		sp, err := f.DatastoreCluster(ctx, path)
521		if err != nil {
522			return nil, err
523		}
524		return sp, nil
525	}
526
527	return f.DefaultDatastoreCluster(ctx)
528}
529
530func (f *Finder) ComputeResourceList(ctx context.Context, path string) ([]*object.ComputeResource, error) {
531	s := &spec{
532		Relative: f.hostFolder,
533	}
534
535	es, err := f.find(ctx, path, s)
536	if err != nil {
537		return nil, err
538	}
539
540	var crs []*object.ComputeResource
541	for _, e := range es {
542		var cr *object.ComputeResource
543
544		switch o := e.Object.(type) {
545		case mo.ComputeResource, mo.ClusterComputeResource:
546			cr = object.NewComputeResource(f.client, o.Reference())
547		default:
548			continue
549		}
550
551		cr.InventoryPath = e.Path
552		crs = append(crs, cr)
553	}
554
555	if len(crs) == 0 {
556		return nil, &NotFoundError{"compute resource", path}
557	}
558
559	return crs, nil
560}
561
562func (f *Finder) ComputeResource(ctx context.Context, path string) (*object.ComputeResource, error) {
563	crs, err := f.ComputeResourceList(ctx, path)
564	if err != nil {
565		return nil, err
566	}
567
568	if len(crs) > 1 {
569		return nil, &MultipleFoundError{"compute resource", path}
570	}
571
572	return crs[0], nil
573}
574
575func (f *Finder) DefaultComputeResource(ctx context.Context) (*object.ComputeResource, error) {
576	cr, err := f.ComputeResource(ctx, "*")
577	if err != nil {
578		return nil, toDefaultError(err)
579	}
580
581	return cr, nil
582}
583
584func (f *Finder) ComputeResourceOrDefault(ctx context.Context, path string) (*object.ComputeResource, error) {
585	if path != "" {
586		cr, err := f.ComputeResource(ctx, path)
587		if err != nil {
588			return nil, err
589		}
590		return cr, nil
591	}
592
593	return f.DefaultComputeResource(ctx)
594}
595
596func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([]*object.ClusterComputeResource, error) {
597	s := &spec{
598		Relative: f.hostFolder,
599	}
600
601	es, err := f.find(ctx, path, s)
602	if err != nil {
603		return nil, err
604	}
605
606	var ccrs []*object.ClusterComputeResource
607	for _, e := range es {
608		var ccr *object.ClusterComputeResource
609
610		switch o := e.Object.(type) {
611		case mo.ClusterComputeResource:
612			ccr = object.NewClusterComputeResource(f.client, o.Reference())
613		default:
614			continue
615		}
616
617		ccr.InventoryPath = e.Path
618		ccrs = append(ccrs, ccr)
619	}
620
621	if len(ccrs) == 0 {
622		return nil, &NotFoundError{"cluster", path}
623	}
624
625	return ccrs, nil
626}
627
628func (f *Finder) DefaultClusterComputeResource(ctx context.Context) (*object.ClusterComputeResource, error) {
629	cr, err := f.ClusterComputeResource(ctx, "*")
630	if err != nil {
631		return nil, toDefaultError(err)
632	}
633
634	return cr, nil
635}
636
637func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
638	ccrs, err := f.ClusterComputeResourceList(ctx, path)
639	if err != nil {
640		return nil, err
641	}
642
643	if len(ccrs) > 1 {
644		return nil, &MultipleFoundError{"cluster", path}
645	}
646
647	return ccrs[0], nil
648}
649
650func (f *Finder) ClusterComputeResourceOrDefault(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
651	if path != "" {
652		cr, err := f.ClusterComputeResource(ctx, path)
653		if err != nil {
654			return nil, err
655		}
656		return cr, nil
657	}
658
659	return f.DefaultClusterComputeResource(ctx)
660}
661
662func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) {
663	s := &spec{
664		Relative: f.hostFolder,
665		Parents:  []string{"ComputeResource", "ClusterComputeResource"},
666		Include:  []string{"HostSystem"},
667	}
668
669	es, err := f.find(ctx, path, s)
670	if err != nil {
671		return nil, err
672	}
673
674	var hss []*object.HostSystem
675	for _, e := range es {
676		var hs *object.HostSystem
677
678		switch o := e.Object.(type) {
679		case mo.HostSystem:
680			hs = object.NewHostSystem(f.client, o.Reference())
681
682			hs.InventoryPath = e.Path
683			hss = append(hss, hs)
684		case mo.ComputeResource, mo.ClusterComputeResource:
685			cr := object.NewComputeResource(f.client, o.Reference())
686
687			cr.InventoryPath = e.Path
688
689			hosts, err := cr.Hosts(ctx)
690			if err != nil {
691				return nil, err
692			}
693
694			hss = append(hss, hosts...)
695		}
696	}
697
698	if len(hss) == 0 {
699		return nil, &NotFoundError{"host", path}
700	}
701
702	return hss, nil
703}
704
705func (f *Finder) HostSystem(ctx context.Context, path string) (*object.HostSystem, error) {
706	hss, err := f.HostSystemList(ctx, path)
707	if err != nil {
708		return nil, err
709	}
710
711	if len(hss) > 1 {
712		return nil, &MultipleFoundError{"host", path}
713	}
714
715	return hss[0], nil
716}
717
718func (f *Finder) DefaultHostSystem(ctx context.Context) (*object.HostSystem, error) {
719	hs, err := f.HostSystem(ctx, "*")
720	if err != nil {
721		return nil, toDefaultError(err)
722	}
723
724	return hs, nil
725}
726
727func (f *Finder) HostSystemOrDefault(ctx context.Context, path string) (*object.HostSystem, error) {
728	if path != "" {
729		hs, err := f.HostSystem(ctx, path)
730		if err != nil {
731			return nil, err
732		}
733		return hs, nil
734	}
735
736	return f.DefaultHostSystem(ctx)
737}
738
739func (f *Finder) NetworkList(ctx context.Context, path string) ([]object.NetworkReference, error) {
740	s := &spec{
741		Relative: f.networkFolder,
742	}
743
744	es, err := f.find(ctx, path, s)
745	if err != nil {
746		return nil, err
747	}
748
749	var ns []object.NetworkReference
750	for _, e := range es {
751		ref := e.Object.Reference()
752		switch ref.Type {
753		case "Network":
754			r := object.NewNetwork(f.client, ref)
755			r.InventoryPath = e.Path
756			ns = append(ns, r)
757		case "OpaqueNetwork":
758			r := object.NewOpaqueNetwork(f.client, ref)
759			r.InventoryPath = e.Path
760			ns = append(ns, r)
761		case "DistributedVirtualPortgroup":
762			r := object.NewDistributedVirtualPortgroup(f.client, ref)
763			r.InventoryPath = e.Path
764			ns = append(ns, r)
765		case "DistributedVirtualSwitch", "VmwareDistributedVirtualSwitch":
766			r := object.NewDistributedVirtualSwitch(f.client, ref)
767			r.InventoryPath = e.Path
768			ns = append(ns, r)
769		}
770	}
771
772	if len(ns) == 0 {
773		return nil, &NotFoundError{"network", path}
774	}
775
776	return ns, nil
777}
778
779func (f *Finder) Network(ctx context.Context, path string) (object.NetworkReference, error) {
780	networks, err := f.NetworkList(ctx, path)
781	if err != nil {
782		return nil, err
783	}
784
785	if len(networks) > 1 {
786		return nil, &MultipleFoundError{"network", path}
787	}
788
789	return networks[0], nil
790}
791
792func (f *Finder) DefaultNetwork(ctx context.Context) (object.NetworkReference, error) {
793	network, err := f.Network(ctx, "*")
794	if err != nil {
795		return nil, toDefaultError(err)
796	}
797
798	return network, nil
799}
800
801func (f *Finder) NetworkOrDefault(ctx context.Context, path string) (object.NetworkReference, error) {
802	if path != "" {
803		network, err := f.Network(ctx, path)
804		if err != nil {
805			return nil, err
806		}
807		return network, nil
808	}
809
810	return f.DefaultNetwork(ctx)
811}
812
813func (f *Finder) ResourcePoolList(ctx context.Context, path string) ([]*object.ResourcePool, error) {
814	s := &spec{
815		Relative: f.hostFolder,
816		Parents:  []string{"ComputeResource", "ClusterComputeResource", "VirtualApp"},
817		Nested:   []string{"ResourcePool"},
818		Contents: true,
819	}
820
821	es, err := f.find(ctx, path, s)
822	if err != nil {
823		return nil, err
824	}
825
826	var rps []*object.ResourcePool
827	for _, e := range es {
828		var rp *object.ResourcePool
829
830		switch o := e.Object.(type) {
831		case mo.ResourcePool:
832			rp = object.NewResourcePool(f.client, o.Reference())
833			rp.InventoryPath = e.Path
834			rps = append(rps, rp)
835		}
836	}
837
838	if len(rps) == 0 {
839		return nil, &NotFoundError{"resource pool", path}
840	}
841
842	return rps, nil
843}
844
845func (f *Finder) ResourcePool(ctx context.Context, path string) (*object.ResourcePool, error) {
846	rps, err := f.ResourcePoolList(ctx, path)
847	if err != nil {
848		return nil, err
849	}
850
851	if len(rps) > 1 {
852		return nil, &MultipleFoundError{"resource pool", path}
853	}
854
855	return rps[0], nil
856}
857
858func (f *Finder) DefaultResourcePool(ctx context.Context) (*object.ResourcePool, error) {
859	rp, err := f.ResourcePool(ctx, "*/Resources")
860	if err != nil {
861		return nil, toDefaultError(err)
862	}
863
864	return rp, nil
865}
866
867func (f *Finder) ResourcePoolOrDefault(ctx context.Context, path string) (*object.ResourcePool, error) {
868	if path != "" {
869		rp, err := f.ResourcePool(ctx, path)
870		if err != nil {
871			return nil, err
872		}
873		return rp, nil
874	}
875
876	return f.DefaultResourcePool(ctx)
877}
878
879// ResourcePoolListAll combines ResourcePoolList and VirtualAppList
880// VirtualAppList is only called if ResourcePoolList does not find any pools with the given path.
881func (f *Finder) ResourcePoolListAll(ctx context.Context, path string) ([]*object.ResourcePool, error) {
882	pools, err := f.ResourcePoolList(ctx, path)
883	if err != nil {
884		if _, ok := err.(*NotFoundError); !ok {
885			return nil, err
886		}
887
888		vapps, _ := f.VirtualAppList(ctx, path)
889
890		if len(vapps) == 0 {
891			return nil, err
892		}
893
894		for _, vapp := range vapps {
895			pools = append(pools, vapp.ResourcePool)
896		}
897	}
898
899	return pools, nil
900}
901
902func (f *Finder) DefaultFolder(ctx context.Context) (*object.Folder, error) {
903	ref, err := f.vmFolder(ctx)
904	if err != nil {
905		return nil, toDefaultError(err)
906	}
907	folder := object.NewFolder(f.client, ref.Reference())
908
909	return folder, nil
910}
911
912func (f *Finder) FolderOrDefault(ctx context.Context, path string) (*object.Folder, error) {
913	if path != "" {
914		folder, err := f.Folder(ctx, path)
915		if err != nil {
916			return nil, err
917		}
918		return folder, nil
919	}
920	return f.DefaultFolder(ctx)
921}
922
923func (f *Finder) VirtualMachineList(ctx context.Context, path string) ([]*object.VirtualMachine, error) {
924	s := &spec{
925		Relative: f.vmFolder,
926		Parents:  []string{"VirtualApp"},
927	}
928
929	es, err := f.find(ctx, path, s)
930	if err != nil {
931		return nil, err
932	}
933
934	var vms []*object.VirtualMachine
935	for _, e := range es {
936		switch o := e.Object.(type) {
937		case mo.VirtualMachine:
938			vm := object.NewVirtualMachine(f.client, o.Reference())
939			vm.InventoryPath = e.Path
940			vms = append(vms, vm)
941		}
942	}
943
944	if len(vms) == 0 {
945		return nil, &NotFoundError{"vm", path}
946	}
947
948	return vms, nil
949}
950
951func (f *Finder) VirtualMachine(ctx context.Context, path string) (*object.VirtualMachine, error) {
952	vms, err := f.VirtualMachineList(ctx, path)
953	if err != nil {
954		return nil, err
955	}
956
957	if len(vms) > 1 {
958		return nil, &MultipleFoundError{"vm", path}
959	}
960
961	return vms[0], nil
962}
963
964func (f *Finder) VirtualAppList(ctx context.Context, path string) ([]*object.VirtualApp, error) {
965	s := &spec{
966		Relative: f.vmFolder,
967	}
968
969	es, err := f.find(ctx, path, s)
970	if err != nil {
971		return nil, err
972	}
973
974	var apps []*object.VirtualApp
975	for _, e := range es {
976		switch o := e.Object.(type) {
977		case mo.VirtualApp:
978			app := object.NewVirtualApp(f.client, o.Reference())
979			app.InventoryPath = e.Path
980			apps = append(apps, app)
981		}
982	}
983
984	if len(apps) == 0 {
985		return nil, &NotFoundError{"app", path}
986	}
987
988	return apps, nil
989}
990
991func (f *Finder) VirtualApp(ctx context.Context, path string) (*object.VirtualApp, error) {
992	apps, err := f.VirtualAppList(ctx, path)
993	if err != nil {
994		return nil, err
995	}
996
997	if len(apps) > 1 {
998		return nil, &MultipleFoundError{"app", path}
999	}
1000
1001	return apps[0], nil
1002}
1003
1004func (f *Finder) FolderList(ctx context.Context, path string) ([]*object.Folder, error) {
1005	es, err := f.ManagedObjectList(ctx, path)
1006	if err != nil {
1007		return nil, err
1008	}
1009
1010	var folders []*object.Folder
1011
1012	for _, e := range es {
1013		switch o := e.Object.(type) {
1014		case mo.Folder, mo.StoragePod:
1015			folder := object.NewFolder(f.client, o.Reference())
1016			folder.InventoryPath = e.Path
1017			folders = append(folders, folder)
1018		case *object.Folder:
1019			// RootFolder
1020			folders = append(folders, o)
1021		}
1022	}
1023
1024	if len(folders) == 0 {
1025		return nil, &NotFoundError{"folder", path}
1026	}
1027
1028	return folders, nil
1029}
1030
1031func (f *Finder) Folder(ctx context.Context, path string) (*object.Folder, error) {
1032	folders, err := f.FolderList(ctx, path)
1033	if err != nil {
1034		return nil, err
1035	}
1036
1037	if len(folders) > 1 {
1038		return nil, &MultipleFoundError{"folder", path}
1039	}
1040
1041	return folders[0], nil
1042}
1043