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