Skip to content

Commit 1b06c59

Browse files
Benjamin Nashdougm
Benjamin Nash
authored andcommitted
govc: add GPU commands
Signed-off-by: Benjamin Nash <[email protected]>
1 parent 7542587 commit 1b06c59

File tree

9 files changed

+713
-15
lines changed

9 files changed

+713
-15
lines changed

cli/gpu/gpu.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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 gpu
6+
7+
import (
8+
_ "github.com/vmware/govmomi/cli/gpu/host"
9+
_ "github.com/vmware/govmomi/cli/gpu/host/profile"
10+
_ "github.com/vmware/govmomi/cli/gpu/vm"
11+
)

cli/gpu/host/info.go

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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 host
6+
7+
import (
8+
"context"
9+
"flag"
10+
"fmt"
11+
"io"
12+
"text/tabwriter"
13+
14+
"github.com/vmware/govmomi/cli"
15+
"github.com/vmware/govmomi/cli/flags"
16+
"github.com/vmware/govmomi/property"
17+
"github.com/vmware/govmomi/vim25/mo"
18+
"github.com/vmware/govmomi/vim25/types"
19+
)
20+
21+
type info struct {
22+
*flags.ClientFlag
23+
*flags.HostSystemFlag
24+
*flags.OutputFlag
25+
}
26+
27+
func init() {
28+
cli.Register("gpu.host.info", &info{})
29+
}
30+
31+
func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) {
32+
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
33+
cmd.ClientFlag.Register(ctx, f)
34+
35+
cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx)
36+
cmd.HostSystemFlag.Register(ctx, f)
37+
38+
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
39+
cmd.OutputFlag.Register(ctx, f)
40+
}
41+
42+
func (cmd *info) Description() string {
43+
return `Display GPU information for a host.
44+
45+
Examples:
46+
govc gpu.host.info -host hostname
47+
govc gpu.host.info -host hostname -json | jq .
48+
govc gpu.host.info -host hostname -json | jq -r '.devices[] | select(.deviceName | contains("NVIDIA"))'
49+
govc find / -type h | xargs -n1 govc gpu.host.info -host # all hosts`
50+
}
51+
52+
func (cmd *info) Process(ctx context.Context) error {
53+
if err := cmd.ClientFlag.Process(ctx); err != nil {
54+
return err
55+
}
56+
if err := cmd.HostSystemFlag.Process(ctx); err != nil {
57+
return err
58+
}
59+
if err := cmd.OutputFlag.Process(ctx); err != nil {
60+
return err
61+
}
62+
return nil
63+
}
64+
65+
// Support NVIDIA devices, and exclude virtual GPUs, which have a SubDeviceId of 0x0000
66+
func isPhysicalGPU(device types.HostPciDevice) bool {
67+
return device.VendorId == 0x10de && device.SubDeviceId != 0x0000
68+
}
69+
70+
type gpuInfo struct {
71+
PciId string `json:"pciId"`
72+
DeviceName string `json:"deviceName"`
73+
VendorName string `json:"vendorName"`
74+
DeviceId int16 `json:"deviceId"`
75+
VendorId int16 `json:"vendorId"`
76+
SubVendorId int16 `json:"subVendorId"`
77+
SubDeviceId int16 `json:"subDeviceId"`
78+
ClassId int16 `json:"classId"`
79+
Bus uint8 `json:"bus"`
80+
Slot uint8 `json:"slot"`
81+
Function uint8 `json:"function"`
82+
Bridge string `json:"bridge,omitempty"`
83+
}
84+
85+
type infoResult struct {
86+
Devices []gpuInfo `json:"devices"`
87+
}
88+
89+
func (r *infoResult) Write(w io.Writer) error {
90+
tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
91+
92+
for _, gpu := range r.Devices {
93+
fmt.Fprintf(tw, "PCI ID: %s\n", gpu.PciId)
94+
fmt.Fprintf(tw, " Device Name: %s\n", gpu.DeviceName)
95+
fmt.Fprintf(tw, " Vendor Name: %s\n", gpu.VendorName)
96+
fmt.Fprintf(tw, " Device ID: 0x%04x\n", gpu.DeviceId)
97+
fmt.Fprintf(tw, " Vendor ID: 0x%04x\n", gpu.VendorId)
98+
fmt.Fprintf(tw, " SubVendor ID: 0x%04x\n", gpu.SubVendorId)
99+
fmt.Fprintf(tw, " SubDevice ID: 0x%04x\n", gpu.SubDeviceId)
100+
fmt.Fprintf(tw, " Class ID: 0x%04x\n", gpu.ClassId)
101+
fmt.Fprintf(tw, " Bus: 0x%02x, Slot: 0x%02x, Function: 0x%02x\n", gpu.Bus, gpu.Slot, gpu.Function)
102+
if gpu.Bridge != "" {
103+
fmt.Fprintf(tw, " Parent Bridge: %s\n", gpu.Bridge)
104+
}
105+
}
106+
107+
return tw.Flush()
108+
}
109+
110+
func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error {
111+
host, err := cmd.HostSystem()
112+
if err != nil {
113+
return err
114+
}
115+
116+
if host == nil {
117+
return flag.ErrHelp
118+
}
119+
120+
var h mo.HostSystem
121+
pc := property.DefaultCollector(host.Client())
122+
err = pc.RetrieveOne(ctx, host.Reference(), []string{"hardware"}, &h)
123+
if err != nil {
124+
return err
125+
}
126+
127+
var res infoResult
128+
for _, device := range h.Hardware.PciDevice {
129+
if isPhysicalGPU(device) {
130+
res.Devices = append(res.Devices, gpuInfo{
131+
PciId: device.Id,
132+
DeviceName: device.DeviceName,
133+
VendorName: device.VendorName,
134+
DeviceId: device.DeviceId,
135+
VendorId: device.VendorId,
136+
SubVendorId: device.SubVendorId,
137+
SubDeviceId: device.SubDeviceId,
138+
ClassId: device.ClassId,
139+
Bus: device.Bus,
140+
Slot: device.Slot,
141+
Function: device.Function,
142+
Bridge: device.ParentBridge,
143+
})
144+
}
145+
}
146+
147+
return cmd.WriteResult(&res)
148+
}

cli/gpu/host/profile/ls.go

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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 profile
6+
7+
import (
8+
"context"
9+
"flag"
10+
"fmt"
11+
"io"
12+
"text/tabwriter"
13+
14+
"github.com/vmware/govmomi/cli"
15+
"github.com/vmware/govmomi/cli/flags"
16+
"github.com/vmware/govmomi/property"
17+
"github.com/vmware/govmomi/vim25/mo"
18+
)
19+
20+
type ls struct {
21+
*flags.ClientFlag
22+
*flags.HostSystemFlag
23+
}
24+
25+
func init() {
26+
cli.Register("gpu.host.profile.ls", &ls{})
27+
}
28+
29+
func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) {
30+
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
31+
cmd.ClientFlag.Register(ctx, f)
32+
33+
cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx)
34+
cmd.HostSystemFlag.Register(ctx, f)
35+
}
36+
37+
func (cmd *ls) Description() string {
38+
return `List available vGPU profiles on host.
39+
40+
Examples:
41+
govc gpu.host.profile.ls -host hostname
42+
govc gpu.host.profile.ls -host hostname -json | jq -r '.profiles[]'
43+
govc gpu.host.profile.ls -host hostname -json | jq -r '.profiles[] | select(contains("nvidia_a40"))'`
44+
}
45+
46+
func (cmd *ls) Process(ctx context.Context) error {
47+
if err := cmd.ClientFlag.Process(ctx); err != nil {
48+
return err
49+
}
50+
if err := cmd.HostSystemFlag.Process(ctx); err != nil {
51+
return err
52+
}
53+
return nil
54+
}
55+
56+
type lsResult struct {
57+
Profiles []string `json:"profiles"`
58+
}
59+
60+
func (r *lsResult) Write(w io.Writer) error {
61+
tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
62+
fmt.Fprintln(tw, "Available vGPU profiles:")
63+
for _, profile := range r.Profiles {
64+
fmt.Fprintf(tw, " %s\n", profile)
65+
}
66+
return tw.Flush()
67+
}
68+
69+
func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
70+
host, err := cmd.HostSystem()
71+
if err != nil {
72+
return err
73+
}
74+
75+
if host == nil {
76+
return flag.ErrHelp
77+
}
78+
79+
var o mo.HostSystem
80+
pc := property.DefaultCollector(host.Client())
81+
err = pc.RetrieveOne(ctx, host.Reference(), []string{"config.sharedPassthruGpuTypes"}, &o)
82+
if err != nil {
83+
return err
84+
}
85+
86+
if o.Config == nil {
87+
return fmt.Errorf("failed to get host configuration")
88+
}
89+
90+
if len(o.Config.SharedPassthruGpuTypes) == 0 {
91+
return fmt.Errorf("no vGPU profiles available on this host")
92+
}
93+
94+
res := &lsResult{
95+
Profiles: o.Config.SharedPassthruGpuTypes,
96+
}
97+
98+
return cmd.WriteResult(res)
99+
}

cli/gpu/vm/add.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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 vm
6+
7+
import (
8+
"context"
9+
"flag"
10+
"fmt"
11+
12+
"github.com/vmware/govmomi/cli"
13+
"github.com/vmware/govmomi/cli/flags"
14+
"github.com/vmware/govmomi/property"
15+
"github.com/vmware/govmomi/vim25/mo"
16+
"github.com/vmware/govmomi/vim25/types"
17+
)
18+
19+
type add struct {
20+
*flags.ClientFlag
21+
*flags.VirtualMachineFlag
22+
profile string
23+
}
24+
25+
func init() {
26+
cli.Register("gpu.vm.add", &add{})
27+
}
28+
29+
func (cmd *add) Register(ctx context.Context, f *flag.FlagSet) {
30+
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
31+
cmd.ClientFlag.Register(ctx, f)
32+
33+
cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
34+
cmd.VirtualMachineFlag.Register(ctx, f)
35+
36+
f.StringVar(&cmd.profile, "profile", "", "vGPU profile")
37+
}
38+
39+
func (cmd *add) Description() string {
40+
return `Add vGPU to VM.
41+
42+
Examples:
43+
govc gpu.vm.add -vm $vm -profile nvidia_a40-1b`
44+
}
45+
46+
func (cmd *add) Process(ctx context.Context) error {
47+
if err := cmd.ClientFlag.Process(ctx); err != nil {
48+
return err
49+
}
50+
if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
51+
return err
52+
}
53+
return nil
54+
}
55+
56+
func (cmd *add) Run(ctx context.Context, f *flag.FlagSet) error {
57+
vm, err := cmd.VirtualMachineFlag.VirtualMachine()
58+
if err != nil {
59+
return err
60+
}
61+
62+
if vm == nil {
63+
return flag.ErrHelp
64+
}
65+
66+
if cmd.profile == "" {
67+
return fmt.Errorf("profile argument must be specified")
68+
}
69+
70+
// Check power state
71+
var o mo.VirtualMachine
72+
pc := property.DefaultCollector(vm.Client())
73+
err = pc.RetrieveOne(ctx, vm.Reference(), []string{"runtime.powerState"}, &o)
74+
if err != nil {
75+
return err
76+
}
77+
78+
if o.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
79+
return fmt.Errorf("VM must be powered off to add vGPU")
80+
}
81+
82+
device := &types.VirtualPCIPassthrough{
83+
VirtualDevice: types.VirtualDevice{
84+
Key: -100,
85+
Backing: &types.VirtualPCIPassthroughVmiopBackingInfo{
86+
Vgpu: cmd.profile,
87+
},
88+
},
89+
}
90+
91+
vmConfigSpec := types.VirtualMachineConfigSpec{
92+
DeviceChange: []types.BaseVirtualDeviceConfigSpec{
93+
&types.VirtualDeviceConfigSpec{
94+
Operation: types.VirtualDeviceConfigSpecOperationAdd,
95+
Device: device,
96+
},
97+
},
98+
}
99+
100+
task, err := vm.Reconfigure(ctx, vmConfigSpec)
101+
if err != nil {
102+
return err
103+
}
104+
105+
return task.Wait(ctx)
106+
}

0 commit comments

Comments
 (0)