github.com/vmware/govmomi@v0.51.0/cli/flags/search.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 flags
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"strings"
    13  
    14  	"github.com/vmware/govmomi/fault"
    15  	"github.com/vmware/govmomi/find"
    16  	"github.com/vmware/govmomi/object"
    17  	"github.com/vmware/govmomi/vim25"
    18  	"github.com/vmware/govmomi/vim25/types"
    19  )
    20  
    21  const (
    22  	SearchVirtualMachines = iota + 1
    23  	SearchHosts
    24  	SearchVirtualApps
    25  )
    26  
    27  type SearchFlag struct {
    28  	common
    29  
    30  	*ClientFlag
    31  	*DatacenterFlag
    32  
    33  	t      int
    34  	entity string
    35  
    36  	byDatastorePath string
    37  	byDNSName       string
    38  	byInventoryPath string
    39  	byIP            string
    40  	byUUID          string
    41  
    42  	isset bool
    43  }
    44  
    45  func NewSearchFlag(ctx context.Context, t int) (*SearchFlag, context.Context) {
    46  	searchFlagKey := flagKey(fmt.Sprintf("search%d", t))
    47  
    48  	if v := ctx.Value(searchFlagKey); v != nil {
    49  		return v.(*SearchFlag), ctx
    50  	}
    51  
    52  	v := &SearchFlag{
    53  		t: t,
    54  	}
    55  
    56  	v.ClientFlag, ctx = NewClientFlag(ctx)
    57  	v.DatacenterFlag, ctx = NewDatacenterFlag(ctx)
    58  
    59  	switch t {
    60  	case SearchVirtualMachines:
    61  		v.entity = "VM"
    62  	case SearchHosts:
    63  		v.entity = "host"
    64  	case SearchVirtualApps:
    65  		v.entity = "vapp"
    66  	default:
    67  		panic("invalid search type")
    68  	}
    69  
    70  	ctx = context.WithValue(ctx, searchFlagKey, v)
    71  	return v, ctx
    72  }
    73  
    74  func (flag *SearchFlag) Register(ctx context.Context, fs *flag.FlagSet) {
    75  	flag.RegisterOnce(func() {
    76  		flag.ClientFlag.Register(ctx, fs)
    77  		flag.DatacenterFlag.Register(ctx, fs)
    78  
    79  		register := func(v *string, f string, d string) {
    80  			f = fmt.Sprintf("%s.%s", strings.ToLower(flag.entity), f)
    81  			d = fmt.Sprintf(d, flag.entity)
    82  			fs.StringVar(v, f, "", d)
    83  		}
    84  
    85  		switch flag.t {
    86  		case SearchVirtualMachines:
    87  			register(&flag.byDatastorePath, "path", "Find %s by path to .vmx file")
    88  		}
    89  
    90  		switch flag.t {
    91  		case SearchVirtualMachines, SearchHosts:
    92  			register(&flag.byDNSName, "dns", "Find %s by FQDN")
    93  			register(&flag.byIP, "ip", "Find %s by IP address")
    94  			register(&flag.byUUID, "uuid", "Find %s by UUID")
    95  		}
    96  
    97  		register(&flag.byInventoryPath, "ipath", "Find %s by inventory path")
    98  	})
    99  }
   100  
   101  func (flag *SearchFlag) Process(ctx context.Context) error {
   102  	return flag.ProcessOnce(func() error {
   103  		if err := flag.ClientFlag.Process(ctx); err != nil {
   104  			return err
   105  		}
   106  		if err := flag.DatacenterFlag.Process(ctx); err != nil {
   107  			return err
   108  		}
   109  
   110  		flags := []string{
   111  			flag.byDatastorePath,
   112  			flag.byDNSName,
   113  			flag.byInventoryPath,
   114  			flag.byIP,
   115  			flag.byUUID,
   116  		}
   117  
   118  		flag.isset = false
   119  		for _, f := range flags {
   120  			if f != "" {
   121  				if flag.isset {
   122  					return errors.New("cannot use more than one search flag")
   123  				}
   124  				flag.isset = true
   125  			}
   126  		}
   127  
   128  		return nil
   129  	})
   130  }
   131  
   132  func (flag *SearchFlag) IsSet() bool {
   133  	return flag.isset
   134  }
   135  
   136  func (flag *SearchFlag) searchIndex(c *vim25.Client) *object.SearchIndex {
   137  	return object.NewSearchIndex(c)
   138  }
   139  
   140  func (flag *SearchFlag) searchByDatastorePath(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) {
   141  	ctx := context.TODO()
   142  	switch flag.t {
   143  	case SearchVirtualMachines:
   144  		return flag.searchIndex(c).FindByDatastorePath(ctx, dc, flag.byDatastorePath)
   145  	default:
   146  		panic("unsupported type")
   147  	}
   148  }
   149  
   150  func (flag *SearchFlag) searchByDNSName(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) {
   151  	ctx := context.TODO()
   152  	switch flag.t {
   153  	case SearchVirtualMachines:
   154  		return flag.searchIndex(c).FindByDnsName(ctx, dc, flag.byDNSName, true)
   155  	case SearchHosts:
   156  		return flag.searchIndex(c).FindByDnsName(ctx, dc, flag.byDNSName, false)
   157  	default:
   158  		panic("unsupported type")
   159  	}
   160  }
   161  
   162  func (flag *SearchFlag) searchByInventoryPath(c *vim25.Client) (object.Reference, error) {
   163  	ctx := context.TODO()
   164  	return flag.searchIndex(c).FindByInventoryPath(ctx, flag.byInventoryPath)
   165  }
   166  
   167  func (flag *SearchFlag) searchByIP(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) {
   168  	ctx := context.TODO()
   169  	switch flag.t {
   170  	case SearchVirtualMachines:
   171  		return flag.searchIndex(c).FindByIp(ctx, dc, flag.byIP, true)
   172  	case SearchHosts:
   173  		return flag.searchIndex(c).FindByIp(ctx, dc, flag.byIP, false)
   174  	default:
   175  		panic("unsupported type")
   176  	}
   177  }
   178  
   179  func (flag *SearchFlag) searchByUUID(c *vim25.Client, dc *object.Datacenter) (object.Reference, error) {
   180  	ctx := context.TODO()
   181  	isVM := false
   182  	switch flag.t {
   183  	case SearchVirtualMachines:
   184  		isVM = true
   185  	case SearchHosts:
   186  	default:
   187  		panic("unsupported type")
   188  	}
   189  
   190  	var ref object.Reference
   191  	var err error
   192  
   193  	for _, iu := range []*bool{nil, types.NewBool(true)} {
   194  		ref, err = flag.searchIndex(c).FindByUuid(ctx, dc, flag.byUUID, isVM, iu)
   195  		if err != nil {
   196  			if fault.Is(err, &types.InvalidArgument{}) {
   197  				continue
   198  			}
   199  			return nil, err
   200  		}
   201  		if ref != nil {
   202  			break
   203  		}
   204  	}
   205  
   206  	return ref, nil
   207  }
   208  
   209  func (flag *SearchFlag) search() (object.Reference, error) {
   210  	ctx := context.TODO()
   211  	var ref object.Reference
   212  	var err error
   213  	var dc *object.Datacenter
   214  
   215  	c, err := flag.Client()
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	isPath := flag.byInventoryPath != ""
   221  	if !isPath {
   222  		// All other SearchIndex methods require a Datacenter param
   223  		dc, err = flag.Datacenter()
   224  		if err != nil {
   225  			return nil, err
   226  		}
   227  	}
   228  
   229  	switch {
   230  	case isPath:
   231  		ref, err = flag.searchByInventoryPath(c)
   232  	case flag.byDatastorePath != "":
   233  		ref, err = flag.searchByDatastorePath(c, dc)
   234  	case flag.byDNSName != "":
   235  		ref, err = flag.searchByDNSName(c, dc)
   236  	case flag.byIP != "":
   237  		ref, err = flag.searchByIP(c, dc)
   238  	case flag.byUUID != "":
   239  		ref, err = flag.searchByUUID(c, dc)
   240  	default:
   241  		err = errors.New("no search flag specified")
   242  	}
   243  
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  
   248  	if ref == nil {
   249  		return nil, fmt.Errorf("no such %s", flag.entity)
   250  	}
   251  
   252  	// set the InventoryPath field
   253  	finder, err := flag.Finder()
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	ref, err = finder.ObjectReference(ctx, ref.Reference())
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	return ref, nil
   263  }
   264  
   265  func (flag *SearchFlag) VirtualMachine() (*object.VirtualMachine, error) {
   266  	ref, err := flag.search()
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	vm, ok := ref.(*object.VirtualMachine)
   272  	if !ok {
   273  		return nil, fmt.Errorf("expected VirtualMachine entity, got %s", ref.Reference().Type)
   274  	}
   275  
   276  	return vm, nil
   277  }
   278  
   279  func (flag *SearchFlag) VirtualMachines(args []string) ([]*object.VirtualMachine, error) {
   280  	ctx := context.TODO()
   281  	var out []*object.VirtualMachine
   282  
   283  	if flag.IsSet() {
   284  		vm, err := flag.VirtualMachine()
   285  		if err != nil {
   286  			return nil, err
   287  		}
   288  
   289  		out = append(out, vm)
   290  		return out, nil
   291  	}
   292  
   293  	// List virtual machines
   294  	if len(args) == 0 {
   295  		return nil, errors.New("no argument")
   296  	}
   297  
   298  	finder, err := flag.Finder()
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  
   303  	var nfe error
   304  
   305  	// List virtual machines for every argument
   306  	for _, arg := range args {
   307  		vms, err := finder.VirtualMachineList(ctx, arg)
   308  		if err != nil {
   309  			if _, ok := err.(*find.NotFoundError); ok {
   310  				// Let caller decide how to handle NotFoundError
   311  				nfe = err
   312  				continue
   313  			}
   314  			return nil, err
   315  		}
   316  
   317  		out = append(out, vms...)
   318  	}
   319  
   320  	return out, nfe
   321  }
   322  
   323  func (flag *SearchFlag) VirtualApp() (*object.VirtualApp, error) {
   324  	ref, err := flag.search()
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	app, ok := ref.(*object.VirtualApp)
   330  	if !ok {
   331  		return nil, fmt.Errorf("expected VirtualApp entity, got %s", ref.Reference().Type)
   332  	}
   333  
   334  	return app, nil
   335  }
   336  
   337  func (flag *SearchFlag) VirtualApps(args []string) ([]*object.VirtualApp, error) {
   338  	ctx := context.TODO()
   339  	var out []*object.VirtualApp
   340  
   341  	if flag.IsSet() {
   342  		app, err := flag.VirtualApp()
   343  		if err != nil {
   344  			return nil, err
   345  		}
   346  
   347  		out = append(out, app)
   348  		return out, nil
   349  	}
   350  
   351  	// List virtual apps
   352  	if len(args) == 0 {
   353  		return nil, errors.New("no argument")
   354  	}
   355  
   356  	finder, err := flag.Finder()
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  
   361  	// List virtual apps for every argument
   362  	for _, arg := range args {
   363  		apps, err := finder.VirtualAppList(ctx, arg)
   364  		if err != nil {
   365  			return nil, err
   366  		}
   367  
   368  		out = append(out, apps...)
   369  	}
   370  
   371  	return out, nil
   372  }
   373  
   374  func (flag *SearchFlag) HostSystem() (*object.HostSystem, error) {
   375  	ref, err := flag.search()
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  
   380  	host, ok := ref.(*object.HostSystem)
   381  	if !ok {
   382  		return nil, fmt.Errorf("expected HostSystem entity, got %s", ref.Reference().Type)
   383  	}
   384  
   385  	return host, nil
   386  }
   387  
   388  func (flag *SearchFlag) HostSystems(args []string) ([]*object.HostSystem, error) {
   389  	ctx := context.TODO()
   390  	var out []*object.HostSystem
   391  
   392  	if flag.IsSet() {
   393  		host, err := flag.HostSystem()
   394  		if err != nil {
   395  			return nil, err
   396  		}
   397  
   398  		out = append(out, host)
   399  		return out, nil
   400  	}
   401  
   402  	// List host system
   403  	if len(args) == 0 {
   404  		return nil, errors.New("no argument")
   405  	}
   406  
   407  	finder, err := flag.Finder()
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  
   412  	// List host systems for every argument
   413  	for _, arg := range args {
   414  		vms, err := finder.HostSystemList(ctx, arg)
   415  		if err != nil {
   416  			return nil, err
   417  		}
   418  
   419  		out = append(out, vms...)
   420  	}
   421  
   422  	return out, nil
   423  }