Skip to content

Commit

Permalink
usbredir: make configurable redirecting usb devices
Browse files Browse the repository at this point in the history
User can configure how many USB redirection devices to have in the
VMI by adding an empty clientPassthrough in the yaml, like:

 | spec:
 |   domain:
 |     devices:
 |       clientPassthrough: {}

KubeVirt will create the maximum value defined by
UsbClientPassthroughMaxNumberOf

Signed-off-by: Victor Toso <[email protected]>
  • Loading branch information
victortoso committed Jul 15, 2021
1 parent 43371b4 commit eca18ea
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 1 deletion.
14 changes: 14 additions & 0 deletions pkg/virt-launcher/virtwrap/api/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,20 @@ type Devices struct {
Watchdog *Watchdog `xml:"watchdog,omitempty"`
Rng *Rng `xml:"rng,omitempty"`
Filesystems []FilesystemDevice `xml:"filesystem,omitempty"`
Redirs []RedirectedDevice `xml:"redirdev,omitempty"`
}

// RedirectedDevice describes a device to be redirected
// See: https://libvirt.org/formatdomain.html#redirected-devices
type RedirectedDevice struct {
Type string `xml:"type,attr"`
Bus string `xml:"bus,attr"`
Source RedirectedDeviceSource `xml:"source"`
}

type RedirectedDeviceSource struct {
Mode string `xml:"mode,attr"`
Path string `xml:"path,attr"`
}

type FilesystemDevice struct {
Expand Down
34 changes: 33 additions & 1 deletion pkg/virt-launcher/virtwrap/converter/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,33 @@ func Convert_v1_Rng_To_api_Rng(_ *v1.Rng, rng *api.Rng, c *ConverterContext) err
return nil
}

func Convert_v1_Usbredir_To_api_Usbredir(vmi *v1.VirtualMachineInstance, domainDevices *api.Devices, _ *ConverterContext) (bool, error) {
clientDevices := vmi.Spec.Domain.Devices.ClientPassthrough

// Default is to have USB Redirection disabled
if clientDevices == nil {
return false, nil
}

// Note that at the moment, we don't require any specific input to configure the USB devices
// so we simply create the maximum allowed dictated by v1.UsbClientPassthroughMaxNumberOf
redirectDevices := make([]api.RedirectedDevice, v1.UsbClientPassthroughMaxNumberOf)

for i := 0; i < v1.UsbClientPassthroughMaxNumberOf; i++ {
path := fmt.Sprintf("/var/run/kubevirt-private/%s/virt-usbredir-%d", vmi.ObjectMeta.UID, i)
redirectDevices[i] = api.RedirectedDevice{
Type: "unix",
Bus: "usb",
Source: api.RedirectedDeviceSource{
Mode: "bind",
Path: path,
},
}
}
domainDevices.Redirs = redirectDevices
return true, nil
}

func Convert_v1_Input_To_api_InputDevice(input *v1.Input, inputDevice *api.Input) error {
if input.Bus != "virtio" && input.Bus != "usb" && input.Bus != "" {
return fmt.Errorf("input contains unsupported bus %s", input.Bus)
Expand Down Expand Up @@ -1507,6 +1534,11 @@ func Convert_v1_VirtualMachineInstance_To_api_Domain(vmi *v1.VirtualMachineInsta
domain.Spec.Devices.Inputs = inputDevices
}

isUSBRedirEnabled, err := Convert_v1_Usbredir_To_api_Usbredir(vmi, &domain.Spec.Devices, c)
if err != nil {
return err
}

domain.Spec.Devices.Ballooning = &api.MemBalloon{}
ConvertV1ToAPIBalloning(&vmi.Spec.Domain.Devices, domain.Spec.Devices.Ballooning, c)

Expand All @@ -1515,7 +1547,7 @@ func Convert_v1_VirtualMachineInstance_To_api_Domain(vmi *v1.VirtualMachineInsta
//In ppc64le usb devices like mouse / keyboard are set by default,
//so we can't disable the controller otherwise we run into the following error:
//"unsupported configuration: USB is disabled for this domain, but USB devices are present in the domain XML"
if !isUSBDevicePresent && c.Architecture != "ppc64le" {
if !isUSBDevicePresent && !isUSBRedirEnabled && c.Architecture != "ppc64le" {
// disable usb controller
domain.Spec.Devices.Controllers = append(domain.Spec.Devices.Controllers, api.Controller{
Type: "usb",
Expand Down
24 changes: 24 additions & 0 deletions pkg/virt-launcher/virtwrap/converter/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,30 @@ var _ = Describe("Converter", func() {
Expect(domain.Spec.Devices.Inputs[0].Bus).To(Equal("usb"), "Expect usb bus")
})

It("should enable usb redirection when number of USB client devices > 0", func() {
v1.SetObjectDefaults_VirtualMachineInstance(vmi)
vmi.Spec.Domain.Devices.ClientPassthrough = &v1.ClientPassthroughDevices{}
domain := vmiToDomain(vmi, c)
Expect(len(domain.Spec.Devices.Redirs)).To(Equal(4))
Expect(domain.Spec.Devices.Controllers).To(ContainElement(api.Controller{
Type: "usb",
Index: "0",
Model: "qemu-xhci",
}))
})

It("should not enable usb redirection when numberOfDevices == 0", func() {
v1.SetObjectDefaults_VirtualMachineInstance(vmi)
vmi.Spec.Domain.Devices.ClientPassthrough = nil
domain := vmiToDomain(vmi, c)
Expect(domain.Spec.Devices.Redirs).To(BeNil())
Expect(domain.Spec.Devices.Controllers).ToNot(ContainElement(api.Controller{
Type: "usb",
Index: "0",
Model: "qemu-xhci",
}))
})

It("should select explicitly chosen network model", func() {
v1.SetObjectDefaults_VirtualMachineInstance(vmi)
vmi.Spec.Domain.Devices.Interfaces[0].Model = "e1000"
Expand Down
20 changes: 20 additions & 0 deletions staging/src/kubevirt.io/client-go/api/v1/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,28 @@ type Devices struct {
// +optional
// +listType=atomic
HostDevices []HostDevice `json:"hostDevices,omitempty"`
// To configure and access client devices such as redirecting USB
// +optional
ClientPassthrough *ClientPassthroughDevices `json:"clientPassthrough,omitempty"`
}

// Represent a subset of client devices that can be accessed by VMI. At the
// moment only, USB devices using Usbredir's library and tooling. Another fit
// would be a smartcard with libcacard.
//
// The struct is currently empty as there is no imediate request for
// user-facing APIs. This structure simply turns on USB redirection of
// UsbClientPassthroughMaxNumberOf devices.
//
// +k8s:openapi-gen=true
type ClientPassthroughDevices struct {
}

// Represents the upper limit allowed by QEMU + KubeVirt.
const (
UsbClientPassthroughMaxNumberOf = 4
)

//
// +k8s:openapi-gen=true
type Input struct {
Expand Down

0 comments on commit eca18ea

Please sign in to comment.