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 }