github.com/vmware/govmomi@v0.51.0/cli/command.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 cli
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"sort"
    15  	"strings"
    16  	"text/tabwriter"
    17  
    18  	"github.com/vmware/govmomi/vim25/types"
    19  )
    20  
    21  type HasFlags interface {
    22  	// Register may be called more than once and should be idempotent.
    23  	Register(ctx context.Context, f *flag.FlagSet)
    24  
    25  	// Process may be called more than once and should be idempotent.
    26  	Process(ctx context.Context) error
    27  }
    28  
    29  type Command interface {
    30  	HasFlags
    31  
    32  	Run(ctx context.Context, f *flag.FlagSet) error
    33  }
    34  
    35  func generalHelp(w io.Writer, filter string) {
    36  	var cmds, matches []string
    37  	for name := range commands {
    38  		cmds = append(cmds, name)
    39  
    40  		if filter != "" && strings.Contains(name, filter) {
    41  			matches = append(matches, name)
    42  		}
    43  	}
    44  
    45  	if len(matches) == 0 {
    46  		fmt.Fprintf(w, `Usage: %[1]s <COMMAND> [COMMON OPTIONS] [PATH]...
    47  
    48  govmomi is a Go library for interacting with VMware vSphere APIs (ESXi and/or
    49  vCenter Server).
    50  It is licensed under the Apache License, Version 2.0
    51  
    52  %[1]s is the CLI for govmomi.
    53  
    54  The available commands are listed below. A detailed description of each
    55  command can be displayed with "govc <COMMAND> -h". The description of all
    56  commands can be also found at https://github.com/vmware/govmomi/blob/main/govc/USAGE.md.
    57  
    58  Examples:
    59    show usage of a command:       govc <COMMAND> -h
    60    show toplevel structure:       govc ls
    61    show datacenter summary:       govc datacenter.info
    62    show all VMs:                  govc find -type m
    63    upload a ISO file:             govc datastore.upload -ds datastore1 ./config.iso vm-name/config.iso
    64  
    65  Common options:
    66    -h                        Show this message
    67    -cert=                    Certificate [GOVC_CERTIFICATE]
    68    -debug=false              Store debug logs [GOVC_DEBUG]
    69    -trace=false              Write SOAP/REST traffic to stderr
    70    -verbose=false            Write request/response data to stderr
    71    -dump=false               Enable output dump
    72    -json=false               Enable JSON output
    73    -xml=false                Enable XML output
    74    -k=false                  Skip verification of server certificate [GOVC_INSECURE]
    75    -key=                     Private key [GOVC_PRIVATE_KEY]
    76    -persist-session=true     Persist session to disk [GOVC_PERSIST_SESSION]
    77    -tls-ca-certs=            TLS CA certificates file [GOVC_TLS_CA_CERTS]
    78    -tls-known-hosts=         TLS known hosts file [GOVC_TLS_KNOWN_HOSTS]
    79    -u=                       ESX or vCenter URL [GOVC_URL]
    80    -vim-namespace=urn:vim25  Vim namespace [GOVC_VIM_NAMESPACE]
    81    -vim-version=6.0          Vim version [GOVC_VIM_VERSION]
    82    -dc=                      Datacenter [GOVC_DATACENTER]
    83    -host.dns=                Find host by FQDN
    84    -host.ip=                 Find host by IP address
    85    -host.ipath=              Find host by inventory path
    86    -host.uuid=               Find host by UUID
    87    -vm.dns=                  Find VM by FQDN
    88    -vm.ip=                   Find VM by IP address
    89    -vm.ipath=                Find VM by inventory path
    90    -vm.path=                 Find VM by path to .vmx file
    91    -vm.uuid=                 Find VM by UUID
    92  
    93  Available commands:
    94  `, filepath.Base(os.Args[0]))
    95  
    96  	} else {
    97  		fmt.Fprintf(w, "%s: command '%s' not found, did you mean:\n", os.Args[0], filter)
    98  		cmds = matches
    99  	}
   100  
   101  	sort.Strings(cmds)
   102  	for _, name := range cmds {
   103  		fmt.Fprintf(w, "  %s\n", name)
   104  	}
   105  }
   106  
   107  func commandHelp(w io.Writer, name string, cmd Command, f *flag.FlagSet) {
   108  	type HasUsage interface {
   109  		Usage() string
   110  	}
   111  
   112  	fmt.Fprintf(w, "Usage: %s %s [OPTIONS]", os.Args[0], name)
   113  	if u, ok := cmd.(HasUsage); ok {
   114  		fmt.Fprintf(w, " %s", u.Usage())
   115  	}
   116  	fmt.Fprintf(w, "\n")
   117  
   118  	type HasDescription interface {
   119  		Description() string
   120  	}
   121  
   122  	if u, ok := cmd.(HasDescription); ok {
   123  		fmt.Fprintf(w, "\n%s\n", u.Description())
   124  	}
   125  
   126  	n := 0
   127  	f.VisitAll(func(_ *flag.Flag) {
   128  		n += 1
   129  	})
   130  
   131  	if n > 0 {
   132  		fmt.Fprintf(w, "\nOptions:\n")
   133  		tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
   134  		f.VisitAll(func(f *flag.Flag) {
   135  			fmt.Fprintf(tw, "\t-%s=%s\t%s\n", f.Name, f.DefValue, f.Usage)
   136  		})
   137  		tw.Flush()
   138  	}
   139  }
   140  
   141  func clientLogout(ctx context.Context, cmd Command) error {
   142  	type logout interface {
   143  		Logout(context.Context) error
   144  	}
   145  
   146  	if l, ok := cmd.(logout); ok {
   147  		return l.Logout(ctx)
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  func Run(args []string) int {
   154  	hw := os.Stderr
   155  	rc := 1
   156  	hwrc := func(arg string) {
   157  		arg = strings.TrimLeft(arg, "-")
   158  		if arg == "h" || arg == "help" {
   159  			hw = os.Stdout
   160  			rc = 0
   161  		}
   162  	}
   163  
   164  	var err error
   165  
   166  	if len(args) == 0 {
   167  		generalHelp(hw, "")
   168  		return rc
   169  	}
   170  
   171  	// Look up real command name in aliases table.
   172  	name, ok := aliases[args[0]]
   173  	if !ok {
   174  		name = args[0]
   175  	}
   176  
   177  	cmd, ok := commands[name]
   178  	if !ok {
   179  		hwrc(name)
   180  		generalHelp(hw, name)
   181  		return rc
   182  	}
   183  
   184  	fs := flag.NewFlagSet("", flag.ContinueOnError)
   185  	fs.SetOutput(io.Discard)
   186  
   187  	ctx := context.Background()
   188  
   189  	if id := os.Getenv("GOVC_OPERATION_ID"); id != "" {
   190  		ctx = context.WithValue(ctx, types.ID{}, id)
   191  	}
   192  
   193  	cmd.Register(ctx, fs)
   194  
   195  	if err = fs.Parse(args[1:]); err != nil {
   196  		goto error
   197  	}
   198  
   199  	if err = cmd.Process(ctx); err != nil {
   200  		goto error
   201  	}
   202  
   203  	if err = cmd.Run(ctx, fs); err != nil {
   204  		goto error
   205  	}
   206  
   207  	if err = clientLogout(ctx, cmd); err != nil {
   208  		goto error
   209  	}
   210  
   211  	return 0
   212  
   213  error:
   214  	if err == flag.ErrHelp {
   215  		if len(args) == 2 {
   216  			hwrc(args[1])
   217  		}
   218  		commandHelp(hw, args[0], cmd, fs)
   219  	} else {
   220  		if x, ok := err.(interface{ ExitCode() int }); ok {
   221  			// propagate exit code, e.g. from guest.run
   222  			rc = x.ExitCode()
   223  		} else {
   224  			w, ok := cmd.(interface{ WriteError(error) bool })
   225  			if ok {
   226  				ok = w.WriteError(err)
   227  			}
   228  			if !ok {
   229  				fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err)
   230  			}
   231  		}
   232  	}
   233  
   234  	_ = clientLogout(ctx, cmd)
   235  
   236  	return rc
   237  }