diff --git a/api/v1/sriovnetworknodepolicy_types.go b/api/v1/sriovnetworknodepolicy_types.go index f933c0ec3..64b380d9f 100644 --- a/api/v1/sriovnetworknodepolicy_types.go +++ b/api/v1/sriovnetworknodepolicy_types.go @@ -46,6 +46,8 @@ type SriovNetworkNodePolicySpec struct { DeviceType string `json:"deviceType,omitempty"` // RDMA mode. Defaults to false. IsRdma bool `json:"isRdma,omitempty"` + // mount vhost-net device. Defaults to false. + NeedVhostNet bool `json:"needVhostNet,omitempty"` // +kubebuilder:validation:Enum=eth;ETH;ib;IB // NIC Link Type. Allowed value "eth", "ETH", "ib", and "IB". LinkType string `json:"linkType,omitempty"` diff --git a/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml b/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml index 351253b1c..f9b7ecfdf 100644 --- a/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml +++ b/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml @@ -66,6 +66,9 @@ spec: description: MTU of VF minimum: 1 type: integer + needVhostNet: + description: mount vhost-net device. Defaults to false. + type: boolean nicSelector: description: NicSelector selects the NICs to be configured properties: diff --git a/controllers/sriovnetworknodepolicy_controller.go b/controllers/sriovnetworknodepolicy_controller.go index 3a318938e..0bc4cead7 100644 --- a/controllers/sriovnetworknodepolicy_controller.go +++ b/controllers/sriovnetworknodepolicy_controller.go @@ -654,6 +654,7 @@ func (r *SriovNetworkNodePolicyReconciler) renderDevicePluginConfigData(pl *srio ResourceName: p.Spec.ResourceName, } netDeviceSelectors.IsRdma = p.Spec.IsRdma + netDeviceSelectors.NeedVhostNet = p.Spec.NeedVhostNet if p.Spec.NicSelector.Vendor != "" { netDeviceSelectors.Vendors = append(netDeviceSelectors.Vendors, p.Spec.NicSelector.Vendor) diff --git a/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml b/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml index 351253b1c..f9b7ecfdf 100644 --- a/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml +++ b/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodepolicies.yaml @@ -66,6 +66,9 @@ spec: description: MTU of VF minimum: 1 type: integer + needVhostNet: + description: mount vhost-net device. Defaults to false. + type: boolean nicSelector: description: NicSelector selects the NICs to be configured properties: diff --git a/test/conformance/tests/sriov_operator.go b/test/conformance/tests/sriov_operator.go index b723581c5..81d303bf5 100644 --- a/test/conformance/tests/sriov_operator.go +++ b/test/conformance/tests/sriov_operator.go @@ -1572,6 +1572,126 @@ var _ = Describe("[sriov] operator", func() { }) + Context("vhost-net device Validation", func() { + var node string + resourceName := "vhostresource" + vhostnetwork := "test-vhostnetwork" + numVfs := 5 + var intf *sriovv1.InterfaceExt + var err error + + execute.BeforeAll(func() { + if discovery.Enabled() { + node, resourceName, numVfs, intf, err = discovery.DiscoveredResources(clients, + sriovInfos, operatorNamespace, + func(policy sriovv1.SriovNetworkNodePolicy) bool { + if !defaultFilterPolicy(policy) { + return false + } + if !policy.Spec.NeedVhostNet { + return false + } + return true + }, + func(node string, sriovDeviceList []*sriovv1.InterfaceExt) (*sriovv1.InterfaceExt, bool) { + if len(sriovDeviceList) == 0 { + return nil, false + } + return sriovDeviceList[0], true + }, + ) + Expect(err).ToNot(HaveOccurred()) + if node == "" || resourceName == "" || numVfs < 5 || intf == nil { + Skip("Insufficient resources to run test in discovery mode") + } + } else { + node = sriovInfos.Nodes[0] + sriovDeviceList, err := sriovInfos.FindSriovDevices(node) + Expect(err).ToNot(HaveOccurred()) + unusedSriovDevices, err := findUnusedSriovDevices(node, sriovDeviceList) + if err != nil { + Skip(err.Error()) + } + intf = unusedSriovDevices[0] + + mtuPolicy := &sriovv1.SriovNetworkNodePolicy{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-vhostpolicy", + Namespace: operatorNamespace, + }, + + Spec: sriovv1.SriovNetworkNodePolicySpec{ + NodeSelector: map[string]string{ + "kubernetes.io/hostname": node, + }, + NumVfs: 5, + ResourceName: resourceName, + Priority: 99, + NicSelector: sriovv1.SriovNetworkNicSelector{ + PfNames: []string{intf.Name}, + }, + DeviceType: "netdevice", + NeedVhostNet: true, + }, + } + + err = clients.Create(context.Background(), mtuPolicy) + Expect(err).ToNot(HaveOccurred()) + + WaitForSRIOVStable() + By("waiting for the resources to be available") + Eventually(func() int64 { + testedNode, err := clients.Nodes().Get(context.Background(), node, metav1.GetOptions{}) + Expect(err).ToNot(HaveOccurred()) + resNum, _ := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)] + allocatable, _ := resNum.AsInt64() + return allocatable + }, 10*time.Minute, time.Second).Should(Equal(int64(5))) + } + + sriovNetwork := &sriovv1.SriovNetwork{ + ObjectMeta: metav1.ObjectMeta{ + Name: vhostnetwork, + Namespace: operatorNamespace, + }, + Spec: sriovv1.SriovNetworkSpec{ + ResourceName: resourceName, + IPAM: `{"type":"host-local","subnet":"10.10.10.0/24","rangeStart":"10.10.10.171","rangeEnd":"10.10.10.181","routes":[{"dst":"0.0.0.0/0"}],"gateway":"10.10.10.1"}`, + NetworkNamespace: namespaces.Test, + LinkState: "enable", + }} + + // We need this to be able to run the connectivity checks on Mellanox cards + if intf.DeviceID == "1015" { + sriovNetwork.Spec.SpoofChk = "off" + } + + err = clients.Create(context.Background(), sriovNetwork) + + Expect(err).ToNot(HaveOccurred()) + + Eventually(func() error { + netAttDef := &netattdefv1.NetworkAttachmentDefinition{} + return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: vhostnetwork, Namespace: namespaces.Test}, netAttDef) + }, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) + + }) + + It("Should have the vhost-net device inside the container", func() { + By("creating a pod") + podObj := createTestPod(node, []string{vhostnetwork}) + ips, err := network.GetSriovNicIPs(podObj, "net1") + Expect(err).ToNot(HaveOccurred()) + Expect(ips).NotTo(BeNil(), "No sriov network interface found.") + Expect(len(ips)).Should(Equal(1)) + + By("check the /dev/vhost device exist inside the container") + output, errOutput, err := pod.ExecCommand(clients, podObj, "ls", "/dev/vhost-net") + Expect(err).ToNot(HaveOccurred()) + Expect(errOutput).To(Equal("")) + Expect(output).ToNot(ContainSubstring("cannot access")) + }) + }) }) })