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 }