github.com/vmware/govmomi@v0.37.2/find/finder.go (about)

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