github.com/vmware/govmomi@v0.37.2/govc/device/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 device
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"flag"
    23  	"fmt"
    24  	"io"
    25  	"path"
    26  	"strings"
    27  	"text/tabwriter"
    28  
    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/vim25/types"
    33  )
    34  
    35  type info struct {
    36  	*flags.VirtualMachineFlag
    37  	*flags.OutputFlag
    38  	*flags.NetworkFlag
    39  }
    40  
    41  func init() {
    42  	cli.Register("device.info", &info{})
    43  }
    44  
    45  func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) {
    46  	cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
    47  	cmd.VirtualMachineFlag.Register(ctx, f)
    48  
    49  	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
    50  	cmd.OutputFlag.Register(ctx, f)
    51  
    52  	cmd.NetworkFlag, ctx = flags.NewNetworkFlag(ctx)
    53  	cmd.NetworkFlag.Register(ctx, f)
    54  }
    55  
    56  func (cmd *info) Process(ctx context.Context) error {
    57  	if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
    58  		return err
    59  	}
    60  	if err := cmd.OutputFlag.Process(ctx); err != nil {
    61  		return err
    62  	}
    63  	if err := cmd.NetworkFlag.Process(ctx); err != nil {
    64  		return err
    65  	}
    66  	return nil
    67  }
    68  
    69  func (cmd *info) Usage() string {
    70  	return "[DEVICE]..."
    71  }
    72  
    73  func (cmd *info) Description() string {
    74  	return `Device info for VM.
    75  
    76  Examples:
    77    govc device.info -vm $name
    78    govc device.info -vm $name disk-*
    79    govc device.info -vm $name -json disk-* | jq -r .devices[].backing.uuid
    80    govc device.info -vm $name -json 'disk-*' | jq -r .devices[].backing.fileName # vmdk path
    81    govc device.info -vm $name -json ethernet-0 | jq -r .devices[].macAddress`
    82  }
    83  
    84  func match(p string, devices object.VirtualDeviceList) object.VirtualDeviceList {
    85  	var matches object.VirtualDeviceList
    86  	match := func(name string) bool {
    87  		matched, _ := path.Match(p, name)
    88  		return matched
    89  	}
    90  
    91  	for _, device := range devices {
    92  		name := devices.Name(device)
    93  		eq := name == p
    94  		if eq || match(name) {
    95  			matches = append(matches, device)
    96  		}
    97  		if eq {
    98  			break
    99  		}
   100  	}
   101  
   102  	return matches
   103  }
   104  
   105  func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error {
   106  	vm, err := cmd.VirtualMachine()
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	if vm == nil {
   112  		return flag.ErrHelp
   113  	}
   114  
   115  	devices, err := vm.Device(ctx)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	res := infoResult{
   121  		list: devices,
   122  	}
   123  
   124  	if cmd.NetworkFlag.IsSet() {
   125  		net, err := cmd.Network()
   126  		if err != nil {
   127  			return err
   128  		}
   129  
   130  		backing, err := net.EthernetCardBackingInfo(ctx)
   131  		if err != nil {
   132  			return err
   133  		}
   134  
   135  		devices = devices.SelectByBackingInfo(backing)
   136  	}
   137  
   138  	if f.NArg() == 0 {
   139  		res.Devices = toInfoList(devices)
   140  	} else {
   141  		for _, name := range f.Args() {
   142  			matches := match(name, devices)
   143  			if len(matches) == 0 {
   144  				return fmt.Errorf("device '%s' not found", name)
   145  			}
   146  
   147  			res.Devices = append(res.Devices, toInfoList(matches)...)
   148  		}
   149  	}
   150  
   151  	return cmd.WriteResult(&res)
   152  }
   153  
   154  func toInfoList(devices object.VirtualDeviceList) []infoDevice {
   155  	var res []infoDevice
   156  
   157  	for _, device := range devices {
   158  		res = append(res, infoDevice{
   159  			Name:              devices.Name(device),
   160  			Type:              devices.TypeName(device),
   161  			BaseVirtualDevice: device,
   162  		})
   163  	}
   164  
   165  	return res
   166  }
   167  
   168  type infoDevice struct {
   169  	Name string `json:"name"`
   170  	Type string `json:"type"`
   171  	types.BaseVirtualDevice
   172  }
   173  
   174  func (d *infoDevice) MarshalJSON() ([]byte, error) {
   175  	b, err := json.Marshal(d.BaseVirtualDevice)
   176  	if err != nil {
   177  		return b, err
   178  	}
   179  
   180  	// TODO: make use of "inline" tag if it comes to be: https://github.com/golang/go/issues/6213
   181  
   182  	return append([]byte(fmt.Sprintf(`{"name":"%s","type":"%s",`, d.Name, d.Type)), b[1:]...), err
   183  }
   184  
   185  type infoResult struct {
   186  	Devices []infoDevice `json:"devices"`
   187  	// need the full list of devices to lookup attached devices and controllers
   188  	list object.VirtualDeviceList
   189  }
   190  
   191  func (r *infoResult) Write(w io.Writer) error {
   192  	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
   193  
   194  	for i := range r.Devices {
   195  		device := r.Devices[i].BaseVirtualDevice
   196  		d := device.GetVirtualDevice()
   197  		info := d.DeviceInfo.GetDescription()
   198  
   199  		fmt.Fprintf(tw, "Name:\t%s\n", r.Devices[i].Name)
   200  		fmt.Fprintf(tw, "  Type:\t%s\n", r.list.TypeName(device))
   201  		fmt.Fprintf(tw, "  Label:\t%s\n", info.Label)
   202  		fmt.Fprintf(tw, "  Summary:\t%s\n", info.Summary)
   203  		fmt.Fprintf(tw, "  Key:\t%d\n", d.Key)
   204  
   205  		if c, ok := device.(types.BaseVirtualController); ok {
   206  			var attached []string
   207  			for _, key := range c.GetVirtualController().Device {
   208  				attached = append(attached, r.list.Name(r.list.FindByKey(key)))
   209  			}
   210  			fmt.Fprintf(tw, "  Devices:\t%s\n", strings.Join(attached, ", "))
   211  		} else {
   212  			if c := r.list.FindByKey(d.ControllerKey); c != nil {
   213  				fmt.Fprintf(tw, "  Controller:\t%s\n", r.list.Name(c))
   214  				if d.UnitNumber != nil {
   215  					fmt.Fprintf(tw, "  Unit number:\t%d\n", *d.UnitNumber)
   216  				} else {
   217  					fmt.Fprintf(tw, "  Unit number:\t<nil>\n")
   218  				}
   219  			}
   220  		}
   221  
   222  		if ca := d.Connectable; ca != nil {
   223  			fmt.Fprintf(tw, "  Connected:\t%t\n", ca.Connected)
   224  			fmt.Fprintf(tw, "  Start connected:\t%t\n", ca.StartConnected)
   225  			fmt.Fprintf(tw, "  Guest control:\t%t\n", ca.AllowGuestControl)
   226  			fmt.Fprintf(tw, "  Status:\t%s\n", ca.Status)
   227  		}
   228  
   229  		switch md := device.(type) {
   230  		case types.BaseVirtualEthernetCard:
   231  			fmt.Fprintf(tw, "  MAC Address:\t%s\n", md.GetVirtualEthernetCard().MacAddress)
   232  			fmt.Fprintf(tw, "  Address type:\t%s\n", md.GetVirtualEthernetCard().AddressType)
   233  		case *types.VirtualDisk:
   234  			if b, ok := md.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
   235  				fmt.Fprintf(tw, "  File:\t%s\n", b.GetVirtualDeviceFileBackingInfo().FileName)
   236  			}
   237  			if b, ok := md.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok && b.Parent != nil {
   238  				fmt.Fprintf(tw, "  Parent:\t%s\n", b.Parent.GetVirtualDeviceFileBackingInfo().FileName)
   239  			}
   240  		case *types.VirtualSerialPort:
   241  			if b, ok := md.Backing.(*types.VirtualSerialPortURIBackingInfo); ok {
   242  				fmt.Fprintf(tw, "  Direction:\t%s\n", b.Direction)
   243  				fmt.Fprintf(tw, "  Service URI:\t%s\n", b.ServiceURI)
   244  				fmt.Fprintf(tw, "  Proxy URI:\t%s\n", b.ProxyURI)
   245  			}
   246  		case *types.VirtualPrecisionClock:
   247  			if b, ok := md.Backing.(*types.VirtualPrecisionClockSystemClockBackingInfo); ok {
   248  				proto := b.Protocol
   249  				if proto == "" {
   250  					proto = string(types.HostDateTimeInfoProtocolPtp)
   251  				}
   252  				fmt.Fprintf(tw, "  Protocol:\t%s\n", proto)
   253  			}
   254  		}
   255  	}
   256  
   257  	return tw.Flush()
   258  }