github.com/vmware/govmomi@v0.37.1/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 }