Skip to content

Commit

Permalink
hostdev/usb: Functional testing
Browse files Browse the repository at this point in the history
This commit adds basic functional testing to USB host passthrough
using NodeConfig.

Thanks to KubeVirtCI kubevirt#996 [0], we can create emulated USB
storage devices in the node to use for functional test.

 > 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
xpivarc authored and victortoso committed Jun 30, 2023
1 parent c0977d8 commit 60cb84a
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 1 deletion.
1 change: 1 addition & 0 deletions tests/tests_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,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
8 changes: 7 additions & 1 deletion tests/testsuite/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,13 @@ func CleanNamespaces() {
for _, policy := range migrationPolicyList.Items {
util.PanicOnError(virtCli.MigrationPolicy().Delete(context.Background(), policy.Name, metav1.DeleteOptions{}))
}

// nodeConfigsList, err := virtCli.GeneratedKubeVirtClient().UsbV1alpha1().NodeConfigs().List(context.TODO(), listOptions)
// util.PanicOnError(err)
// for _, nodeConfig := range nodeConfigsList.Items {
util.PanicOnError(virtCli.GeneratedKubeVirtClient().UsbV1alpha1().
NodeConfigs().
DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{}))
// }
// Remove clones
clonesList, err := virtCli.VirtualMachineClone(namespace).List(context.Background(), metav1.ListOptions{})
util.PanicOnError(err)
Expand Down
141 changes: 141 additions & 0 deletions tests/usb/usb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package usb

import (
"context"
"fmt"
"strings"
"time"

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

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

v1 "kubevirt.io/api/core/v1"
"kubevirt.io/api/usb/v1alpha1"
usbv1alpha1 "kubevirt.io/client-go/generated/kubevirt/clientset/versioned/typed/usb/v1alpha1"
"kubevirt.io/client-go/kubecli"

"kubevirt.io/kubevirt/tests"
"kubevirt.io/kubevirt/tests/console"
"kubevirt.io/kubevirt/tests/decorators"
"kubevirt.io/kubevirt/tests/framework/cleanup"
"kubevirt.io/kubevirt/tests/framework/kubevirt"
"kubevirt.io/kubevirt/tests/libvmi"
"kubevirt.io/kubevirt/tests/util"
)

var _ = Describe("[sig-compute-realtime][Serial]USB", Serial, decorators.SigCompute, func() {
var (
virtClient kubecli.KubevirtClient
usbClient usbv1alpha1.UsbV1alpha1Interface
)

BeforeEach(func() {
virtClient = kubevirt.Client()
usbClient = virtClient.GeneratedKubeVirtClient().UsbV1alpha1()
})

Context("valid nodeconfig", func() {

const resourceName = "kubevirt.io/usb-storage"

createTestNodeConfig := func() *v1alpha1.NodeConfig {
nodeConfig := &v1alpha1.NodeConfig{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test",
Labels: map[string]string{
cleanup.TestLabelForNamespace(util.NamespaceTestDefault): "true",
},
},
Spec: v1alpha1.NodeConfigSpec{
// Validate there is at least one usb
USB: v1alpha1.USB{
ResourceName: resourceName,
USBHostDevices: []v1alpha1.USBHostDevices{
{
// Todo we could do VendorProductSelector and validate it
SelectByVendorProduct: "46f4:0001",
},
},
},
},
}
nodeConfig, err := usbClient.NodeConfigs().Create(context.TODO(), nodeConfig, metav1.CreateOptions{})
Expect(err).ToNot(HaveOccurred())
return nodeConfig
}

It("sanity check", func() {
nodeConfig := createTestNodeConfig()

expectResourceToAppear(virtClient, resourceName)

By("Deleting node config")
err := usbClient.NodeConfigs().Delete(context.TODO(), nodeConfig.Name, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())

expectResourceToDisappear(virtClient, resourceName)
})

It("able to consume with VM", func() {
_ = tests.EnableFeatureGate("HostDevices")

_ = createTestNodeConfig()

expectResourceToAppear(virtClient, resourceName)

vmi := libvmi.NewCirros(withUsb(resourceName))
vmi = tests.RunVMIAndExpectLaunch(vmi, 90)

By("Check there is usb device available inside guest")
Expect(console.LoginToCirros(vmi)).To(Succeed())
Expect(console.SafeExpectBatch(vmi, []expect.Batcher{
&expect.BSnd{S: "dmesg | grep -i vendor=46f4 | wc -l\n"},
&expect.BExp{R: "1"},
}, 200)).To(Succeed())
})

})
})

func expectResourceToAppear(virtClient kubecli.KubevirtClient, resourceName string) {
By("Expecting to find usb devices")
retriveResourceAssertion(virtClient, resourceName).Should(BeNumerically(">", 0), fmt.Sprintf("Failed to find %s on any node", resourceName))
}

func expectResourceToDisappear(virtClient kubecli.KubevirtClient, resourceName string) {
By("Expecting to not find usb devices")
retriveResourceAssertion(virtClient, resourceName).Should(BeNumerically("==", 0), fmt.Sprintf("Failed to remove %s on any node", resourceName))
}

func retriveResourceAssertion(virtClient kubecli.KubevirtClient, resourceName string) types.AsyncAssertion {
return Eventually(func() int64 {
nodes, err := virtClient.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
Expect(err).ToNot(HaveOccurred())

for _, node := range nodes.Items {
for key, amount := range node.Status.Capacity {
if strings.HasPrefix(string(key), resourceName) {
value, ok := amount.AsInt64()
Expect(ok).To(BeTrue())
return value
}
}
}
return 0
}, 90*time.Second, time.Second)
}

func withUsb(resourceName string) libvmi.Option {
return func(vmi *v1.VirtualMachineInstance) {
vmi.Spec.Domain.Devices.HostDevices = append(vmi.Spec.Domain.Devices.HostDevices,
v1.HostDevice{
Name: "usb-storage",
DeviceName: resourceName,
},
)
}
}

0 comments on commit 60cb84a

Please sign in to comment.