github.com/vmware/govmomi@v0.37.2/govc/cli/command.go (about)

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