github.com/vmware/govmomi@v0.43.0/govc/vm/info.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 vm
    18  
    19  import (
    20  	"context"
    21  	"flag"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"strings"
    26  	"text/tabwriter"
    27  
    28  	"github.com/vmware/govmomi/find"
    29  	"github.com/vmware/govmomi/govc/cli"
    30  	"github.com/vmware/govmomi/govc/flags"
    31  	"github.com/vmware/govmomi/object"
    32  	"github.com/vmware/govmomi/property"
    33  	"github.com/vmware/govmomi/units"
    34  	"github.com/vmware/govmomi/vim25/mo"
    35  	"github.com/vmware/govmomi/vim25/types"
    36  )
    37  
    38  type info struct {
    39  	*flags.ClientFlag
    40  	*flags.OutputFlag
    41  	*flags.SearchFlag
    42  
    43  	WaitForIP       bool
    44  	General         bool
    45  	ExtraConfig     bool
    46  	Resources       bool
    47  	ToolsConfigInfo bool
    48  }
    49  
    50  func init() {
    51  	cli.Register("vm.info", &info{})
    52  }
    53  
    54  func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) {
    55  	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
    56  	cmd.ClientFlag.Register(ctx, f)
    57  
    58  	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
    59  	cmd.OutputFlag.Register(ctx, f)
    60  
    61  	cmd.SearchFlag, ctx = flags.NewSearchFlag(ctx, flags.SearchVirtualMachines)
    62  	cmd.SearchFlag.Register(ctx, f)
    63  
    64  	f.BoolVar(&cmd.WaitForIP, "waitip", false, "Wait for VM to acquire IP address")
    65  	f.BoolVar(&cmd.General, "g", true, "Show general summary")
    66  	f.BoolVar(&cmd.ExtraConfig, "e", false, "Show ExtraConfig")
    67  	f.BoolVar(&cmd.Resources, "r", false, "Show resource summary")
    68  	f.BoolVar(&cmd.ToolsConfigInfo, "t", false, "Show ToolsConfigInfo")
    69  }
    70  
    71  func (cmd *info) Process(ctx context.Context) error {
    72  	if err := cmd.ClientFlag.Process(ctx); err != nil {
    73  		return err
    74  	}
    75  	if err := cmd.OutputFlag.Process(ctx); err != nil {
    76  		return err
    77  	}
    78  	if err := cmd.SearchFlag.Process(ctx); err != nil {
    79  		return err
    80  	}
    81  	return nil
    82  }
    83  
    84  func (cmd *info) Usage() string {
    85  	return `VM...`
    86  }
    87  
    88  func (cmd *info) Description() string {
    89  	return `Display info for VM.
    90  
    91  The '-r' flag displays additional info for CPU, memory and storage usage,
    92  along with the VM's Datastores, Networks and PortGroups.
    93  
    94  Examples:
    95    govc vm.info $vm
    96    govc vm.info -r $vm | grep Network:
    97    govc vm.info -json $vm
    98    govc find . -type m -runtime.powerState poweredOn | xargs govc vm.info`
    99  }
   100  
   101  func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error {
   102  	c, err := cmd.Client()
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	vms, err := cmd.VirtualMachines(f.Args())
   108  	if err != nil {
   109  		if _, ok := err.(*find.NotFoundError); ok {
   110  			// Continue with empty VM slice
   111  		} else {
   112  			return err
   113  		}
   114  	}
   115  
   116  	refs := make([]types.ManagedObjectReference, 0, len(vms))
   117  	for _, vm := range vms {
   118  		refs = append(refs, vm.Reference())
   119  	}
   120  
   121  	var res infoResult
   122  	var props []string
   123  
   124  	if cmd.OutputFlag.All() {
   125  		props = nil // Load everything
   126  	} else {
   127  		props = []string{"summary"} // Load summary
   128  		if cmd.General {
   129  			props = append(props, "guest.ipAddress")
   130  		}
   131  		if cmd.ExtraConfig {
   132  			props = append(props, "config.extraConfig")
   133  		}
   134  		if cmd.Resources {
   135  			props = append(props, "datastore", "network")
   136  		}
   137  		if cmd.ToolsConfigInfo {
   138  			props = append(props, "config.tools")
   139  		}
   140  	}
   141  
   142  	pc := property.DefaultCollector(c)
   143  	if len(refs) != 0 {
   144  		err = pc.Retrieve(ctx, refs, props, &res.VirtualMachines)
   145  		if err != nil {
   146  			return err
   147  		}
   148  	}
   149  
   150  	if cmd.WaitForIP {
   151  		for i, vm := range res.VirtualMachines {
   152  			if vm.Guest == nil || vm.Guest.IpAddress == "" {
   153  				_, err = vms[i].WaitForIP(ctx)
   154  				if err != nil {
   155  					return err
   156  				}
   157  				// Reload virtual machine object
   158  				err = pc.RetrieveOne(ctx, vms[i].Reference(), props, &res.VirtualMachines[i])
   159  				if err != nil {
   160  					return err
   161  				}
   162  			}
   163  		}
   164  	}
   165  
   166  	if !cmd.OutputFlag.All() {
   167  		res.objects = vms
   168  		res.cmd = cmd
   169  		if err = res.collectReferences(pc, ctx); err != nil {
   170  			return err
   171  		}
   172  	}
   173  
   174  	return cmd.WriteResult(&res)
   175  }
   176  
   177  type infoResult struct {
   178  	VirtualMachines []mo.VirtualMachine `json:"virtualMachines"`
   179  	objects         []*object.VirtualMachine
   180  	entities        map[types.ManagedObjectReference]string
   181  	cmd             *info
   182  }
   183  
   184  // collectReferences builds a unique set of MORs to the set of VirtualMachines,
   185  // so we can collect properties in a single call for each reference type {host,datastore,network}.
   186  func (r *infoResult) collectReferences(pc *property.Collector, ctx context.Context) error {
   187  	// MOR -> Name map
   188  	r.entities = make(map[types.ManagedObjectReference]string)
   189  
   190  	var host []mo.HostSystem
   191  	var network []mo.Network
   192  	var opaque []mo.OpaqueNetwork
   193  	var dvp []mo.DistributedVirtualPortgroup
   194  	var datastore []mo.Datastore
   195  	// Table to drive inflating refs to their mo.* counterparts (dest)
   196  	// and save() the Name to r.entities w/o using reflection here.
   197  	// Note that we cannot use a []mo.ManagedEntity here, since mo.Network has its own 'Name' field,
   198  	// the mo.Network.ManagedEntity.Name field will not be set.
   199  	vrefs := map[string]*struct {
   200  		dest interface{}
   201  		refs []types.ManagedObjectReference
   202  		save func()
   203  	}{
   204  		"HostSystem": {
   205  			&host, nil, func() {
   206  				for _, e := range host {
   207  					r.entities[e.Reference()] = e.Name
   208  				}
   209  			},
   210  		},
   211  		"Network": {
   212  			&network, nil, func() {
   213  				for _, e := range network {
   214  					r.entities[e.Reference()] = e.Name
   215  				}
   216  			},
   217  		},
   218  		"OpaqueNetwork": {
   219  			&opaque, nil, func() {
   220  				for _, e := range opaque {
   221  					r.entities[e.Reference()] = e.Name
   222  				}
   223  			},
   224  		},
   225  		"DistributedVirtualPortgroup": {
   226  			&dvp, nil, func() {
   227  				for _, e := range dvp {
   228  					r.entities[e.Reference()] = e.Name
   229  				}
   230  			},
   231  		},
   232  		"Datastore": {
   233  			&datastore, nil, func() {
   234  				for _, e := range datastore {
   235  					r.entities[e.Reference()] = e.Name
   236  				}
   237  			},
   238  		},
   239  	}
   240  
   241  	xrefs := make(map[types.ManagedObjectReference]bool)
   242  	// Add MOR to vrefs[kind].refs avoiding any duplicates.
   243  	addRef := func(refs ...types.ManagedObjectReference) {
   244  		for _, ref := range refs {
   245  			if _, exists := xrefs[ref]; exists {
   246  				return
   247  			}
   248  			xrefs[ref] = true
   249  			vref := vrefs[ref.Type]
   250  			vref.refs = append(vref.refs, ref)
   251  		}
   252  	}
   253  
   254  	for _, vm := range r.VirtualMachines {
   255  		if r.cmd.General {
   256  			if ref := vm.Summary.Runtime.Host; ref != nil {
   257  				addRef(*ref)
   258  			}
   259  		}
   260  
   261  		if r.cmd.Resources {
   262  			addRef(vm.Datastore...)
   263  			addRef(vm.Network...)
   264  		}
   265  	}
   266  
   267  	for _, vref := range vrefs {
   268  		if vref.refs == nil {
   269  			continue
   270  		}
   271  		err := pc.Retrieve(ctx, vref.refs, []string{"name"}, vref.dest)
   272  		if err != nil {
   273  			return err
   274  		}
   275  		vref.save()
   276  	}
   277  
   278  	return nil
   279  }
   280  
   281  func (r *infoResult) entityNames(refs []types.ManagedObjectReference) string {
   282  	var names []string
   283  	for _, ref := range refs {
   284  		names = append(names, r.entities[ref])
   285  	}
   286  	return strings.Join(names, ", ")
   287  }
   288  
   289  func (r *infoResult) Write(w io.Writer) error {
   290  	// Maintain order via r.objects as Property collector does not always return results in order.
   291  	objects := make(map[types.ManagedObjectReference]mo.VirtualMachine, len(r.VirtualMachines))
   292  	for _, o := range r.VirtualMachines {
   293  		objects[o.Reference()] = o
   294  	}
   295  
   296  	tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
   297  
   298  	for _, o := range r.objects {
   299  		vm := objects[o.Reference()]
   300  		s := vm.Summary
   301  
   302  		fmt.Fprintf(tw, "Name:\t%s\n", s.Config.Name)
   303  
   304  		if r.cmd.General {
   305  			hostName := "<unavailable>"
   306  
   307  			if href := vm.Summary.Runtime.Host; href != nil {
   308  				if name, ok := r.entities[*href]; ok {
   309  					hostName = name
   310  				}
   311  			}
   312  
   313  			fmt.Fprintf(tw, "  Path:\t%s\n", o.InventoryPath)
   314  			fmt.Fprintf(tw, "  UUID:\t%s\n", s.Config.Uuid)
   315  			fmt.Fprintf(tw, "  Guest name:\t%s\n", s.Config.GuestFullName)
   316  			fmt.Fprintf(tw, "  Memory:\t%dMB\n", s.Config.MemorySizeMB)
   317  			fmt.Fprintf(tw, "  CPU:\t%d vCPU(s)\n", s.Config.NumCpu)
   318  			fmt.Fprintf(tw, "  Power state:\t%s\n", s.Runtime.PowerState)
   319  			fmt.Fprintf(tw, "  Boot time:\t%s\n", s.Runtime.BootTime)
   320  			fmt.Fprintf(tw, "  IP address:\t%s\n", s.Guest.IpAddress)
   321  			fmt.Fprintf(tw, "  Host:\t%s\n", hostName)
   322  		}
   323  
   324  		if r.cmd.Resources {
   325  			if s.Storage == nil {
   326  				s.Storage = new(types.VirtualMachineStorageSummary)
   327  			}
   328  			fmt.Fprintf(tw, "  CPU usage:\t%dMHz\n", s.QuickStats.OverallCpuUsage)
   329  			fmt.Fprintf(tw, "  Host memory usage:\t%dMB\n", s.QuickStats.HostMemoryUsage)
   330  			fmt.Fprintf(tw, "  Guest memory usage:\t%dMB\n", s.QuickStats.GuestMemoryUsage)
   331  			fmt.Fprintf(tw, "  Storage uncommitted:\t%s\n", units.ByteSize(s.Storage.Uncommitted))
   332  			fmt.Fprintf(tw, "  Storage committed:\t%s\n", units.ByteSize(s.Storage.Committed))
   333  			fmt.Fprintf(tw, "  Storage unshared:\t%s\n", units.ByteSize(s.Storage.Unshared))
   334  			fmt.Fprintf(tw, "  Storage:\t%s\n", r.entityNames(vm.Datastore))
   335  			fmt.Fprintf(tw, "  Network:\t%s\n", r.entityNames(vm.Network))
   336  		}
   337  
   338  		if r.cmd.ExtraConfig {
   339  			fmt.Fprintf(tw, "  ExtraConfig:\n")
   340  			for _, v := range vm.Config.ExtraConfig {
   341  				fmt.Fprintf(tw, "    %s:\t%s\n", v.GetOptionValue().Key, v.GetOptionValue().Value)
   342  			}
   343  		}
   344  
   345  		if r.cmd.ToolsConfigInfo {
   346  			t := vm.Config.Tools
   347  			fmt.Fprintf(tw, "  ToolsConfigInfo:\n")
   348  			fmt.Fprintf(tw, "    ToolsVersion:\t%d\n", t.ToolsVersion)
   349  			fmt.Fprintf(tw, "    AfterPowerOn:\t%s\n", flags.NewOptionalBool(&t.AfterPowerOn).String())
   350  			fmt.Fprintf(tw, "    AfterResume:\t%s\n", flags.NewOptionalBool(&t.AfterResume).String())
   351  			fmt.Fprintf(tw, "    BeforeGuestStandby:\t%s\n", flags.NewOptionalBool(&t.BeforeGuestStandby).String())
   352  			fmt.Fprintf(tw, "    BeforeGuestShutdown:\t%s\n", flags.NewOptionalBool(&t.BeforeGuestShutdown).String())
   353  			fmt.Fprintf(tw, "    BeforeGuestReboot:\t%s\n", flags.NewOptionalBool(&t.BeforeGuestReboot).String())
   354  			fmt.Fprintf(tw, "    ToolsUpgradePolicy:\t%s\n", t.ToolsUpgradePolicy)
   355  			fmt.Fprintf(tw, "    PendingCustomization:\t%s\n", t.PendingCustomization)
   356  			fmt.Fprintf(tw, "    SyncTimeWithHost:\t%s\n", flags.NewOptionalBool(&t.SyncTimeWithHost).String())
   357  		}
   358  	}
   359  
   360  	return tw.Flush()
   361  }