mirror of
https://github.com/containers/podman.git
synced 2025-06-24 11:28:24 +08:00
machine: qemu: add usb host passthrough
QEMU usb-host driver which is the one for passthrough, supports two options for selecting an USB devices in the host to provide it to the VM: - Bus and Device number the device is plugged - Vendor and Product information of the USB devices https://qemu-project.gitlab.io/qemu/system/devices/usb.html This commit allows a user to configure podman machine with either of options, with new --usb command line option for podman machine init. Examples podman machine init tosovm4 --usb vendor=13d3,product=5406 podman machine init tosovm3 --usb bus=1,devnum=4 --usb bus=1,devnum=3 This commit also allows a user to change the USBs configured with --usb command line option for podman machine set. Note that this commit does not handle host device permissions nor verify that the USB devices exists. Signed-off-by: Victor Toso <victortoso@redhat.com>
This commit is contained in:
@ -107,6 +107,11 @@ func init() {
|
|||||||
flags.StringArrayVarP(&initOpts.Volumes, VolumeFlagName, "v", cfg.ContainersConfDefaultsRO.Machine.Volumes.Get(), "Volumes to mount, source:target")
|
flags.StringArrayVarP(&initOpts.Volumes, VolumeFlagName, "v", cfg.ContainersConfDefaultsRO.Machine.Volumes.Get(), "Volumes to mount, source:target")
|
||||||
_ = initCmd.RegisterFlagCompletionFunc(VolumeFlagName, completion.AutocompleteDefault)
|
_ = initCmd.RegisterFlagCompletionFunc(VolumeFlagName, completion.AutocompleteDefault)
|
||||||
|
|
||||||
|
USBFlagName := "usb"
|
||||||
|
flags.StringArrayVarP(&initOpts.USBs, USBFlagName, "", []string{},
|
||||||
|
"USB Host passthrough: bus=$1,devnum=$2 or vendor=$1,product=$2")
|
||||||
|
_ = initCmd.RegisterFlagCompletionFunc(USBFlagName, completion.AutocompleteDefault)
|
||||||
|
|
||||||
VolumeDriverFlagName := "volume-driver"
|
VolumeDriverFlagName := "volume-driver"
|
||||||
flags.StringVar(&initOpts.VolumeDriver, VolumeDriverFlagName, "", "Optional volume driver")
|
flags.StringVar(&initOpts.VolumeDriver, VolumeDriverFlagName, "", "Optional volume driver")
|
||||||
_ = initCmd.RegisterFlagCompletionFunc(VolumeDriverFlagName, completion.AutocompleteDefault)
|
_ = initCmd.RegisterFlagCompletionFunc(VolumeDriverFlagName, completion.AutocompleteDefault)
|
||||||
|
@ -37,6 +37,7 @@ type SetFlags struct {
|
|||||||
Memory uint64
|
Memory uint64
|
||||||
Rootful bool
|
Rootful bool
|
||||||
UserModeNetworking bool
|
UserModeNetworking bool
|
||||||
|
USBs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -74,6 +75,13 @@ func init() {
|
|||||||
)
|
)
|
||||||
_ = setCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
|
_ = setCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
|
usbFlagName := "usb"
|
||||||
|
flags.StringArrayVarP(
|
||||||
|
&setFlags.USBs,
|
||||||
|
usbFlagName, "", []string{},
|
||||||
|
"USBs bus=$1,devnum=$2 or vendor=$1,product=$2")
|
||||||
|
_ = setCmd.RegisterFlagCompletionFunc(usbFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
userModeNetFlagName := "user-mode-networking"
|
userModeNetFlagName := "user-mode-networking"
|
||||||
flags.BoolVar(&setFlags.UserModeNetworking, userModeNetFlagName, false, // defaults not-relevant due to use of Changed()
|
flags.BoolVar(&setFlags.UserModeNetworking, userModeNetFlagName, false, // defaults not-relevant due to use of Changed()
|
||||||
"Whether this machine should use user-mode networking, routing traffic through a host user-space process")
|
"Whether this machine should use user-mode networking, routing traffic through a host user-space process")
|
||||||
@ -110,6 +118,9 @@ func setMachine(cmd *cobra.Command, args []string) error {
|
|||||||
if cmd.Flags().Changed("user-mode-networking") {
|
if cmd.Flags().Changed("user-mode-networking") {
|
||||||
setOpts.UserModeNetworking = &setFlags.UserModeNetworking
|
setOpts.UserModeNetworking = &setFlags.UserModeNetworking
|
||||||
}
|
}
|
||||||
|
if cmd.Flags().Changed("usb") {
|
||||||
|
setOpts.USBs = &setFlags.USBs
|
||||||
|
}
|
||||||
|
|
||||||
setErrs, lasterr := vm.Set(vmName, setOpts)
|
setErrs, lasterr := vm.Set(vmName, setOpts)
|
||||||
for _, err := range setErrs {
|
for _, err := range setErrs {
|
||||||
|
@ -104,6 +104,20 @@ means to use the timezone of the machine host.
|
|||||||
The timezone setting is not used with WSL. WSL automatically sets the timezone to the same
|
The timezone setting is not used with WSL. WSL automatically sets the timezone to the same
|
||||||
as the host Windows operating system.
|
as the host Windows operating system.
|
||||||
|
|
||||||
|
#### **--usb**=*bus=number,devnum=number* or *vendor=hexadecimal,product=hexadecimal*
|
||||||
|
|
||||||
|
Assign a USB device from the host to the VM via USB passthrough.
|
||||||
|
Only supported for QEMU Machines.
|
||||||
|
|
||||||
|
The device needs to have proper permissions in order to be passed to the machine. This
|
||||||
|
means the device needs to be under your user group.
|
||||||
|
|
||||||
|
Note that using bus and device number are simpler but the values can change every boot
|
||||||
|
or when the device is unplugged.
|
||||||
|
|
||||||
|
When specifying a USB using vendor and product ID's, if more than one device has the
|
||||||
|
same vendor and product ID, the first available device is assigned.
|
||||||
|
|
||||||
@@option user-mode-networking
|
@@option user-mode-networking
|
||||||
|
|
||||||
#### **--username**
|
#### **--username**
|
||||||
@ -160,6 +174,8 @@ $ podman machine init --rootful
|
|||||||
$ podman machine init --disk-size 50
|
$ podman machine init --disk-size 50
|
||||||
$ podman machine init --memory=1024 myvm
|
$ podman machine init --memory=1024 myvm
|
||||||
$ podman machine init -v /Users:/mnt/Users
|
$ podman machine init -v /Users:/mnt/Users
|
||||||
|
$ podman machine init --usb vendor=13d3,product=5406
|
||||||
|
$ podman machine init --usb bus=1,devnum=3
|
||||||
```
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
|
@ -52,6 +52,20 @@ are no longer visible with the default connection/socket. This is because the ro
|
|||||||
users in the VM are completely separated and do not share any storage. The data however is not
|
users in the VM are completely separated and do not share any storage. The data however is not
|
||||||
lost and you can always change this option back or use the other connection to access it.
|
lost and you can always change this option back or use the other connection to access it.
|
||||||
|
|
||||||
|
#### **--usb**=*bus=number,devnum=number* or *vendor=hexadecimal,product=hexadecimal* or *""*
|
||||||
|
|
||||||
|
Assign a USB device from the host to the VM.
|
||||||
|
Only supported for QEMU Machines.
|
||||||
|
|
||||||
|
The device needs to be present when the VM starts.
|
||||||
|
The device needs to have proper permissions in order to be assign to podman machine.
|
||||||
|
|
||||||
|
Use an empty string to remove all previously set USB devices.
|
||||||
|
|
||||||
|
Note that using bus and device number are simpler but the values can change every boot or when the
|
||||||
|
device is unplugged. Using vendor and product might lead to collision in the case of multiple
|
||||||
|
devices with the same vendor product value, the first available device is assigned.
|
||||||
|
|
||||||
@@option user-mode-networking
|
@@option user-mode-networking
|
||||||
|
|
||||||
## EXAMPLES
|
## EXAMPLES
|
||||||
|
@ -113,6 +113,10 @@ func (v AppleHVVirtualization) LoadVMByName(name string) (machine.VM, error) {
|
|||||||
func (v AppleHVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
func (v AppleHVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||||
m := MacMachine{Name: opts.Name}
|
m := MacMachine{Name: opts.Name}
|
||||||
|
|
||||||
|
if len(opts.USBs) > 0 {
|
||||||
|
return nil, fmt.Errorf("USB host passtrough not supported for applehv machines")
|
||||||
|
}
|
||||||
|
|
||||||
configDir, err := machine.GetConfDir(machine.AppleHvVirt)
|
configDir, err := machine.GetConfDir(machine.AppleHvVirt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -458,6 +458,9 @@ func (m *MacMachine) Set(name string, opts machine.SetOptions) ([]error, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if opts.USBs != nil {
|
||||||
|
setErrors = append(setErrors, errors.New("changing USBs not supported for applehv machines"))
|
||||||
|
}
|
||||||
|
|
||||||
// Write the machine config to the filesystem
|
// Write the machine config to the filesystem
|
||||||
err = m.writeConfig()
|
err = m.writeConfig()
|
||||||
|
@ -39,6 +39,7 @@ type InitOptions struct {
|
|||||||
Rootful bool
|
Rootful bool
|
||||||
UID string // uid of the user that called machine
|
UID string // uid of the user that called machine
|
||||||
UserModeNetworking *bool // nil = use backend/system default, false = disable, true = enable
|
UserModeNetworking *bool // nil = use backend/system default, false = disable, true = enable
|
||||||
|
USBs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Status = string
|
type Status = string
|
||||||
@ -106,6 +107,7 @@ type SetOptions struct {
|
|||||||
Memory *uint64
|
Memory *uint64
|
||||||
Rootful *bool
|
Rootful *bool
|
||||||
UserModeNetworking *bool
|
UserModeNetworking *bool
|
||||||
|
USBs *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSHOptions struct {
|
type SSHOptions struct {
|
||||||
@ -271,6 +273,13 @@ func ConfDirPrefix() (string, error) {
|
|||||||
return confDir, nil
|
return confDir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type USBConfig struct {
|
||||||
|
Bus string
|
||||||
|
DevNumber string
|
||||||
|
Vendor int
|
||||||
|
Product int
|
||||||
|
}
|
||||||
|
|
||||||
// ResourceConfig describes physical attributes of the machine
|
// ResourceConfig describes physical attributes of the machine
|
||||||
type ResourceConfig struct {
|
type ResourceConfig struct {
|
||||||
// CPUs to be assigned to the VM
|
// CPUs to be assigned to the VM
|
||||||
@ -279,6 +288,8 @@ type ResourceConfig struct {
|
|||||||
DiskSize uint64
|
DiskSize uint64
|
||||||
// Memory in megabytes assigned to the vm
|
// Memory in megabytes assigned to the vm
|
||||||
Memory uint64
|
Memory uint64
|
||||||
|
// Usbs
|
||||||
|
USBs []USBConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mount struct {
|
type Mount struct {
|
||||||
|
@ -111,6 +111,9 @@ func (v HyperVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
|
|||||||
if len(opts.ImagePath) < 1 {
|
if len(opts.ImagePath) < 1 {
|
||||||
return nil, errors.New("must define --image-path for hyperv support")
|
return nil, errors.New("must define --image-path for hyperv support")
|
||||||
}
|
}
|
||||||
|
if len(opts.USBs) > 0 {
|
||||||
|
return nil, fmt.Errorf("USB host passtrough not supported for hyperv machines")
|
||||||
|
}
|
||||||
|
|
||||||
m.RemoteUsername = opts.Username
|
m.RemoteUsername = opts.Username
|
||||||
|
|
||||||
|
@ -518,6 +518,10 @@ func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, erro
|
|||||||
memoryChanged = true
|
memoryChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.USBs != nil {
|
||||||
|
setErrors = append(setErrors, errors.New("changing USBs not supported for hyperv machines"))
|
||||||
|
}
|
||||||
|
|
||||||
if cpuChanged || memoryChanged {
|
if cpuChanged || memoryChanged {
|
||||||
err := vm.UpdateProcessorMemSettings(func(ps *hypervctl.ProcessorSettings) {
|
err := vm.UpdateProcessorMemSettings(func(ps *hypervctl.ProcessorSettings) {
|
||||||
if cpuChanged {
|
if cpuChanged {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v4/pkg/machine"
|
||||||
"github.com/containers/podman/v4/pkg/machine/define"
|
"github.com/containers/podman/v4/pkg/machine/define"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,6 +47,24 @@ func (q *QemuCmd) SetNetwork() {
|
|||||||
*q = append(*q, "-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee")
|
*q = append(*q, "-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNetwork adds a network device to the machine
|
||||||
|
func (q *QemuCmd) SetUSBHostPassthrough(usbs []machine.USBConfig) {
|
||||||
|
if len(usbs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Add xhci usb emulation first and then each usb device
|
||||||
|
*q = append(*q, "-device", "qemu-xhci")
|
||||||
|
for _, usb := range usbs {
|
||||||
|
var dev string
|
||||||
|
if usb.Bus != "" && usb.DevNumber != "" {
|
||||||
|
dev = fmt.Sprintf("usb-host,hostbus=%s,hostaddr=%s", usb.Bus, usb.DevNumber)
|
||||||
|
} else {
|
||||||
|
dev = fmt.Sprintf("usb-host,vendorid=%d,productid=%d", usb.Vendor, usb.Product)
|
||||||
|
}
|
||||||
|
*q = append(*q, "-device", dev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SetSerialPort adds a serial port to the machine for readiness
|
// SetSerialPort adds a serial port to the machine for readiness
|
||||||
func (q *QemuCmd) SetSerialPort(readySocket, vmPidFile define.VMFile, name string) {
|
func (q *QemuCmd) SetSerialPort(readySocket, vmPidFile define.VMFile, name string) {
|
||||||
*q = append(*q,
|
*q = append(*q,
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -59,6 +60,78 @@ func (v *MachineVM) setNewMachineCMD(qemuBinary string, cmdOpts *setNewMachineCM
|
|||||||
v.CmdLine.SetQmpMonitor(v.QMPMonitor)
|
v.CmdLine.SetQmpMonitor(v.QMPMonitor)
|
||||||
v.CmdLine.SetNetwork()
|
v.CmdLine.SetNetwork()
|
||||||
v.CmdLine.SetSerialPort(v.ReadySocket, v.VMPidFilePath, v.Name)
|
v.CmdLine.SetSerialPort(v.ReadySocket, v.VMPidFilePath, v.Name)
|
||||||
|
v.CmdLine.SetUSBHostPassthrough(v.USBs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUSBs(usbs []string) ([]machine.USBConfig, error) {
|
||||||
|
configs := []machine.USBConfig{}
|
||||||
|
for _, str := range usbs {
|
||||||
|
if str == "" {
|
||||||
|
// Ignore --usb="" as it can be used to reset USBConfigs
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := strings.Split(str, ",")
|
||||||
|
if len(vals) != 2 {
|
||||||
|
return configs, fmt.Errorf("usb: fail to parse: missing ',': %s", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
left := strings.Split(vals[0], "=")
|
||||||
|
if len(left) != 2 {
|
||||||
|
return configs, fmt.Errorf("usb: fail to parse: missing '=': %s", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
right := strings.Split(vals[1], "=")
|
||||||
|
if len(right) != 2 {
|
||||||
|
return configs, fmt.Errorf("usb: fail to parse: missing '=': %s", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
option := ""
|
||||||
|
if (left[0] == "bus" && right[0] == "devnum") ||
|
||||||
|
(right[0] == "bus" && left[0] == "devnum") {
|
||||||
|
option = "bus_devnum"
|
||||||
|
}
|
||||||
|
if (left[0] == "vendor" && right[0] == "product") ||
|
||||||
|
(right[0] == "vendor" && left[0] == "product") {
|
||||||
|
option = "vendor_product"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch option {
|
||||||
|
case "bus_devnum":
|
||||||
|
bus, devnumber := left[1], right[1]
|
||||||
|
if right[0] == "bus" {
|
||||||
|
bus, devnumber = devnumber, bus
|
||||||
|
}
|
||||||
|
|
||||||
|
configs = append(configs, machine.USBConfig{
|
||||||
|
Bus: bus,
|
||||||
|
DevNumber: devnumber,
|
||||||
|
})
|
||||||
|
case "vendor_product":
|
||||||
|
vendorStr, productStr := left[1], right[1]
|
||||||
|
if right[0] == "vendor" {
|
||||||
|
vendorStr, productStr = productStr, vendorStr
|
||||||
|
}
|
||||||
|
|
||||||
|
vendor, err := strconv.ParseInt(vendorStr, 16, 0)
|
||||||
|
if err != nil {
|
||||||
|
return configs, fmt.Errorf("fail to convert vendor of %s: %s", str, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
product, err := strconv.ParseInt(productStr, 16, 0)
|
||||||
|
if err != nil {
|
||||||
|
return configs, fmt.Errorf("fail to convert product of %s: %s", str, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configs = append(configs, machine.USBConfig{
|
||||||
|
Vendor: int(vendor),
|
||||||
|
Product: int(product),
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return configs, fmt.Errorf("usb: fail to parse: %s", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMachine initializes an instance of a virtual machine based on the qemu
|
// NewMachine initializes an instance of a virtual machine based on the qemu
|
||||||
@ -104,6 +177,9 @@ func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, e
|
|||||||
vm.CPUs = opts.CPUS
|
vm.CPUs = opts.CPUS
|
||||||
vm.Memory = opts.Memory
|
vm.Memory = opts.Memory
|
||||||
vm.DiskSize = opts.DiskSize
|
vm.DiskSize = opts.DiskSize
|
||||||
|
if vm.USBs, err = parseUSBs(opts.USBs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
vm.Created = time.Now()
|
vm.Created = time.Now()
|
||||||
|
|
||||||
|
79
pkg/machine/qemu/config_test.go
Normal file
79
pkg/machine/qemu/config_test.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package qemu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v4/pkg/machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUSBParsing(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
result []machine.USBConfig
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Good vendor and product",
|
||||||
|
args: []string{"vendor=13d3,product=5406", "vendor=08ec,product=0016"},
|
||||||
|
result: []machine.USBConfig{
|
||||||
|
{
|
||||||
|
Vendor: 5075,
|
||||||
|
Product: 21510,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vendor: 2284,
|
||||||
|
Product: 22,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Good bus and device number",
|
||||||
|
args: []string{"bus=1,devnum=4", "bus=1,devnum=3"},
|
||||||
|
result: []machine.USBConfig{
|
||||||
|
{
|
||||||
|
Bus: "1",
|
||||||
|
DevNumber: "4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Bus: "1",
|
||||||
|
DevNumber: "3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Bad vendor and product, not hexa",
|
||||||
|
args: []string{"vendor=13dk,product=5406"},
|
||||||
|
result: []machine.USBConfig{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Bad vendor and product, bad separator",
|
||||||
|
args: []string{"vendor=13d3:product=5406"},
|
||||||
|
result: []machine.USBConfig{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Bad vendor and product, missing equal",
|
||||||
|
args: []string{"vendor=13d3:product-5406"},
|
||||||
|
result: []machine.USBConfig{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
got, err := parseUSBs(test.args)
|
||||||
|
if (err != nil) != test.wantErr {
|
||||||
|
t.Errorf("parseUUBs error = %v, wantErr %v", err, test.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, test.result) {
|
||||||
|
t.Errorf("parseUUBs got %v, want %v", got, test.result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -401,6 +401,14 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.USBs != nil {
|
||||||
|
if usbConfigs, err := parseUSBs(*opts.USBs); err != nil {
|
||||||
|
setErrors = append(setErrors, fmt.Errorf("failed to set usb: %w", err))
|
||||||
|
} else {
|
||||||
|
v.USBs = usbConfigs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = v.writeConfig()
|
err = v.writeConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
setErrors = append(setErrors, err)
|
setErrors = append(setErrors, err)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package wsl
|
package wsl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -29,6 +30,9 @@ func VirtualizationProvider() machine.VirtProvider {
|
|||||||
// NewMachine initializes an instance of a wsl machine
|
// NewMachine initializes an instance of a wsl machine
|
||||||
func (p *WSLVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
func (p *WSLVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||||
vm := new(MachineVM)
|
vm := new(MachineVM)
|
||||||
|
if len(opts.USBs) > 0 {
|
||||||
|
return nil, fmt.Errorf("USB host passtrough not supported for WSL machines")
|
||||||
|
}
|
||||||
if len(opts.Name) > 0 {
|
if len(opts.Name) > 0 {
|
||||||
vm.Name = opts.Name
|
vm.Name = opts.Name
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1135,10 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
|
|||||||
|
|
||||||
if opts.Memory != nil {
|
if opts.Memory != nil {
|
||||||
setErrors = append(setErrors, errors.New("changing memory not supported for WSL machines"))
|
setErrors = append(setErrors, errors.New("changing memory not supported for WSL machines"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.USBs != nil {
|
||||||
|
setErrors = append(setErrors, errors.New("changing USBs not supported for WSL machines"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.DiskSize != nil {
|
if opts.DiskSize != nil {
|
||||||
|
Reference in New Issue
Block a user