forked from kubevirt/kubevirt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
1 parent
9dc3e46
commit 2ae2746
Showing
3 changed files
with
149 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}, | ||
) | ||
} | ||
} |