Skip to content

Commit

Permalink
hostdev: usb: Add functional testing
Browse files Browse the repository at this point in the history
The test takes inspiration on PCI's tests/vmi_hostdev_test.go

Since the introduction of emulated USB devices [0], this can be tested
locally with:

 > export KUBEVIRT_PROVIDER=k8s-1.26-centos9
 > export KUBEVIRTCI_TAG=latest
 > export KUBEVIRT_PROVIDER_EXTRA_ARGS="--usb 20M --usb 40M"
 > make cluster-up

[0] kubevirt/kubevirtci#996

Signed-off-by: Luboslav Pivarc <[email protected]>
Signed-off-by: Victor Toso <[email protected]>
  • Loading branch information
victortoso committed Sep 16, 2023
1 parent b2c197e commit bbde409
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 1 deletion.
2 changes: 1 addition & 1 deletion pkg/virt-handler/device-manager/usb_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ func parseSysUeventFile(path string) *USBDevice {
}
u.BCD = int(val)
case "DEVNAME":
u.DevicePath = "/dev/" + values[1]
u.DevicePath = filepath.Join("/dev", values[1])
default:
log.Log.V(5).Infof("Skipping unhandled line: %s", line)
}
Expand Down
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ go_test(
"//tests/scale:go_default_library",
"//tests/storage:go_default_library",
"//tests/testsuite:go_default_library",
"//tests/usb:go_default_library",
"//tests/util:go_default_library",
"//tests/virtctl:go_default_library",
"//tests/virtiofs:go_default_library",
Expand Down
1 change: 1 addition & 0 deletions tests/decorators/decorators.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ var (
RequiresTwoSchedulableNodes = []interface{}{Label("requires-two-schedulable-nodes")}
VMLiveUpdateFeaturesGate = []interface{}{Label("VMLiveUpdateFeaturesGate")}
RequiresRWXFilesystemStorage = []interface{}{Label("rwxfs")}
USB = []interface{}{Label("USB")}
)
1 change: 1 addition & 0 deletions tests/tests_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import (
_ "kubevirt.io/kubevirt/tests/realtime"
_ "kubevirt.io/kubevirt/tests/scale"
_ "kubevirt.io/kubevirt/tests/storage"
_ "kubevirt.io/kubevirt/tests/usb"
_ "kubevirt.io/kubevirt/tests/virtctl"
_ "kubevirt.io/kubevirt/tests/virtiofs"
)
Expand Down
24 changes: 24 additions & 0 deletions tests/usb/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["usb.go"],
importpath = "kubevirt.io/kubevirt/tests/usb",
visibility = ["//visibility:public"],
deps = [
"//pkg/virt-config:go_default_library",
"//staging/src/kubevirt.io/api/core/v1:go_default_library",
"//staging/src/kubevirt.io/client-go/kubecli:go_default_library",
"//tests:go_default_library",
"//tests/console:go_default_library",
"//tests/decorators:go_default_library",
"//tests/framework/kubevirt:go_default_library",
"//tests/libvmi:go_default_library",
"//tests/libwait:go_default_library",
"//tests/util:go_default_library",
"//vendor/github.com/google/goexpect:go_default_library",
"//vendor/github.com/onsi/ginkgo/v2:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
],
)
117 changes: 117 additions & 0 deletions tests/usb/usb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package usb

import (
"context"
"fmt"
"strings"

expect "github.com/google/goexpect"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

v1 "kubevirt.io/api/core/v1"
"kubevirt.io/client-go/kubecli"

virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
"kubevirt.io/kubevirt/tests"
"kubevirt.io/kubevirt/tests/console"
"kubevirt.io/kubevirt/tests/decorators"
"kubevirt.io/kubevirt/tests/framework/kubevirt"
"kubevirt.io/kubevirt/tests/libvmi"
"kubevirt.io/kubevirt/tests/libwait"
"kubevirt.io/kubevirt/tests/util"
)

const (
failedDeleteVMI = "Failed to delete VMI"
cmdNumberUSBs = "dmesg | grep -c idVendor=46f4"
)

var _ = Describe("[Serial][sig-compute][USB] host USB Passthrough", Serial, decorators.SigCompute, decorators.USB, func() {
var (
virtClient kubecli.KubevirtClient
config v1.KubeVirtConfiguration
)

BeforeEach(func() {
virtClient = kubevirt.Client()
kv := util.GetCurrentKv(virtClient)
config = kv.Spec.Configuration

nodeName := tests.NodeNameWithHandler()
Expect(nodeName).ToNot(BeEmpty())

// Emulated USB devices only on c9s providers. Remove this when sig-compute 1.26 is the
// oldest sig-compute with test with.
// See: https://github.com/kubevirt/project-infra/pull/2922
stdout, err := tests.ExecuteCommandInVirtHandlerPod(nodeName, []string{"dmesg"})
Expect(err).ToNot(HaveOccurred())
if strings.Count(stdout, "idVendor=46f4") == 0 {
Skip("No emulated USB devices present for functional test.")
}
})

AfterEach(func() {
kv := util.GetCurrentKv(virtClient)
// Reinitialized the DeveloperConfiguration to avoid to influence the next test
config = kv.Spec.Configuration
config.DeveloperConfiguration = &v1.DeveloperConfiguration{}
config.PermittedHostDevices = &v1.PermittedHostDevices{}
tests.UpdateKubeVirtConfigValueAndWait(config)
})

Context("with usb storage", func() {
DescribeTable("with emulated USB devices", func(deviceNames []string) {
const resourceName = "kubevirt.io/usb-storage"

By("Adding the emulated USB device to the permitted host devices")
config.DeveloperConfiguration = &v1.DeveloperConfiguration{
FeatureGates: []string{virtconfig.HostDevicesGate},
}
config.PermittedHostDevices = &v1.PermittedHostDevices{
USB: []v1.USBHostDevice{
{
ResourceName: resourceName,
Selectors: []v1.USBSelector{
{
Vendor: "46f4",
Product: "0001",
}},
}},
}
tests.UpdateKubeVirtConfigValueAndWait(config)

By("Creating a Fedora VMI with the usb host device")
hostDevs := []v1.HostDevice{}
for i, name := range deviceNames {
hostDevs = append(hostDevs, v1.HostDevice{
Name: fmt.Sprintf("usb-%d-%s", i, name),
DeviceName: resourceName,
})
}

randomVMI := libvmi.NewCirros()
randomVMI.Spec.Domain.Devices.HostDevices = hostDevs
vmi, err := virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(context.Background(), randomVMI)
Expect(err).ToNot(HaveOccurred())
libwait.WaitForSuccessfulVMIStart(vmi)
Expect(console.LoginToCirros(vmi)).To(Succeed())

By("Making sure the usb is present inside the VMI")
Expect(console.SafeExpectBatch(vmi, []expect.Batcher{
&expect.BSnd{S: fmt.Sprintf("%s\n", cmdNumberUSBs)},
&expect.BExp{R: console.RetValue(fmt.Sprintf("%d", len(deviceNames)))},
}, 15)).To(Succeed(), "Device not found")

// Make sure to delete the VMI before ending the test otherwise a device could still be taken
err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Delete(context.Background(), vmi.ObjectMeta.Name, &metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred(), failedDeleteVMI)
libwait.WaitForVirtualMachineToDisappearWithTimeout(vmi, 180)
},
Entry("Should successfully passthrough 1 emulated USB device", []string{"slow-storage"}),
Entry("Should successfully passthrough 2 emulated USB devices", []string{"fast-storage", "low-storage"}),
)
})
})

0 comments on commit bbde409

Please sign in to comment.