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

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