github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/test/conformance/tests/test_sriov_operator.go (about)

     1  package tests
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	. "github.com/onsi/ginkgo/v2"
    14  	. "github.com/onsi/gomega"
    15  	. "github.com/onsi/gomega/gstruct"
    16  
    17  	netattdefv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
    18  	admission "k8s.io/api/admissionregistration/v1"
    19  	appsv1 "k8s.io/api/apps/v1"
    20  	corev1 "k8s.io/api/core/v1"
    21  	rbacv1 "k8s.io/api/rbac/v1"
    22  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/fields"
    25  	"k8s.io/apimachinery/pkg/labels"
    26  	"k8s.io/apimachinery/pkg/types"
    27  	"k8s.io/utils/pointer"
    28  	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
    29  
    30  	sriovv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
    31  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/clean"
    32  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/cluster"
    33  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/discovery"
    34  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/execute"
    35  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/namespaces"
    36  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/network"
    37  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/nodes"
    38  	"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/pod"
    39  )
    40  
    41  var waitingTime = 20 * time.Minute
    42  var sriovNetworkName = "test-sriovnetwork"
    43  var snoTimeoutMultiplier time.Duration = 0
    44  
    45  const (
    46  	operatorNetworkInjectorFlag = "network-resources-injector"
    47  	operatorWebhookFlag         = "operator-webhook"
    48  	on                          = "on"
    49  	off                         = "off"
    50  	testResourceName            = "testresource"
    51  	testIpv6NetworkName         = "test-ipv6network"
    52  	volumePodNetInfo            = "podnetinfo"
    53  	ipamIpv6                    = `{"type": "host-local","ranges": [[{"subnet": "3ffe:ffff:0:01ff::/64"}]],"dataDir": "/run/my-orchestrator/container-ipam-state"}`
    54  	ipamIpv4                    = `{"type": "host-local","ranges": [[{"subnet": "1.1.1.0/24"}]],"dataDir": "/run/my-orchestrator/container-ipam-state"}`
    55  )
    56  
    57  type patchBody struct {
    58  	Op    string `json:"op"`
    59  	Path  string `json:"path"`
    60  	Value string `json:"value"`
    61  }
    62  
    63  func init() {
    64  	waitingEnv := os.Getenv("SRIOV_WAITING_TIME")
    65  	newTime, err := strconv.Atoi(waitingEnv)
    66  	if err == nil && newTime != 0 {
    67  		waitingTime = time.Duration(newTime) * time.Minute
    68  	}
    69  }
    70  
    71  var _ = Describe("[sriov] operator", func() {
    72  	var sriovInfos *cluster.EnabledNodes
    73  	var initPassed bool
    74  	execute.BeforeAll(func() {
    75  		isSingleNode, err := cluster.IsSingleNode(clients)
    76  		Expect(err).ToNot(HaveOccurred())
    77  		if isSingleNode {
    78  			disableDrainState, err := cluster.GetNodeDrainState(clients, operatorNamespace)
    79  			Expect(err).ToNot(HaveOccurred())
    80  			if discovery.Enabled() {
    81  				if !disableDrainState {
    82  					Skip("SriovOperatorConfig DisableDrain property must be enabled in a single node environment")
    83  				}
    84  			}
    85  			snoTimeoutMultiplier = 1
    86  			if !disableDrainState {
    87  				err = cluster.SetDisableNodeDrainState(clients, operatorNamespace, true)
    88  				Expect(err).ToNot(HaveOccurred())
    89  				clean.RestoreNodeDrainState = true
    90  			}
    91  		}
    92  
    93  		Expect(clients).NotTo(BeNil(), "Client misconfigured, check the $KUBECONFIG env variable")
    94  		err = namespaces.Create(namespaces.Test, clients)
    95  		Expect(err).ToNot(HaveOccurred())
    96  		err = namespaces.Clean(operatorNamespace, namespaces.Test, clients, discovery.Enabled())
    97  		Expect(err).ToNot(HaveOccurred())
    98  		WaitForSRIOVStable()
    99  		sriovInfos, err = cluster.DiscoverSriov(clients, operatorNamespace)
   100  		Expect(err).ToNot(HaveOccurred())
   101  		initPassed = true
   102  	})
   103  
   104  	BeforeEach(func() {
   105  		Expect(initPassed).To(BeTrue(), "Global setup failed")
   106  	})
   107  
   108  	Describe("No SriovNetworkNodePolicy", func() {
   109  		Context("SR-IOV network config daemon can be set by nodeselector", func() {
   110  			// 26186
   111  			It("Should schedule the config daemon on selected nodes", func() {
   112  				if discovery.Enabled() {
   113  					Skip("Test unsuitable to be run in discovery mode")
   114  				}
   115  
   116  				testStartingTime := time.Now()
   117  
   118  				By("Checking that a daemon is scheduled on each worker node")
   119  				Eventually(func() bool {
   120  					return daemonsScheduledOnNodes("node-role.kubernetes.io/worker=")
   121  				}, 3*time.Minute, 1*time.Second).Should(Equal(true))
   122  
   123  				By("Labeling one worker node with the label needed for the daemon")
   124  				allNodes, err := clients.CoreV1Interface.Nodes().List(context.Background(), metav1.ListOptions{
   125  					LabelSelector: "node-role.kubernetes.io/worker",
   126  				})
   127  				Expect(err).ToNot(HaveOccurred())
   128  
   129  				selectedNodes, err := nodes.MatchingOptionalSelector(clients, allNodes.Items)
   130  				Expect(err).ToNot(HaveOccurred())
   131  
   132  				Expect(len(selectedNodes)).To(BeNumerically(">", 0), "There must be at least one worker")
   133  				candidate := selectedNodes[0]
   134  				candidate.Labels["sriovenabled"] = "true"
   135  				_, err = clients.CoreV1Interface.Nodes().Update(context.Background(), &candidate, metav1.UpdateOptions{})
   136  				Expect(err).ToNot(HaveOccurred())
   137  
   138  				By("Setting the node selector for each daemon")
   139  				cfg := sriovv1.SriovOperatorConfig{}
   140  				err = clients.Get(context.TODO(), runtimeclient.ObjectKey{
   141  					Name:      "default",
   142  					Namespace: operatorNamespace,
   143  				}, &cfg)
   144  				Expect(err).ToNot(HaveOccurred())
   145  				cfg.Spec.ConfigDaemonNodeSelector = map[string]string{
   146  					"sriovenabled": "true",
   147  				}
   148  				Eventually(func() error {
   149  					return clients.Update(context.TODO(), &cfg)
   150  				}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
   151  
   152  				By("Checking that a daemon is scheduled only on selected node")
   153  				Eventually(func() bool {
   154  					return !daemonsScheduledOnNodes("sriovenabled!=true") &&
   155  						daemonsScheduledOnNodes("sriovenabled=true")
   156  				}, 1*time.Minute, 1*time.Second).Should(Equal(true))
   157  
   158  				By("Restoring the node selector for daemons")
   159  				err = clients.Get(context.TODO(), runtimeclient.ObjectKey{
   160  					Name:      "default",
   161  					Namespace: operatorNamespace,
   162  				}, &cfg)
   163  				Expect(err).ToNot(HaveOccurred())
   164  				cfg.Spec.ConfigDaemonNodeSelector = map[string]string{}
   165  				Eventually(func() error {
   166  					return clients.Update(context.TODO(), &cfg)
   167  				}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
   168  
   169  				By("Checking that a daemon is scheduled on each worker node")
   170  				Eventually(func() bool {
   171  					return daemonsScheduledOnNodes("node-role.kubernetes.io/worker")
   172  				}, 1*time.Minute, 1*time.Second).Should(Equal(true))
   173  
   174  				By("Checking that a daemon is able to publish events")
   175  				Eventually(func() bool {
   176  					events, err := clients.Events(operatorNamespace).List(
   177  						context.Background(), metav1.ListOptions{TypeMeta: sriovv1.SriovNetworkNodeState{}.TypeMeta})
   178  					Expect(err).ToNot(HaveOccurred())
   179  
   180  					for _, e := range events.Items {
   181  						if e.Reason == "ConfigDaemonStart" &&
   182  							e.CreationTimestamp.Time.After(testStartingTime) {
   183  							return true
   184  						}
   185  					}
   186  					return false
   187  				}, 30*time.Second, 5*time.Second).Should(BeTrue(), "Config Daemon should record an event when starting")
   188  			})
   189  		})
   190  
   191  		Context("LogLevel affects operator's logs", func() {
   192  			It("when set to 0 no lifecycle logs are present", func() {
   193  				if discovery.Enabled() {
   194  					Skip("Test unsuitable to be run in discovery mode")
   195  				}
   196  
   197  				initialLogLevelValue := getOperatorConfigLogLevel()
   198  				DeferCleanup(func() {
   199  					By("Restore LogLevel to its initial value")
   200  					setOperatorConfigLogLevel(initialLogLevelValue)
   201  				})
   202  
   203  				initialDisableDrain, err := cluster.GetNodeDrainState(clients, operatorNamespace)
   204  				Expect(err).ToNot(HaveOccurred())
   205  
   206  				DeferCleanup(func() {
   207  					By("Restore DisableDrain to its initial value")
   208  					Eventually(func() error {
   209  						return cluster.SetDisableNodeDrainState(clients, operatorNamespace, initialDisableDrain)
   210  					}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
   211  				})
   212  
   213  				By("Set operator LogLevel to 2")
   214  				setOperatorConfigLogLevel(2)
   215  
   216  				By("Flip DisableDrain to trigger operator activity")
   217  				since := time.Now().Add(-10 * time.Second)
   218  				Eventually(func() error {
   219  					return cluster.SetDisableNodeDrainState(clients, operatorNamespace, !initialDisableDrain)
   220  				}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
   221  
   222  				By("Assert logs contains verbose output")
   223  				Eventually(func(g Gomega) {
   224  					logs := getOperatorLogs(since)
   225  					g.Expect(logs).To(
   226  						ContainElement(And(
   227  							ContainSubstring("Reconciling SriovOperatorConfig"),
   228  						)),
   229  					)
   230  
   231  					// Should contain verbose logging
   232  					g.Expect(logs).To(
   233  						ContainElement(
   234  							ContainSubstring("Start to sync webhook objects"),
   235  						),
   236  					)
   237  				}, 1*time.Minute, 5*time.Second).Should(Succeed())
   238  
   239  				By("Reduce operator LogLevel to 0")
   240  				setOperatorConfigLogLevel(0)
   241  
   242  				By("Flip DisableDrain again to trigger operator activity")
   243  				since = time.Now().Add(-10 * time.Second)
   244  				Eventually(func() error {
   245  					return cluster.SetDisableNodeDrainState(clients, operatorNamespace, initialDisableDrain)
   246  				}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
   247  
   248  				By("Assert logs contains less operator activity")
   249  				Eventually(func(g Gomega) {
   250  					logs := getOperatorLogs(since)
   251  
   252  					// time only contains sec, but we can have race here that in the same sec there was a sync
   253  					afterLogs := []string{}
   254  					found := false
   255  					for _, log := range logs {
   256  						if found {
   257  							afterLogs = append(afterLogs, log)
   258  						}
   259  						if strings.Contains(log, "{\"new-level\": 0, \"current-level\": 2}") {
   260  							found = true
   261  						}
   262  					}
   263  					g.Expect(found).To(BeTrue())
   264  					g.Expect(afterLogs).To(
   265  						ContainElement(And(
   266  							ContainSubstring("Reconciling SriovOperatorConfig"),
   267  						)),
   268  					)
   269  
   270  					// Should not contain verbose logging
   271  					g.Expect(afterLogs).ToNot(
   272  						ContainElement(
   273  							ContainSubstring("Start to sync webhook objects"),
   274  						),
   275  					)
   276  				}, 3*time.Minute, 5*time.Second).Should(Succeed())
   277  			})
   278  		})
   279  	})
   280  
   281  	Describe("Generic SriovNetworkNodePolicy", func() {
   282  		numVfs := 5
   283  		resourceName := testResourceName
   284  		var node string
   285  		var sriovDevice *sriovv1.InterfaceExt
   286  		var discoveryFailed bool
   287  
   288  		execute.BeforeAll(func() {
   289  			var err error
   290  
   291  			err = namespaces.Clean(operatorNamespace, namespaces.Test, clients, discovery.Enabled())
   292  			Expect(err).ToNot(HaveOccurred())
   293  			if discovery.Enabled() {
   294  				node, resourceName, numVfs, sriovDevice, err = discovery.DiscoveredResources(clients,
   295  					sriovInfos, operatorNamespace, defaultFilterPolicy,
   296  					func(node string, sriovDeviceList []*sriovv1.InterfaceExt) (*sriovv1.InterfaceExt, bool) {
   297  						if len(sriovDeviceList) == 0 {
   298  							return nil, false
   299  						}
   300  						return sriovDeviceList[0], true
   301  					},
   302  				)
   303  
   304  				Expect(err).ToNot(HaveOccurred())
   305  				discoveryFailed = node == "" || resourceName == "" || numVfs < 5
   306  			} else {
   307  				node = sriovInfos.Nodes[0]
   308  				createVanillaNetworkPolicy(node, sriovInfos, numVfs, resourceName)
   309  				WaitForSRIOVStable()
   310  				sriovDevice, err = sriovInfos.FindOneSriovDevice(node)
   311  				Expect(err).ToNot(HaveOccurred())
   312  				By("Using device " + sriovDevice.Name + " on node " + node)
   313  
   314  				Eventually(func() int64 {
   315  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
   316  					Expect(err).ToNot(HaveOccurred())
   317  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
   318  					allocatable, _ := resNum.AsInt64()
   319  					return allocatable
   320  				}, 10*time.Minute, time.Second).Should(Equal(int64(numVfs)))
   321  			}
   322  		})
   323  
   324  		BeforeEach(func() {
   325  			if discovery.Enabled() && discoveryFailed {
   326  				Skip("Insufficient resources to run tests in discovery mode")
   327  			}
   328  			err := namespaces.CleanPods(namespaces.Test, clients)
   329  			Expect(err).ToNot(HaveOccurred())
   330  			err = namespaces.CleanNetworks(operatorNamespace, clients)
   331  			Expect(err).ToNot(HaveOccurred())
   332  		})
   333  		Context("Resource Injector", func() {
   334  			// 25815
   335  			It("Should inject downward api volume with no labels present", func() {
   336  				sriovNetwork := &sriovv1.SriovNetwork{
   337  					ObjectMeta: metav1.ObjectMeta{
   338  						Name:      "test-apivolnetwork",
   339  						Namespace: operatorNamespace,
   340  					},
   341  					Spec: sriovv1.SriovNetworkSpec{
   342  						ResourceName:     resourceName,
   343  						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"}`,
   344  						NetworkNamespace: namespaces.Test,
   345  					}}
   346  				err := clients.Create(context.Background(), sriovNetwork)
   347  				Expect(err).ToNot(HaveOccurred())
   348  
   349  				Eventually(func() error {
   350  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   351  					return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-apivolnetwork", Namespace: namespaces.Test}, netAttDef)
   352  				}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
   353  
   354  				podDefinition := pod.DefineWithNetworks([]string{sriovNetwork.Name})
   355  				created, err := clients.Pods(namespaces.Test).Create(context.Background(), podDefinition, metav1.CreateOptions{})
   356  				Expect(err).ToNot(HaveOccurred())
   357  
   358  				var runningPod *corev1.Pod
   359  				Eventually(func() corev1.PodPhase {
   360  					runningPod, err = clients.Pods(namespaces.Test).Get(context.Background(), created.Name, metav1.GetOptions{})
   361  					if k8serrors.IsNotFound(err) {
   362  						return corev1.PodUnknown
   363  					}
   364  					Expect(err).ToNot(HaveOccurred())
   365  
   366  					return runningPod.Status.Phase
   367  				}, 3*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
   368  
   369  				var downwardVolume *corev1.Volume
   370  				for _, v := range runningPod.Spec.Volumes {
   371  					if v.Name == volumePodNetInfo {
   372  						downwardVolume = v.DeepCopy()
   373  						break
   374  					}
   375  				}
   376  
   377  				Expect(downwardVolume).ToNot(BeNil(), "Downward volume not found")
   378  				Expect(downwardVolume.DownwardAPI).ToNot(BeNil(), "Downward api not found in volume")
   379  				Expect(downwardVolume.DownwardAPI.Items).ToNot(
   380  					ContainElement(corev1.DownwardAPIVolumeFile{
   381  						Path: "labels",
   382  						FieldRef: &corev1.ObjectFieldSelector{
   383  							APIVersion: "v1",
   384  							FieldPath:  "metadata.labels",
   385  						},
   386  					}))
   387  				Expect(downwardVolume.DownwardAPI.Items).To(
   388  					ContainElement(corev1.DownwardAPIVolumeFile{
   389  						Path: "annotations",
   390  						FieldRef: &corev1.ObjectFieldSelector{
   391  							APIVersion: "v1",
   392  							FieldPath:  "metadata.annotations",
   393  						},
   394  					}))
   395  			})
   396  
   397  			It("Should inject downward api volume with labels present", func() {
   398  				sriovNetwork := &sriovv1.SriovNetwork{
   399  					ObjectMeta: metav1.ObjectMeta{
   400  						Name:      "test-apivolnetwork",
   401  						Namespace: operatorNamespace,
   402  					},
   403  					Spec: sriovv1.SriovNetworkSpec{
   404  						ResourceName:     resourceName,
   405  						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"}`,
   406  						NetworkNamespace: namespaces.Test,
   407  					}}
   408  				err := clients.Create(context.Background(), sriovNetwork)
   409  				Expect(err).ToNot(HaveOccurred())
   410  
   411  				Eventually(func() error {
   412  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   413  					return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-apivolnetwork", Namespace: namespaces.Test}, netAttDef)
   414  				}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
   415  
   416  				podDefinition := pod.DefineWithNetworks([]string{sriovNetwork.Name})
   417  				podDefinition.ObjectMeta.Labels = map[string]string{"anyname": "anyvalue"}
   418  				created, err := clients.Pods(namespaces.Test).Create(context.Background(), podDefinition, metav1.CreateOptions{})
   419  				Expect(err).ToNot(HaveOccurred())
   420  
   421  				var runningPod *corev1.Pod
   422  				Eventually(func() corev1.PodPhase {
   423  					runningPod, err = clients.Pods(namespaces.Test).Get(context.Background(), created.Name, metav1.GetOptions{})
   424  					if k8serrors.IsNotFound(err) {
   425  						return corev1.PodUnknown
   426  					}
   427  					Expect(err).ToNot(HaveOccurred())
   428  
   429  					return runningPod.Status.Phase
   430  				}, 3*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
   431  
   432  				var downwardVolume *corev1.Volume
   433  				for _, v := range runningPod.Spec.Volumes {
   434  					if v.Name == volumePodNetInfo {
   435  						downwardVolume = v.DeepCopy()
   436  						break
   437  					}
   438  				}
   439  
   440  				Expect(downwardVolume).ToNot(BeNil(), "Downward volume not found")
   441  				Expect(downwardVolume.DownwardAPI).ToNot(BeNil(), "Downward api not found in volume")
   442  				Expect(downwardVolume.DownwardAPI.Items).To(SatisfyAll(
   443  					ContainElement(corev1.DownwardAPIVolumeFile{
   444  						Path: "labels",
   445  						FieldRef: &corev1.ObjectFieldSelector{
   446  							APIVersion: "v1",
   447  							FieldPath:  "metadata.labels",
   448  						},
   449  					}), ContainElement(corev1.DownwardAPIVolumeFile{
   450  						Path: "annotations",
   451  						FieldRef: &corev1.ObjectFieldSelector{
   452  							APIVersion: "v1",
   453  							FieldPath:  "metadata.annotations",
   454  						},
   455  					})))
   456  			})
   457  		})
   458  
   459  		Context("VF flags", func() {
   460  			hostNetPod := &corev1.Pod{} // Initialized in BeforeEach
   461  			intf := &sriovv1.InterfaceExt{}
   462  
   463  			validationFunction := func(networks []string, containsFunc func(line string) bool) {
   464  				podObj := pod.RedefineWithNodeSelector(pod.DefineWithNetworks(networks), node)
   465  				err := clients.Create(context.Background(), podObj)
   466  				Expect(err).ToNot(HaveOccurred())
   467  				Eventually(func() corev1.PodPhase {
   468  					podObj, err = clients.Pods(namespaces.Test).Get(context.Background(), podObj.Name, metav1.GetOptions{})
   469  					Expect(err).ToNot(HaveOccurred())
   470  					return podObj.Status.Phase
   471  				}, 5*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
   472  
   473  				vfIndex, err := podVFIndexInHost(hostNetPod, podObj, "net1")
   474  				Expect(err).ToNot(HaveOccurred())
   475  
   476  				Eventually(func() bool {
   477  					stdout, stderr, err := pod.ExecCommand(clients, hostNetPod, "ip", "link", "show")
   478  					Expect(err).ToNot(HaveOccurred())
   479  					Expect(stderr).To(Equal(""))
   480  
   481  					found := false
   482  					for _, line := range strings.Split(stdout, "\n") {
   483  						if strings.Contains(line, fmt.Sprintf("vf %d ", vfIndex)) && containsFunc(line) {
   484  							found = true
   485  							break
   486  						}
   487  					}
   488  					if !found {
   489  						return found
   490  					}
   491  
   492  					err = clients.Pods(namespaces.Test).Delete(context.Background(), podObj.Name, metav1.DeleteOptions{
   493  						GracePeriodSeconds: pointer.Int64Ptr(0)})
   494  					Expect(err).ToNot(HaveOccurred())
   495  
   496  					return found
   497  				}, time.Minute, time.Second).Should(BeTrue())
   498  
   499  			}
   500  
   501  			validateNetworkFields := func(sriovNetwork *sriovv1.SriovNetwork, validationString string) {
   502  				netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   503  				Eventually(func() error {
   504  					return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: sriovNetwork.Name, Namespace: namespaces.Test}, netAttDef)
   505  				}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
   506  
   507  				checkFunc := func(line string) bool {
   508  					return strings.Contains(line, validationString)
   509  				}
   510  
   511  				validationFunction([]string{sriovNetwork.Name}, checkFunc)
   512  			}
   513  
   514  			BeforeEach(func() {
   515  				Eventually(func() int64 {
   516  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
   517  					Expect(err).ToNot(HaveOccurred())
   518  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
   519  					allocatable, _ := resNum.AsInt64()
   520  					return allocatable
   521  				}, 3*time.Minute, time.Second).Should(Equal(int64(numVfs)))
   522  
   523  				hostNetPod = pod.DefineWithHostNetwork(node)
   524  				err := clients.Create(context.Background(), hostNetPod)
   525  				Expect(err).ToNot(HaveOccurred())
   526  				Eventually(func() corev1.PodPhase {
   527  					hostNetPod, err = clients.Pods(namespaces.Test).Get(context.Background(), hostNetPod.Name, metav1.GetOptions{})
   528  					Expect(err).ToNot(HaveOccurred())
   529  					return hostNetPod.Status.Phase
   530  				}, 3*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
   531  			})
   532  
   533  			// 25959
   534  			It("Should configure the spoofChk boolean variable", func() {
   535  				sriovNetwork := &sriovv1.SriovNetwork{
   536  					ObjectMeta: metav1.ObjectMeta{Name: "test-spoofnetwork", Namespace: operatorNamespace},
   537  					Spec: sriovv1.SriovNetworkSpec{
   538  						ResourceName: resourceName,
   539  						IPAM: `{"type":"host-local",
   540  								"subnet":"10.10.10.0/24",
   541  								"rangeStart":"10.10.10.171",
   542  								"rangeEnd":"10.10.10.181",
   543  								"routes":[{"dst":"0.0.0.0/0"}],
   544  								"gateway":"10.10.10.1"}`,
   545  						NetworkNamespace: namespaces.Test,
   546  					}}
   547  
   548  				By("configuring spoofChk on")
   549  				copyObj := sriovNetwork.DeepCopy()
   550  				copyObj.Spec.SpoofChk = on
   551  				spoofChkStatusValidation := "spoof checking on"
   552  				err := clients.Create(context.Background(), copyObj)
   553  				Expect(err).ToNot(HaveOccurred())
   554  
   555  				validateNetworkFields(copyObj, spoofChkStatusValidation)
   556  
   557  				By("removing sriov network")
   558  				err = clients.Delete(context.Background(), sriovNetwork)
   559  				Expect(err).ToNot(HaveOccurred())
   560  
   561  				Eventually(func() bool {
   562  					networkDef := &sriovv1.SriovNetwork{}
   563  					err := clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-spoofnetwork",
   564  						Namespace: operatorNamespace}, networkDef)
   565  					return k8serrors.IsNotFound(err)
   566  				}, 10*time.Second, 1*time.Second).Should(BeTrue())
   567  
   568  				By("configuring spoofChk off")
   569  				copyObj = sriovNetwork.DeepCopy()
   570  				copyObj.Spec.SpoofChk = off
   571  				spoofChkStatusValidation = "spoof checking off"
   572  				err = clients.Create(context.Background(), copyObj)
   573  				Expect(err).ToNot(HaveOccurred())
   574  
   575  				validateNetworkFields(copyObj, spoofChkStatusValidation)
   576  			})
   577  
   578  			// 25960
   579  			It("Should configure the trust boolean variable", func() {
   580  				sriovNetwork := &sriovv1.SriovNetwork{
   581  					ObjectMeta: metav1.ObjectMeta{Name: "test-trustnetwork", Namespace: operatorNamespace},
   582  					Spec: sriovv1.SriovNetworkSpec{
   583  						ResourceName: resourceName,
   584  						IPAM: `{"type":"host-local",
   585  								"subnet":"10.10.10.0/24",
   586  								"rangeStart":"10.10.10.171",
   587  								"rangeEnd":"10.10.10.181",
   588  								"routes":[{"dst":"0.0.0.0/0"}],
   589  								"gateway":"10.10.10.1"}`,
   590  						NetworkNamespace: namespaces.Test,
   591  					}}
   592  
   593  				By("configuring trust on")
   594  				copyObj := sriovNetwork.DeepCopy()
   595  				copyObj.Spec.Trust = on
   596  				trustChkStatusValidation := "trust on"
   597  				err := clients.Create(context.Background(), copyObj)
   598  				Expect(err).ToNot(HaveOccurred())
   599  
   600  				validateNetworkFields(copyObj, trustChkStatusValidation)
   601  
   602  				By("removing sriov network")
   603  				err = clients.Delete(context.Background(), sriovNetwork)
   604  				Expect(err).ToNot(HaveOccurred())
   605  				Eventually(func() bool {
   606  					networkDef := &sriovv1.SriovNetwork{}
   607  					err := clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-trustnetwork",
   608  						Namespace: operatorNamespace}, networkDef)
   609  					return k8serrors.IsNotFound(err)
   610  				}, 10*time.Second, 1*time.Second).Should(BeTrue())
   611  
   612  				By("configuring trust off")
   613  				copyObj = sriovNetwork.DeepCopy()
   614  				copyObj.Spec.Trust = off
   615  				trustChkStatusValidation = "trust off"
   616  				err = clients.Create(context.Background(), copyObj)
   617  				Expect(err).ToNot(HaveOccurred())
   618  
   619  				validateNetworkFields(copyObj, trustChkStatusValidation)
   620  			})
   621  
   622  			// 25961
   623  			It("Should configure the the link state variable", func() {
   624  				if cluster.VirtualCluster() {
   625  					// https://bugzilla.redhat.com/show_bug.cgi?id=2214976
   626  					Skip("Bug in IGB driver")
   627  				}
   628  
   629  				sriovNetwork := &sriovv1.SriovNetwork{
   630  					ObjectMeta: metav1.ObjectMeta{Name: "test-statenetwork", Namespace: operatorNamespace},
   631  					Spec: sriovv1.SriovNetworkSpec{
   632  						ResourceName: resourceName,
   633  						IPAM: `{"type":"host-local",
   634  								"subnet":"10.10.10.0/24",
   635  								"rangeStart":"10.10.10.171",
   636  								"rangeEnd":"10.10.10.181",
   637  								"routes":[{"dst":"0.0.0.0/0"}],
   638  								"gateway":"10.10.10.1"}`,
   639  						NetworkNamespace: namespaces.Test,
   640  					}}
   641  
   642  				By("configuring link-state as enabled")
   643  				enabledLinkNetwork := sriovNetwork.DeepCopy()
   644  				enabledLinkNetwork.Spec.LinkState = "enable"
   645  				linkStateChkStatusValidation := "link-state enable"
   646  				err := clients.Create(context.Background(), enabledLinkNetwork)
   647  				Expect(err).ToNot(HaveOccurred())
   648  
   649  				validateNetworkFields(enabledLinkNetwork, linkStateChkStatusValidation)
   650  
   651  				By("removing sriov network")
   652  				err = clients.Delete(context.Background(), enabledLinkNetwork)
   653  				Expect(err).ToNot(HaveOccurred())
   654  				Eventually(func() bool {
   655  					networkDef := &sriovv1.SriovNetwork{}
   656  					err := clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-statenetwork",
   657  						Namespace: operatorNamespace}, networkDef)
   658  					return k8serrors.IsNotFound(err)
   659  				}, 10*time.Second, 1*time.Second).Should(BeTrue())
   660  
   661  				By("configuring link-state as disable")
   662  				disabledLinkNetwork := sriovNetwork.DeepCopy()
   663  				disabledLinkNetwork.Spec.LinkState = "disable"
   664  				linkStateChkStatusValidation = "link-state disable"
   665  				err = clients.Create(context.Background(), disabledLinkNetwork)
   666  				Expect(err).ToNot(HaveOccurred())
   667  
   668  				validateNetworkFields(disabledLinkNetwork, linkStateChkStatusValidation)
   669  
   670  				By("removing sriov network")
   671  				err = clients.Delete(context.Background(), disabledLinkNetwork)
   672  				Expect(err).ToNot(HaveOccurred())
   673  				Eventually(func() bool {
   674  					networkDef := &sriovv1.SriovNetwork{}
   675  					err := clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-statenetwork",
   676  						Namespace: operatorNamespace}, networkDef)
   677  					return k8serrors.IsNotFound(err)
   678  				}, 10*time.Second, 1*time.Second).Should(BeTrue())
   679  
   680  				By("configuring link-state as auto")
   681  				autoLinkNetwork := sriovNetwork.DeepCopy()
   682  				autoLinkNetwork.Spec.LinkState = "auto"
   683  				linkStateChkStatusValidation = "link-state auto"
   684  				err = clients.Create(context.Background(), autoLinkNetwork)
   685  				Expect(err).ToNot(HaveOccurred())
   686  
   687  				validateNetworkFields(autoLinkNetwork, linkStateChkStatusValidation)
   688  			})
   689  
   690  			// 25963
   691  			Describe("rate limit", func() {
   692  				It("Should configure the requested rate limit flags under the vf", func() {
   693  					if intf.Driver != "mlx5_core" {
   694  						// There is an issue with the intel cards both driver i40 and ixgbe
   695  						// BZ 1772847
   696  						// BZ 1772815
   697  						// BZ 1236146
   698  						Skip("Skip rate limit test no mellanox driver found")
   699  					}
   700  
   701  					var maxTxRate = 100
   702  					var minTxRate = 40
   703  					sriovNetwork := &sriovv1.SriovNetwork{ObjectMeta: metav1.ObjectMeta{Name: "test-ratenetwork", Namespace: operatorNamespace},
   704  						Spec: sriovv1.SriovNetworkSpec{
   705  							ResourceName: resourceName,
   706  							IPAM: `{"type":"host-local",
   707  								"subnet":"10.10.10.0/24",
   708  								"rangeStart":"10.10.10.171",
   709  								"rangeEnd":"10.10.10.181",
   710  								"routes":[{"dst":"0.0.0.0/0"}],
   711  								"gateway":"10.10.10.1"}`,
   712  							MaxTxRate:        &maxTxRate,
   713  							MinTxRate:        &minTxRate,
   714  							NetworkNamespace: namespaces.Test,
   715  						}}
   716  					err := clients.Create(context.Background(), sriovNetwork)
   717  					Expect(err).ToNot(HaveOccurred())
   718  
   719  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   720  					Eventually(func() error {
   721  						return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-ratenetwork", Namespace: namespaces.Test}, netAttDef)
   722  					}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
   723  
   724  					checkFunc := func(line string) bool {
   725  						if strings.Contains(line, "max_tx_rate 100Mbps") &&
   726  							strings.Contains(line, "min_tx_rate 40Mbps") {
   727  							return true
   728  						}
   729  						return false
   730  					}
   731  
   732  					validationFunction([]string{"test-ratenetwork"}, checkFunc)
   733  				})
   734  			})
   735  
   736  			// 25963
   737  			Describe("vlan and Qos vlan", func() {
   738  				It("Should configure the requested vlan and Qos vlan flags under the vf", func() {
   739  					sriovNetwork := &sriovv1.SriovNetwork{ObjectMeta: metav1.ObjectMeta{Name: "test-quosnetwork", Namespace: operatorNamespace},
   740  						Spec: sriovv1.SriovNetworkSpec{
   741  							ResourceName: resourceName,
   742  							IPAM: `{"type":"host-local",
   743  								"subnet":"10.10.10.0/24",
   744  								"rangeStart":"10.10.10.171",
   745  								"rangeEnd":"10.10.10.181",
   746  								"routes":[{"dst":"0.0.0.0/0"}],
   747  								"gateway":"10.10.10.1"}`,
   748  							Vlan:             1,
   749  							VlanQoS:          2,
   750  							NetworkNamespace: namespaces.Test,
   751  						}}
   752  					err := clients.Create(context.Background(), sriovNetwork)
   753  					Expect(err).ToNot(HaveOccurred())
   754  
   755  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   756  					Eventually(func() error {
   757  						return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-quosnetwork", Namespace: namespaces.Test}, netAttDef)
   758  					}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
   759  
   760  					checkFunc := func(line string) bool {
   761  						if strings.Contains(line, "vlan 1") &&
   762  							strings.Contains(line, "qos 2") {
   763  							return true
   764  						}
   765  						return false
   766  					}
   767  
   768  					validationFunction([]string{"test-quosnetwork"}, checkFunc)
   769  				})
   770  			})
   771  		})
   772  
   773  		Context("Multiple sriov device and attachment", func() {
   774  			// 25834
   775  			It("Should configure multiple network attachments", func() {
   776  				ipam := ipamIpv4
   777  				err := network.CreateSriovNetwork(clients, sriovDevice, sriovNetworkName, namespaces.Test, operatorNamespace, resourceName, ipam)
   778  				Expect(err).ToNot(HaveOccurred())
   779  				waitForNetAttachDef(sriovNetworkName, namespaces.Test)
   780  
   781  				pod := createTestPod(node, []string{sriovNetworkName, sriovNetworkName})
   782  				nics, err := network.GetNicsByPrefix(pod, "net")
   783  				Expect(err).ToNot(HaveOccurred())
   784  				Expect(len(nics)).To(Equal(2), "No sriov network interfaces found.")
   785  			})
   786  		})
   787  
   788  		Context("IPv6 configured secondary interfaces on pods", func() {
   789  			// 25874
   790  			It("should be able to ping each other", func() {
   791  				ipv6NetworkName := testIpv6NetworkName
   792  				ipam := ipamIpv6
   793  				err := network.CreateSriovNetwork(clients, sriovDevice, ipv6NetworkName, namespaces.Test, operatorNamespace, resourceName, ipam)
   794  				Expect(err).ToNot(HaveOccurred())
   795  				waitForNetAttachDef(ipv6NetworkName, namespaces.Test)
   796  
   797  				pod := createTestPod(node, []string{ipv6NetworkName})
   798  				ips, err := network.GetSriovNicIPs(pod, "net1")
   799  				Expect(err).ToNot(HaveOccurred())
   800  				Expect(ips).NotTo(BeNil(), "No sriov network interface found.")
   801  				Expect(len(ips)).Should(Equal(1))
   802  				for _, ip := range ips {
   803  					pingPod(ip, node, ipv6NetworkName)
   804  				}
   805  			})
   806  		})
   807  
   808  		Context("NAD update", func() {
   809  			// 24713
   810  			It("NAD is updated when SriovNetwork spec/networkNamespace is changed", func() {
   811  				ns1 := "test-z1"
   812  				ns2 := "test-z2"
   813  				defer namespaces.DeleteAndWait(clients, ns1, 1*time.Minute)
   814  				defer namespaces.DeleteAndWait(clients, ns2, 1*time.Minute)
   815  				err := namespaces.Create(ns1, clients)
   816  				Expect(err).ToNot(HaveOccurred())
   817  				err = namespaces.Create(ns2, clients)
   818  				Expect(err).ToNot(HaveOccurred())
   819  
   820  				ipam := ipamIpv4
   821  				err = network.CreateSriovNetwork(clients, sriovDevice, sriovNetworkName, ns1, operatorNamespace, resourceName, ipam)
   822  				Expect(err).ToNot(HaveOccurred())
   823  				waitForNetAttachDef(sriovNetworkName, ns1)
   824  
   825  				body, _ := json.Marshal([]patchBody{{
   826  					Op:    "replace",
   827  					Path:  "/spec/networkNamespace",
   828  					Value: ns2,
   829  				}})
   830  				clients.SriovnetworkV1Interface.RESTClient().Patch(types.JSONPatchType).Namespace(operatorNamespace).Resource("sriovnetworks").Name(sriovNetworkName).Body(body).Do(context.Background())
   831  
   832  				waitForNetAttachDef(sriovNetworkName, ns2)
   833  
   834  				Consistently(func() error {
   835  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   836  					return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: sriovNetworkName, Namespace: ns1}, netAttDef)
   837  				}, 5*time.Second, 1*time.Second).Should(HaveOccurred())
   838  			})
   839  		})
   840  
   841  		Context("NAD update", func() {
   842  			// 24714
   843  			It("NAD default gateway is updated when SriovNetwork ipam is changed", func() {
   844  
   845  				ipam := `{
   846  					"type": "host-local",
   847  					"subnet": "10.11.11.0/24",
   848  					"gateway": "%s"
   849  				  }`
   850  				err := network.CreateSriovNetwork(clients, sriovDevice, sriovNetworkName, namespaces.Test, operatorNamespace, resourceName, fmt.Sprintf(ipam, "10.11.11.1"))
   851  				Expect(err).ToNot(HaveOccurred())
   852  
   853  				Eventually(func() bool {
   854  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   855  					err := clients.Get(context.Background(), runtimeclient.ObjectKey{Name: sriovNetworkName, Namespace: namespaces.Test}, netAttDef)
   856  					if k8serrors.IsNotFound(err) {
   857  						return false
   858  					}
   859  					return strings.Contains(netAttDef.Spec.Config, "10.11.11.1")
   860  				}, (30+snoTimeoutMultiplier*90)*time.Second, 1*time.Second).Should(BeTrue())
   861  
   862  				sriovNetwork := &sriovv1.SriovNetwork{}
   863  				err = clients.Get(context.TODO(), runtimeclient.ObjectKey{Name: sriovNetworkName, Namespace: operatorNamespace}, sriovNetwork)
   864  				Expect(err).ToNot(HaveOccurred())
   865  				sriovNetwork.Spec.IPAM = fmt.Sprintf(ipam, "10.11.11.100")
   866  				err = clients.Update(context.Background(), sriovNetwork)
   867  				Expect(err).ToNot(HaveOccurred())
   868  
   869  				Eventually(func() bool {
   870  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   871  					clients.Get(context.Background(), runtimeclient.ObjectKey{Name: sriovNetworkName, Namespace: namespaces.Test}, netAttDef)
   872  					return strings.Contains(netAttDef.Spec.Config, "10.11.11.100")
   873  				}, (30+snoTimeoutMultiplier*90)*time.Second, 1*time.Second).Should(BeTrue())
   874  			})
   875  		})
   876  
   877  		Context("SRIOV and macvlan", func() {
   878  			// 25834
   879  			It("Should be able to create a pod with both sriov and macvlan interfaces", func() {
   880  				ipam := ipamIpv4
   881  				err := network.CreateSriovNetwork(clients, sriovDevice, sriovNetworkName, namespaces.Test, operatorNamespace, resourceName, ipam)
   882  				Expect(err).ToNot(HaveOccurred())
   883  				waitForNetAttachDef(sriovNetworkName, namespaces.Test)
   884  
   885  				macvlanNadName := "macvlan-nad"
   886  				macvlanNad := network.CreateMacvlanNetworkAttachmentDefinition(macvlanNadName, namespaces.Test)
   887  				err = clients.Create(context.Background(), &macvlanNad)
   888  				Expect(err).ToNot(HaveOccurred())
   889  				defer clients.Delete(context.Background(), &macvlanNad)
   890  				Eventually(func() error {
   891  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
   892  					return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: macvlanNadName, Namespace: namespaces.Test}, netAttDef)
   893  				}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
   894  
   895  				createdPod := createTestPod(node, []string{sriovNetworkName, macvlanNadName})
   896  
   897  				nics, err := network.GetNicsByPrefix(createdPod, "net")
   898  				Expect(err).ToNot(HaveOccurred())
   899  				Expect(len(nics)).To(Equal(2), "Pod should have two multus nics.")
   900  
   901  				stdout, _, err := pod.ExecCommand(clients, createdPod, "ethtool", "-i", "net1")
   902  				Expect(err).ToNot(HaveOccurred())
   903  
   904  				sriovVfDriver := getDriver(stdout)
   905  				Expect(cluster.IsVFDriverSupported(sriovVfDriver)).To(BeTrue())
   906  
   907  				stdout, _, err = pod.ExecCommand(clients, createdPod, "ethtool", "-i", "net2")
   908  				macvlanDriver := getDriver(stdout)
   909  				Expect(err).ToNot(HaveOccurred())
   910  				Expect(macvlanDriver).To(Equal("macvlan"))
   911  
   912  			})
   913  		})
   914  
   915  		Context("Meta Plugin Configuration", func() {
   916  			It("Should be able to configure a metaplugin", func() {
   917  				ipam := ipamIpv4
   918  				config := func(network *sriovv1.SriovNetwork) {
   919  					network.Spec.MetaPluginsConfig = `{ "type": "tuning", "sysctl": { "net.ipv4.conf.IFNAME.accept_redirects": "1"}}`
   920  				}
   921  				err := network.CreateSriovNetwork(clients, sriovDevice, sriovNetworkName, namespaces.Test, operatorNamespace, resourceName, ipam, []network.SriovNetworkOptions{config}...)
   922  				Expect(err).ToNot(HaveOccurred())
   923  				waitForNetAttachDef(sriovNetworkName, namespaces.Test)
   924  
   925  				testPod := createTestPod(node, []string{sriovNetworkName})
   926  				stdout, _, err := pod.ExecCommand(clients, testPod, "more", "/proc/sys/net/ipv4/conf/net1/accept_redirects")
   927  				Expect(err).ToNot(HaveOccurred())
   928  
   929  				Expect(strings.TrimSpace(stdout)).To(Equal("1"))
   930  			})
   931  		})
   932  
   933  		Context("Virtual Functions", func() {
   934  			// 21396
   935  			It("should release the VFs once the pod deleted and same VFs can be used by the new created pods", func() {
   936  				if discovery.Enabled() {
   937  					Skip("Virtual functions allocation test consumes all the available vfs, not suitable for discovery mode")
   938  					// TODO Split this so we check the allocation / unallocation but with a limited number of
   939  					// resources.
   940  				}
   941  
   942  				By("Create first Pod which consumes all available VFs")
   943  				sriovDevice, err := sriovInfos.FindOneSriovDevice(node)
   944  				Expect(err).ToNot(HaveOccurred())
   945  				By("Using device " + sriovDevice.Name + " on node " + node)
   946  
   947  				ipam := ipamIpv6
   948  				err = network.CreateSriovNetwork(clients, sriovDevice, sriovNetworkName, namespaces.Test, operatorNamespace, resourceName, ipam)
   949  				Expect(err).ToNot(HaveOccurred())
   950  				waitForNetAttachDef(sriovNetworkName, namespaces.Test)
   951  
   952  				testPodA := pod.RedefineWithNodeSelector(
   953  					pod.DefineWithNetworks([]string{sriovNetworkName, sriovNetworkName, sriovNetworkName, sriovNetworkName, sriovNetworkName}),
   954  					node,
   955  				)
   956  				runningPodA, err := clients.Pods(testPodA.Namespace).Create(context.Background(), testPodA, metav1.CreateOptions{})
   957  				Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("Error to create pod %s", testPodA.Name))
   958  				By("Checking that first Pod is in Running state")
   959  				Eventually(func() corev1.PodPhase {
   960  					runningPodA, err = clients.Pods(namespaces.Test).Get(context.Background(), runningPodA.Name, metav1.GetOptions{})
   961  					Expect(err).ToNot(HaveOccurred())
   962  					return runningPodA.Status.Phase
   963  				}, 3*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
   964  				By("Create second Pod which consumes one more VF")
   965  
   966  				testPodB := pod.RedefineWithNodeSelector(
   967  					pod.DefineWithNetworks([]string{sriovNetworkName}),
   968  					node,
   969  				)
   970  				runningPodB, err := clients.Pods(testPodB.Namespace).Create(context.Background(), testPodB, metav1.CreateOptions{})
   971  				Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("Error to create pod %s", testPodB.Name))
   972  				By("Checking second that pod is in Pending state")
   973  				Eventually(func() corev1.PodPhase {
   974  					runningPodB, err = clients.Pods(namespaces.Test).Get(context.Background(), runningPodB.Name, metav1.GetOptions{})
   975  					Expect(err).ToNot(HaveOccurred())
   976  					return runningPodB.Status.Phase
   977  				}, 3*time.Minute, time.Second).Should(Equal(corev1.PodPending))
   978  
   979  				By("Checking that relevant error event was originated")
   980  				Eventually(func() bool {
   981  					events, err := clients.Events(namespaces.Test).List(context.Background(), metav1.ListOptions{})
   982  					Expect(err).ToNot(HaveOccurred())
   983  
   984  					for _, val := range events.Items {
   985  						if val.InvolvedObject.Name == runningPodB.Name && strings.Contains(val.Message, fmt.Sprintf("Insufficient openshift.io/%s", resourceName)) {
   986  							return true
   987  						}
   988  					}
   989  					return false
   990  				}, 2*time.Minute, 10*time.Second).Should(BeTrue(), "Error to detect Required Event")
   991  				By("Delete first pod and release all VFs")
   992  				err = clients.Pods(namespaces.Test).Delete(context.Background(), runningPodA.Name, metav1.DeleteOptions{
   993  					GracePeriodSeconds: pointer.Int64Ptr(0),
   994  				})
   995  				Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("Error to delete pod %s", runningPodA.Name))
   996  				By("Checking that second pod is able to use released VF")
   997  				Eventually(func() corev1.PodPhase {
   998  					runningPodB, err = clients.Pods(namespaces.Test).Get(context.Background(), runningPodB.Name, metav1.GetOptions{})
   999  					Expect(err).ToNot(HaveOccurred())
  1000  					return runningPodB.Status.Phase
  1001  				}, 3*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
  1002  			})
  1003  		})
  1004  
  1005  		Context("CNI Logging level", func() {
  1006  			It("Debug logging should be visible in multus pod", func() {
  1007  				sriovNetworkName := "test-log-level-debug-no-file"
  1008  				err := network.CreateSriovNetwork(clients, sriovDevice, sriovNetworkName,
  1009  					namespaces.Test, operatorNamespace, resourceName, ipamIpv4,
  1010  					func(sn *sriovv1.SriovNetwork) {
  1011  						sn.Spec.LogLevel = "debug"
  1012  					})
  1013  				Expect(err).ToNot(HaveOccurred())
  1014  				waitForNetAttachDef(sriovNetworkName, namespaces.Test)
  1015  
  1016  				testPod := createTestPod(node, []string{sriovNetworkName})
  1017  
  1018  				recentMultusLogs := getMultusPodLogs(testPod.Spec.NodeName, testPod.ObjectMeta.CreationTimestamp.Time)
  1019  
  1020  				Expect(recentMultusLogs).To(
  1021  					ContainElement(
  1022  						// Assert against multiple ContainSubstring condition because we can't make assumption on the order of the chunks
  1023  						And(
  1024  							ContainSubstring(`level="debug"`),
  1025  							ContainSubstring(`msg="function called"`),
  1026  							ContainSubstring(`func="cmdAdd"`),
  1027  							ContainSubstring(`cniName="sriov-cni"`),
  1028  							ContainSubstring(`ifname="net1"`),
  1029  						),
  1030  					))
  1031  			})
  1032  		})
  1033  	})
  1034  
  1035  	Describe("Custom SriovNetworkNodePolicy", func() {
  1036  		BeforeEach(func() {
  1037  			err := namespaces.Clean(operatorNamespace, namespaces.Test, clients, discovery.Enabled())
  1038  			Expect(err).ToNot(HaveOccurred())
  1039  			WaitForSRIOVStable()
  1040  		})
  1041  
  1042  		Describe("Configuration", func() {
  1043  			Context("Create vfio-pci node policy", func() {
  1044  				var vfioNode string
  1045  				var vfioNic sriovv1.InterfaceExt
  1046  
  1047  				BeforeEach(func() {
  1048  					if discovery.Enabled() {
  1049  						Skip("Test unsuitable to be run in discovery mode")
  1050  					}
  1051  
  1052  					vfioNode, vfioNic = sriovInfos.FindOneVfioSriovDevice()
  1053  					if vfioNode == "" {
  1054  						Skip("skip test as no vfio-pci capable PF was found")
  1055  					}
  1056  					By("Using device " + vfioNic.Name + " on node " + vfioNode)
  1057  				})
  1058  
  1059  				It("Should be possible to create a vfio-pci resource", func() {
  1060  					By("creating a vfio-pci node policy")
  1061  					resourceName := "testvfio"
  1062  					_, err := network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, vfioNic.Name, vfioNode, 5, resourceName, "vfio-pci")
  1063  					Expect(err).ToNot(HaveOccurred())
  1064  
  1065  					By("waiting for the node state to be updated")
  1066  					Eventually(func() sriovv1.Interfaces {
  1067  						nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), vfioNode, metav1.GetOptions{})
  1068  						Expect(err).ToNot(HaveOccurred())
  1069  						return nodeState.Spec.Interfaces
  1070  					}, 1*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  1071  						IgnoreExtras,
  1072  						Fields{
  1073  							"Name":   Equal(vfioNic.Name),
  1074  							"NumVfs": Equal(5),
  1075  						})))
  1076  
  1077  					By("waiting the sriov to be stable on the node")
  1078  					WaitForSRIOVStable()
  1079  
  1080  					By("waiting for the resources to be available")
  1081  					Eventually(func() int64 {
  1082  						testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), vfioNode, metav1.GetOptions{})
  1083  						Expect(err).ToNot(HaveOccurred())
  1084  						resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  1085  						allocatable, _ := resNum.AsInt64()
  1086  						return allocatable
  1087  					}, 10*time.Minute, time.Second).Should(Equal(int64(5)))
  1088  				})
  1089  			})
  1090  
  1091  			Context("PF Partitioning", func() {
  1092  				// 27633
  1093  				BeforeEach(func() {
  1094  					if discovery.Enabled() {
  1095  						Skip("Test unsuitable to be run in discovery mode")
  1096  					}
  1097  				})
  1098  
  1099  				It("Should be possible to partition the pf's vfs", func() {
  1100  					vfioNode, vfioNic := sriovInfos.FindOneVfioSriovDevice()
  1101  					if vfioNode == "" {
  1102  						Skip("skip test as no vfio-pci capable PF was found")
  1103  					}
  1104  					By("Using device " + vfioNic.Name + " on node " + vfioNode)
  1105  
  1106  					_, err := network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, vfioNic.Name+"#2-4", vfioNode, 5, testResourceName, "netdevice")
  1107  					Expect(err).ToNot(HaveOccurred())
  1108  
  1109  					Eventually(func() sriovv1.Interfaces {
  1110  						nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), vfioNode, metav1.GetOptions{})
  1111  						Expect(err).ToNot(HaveOccurred())
  1112  						return nodeState.Spec.Interfaces
  1113  					}, 3*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  1114  						IgnoreExtras,
  1115  						Fields{
  1116  							"Name":   Equal(vfioNic.Name),
  1117  							"NumVfs": Equal(5),
  1118  							"VfGroups": ContainElement(
  1119  								MatchFields(
  1120  									IgnoreExtras,
  1121  									Fields{
  1122  										"ResourceName": Equal(testResourceName),
  1123  										"DeviceType":   Equal("netdevice"),
  1124  										"VfRange":      Equal("2-4"),
  1125  									})),
  1126  						})))
  1127  
  1128  					WaitForSRIOVStable()
  1129  
  1130  					Eventually(func() int64 {
  1131  						testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), vfioNode, metav1.GetOptions{})
  1132  						Expect(err).ToNot(HaveOccurred())
  1133  						resNum := testedNode.Status.Allocatable["openshift.io/testresource"]
  1134  						capacity, _ := resNum.AsInt64()
  1135  						return capacity
  1136  					}, 3*time.Minute, time.Second).Should(Equal(int64(3)))
  1137  
  1138  					_, err = network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, vfioNic.Name+"#0-1", vfioNode, 5, "testresource1", "vfio-pci")
  1139  					Expect(err).ToNot(HaveOccurred())
  1140  
  1141  					Eventually(func() sriovv1.Interfaces {
  1142  						nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), vfioNode, metav1.GetOptions{})
  1143  						Expect(err).ToNot(HaveOccurred())
  1144  						return nodeState.Spec.Interfaces
  1145  					}, 3*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  1146  						IgnoreExtras,
  1147  						Fields{
  1148  							"Name":   Equal(vfioNic.Name),
  1149  							"NumVfs": Equal(5),
  1150  							"VfGroups": SatisfyAll(
  1151  								ContainElement(
  1152  									MatchFields(
  1153  										IgnoreExtras,
  1154  										Fields{
  1155  											"ResourceName": Equal(testResourceName),
  1156  											"DeviceType":   Equal("netdevice"),
  1157  											"VfRange":      Equal("2-4"),
  1158  										})),
  1159  								ContainElement(
  1160  									MatchFields(
  1161  										IgnoreExtras,
  1162  										Fields{
  1163  											"ResourceName": Equal("testresource1"),
  1164  											"DeviceType":   Equal("vfio-pci"),
  1165  											"VfRange":      Equal("0-1"),
  1166  										})),
  1167  							),
  1168  						},
  1169  					)))
  1170  
  1171  					// The node may reset here so we put a larger timeout here
  1172  					Eventually(func() bool {
  1173  						res, err := cluster.SriovStable(operatorNamespace, clients)
  1174  						Expect(err).ToNot(HaveOccurred())
  1175  						return res
  1176  					}, 15*time.Minute, 5*time.Second).Should(BeTrue())
  1177  
  1178  					Eventually(func() map[string]int64 {
  1179  						testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), vfioNode, metav1.GetOptions{})
  1180  						Expect(err).ToNot(HaveOccurred())
  1181  						resNum := testedNode.Status.Allocatable["openshift.io/testresource"]
  1182  						capacity, _ := resNum.AsInt64()
  1183  						res := make(map[string]int64)
  1184  						res["openshift.io/testresource"] = capacity
  1185  						resNum = testedNode.Status.Allocatable["openshift.io/testresource1"]
  1186  						capacity, _ = resNum.AsInt64()
  1187  						res["openshift.io/testresource1"] = capacity
  1188  						return res
  1189  					}, 15*time.Minute, time.Second).Should(Equal(map[string]int64{
  1190  						"openshift.io/testresource":  int64(3),
  1191  						"openshift.io/testresource1": int64(2),
  1192  					}))
  1193  				})
  1194  
  1195  				It("Should configure the mtu only for vfs which are part of the partition", func() {
  1196  					defaultMtu := 1500
  1197  					newMtu := 2000
  1198  
  1199  					node := sriovInfos.Nodes[0]
  1200  					intf, err := sriovInfos.FindOneSriovDevice(node)
  1201  					Expect(err).ToNot(HaveOccurred())
  1202  					By("Using device " + intf.Name + " on node " + node)
  1203  
  1204  					_, err = network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, intf.Name+"#0-1", node, 5, testResourceName, "netdevice", func(policy *sriovv1.SriovNetworkNodePolicy) {
  1205  						policy.Spec.Mtu = newMtu
  1206  					})
  1207  					Expect(err).ToNot(HaveOccurred())
  1208  
  1209  					Eventually(func() sriovv1.Interfaces {
  1210  						nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), node, metav1.GetOptions{})
  1211  						Expect(err).ToNot(HaveOccurred())
  1212  						return nodeState.Spec.Interfaces
  1213  					}, 3*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  1214  						IgnoreExtras,
  1215  						Fields{
  1216  							"Name":   Equal(intf.Name),
  1217  							"NumVfs": Equal(5),
  1218  							"Mtu":    Equal(newMtu),
  1219  							"VfGroups": ContainElement(
  1220  								MatchFields(
  1221  									IgnoreExtras,
  1222  									Fields{
  1223  										"ResourceName": Equal(testResourceName),
  1224  										"DeviceType":   Equal("netdevice"),
  1225  										"VfRange":      Equal("0-1"),
  1226  									})),
  1227  						})))
  1228  
  1229  					WaitForSRIOVStable()
  1230  
  1231  					Eventually(func() int64 {
  1232  						testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  1233  						Expect(err).ToNot(HaveOccurred())
  1234  						resNum := testedNode.Status.Allocatable["openshift.io/testresource"]
  1235  						capacity, _ := resNum.AsInt64()
  1236  						return capacity
  1237  					}, 3*time.Minute, time.Second).Should(Equal(int64(2)))
  1238  
  1239  					By(fmt.Sprintf("verifying that only VF 0 and 1 have mtu set to %d", newMtu))
  1240  					Eventually(func() sriovv1.InterfaceExts {
  1241  						nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), node, metav1.GetOptions{})
  1242  						Expect(err).ToNot(HaveOccurred())
  1243  						return nodeState.Status.Interfaces
  1244  					}, 3*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  1245  						IgnoreExtras,
  1246  						Fields{
  1247  							"VFs": SatisfyAll(
  1248  								ContainElement(
  1249  									MatchFields(
  1250  										IgnoreExtras,
  1251  										Fields{
  1252  											"VfID": Equal(0),
  1253  											"Mtu":  Equal(newMtu),
  1254  										})),
  1255  								ContainElement(
  1256  									MatchFields(
  1257  										IgnoreExtras,
  1258  										Fields{
  1259  											"VfID": Equal(1),
  1260  											"Mtu":  Equal(newMtu),
  1261  										})),
  1262  								ContainElement(
  1263  									MatchFields(
  1264  										IgnoreExtras,
  1265  										Fields{
  1266  											"VfID": Equal(2),
  1267  											"Mtu":  Equal(defaultMtu),
  1268  										})),
  1269  							),
  1270  						})))
  1271  				})
  1272  
  1273  				// 27630
  1274  				It("Should not be possible to have overlapping pf ranges", func() {
  1275  					node := sriovInfos.Nodes[0]
  1276  					intf, err := sriovInfos.FindOneSriovDevice(node)
  1277  					Expect(err).ToNot(HaveOccurred())
  1278  					By("Using device " + intf.Name + " on node " + node)
  1279  
  1280  					firstConfig := &sriovv1.SriovNetworkNodePolicy{
  1281  						ObjectMeta: metav1.ObjectMeta{
  1282  							GenerateName: "test-policy",
  1283  							Namespace:    operatorNamespace,
  1284  						},
  1285  
  1286  						Spec: sriovv1.SriovNetworkNodePolicySpec{
  1287  							NodeSelector: map[string]string{
  1288  								"kubernetes.io/hostname": node,
  1289  							},
  1290  							NumVfs:       5,
  1291  							ResourceName: testResourceName,
  1292  							Priority:     99,
  1293  							NicSelector: sriovv1.SriovNetworkNicSelector{
  1294  								PfNames: []string{intf.Name + "#1-4"},
  1295  							},
  1296  							DeviceType: "netdevice",
  1297  						},
  1298  					}
  1299  
  1300  					err = clients.Create(context.Background(), firstConfig)
  1301  					Expect(err).ToNot(HaveOccurred())
  1302  
  1303  					Eventually(func() sriovv1.Interfaces {
  1304  						nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), node, metav1.GetOptions{})
  1305  						Expect(err).ToNot(HaveOccurred())
  1306  						return nodeState.Spec.Interfaces
  1307  					}, 3*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  1308  						IgnoreExtras,
  1309  						Fields{
  1310  							"Name":     Equal(intf.Name),
  1311  							"NumVfs":   Equal(5),
  1312  							"VfGroups": ContainElement(sriovv1.VfGroup{ResourceName: testResourceName, DeviceType: "netdevice", VfRange: "1-4", PolicyName: firstConfig.Name}),
  1313  						})))
  1314  
  1315  					secondConfig := &sriovv1.SriovNetworkNodePolicy{
  1316  						ObjectMeta: metav1.ObjectMeta{
  1317  							GenerateName: "test-policy",
  1318  							Namespace:    operatorNamespace,
  1319  						},
  1320  
  1321  						Spec: sriovv1.SriovNetworkNodePolicySpec{
  1322  							NodeSelector: map[string]string{
  1323  								"kubernetes.io/hostname": node,
  1324  							},
  1325  							NumVfs:       5,
  1326  							ResourceName: "testresource1",
  1327  							Priority:     99,
  1328  							NicSelector: sriovv1.SriovNetworkNicSelector{
  1329  								PfNames: []string{intf.Name + "#0-2"},
  1330  							},
  1331  							DeviceType: "vfio-pci",
  1332  						},
  1333  					}
  1334  
  1335  					err = clients.Create(context.Background(), secondConfig)
  1336  					Expect(err).To(HaveOccurred())
  1337  				})
  1338  			})
  1339  			Context("Main PF", func() {
  1340  				It("should work when vfs are used by pods", func() {
  1341  					if !discovery.Enabled() {
  1342  						testNode := sriovInfos.Nodes[0]
  1343  						resourceName := "mainpfresource"
  1344  						sriovDeviceList, err := sriovInfos.FindSriovDevices(testNode)
  1345  						Expect(err).ToNot(HaveOccurred())
  1346  						executorPod := createCustomTestPod(testNode, []string{}, true, nil)
  1347  						mainDeviceForNode := findMainSriovDevice(executorPod, sriovDeviceList)
  1348  						if mainDeviceForNode == nil {
  1349  							Skip("Could not find pf used as gateway")
  1350  						}
  1351  						By("Using device " + mainDeviceForNode.Name + " on node " + testNode)
  1352  
  1353  						createSriovPolicy(mainDeviceForNode.Name, testNode, 2, resourceName)
  1354  					}
  1355  
  1356  					mainDevice, resourceName, nodeToTest, ok := discoverResourceForMainSriov(sriovInfos)
  1357  					if !ok {
  1358  						Skip("Could not find a policy configured to use the main pf")
  1359  					}
  1360  					ipam := ipamIpv4
  1361  					err := network.CreateSriovNetwork(clients, mainDevice, sriovNetworkName, namespaces.Test, operatorNamespace, resourceName, ipam)
  1362  					Expect(err).ToNot(HaveOccurred())
  1363  					waitForNetAttachDef(sriovNetworkName, namespaces.Test)
  1364  					createTestPod(nodeToTest, []string{sriovNetworkName})
  1365  				})
  1366  			})
  1367  			Context("PF shutdown", func() {
  1368  				// 29398
  1369  				It("Should be able to create pods successfully if PF is down.Pods are able to communicate with each other on the same node", func() {
  1370  					if cluster.VirtualCluster() {
  1371  						// https://bugzilla.redhat.com/show_bug.cgi?id=2214976
  1372  						Skip("Bug in IGB driver")
  1373  					}
  1374  
  1375  					resourceName := testResourceName
  1376  					var testNode string
  1377  					var unusedSriovDevice *sriovv1.InterfaceExt
  1378  
  1379  					if discovery.Enabled() {
  1380  						Skip("PF Shutdown test not enabled in discovery mode")
  1381  					}
  1382  
  1383  					testNode = sriovInfos.Nodes[0]
  1384  					sriovDeviceList, err := sriovInfos.FindSriovDevices(testNode)
  1385  					Expect(err).ToNot(HaveOccurred())
  1386  					unusedSriovDevices, err := findUnusedSriovDevices(testNode, sriovDeviceList)
  1387  					if err != nil {
  1388  						Skip(err.Error())
  1389  					}
  1390  					unusedSriovDevice = unusedSriovDevices[0]
  1391  
  1392  					By("Using device " + unusedSriovDevice.Name + " on node " + testNode)
  1393  
  1394  					defer changeNodeInterfaceState(testNode, unusedSriovDevices[0].Name, true)
  1395  					Expect(err).ToNot(HaveOccurred())
  1396  					createSriovPolicy(unusedSriovDevice.Name, testNode, 2, resourceName)
  1397  
  1398  					ipam := `{
  1399  						"type":"host-local",
  1400  						"subnet":"10.10.10.0/24",
  1401  						"rangeStart":"10.10.10.171",
  1402  						"rangeEnd":"10.10.10.181",
  1403  						"routes":[{"dst":"0.0.0.0/0"}],
  1404  						"gateway":"10.10.10.1"
  1405  						}`
  1406  
  1407  					err = network.CreateSriovNetwork(clients, unusedSriovDevice, sriovNetworkName, namespaces.Test, operatorNamespace, resourceName, ipam)
  1408  					Expect(err).ToNot(HaveOccurred())
  1409  					waitForNetAttachDef(sriovNetworkName, namespaces.Test)
  1410  					changeNodeInterfaceState(testNode, unusedSriovDevice.Name, false)
  1411  					pod := createTestPod(testNode, []string{sriovNetworkName})
  1412  					ips, err := network.GetSriovNicIPs(pod, "net1")
  1413  					Expect(err).ToNot(HaveOccurred())
  1414  					Expect(ips).NotTo(BeNil(), "No sriov network interface found.")
  1415  					Expect(len(ips)).Should(Equal(1))
  1416  					for _, ip := range ips {
  1417  						pingPod(ip, testNode, sriovNetworkName)
  1418  					}
  1419  				})
  1420  			})
  1421  
  1422  			Context("MTU", func() {
  1423  				BeforeEach(func() {
  1424  
  1425  					var node string
  1426  					resourceName := "mturesource"
  1427  					var numVfs int
  1428  					var intf *sriovv1.InterfaceExt
  1429  					var err error
  1430  
  1431  					if discovery.Enabled() {
  1432  						node, resourceName, numVfs, intf, err = discovery.DiscoveredResources(clients,
  1433  							sriovInfos, operatorNamespace,
  1434  							func(policy sriovv1.SriovNetworkNodePolicy) bool {
  1435  								if !defaultFilterPolicy(policy) {
  1436  									return false
  1437  								}
  1438  								if policy.Spec.Mtu != 9000 {
  1439  									return false
  1440  								}
  1441  								return true
  1442  							},
  1443  							func(node string, sriovDeviceList []*sriovv1.InterfaceExt) (*sriovv1.InterfaceExt, bool) {
  1444  								if len(sriovDeviceList) == 0 {
  1445  									return nil, false
  1446  								}
  1447  								nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), node, metav1.GetOptions{})
  1448  								Expect(err).ToNot(HaveOccurred())
  1449  
  1450  								for _, ifc := range nodeState.Spec.Interfaces {
  1451  									if ifc.Mtu == 9000 && ifc.NumVfs > 0 {
  1452  										for _, device := range sriovDeviceList {
  1453  											if device.Name == ifc.Name {
  1454  												return device, true
  1455  											}
  1456  										}
  1457  									}
  1458  								}
  1459  								return nil, false
  1460  							},
  1461  						)
  1462  						Expect(err).ToNot(HaveOccurred())
  1463  						if node == "" || resourceName == "" || numVfs < 5 || intf == nil {
  1464  							Skip("Insufficient resources to run test in discovery mode")
  1465  						}
  1466  					} else {
  1467  						node = sriovInfos.Nodes[0]
  1468  						sriovDeviceList, err := sriovInfos.FindSriovDevices(node)
  1469  						Expect(err).ToNot(HaveOccurred())
  1470  						unusedSriovDevices, err := findUnusedSriovDevices(node, sriovDeviceList)
  1471  						if err != nil {
  1472  							Skip(err.Error())
  1473  						}
  1474  						intf = unusedSriovDevices[0]
  1475  						By("Using device " + intf.Name + " on node " + node)
  1476  
  1477  						mtuPolicy := &sriovv1.SriovNetworkNodePolicy{
  1478  							ObjectMeta: metav1.ObjectMeta{
  1479  								GenerateName: "test-mtupolicy",
  1480  								Namespace:    operatorNamespace,
  1481  							},
  1482  
  1483  							Spec: sriovv1.SriovNetworkNodePolicySpec{
  1484  								NodeSelector: map[string]string{
  1485  									"kubernetes.io/hostname": node,
  1486  								},
  1487  								Mtu:          9000,
  1488  								NumVfs:       5,
  1489  								ResourceName: resourceName,
  1490  								Priority:     99,
  1491  								NicSelector: sriovv1.SriovNetworkNicSelector{
  1492  									PfNames: []string{intf.Name},
  1493  								},
  1494  								DeviceType: "netdevice",
  1495  							},
  1496  						}
  1497  
  1498  						err = clients.Create(context.Background(), mtuPolicy)
  1499  						Expect(err).ToNot(HaveOccurred())
  1500  
  1501  						WaitForSRIOVStable()
  1502  						By("waiting for the resources to be available")
  1503  						Eventually(func() int64 {
  1504  							testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  1505  							Expect(err).ToNot(HaveOccurred())
  1506  							resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  1507  							allocatable, _ := resNum.AsInt64()
  1508  							return allocatable
  1509  						}, 10*time.Minute, time.Second).Should(Equal(int64(5)))
  1510  					}
  1511  
  1512  					sriovNetwork := &sriovv1.SriovNetwork{
  1513  						ObjectMeta: metav1.ObjectMeta{
  1514  							Name:      "test-mtuvolnetwork",
  1515  							Namespace: operatorNamespace,
  1516  						},
  1517  						Spec: sriovv1.SriovNetworkSpec{
  1518  							ResourceName:     resourceName,
  1519  							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"}`,
  1520  							NetworkNamespace: namespaces.Test,
  1521  						}}
  1522  
  1523  					// We need this to be able to run the connectivity checks on Mellanox cards
  1524  					if intf.DeviceID == "1015" {
  1525  						sriovNetwork.Spec.SpoofChk = off
  1526  					}
  1527  
  1528  					err = clients.Create(context.Background(), sriovNetwork)
  1529  
  1530  					Expect(err).ToNot(HaveOccurred())
  1531  
  1532  					Eventually(func() error {
  1533  						netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
  1534  						return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: "test-mtuvolnetwork", Namespace: namespaces.Test}, netAttDef)
  1535  					}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
  1536  
  1537  				})
  1538  
  1539  				// 27662
  1540  				It("Should support jumbo frames", func() {
  1541  					podDefinition := pod.DefineWithNetworks([]string{"test-mtuvolnetwork"})
  1542  					firstPod, err := clients.Pods(namespaces.Test).Create(context.Background(), podDefinition, metav1.CreateOptions{})
  1543  					Expect(err).ToNot(HaveOccurred())
  1544  
  1545  					Eventually(func() corev1.PodPhase {
  1546  						firstPod, _ = clients.Pods(namespaces.Test).Get(context.Background(), firstPod.Name, metav1.GetOptions{})
  1547  						return firstPod.Status.Phase
  1548  					}, 3*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
  1549  
  1550  					var stdout, stderr string
  1551  					Eventually(func() error {
  1552  						stdout, stderr, err = pod.ExecCommand(clients, firstPod, "ip", "link", "show", "net1")
  1553  						if stdout == "" {
  1554  							return fmt.Errorf("empty response from pod exec")
  1555  						}
  1556  
  1557  						if err != nil {
  1558  							return fmt.Errorf("failed to show net1")
  1559  						}
  1560  
  1561  						return nil
  1562  					}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
  1563  					Expect(stdout).To(ContainSubstring("mtu 9000"))
  1564  					firstPodIPs, err := network.GetSriovNicIPs(firstPod, "net1")
  1565  					Expect(err).ToNot(HaveOccurred())
  1566  					Expect(len(firstPodIPs)).To(Equal(1))
  1567  
  1568  					podDefinition = pod.DefineWithNetworks([]string{"test-mtuvolnetwork"})
  1569  					secondPod, err := clients.Pods(namespaces.Test).Create(context.Background(), podDefinition, metav1.CreateOptions{})
  1570  					Expect(err).ToNot(HaveOccurred())
  1571  
  1572  					Eventually(func() corev1.PodPhase {
  1573  						secondPod, _ = clients.Pods(namespaces.Test).Get(context.Background(), secondPod.Name, metav1.GetOptions{})
  1574  						return secondPod.Status.Phase
  1575  					}, 3*time.Minute, time.Second).Should(Equal(corev1.PodRunning))
  1576  
  1577  					stdout, stderr, err = pod.ExecCommand(clients, secondPod,
  1578  						"ping", firstPodIPs[0], "-s", "8972", "-M", "do", "-c", "2")
  1579  					Expect(err).ToNot(HaveOccurred(), "Failed to ping first pod", stderr)
  1580  					Expect(stdout).To(ContainSubstring("2 packets transmitted, 2 received, 0% packet loss"))
  1581  				})
  1582  			})
  1583  
  1584  			Context("ExcludeTopology", func() {
  1585  
  1586  				var excludeTopologyTrueResourceXXX, excludeTopologyFalseResourceXXX, excludeTopologyFalseResourceYYY *sriovv1.SriovNetworkNodePolicy
  1587  				var node string
  1588  				var intf *sriovv1.InterfaceExt
  1589  
  1590  				BeforeEach(func() {
  1591  					if discovery.Enabled() {
  1592  						Skip("Test unsuitable to be run in discovery mode")
  1593  					}
  1594  
  1595  					node = sriovInfos.Nodes[0]
  1596  					sriovDeviceList, err := sriovInfos.FindSriovDevices(node)
  1597  					Expect(err).ToNot(HaveOccurred())
  1598  					unusedSriovDevices, err := findUnusedSriovDevices(node, sriovDeviceList)
  1599  					Expect(err).ToNot(HaveOccurred())
  1600  
  1601  					intf = unusedSriovDevices[0]
  1602  					By("Using device " + intf.Name + " on node " + node)
  1603  
  1604  					excludeTopologyTrueResourceXXX = &sriovv1.SriovNetworkNodePolicy{
  1605  						ObjectMeta: metav1.ObjectMeta{
  1606  							Name:      "test-exclude-topology-true-res-xxx",
  1607  							Namespace: operatorNamespace,
  1608  						},
  1609  
  1610  						Spec: sriovv1.SriovNetworkNodePolicySpec{
  1611  							NumVfs:       7,
  1612  							ResourceName: "resourceXXX",
  1613  							NodeSelector: map[string]string{"kubernetes.io/hostname": node},
  1614  							NicSelector: sriovv1.SriovNetworkNicSelector{
  1615  								PfNames: []string{intf.Name + "#0-3"},
  1616  							},
  1617  							ExcludeTopology: true,
  1618  						},
  1619  					}
  1620  
  1621  					excludeTopologyFalseResourceXXX = &sriovv1.SriovNetworkNodePolicy{
  1622  						ObjectMeta: metav1.ObjectMeta{
  1623  							Name:      "test-exclude-topology-false-res-xxx",
  1624  							Namespace: operatorNamespace,
  1625  						},
  1626  
  1627  						Spec: sriovv1.SriovNetworkNodePolicySpec{
  1628  							NumVfs:       7,
  1629  							ResourceName: "resourceXXX",
  1630  							NodeSelector: map[string]string{"kubernetes.io/hostname": node},
  1631  							NicSelector: sriovv1.SriovNetworkNicSelector{
  1632  								PfNames: []string{intf.Name + "#4-6"},
  1633  							},
  1634  							ExcludeTopology: false,
  1635  						},
  1636  					}
  1637  
  1638  					excludeTopologyFalseResourceYYY = &sriovv1.SriovNetworkNodePolicy{
  1639  						ObjectMeta: metav1.ObjectMeta{
  1640  							Name:      "test-exclude-topology-false-res-yyy",
  1641  							Namespace: operatorNamespace,
  1642  						},
  1643  
  1644  						Spec: sriovv1.SriovNetworkNodePolicySpec{
  1645  							NumVfs:       7,
  1646  							ResourceName: "resourceYYY",
  1647  							NodeSelector: map[string]string{"kubernetes.io/hostname": node},
  1648  							NicSelector: sriovv1.SriovNetworkNicSelector{
  1649  								PfNames: []string{intf.Name + "#4-6"},
  1650  							},
  1651  							ExcludeTopology: false,
  1652  						},
  1653  					}
  1654  
  1655  				})
  1656  
  1657  				It("field is forwarded to the device plugin configuration", func() {
  1658  
  1659  					err := clients.Create(context.Background(), excludeTopologyTrueResourceXXX)
  1660  					Expect(err).ToNot(HaveOccurred())
  1661  
  1662  					assertDevicePluginConfigurationContains(node,
  1663  						fmt.Sprintf(`{"resourceName":"resourceXXX","excludeTopology":true,"selectors":{"pfNames":["%s#0-3"],"IsRdma":false,"NeedVhostNet":false},"SelectorObj":null}`, intf.Name))
  1664  
  1665  					err = clients.Create(context.Background(), excludeTopologyFalseResourceYYY)
  1666  					Expect(err).ToNot(HaveOccurred())
  1667  
  1668  					assertDevicePluginConfigurationContains(node,
  1669  						fmt.Sprintf(`{"resourceName":"resourceXXX","excludeTopology":true,"selectors":{"pfNames":["%s#0-3"],"IsRdma":false,"NeedVhostNet":false},"SelectorObj":null}`, intf.Name))
  1670  					assertDevicePluginConfigurationContains(node,
  1671  						fmt.Sprintf(`{"resourceName":"resourceYYY","selectors":{"pfNames":["%s#4-6"],"IsRdma":false,"NeedVhostNet":false},"SelectorObj":null}`, intf.Name))
  1672  				})
  1673  
  1674  				It("multiple values for the same resource should not be allowed", func() {
  1675  
  1676  					err := clients.Create(context.Background(), excludeTopologyTrueResourceXXX)
  1677  					Expect(err).ToNot(HaveOccurred())
  1678  
  1679  					err = clients.Create(context.Background(), excludeTopologyFalseResourceXXX)
  1680  					Expect(err).To(HaveOccurred())
  1681  
  1682  					Expect(err.Error()).To(ContainSubstring(
  1683  						"excludeTopology[false] field conflicts with policy [test-exclude-topology-true-res-xxx].ExcludeTopology[true]" +
  1684  							" as they target the same resource[resourceXXX]"))
  1685  				})
  1686  			})
  1687  		})
  1688  
  1689  		Context("Nic Validation", func() {
  1690  			numVfs := 5
  1691  			resourceName := testResourceName
  1692  
  1693  			BeforeEach(func() {
  1694  				if discovery.Enabled() {
  1695  					Skip("Test unsuitable to be run in discovery mode")
  1696  				}
  1697  			})
  1698  
  1699  			findSriovDevice := func(vendorID, deviceID string) (string, sriovv1.InterfaceExt) {
  1700  				for _, node := range sriovInfos.Nodes {
  1701  					for _, nic := range sriovInfos.States[node].Status.Interfaces {
  1702  						if vendorID != "" && deviceID != "" && nic.Vendor == vendorID && nic.DeviceID == deviceID {
  1703  							return node, nic
  1704  						}
  1705  					}
  1706  				}
  1707  
  1708  				return "", sriovv1.InterfaceExt{}
  1709  			}
  1710  
  1711  			DescribeTable("Test connectivity using the requested nic", func(vendorID, deviceID string) {
  1712  				By("searching for the requested network card")
  1713  				node, nic := findSriovDevice(vendorID, deviceID)
  1714  				if node == "" {
  1715  					Skip(fmt.Sprintf("skip nic validate as wasn't able to find a nic with vendorID %s and deviceID %s", vendorID, deviceID))
  1716  				}
  1717  
  1718  				By("creating a network policy")
  1719  				config := &sriovv1.SriovNetworkNodePolicy{
  1720  					ObjectMeta: metav1.ObjectMeta{
  1721  						GenerateName: "test-policy",
  1722  						Namespace:    operatorNamespace,
  1723  					},
  1724  
  1725  					Spec: sriovv1.SriovNetworkNodePolicySpec{
  1726  						NodeSelector: map[string]string{
  1727  							"kubernetes.io/hostname": node,
  1728  						},
  1729  						NumVfs:       numVfs,
  1730  						ResourceName: resourceName,
  1731  						Priority:     99,
  1732  						NicSelector: sriovv1.SriovNetworkNicSelector{
  1733  							PfNames: []string{nic.Name},
  1734  						},
  1735  						DeviceType: "netdevice",
  1736  					},
  1737  				}
  1738  				err := clients.Create(context.Background(), config)
  1739  				Expect(err).ToNot(HaveOccurred())
  1740  
  1741  				By("waiting for the node state to be updated")
  1742  				Eventually(func() sriovv1.Interfaces {
  1743  					nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), node, metav1.GetOptions{})
  1744  					Expect(err).ToNot(HaveOccurred())
  1745  					return nodeState.Spec.Interfaces
  1746  				}, 1*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  1747  					IgnoreExtras,
  1748  					Fields{
  1749  						"Name":   Equal(nic.Name),
  1750  						"NumVfs": Equal(numVfs),
  1751  					})))
  1752  
  1753  				By("waiting the sriov to be stable on the node")
  1754  				WaitForSRIOVStable()
  1755  
  1756  				By("waiting for the resources to be available")
  1757  				Eventually(func() int64 {
  1758  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  1759  					Expect(err).ToNot(HaveOccurred())
  1760  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  1761  					allocatable, _ := resNum.AsInt64()
  1762  					return allocatable
  1763  				}, 10*time.Minute, time.Second).Should(Equal(int64(numVfs)))
  1764  
  1765  				By("creating a network object")
  1766  				ipv6NetworkName := testIpv6NetworkName
  1767  				ipam := ipamIpv4
  1768  				err = network.CreateSriovNetwork(clients, &nic, ipv6NetworkName, namespaces.Test, operatorNamespace, resourceName, ipam)
  1769  				Expect(err).ToNot(HaveOccurred())
  1770  				waitForNetAttachDef(ipv6NetworkName, namespaces.Test)
  1771  
  1772  				By("creating a pod")
  1773  				pod := createTestPod(node, []string{ipv6NetworkName})
  1774  				ips, err := network.GetSriovNicIPs(pod, "net1")
  1775  				Expect(err).ToNot(HaveOccurred())
  1776  				Expect(ips).NotTo(BeNil(), "No sriov network interface found.")
  1777  				Expect(len(ips)).Should(Equal(1))
  1778  
  1779  				By("run pod from another pod")
  1780  				for _, ip := range ips {
  1781  					pingPod(ip, node, ipv6NetworkName)
  1782  				}
  1783  			},
  1784  				//25321
  1785  				Entry("Intel Corporation Ethernet Controller XXV710 for 25GbE SFP28", "8086", "158b"),
  1786  				Entry("Ethernet Controller XXV710 Intel(R) FPGA Programmable Acceleration Card N3000 for Networking", "8086", "0d58"))
  1787  		})
  1788  
  1789  		Context("Resource Injector", func() {
  1790  			BeforeEach(func() {
  1791  				if discovery.Enabled() {
  1792  					Skip("Test unsuitable to be run in discovery mode")
  1793  				}
  1794  			})
  1795  
  1796  			AfterEach(func() {
  1797  				if !discovery.Enabled() {
  1798  					setSriovOperatorSpecFlag(operatorNetworkInjectorFlag, true)
  1799  					setSriovOperatorSpecFlag(operatorWebhookFlag, true)
  1800  				}
  1801  			})
  1802  
  1803  			It("SR-IOV Operator Config, disable Resource Injector", func() {
  1804  
  1805  				setSriovOperatorSpecFlag(operatorNetworkInjectorFlag, false)
  1806  
  1807  				assertObjectIsNotFound("network-resources-injector", &appsv1.DaemonSet{})
  1808  
  1809  				Eventually(func() int {
  1810  					podsList, err := clients.Pods(operatorNamespace).
  1811  						List(context.Background(),
  1812  							metav1.ListOptions{LabelSelector: "app=network-resources-injector"})
  1813  					Expect(err).ToNot(HaveOccurred())
  1814  					return len(podsList.Items)
  1815  				}, 2*time.Minute, 10*time.Second).Should(BeZero())
  1816  
  1817  				assertObjectIsNotFound("network-resources-injector-service", &corev1.Service{})
  1818  				assertObjectIsNotFound("network-resources-injector", &rbacv1.ClusterRole{})
  1819  				assertObjectIsNotFound("network-resources-injector-role-binding", &rbacv1.ClusterRoleBinding{})
  1820  				assertObjectIsNotFound("network-resources-injector-config", &admission.MutatingWebhookConfiguration{})
  1821  				assertObjectIsNotFound("nri-control-switches", &corev1.ConfigMap{})
  1822  			})
  1823  
  1824  			It("SR-IOV Operator Config, disable Operator Webhook", func() {
  1825  
  1826  				setSriovOperatorSpecFlag(operatorWebhookFlag, false)
  1827  
  1828  				assertObjectIsNotFound("operator-webhook", &appsv1.DaemonSet{})
  1829  
  1830  				Eventually(func() int {
  1831  					podsList, err := clients.Pods(operatorNamespace).
  1832  						List(context.Background(),
  1833  							metav1.ListOptions{LabelSelector: "app=operator-webhook"})
  1834  					Expect(err).ToNot(HaveOccurred())
  1835  					return len(podsList.Items)
  1836  				}, 2*time.Minute, 10*time.Second).Should(BeZero())
  1837  
  1838  				assertObjectIsNotFound("operator-webhook-service", &corev1.Service{})
  1839  				assertObjectIsNotFound("operator-webhook", &rbacv1.ClusterRole{})
  1840  				assertObjectIsNotFound("operator-webhook-role-binding", &rbacv1.ClusterRoleBinding{})
  1841  				assertObjectIsNotFound("sriov-operator-webhook-config", &admission.MutatingWebhookConfiguration{})
  1842  			})
  1843  
  1844  			It("SR-IOV Operator Config, disable Resource Injector and Operator Webhook", func() {
  1845  
  1846  				setSriovOperatorSpecFlag(operatorNetworkInjectorFlag, false)
  1847  				setSriovOperatorSpecFlag(operatorWebhookFlag, false)
  1848  
  1849  				// Assert disabling both flags works, no need to check every resource as above tests.
  1850  				assertObjectIsNotFound("operator-webhook", &appsv1.DaemonSet{})
  1851  				assertObjectIsNotFound("network-resources-injector", &appsv1.DaemonSet{})
  1852  			})
  1853  
  1854  		})
  1855  
  1856  		Context("vhost-net and tun devices Validation", func() {
  1857  			var node string
  1858  			resourceName := "vhostresource"
  1859  			vhostnetwork := "test-vhostnetwork"
  1860  			numVfs := 5
  1861  			var intf *sriovv1.InterfaceExt
  1862  			var err error
  1863  
  1864  			execute.BeforeAll(func() {
  1865  				if discovery.Enabled() {
  1866  					node, resourceName, numVfs, intf, err = discovery.DiscoveredResources(clients,
  1867  						sriovInfos, operatorNamespace,
  1868  						func(policy sriovv1.SriovNetworkNodePolicy) bool {
  1869  							if !defaultFilterPolicy(policy) {
  1870  								return false
  1871  							}
  1872  							if !policy.Spec.NeedVhostNet {
  1873  								return false
  1874  							}
  1875  							return true
  1876  						},
  1877  						func(node string, sriovDeviceList []*sriovv1.InterfaceExt) (*sriovv1.InterfaceExt, bool) {
  1878  							if len(sriovDeviceList) == 0 {
  1879  								return nil, false
  1880  							}
  1881  							return sriovDeviceList[0], true
  1882  						},
  1883  					)
  1884  					Expect(err).ToNot(HaveOccurred())
  1885  					if node == "" || resourceName == "" || numVfs < 5 || intf == nil {
  1886  						Skip("Insufficient resources to run test in discovery mode")
  1887  					}
  1888  				} else {
  1889  					node = sriovInfos.Nodes[0]
  1890  					sriovDeviceList, err := sriovInfos.FindSriovDevices(node)
  1891  					Expect(err).ToNot(HaveOccurred())
  1892  					unusedSriovDevices, err := findUnusedSriovDevices(node, sriovDeviceList)
  1893  					if err != nil {
  1894  						Skip(err.Error())
  1895  					}
  1896  					intf = unusedSriovDevices[0]
  1897  					By("Using device " + intf.Name + " on node " + node)
  1898  
  1899  					mtuPolicy := &sriovv1.SriovNetworkNodePolicy{
  1900  						ObjectMeta: metav1.ObjectMeta{
  1901  							GenerateName: "test-vhostpolicy",
  1902  							Namespace:    operatorNamespace,
  1903  						},
  1904  
  1905  						Spec: sriovv1.SriovNetworkNodePolicySpec{
  1906  							NodeSelector: map[string]string{
  1907  								"kubernetes.io/hostname": node,
  1908  							},
  1909  							NumVfs:       5,
  1910  							ResourceName: resourceName,
  1911  							Priority:     99,
  1912  							NicSelector: sriovv1.SriovNetworkNicSelector{
  1913  								PfNames: []string{intf.Name},
  1914  							},
  1915  							DeviceType:   "netdevice",
  1916  							NeedVhostNet: true,
  1917  						},
  1918  					}
  1919  
  1920  					err = clients.Create(context.Background(), mtuPolicy)
  1921  					Expect(err).ToNot(HaveOccurred())
  1922  
  1923  					WaitForSRIOVStable()
  1924  					By("waiting for the resources to be available")
  1925  					Eventually(func() int64 {
  1926  						testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  1927  						Expect(err).ToNot(HaveOccurred())
  1928  						resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  1929  						allocatable, _ := resNum.AsInt64()
  1930  						return allocatable
  1931  					}, 10*time.Minute, time.Second).Should(Equal(int64(5)))
  1932  				}
  1933  
  1934  				sriovNetwork := &sriovv1.SriovNetwork{
  1935  					ObjectMeta: metav1.ObjectMeta{
  1936  						Name:      vhostnetwork,
  1937  						Namespace: operatorNamespace,
  1938  					},
  1939  					Spec: sriovv1.SriovNetworkSpec{
  1940  						ResourceName:     resourceName,
  1941  						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"}`,
  1942  						NetworkNamespace: namespaces.Test,
  1943  					}}
  1944  
  1945  				// We need this to be able to run the connectivity checks on Mellanox cards
  1946  				if intf.DeviceID == "1015" {
  1947  					sriovNetwork.Spec.SpoofChk = off
  1948  				}
  1949  
  1950  				err = clients.Create(context.Background(), sriovNetwork)
  1951  
  1952  				Expect(err).ToNot(HaveOccurred())
  1953  
  1954  				Eventually(func() error {
  1955  					netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
  1956  					return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: vhostnetwork, Namespace: namespaces.Test}, netAttDef)
  1957  				}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
  1958  
  1959  			})
  1960  
  1961  			It("Should have the vhost-net device inside the container", func() {
  1962  				By("creating a pod")
  1963  				podObj := createCustomTestPod(node, []string{vhostnetwork}, false, []corev1.Capability{"NET_ADMIN", "NET_RAW"})
  1964  				ips, err := network.GetSriovNicIPs(podObj, "net1")
  1965  				Expect(err).ToNot(HaveOccurred())
  1966  				Expect(ips).NotTo(BeNil(), "No sriov network interface found.")
  1967  				Expect(len(ips)).Should(Equal(1))
  1968  
  1969  				By("checking the /dev/vhost device exist inside the container")
  1970  				output, errOutput, err := pod.ExecCommand(clients, podObj, "ls", "/dev/vhost-net")
  1971  				Expect(err).ToNot(HaveOccurred())
  1972  				Expect(errOutput).To(Equal(""))
  1973  				Expect(output).ToNot(ContainSubstring("cannot access"))
  1974  
  1975  				By("checking the /dev/vhost device exist inside the container")
  1976  				output, errOutput, err = pod.ExecCommand(clients, podObj, "ls", "/dev/net/tun")
  1977  				Expect(err).ToNot(HaveOccurred())
  1978  				Expect(errOutput).To(Equal(""))
  1979  				Expect(output).ToNot(ContainSubstring("cannot access"))
  1980  
  1981  				By("creating a tap device inside the container")
  1982  				output, errOutput, err = pod.ExecCommand(clients, podObj, "ip", "tuntap", "add", "tap23", "mode", "tap", "multi_queue")
  1983  				Expect(err).ToNot(HaveOccurred())
  1984  				Expect(errOutput).To(Equal(""))
  1985  				Expect(output).ToNot(ContainSubstring("No such file"))
  1986  
  1987  				By("checking the tap device was created inside the container")
  1988  				output, errOutput, err = pod.ExecCommand(clients, podObj, "ip", "link", "show", "tap23")
  1989  				Expect(err).ToNot(HaveOccurred())
  1990  				Expect(errOutput).To(Equal(""))
  1991  				Expect(output).To(ContainSubstring("tap23: <BROADCAST,MULTICAST> mtu 1500"))
  1992  
  1993  				err = clients.Delete(context.Background(), podObj)
  1994  				Expect(err).ToNot(HaveOccurred())
  1995  			})
  1996  		})
  1997  
  1998  		Context("ExternallyManaged Validation", func() {
  1999  			numVfs := 5
  2000  			var node string
  2001  			var nic *sriovv1.InterfaceExt
  2002  			externallyManage := func(policy *sriovv1.SriovNetworkNodePolicy) {
  2003  				policy.Spec.ExternallyManaged = true
  2004  			}
  2005  
  2006  			execute.BeforeAll(func() {
  2007  				var err error
  2008  				node, nic, err = sriovInfos.FindOneSriovNodeAndDevice()
  2009  				Expect(err).ToNot(HaveOccurred())
  2010  
  2011  				By("Using device " + nic.Name + " on node " + node)
  2012  			})
  2013  
  2014  			It("Should not allow to create a policy if there are no vfs configured", func() {
  2015  				resourceName := "test"
  2016  				_, err := network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, nic.Name, node, numVfs, resourceName, "netdevice", externallyManage)
  2017  				Expect(err).To(HaveOccurred())
  2018  				Expect(err.Error()).To(ContainSubstring("is higher than the virtual functions allocated for the PF externally"))
  2019  			})
  2020  
  2021  			It("Should create a policy if the number of requested vfs is equal", func() {
  2022  				resourceName := "testexternally" //nolint:goconst
  2023  				By("allocating the 5 virtual functions to the selected device")
  2024  				_, errOutput, err := runCommandOnConfigDaemon(node, "/bin/bash", "-c", fmt.Sprintf("echo 5 > /host/sys/class/net/%s/device/sriov_numvfs", nic.Name))
  2025  				Expect(err).ToNot(HaveOccurred())
  2026  				Expect(errOutput).To(Equal(""))
  2027  
  2028  				By("creating the policy that will use the 5 virtual functions we create manually on the system")
  2029  				Eventually(func() error {
  2030  					_, err := network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, nic.Name, node, numVfs, resourceName, "netdevice", externallyManage)
  2031  					return err
  2032  				}, 1*time.Minute, time.Second).ShouldNot(HaveOccurred())
  2033  
  2034  				Eventually(func() int64 {
  2035  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  2036  					Expect(err).ToNot(HaveOccurred())
  2037  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  2038  					allocatable, _ := resNum.AsInt64()
  2039  					return allocatable
  2040  				}, 2*time.Minute, time.Second).Should(Equal(int64(numVfs)))
  2041  
  2042  				By("cleaning the manual sriov created")
  2043  				_, errOutput, err = runCommandOnConfigDaemon(node, "/bin/bash", "-c", fmt.Sprintf("echo 0 > /host/sys/class/net/%s/device/sriov_numvfs", nic.Name))
  2044  				Expect(err).ToNot(HaveOccurred())
  2045  				Expect(errOutput).To(Equal(""))
  2046  			})
  2047  
  2048  			It("Should create a policy if the number of requested vfs is equal and not delete them when the policy is removed", func() {
  2049  				resourceName := "testexternally"
  2050  				var sriovPolicy *sriovv1.SriovNetworkNodePolicy
  2051  				By("allocating the 5 virtual functions to the selected device")
  2052  				_, errOutput, err := runCommandOnConfigDaemon(node, "/bin/bash", "-c", fmt.Sprintf("echo 5 > /host/sys/class/net/%s/device/sriov_numvfs", nic.Name))
  2053  				Expect(err).ToNot(HaveOccurred())
  2054  				Expect(errOutput).To(Equal(""))
  2055  
  2056  				By("creating the policy that will use the 5 virtual functions we create manually on the system")
  2057  				Eventually(func() error {
  2058  					sriovPolicy, err = network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, nic.Name, node, numVfs, resourceName, "netdevice", externallyManage)
  2059  					return err
  2060  				}, 2*time.Minute, time.Second).ShouldNot(HaveOccurred())
  2061  
  2062  				Eventually(func() int64 {
  2063  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  2064  					Expect(err).ToNot(HaveOccurred())
  2065  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  2066  					allocatable, _ := resNum.AsInt64()
  2067  					return allocatable
  2068  				}, 3*time.Minute, time.Second).Should(Equal(int64(numVfs)))
  2069  
  2070  				By("deleting the policy")
  2071  				err = clients.Delete(context.Background(), sriovPolicy, &runtimeclient.DeleteOptions{})
  2072  				Expect(err).ToNot(HaveOccurred())
  2073  				WaitForSRIOVStable()
  2074  
  2075  				Eventually(func() int64 {
  2076  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  2077  					Expect(err).ToNot(HaveOccurred())
  2078  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  2079  					allocatable, _ := resNum.AsInt64()
  2080  					return allocatable
  2081  				}, 2*time.Minute, time.Second).Should(Equal(int64(0)))
  2082  
  2083  				By("checking the virtual functions are still on the host")
  2084  				output, errOutput, err := runCommandOnConfigDaemon(node, "/bin/bash", "-c", fmt.Sprintf("cat /host/sys/class/net/%s/device/sriov_numvfs", nic.Name))
  2085  				Expect(err).ToNot(HaveOccurred())
  2086  				Expect(errOutput).To(Equal(""))
  2087  				Expect(output).To(ContainSubstring("5"))
  2088  
  2089  				By("cleaning the manual sriov created")
  2090  				_, errOutput, err = runCommandOnConfigDaemon(node, "/bin/bash", "-c", fmt.Sprintf("echo 0 > /host/sys/class/net/%s/device/sriov_numvfs", nic.Name))
  2091  				Expect(err).ToNot(HaveOccurred())
  2092  				Expect(errOutput).To(Equal(""))
  2093  			})
  2094  
  2095  			It("should reset the virtual functions if externallyManaged is false", func() {
  2096  				resourceName := "testexternally" //nolint:goconst
  2097  
  2098  				var sriovPolicy *sriovv1.SriovNetworkNodePolicy
  2099  				By("creating the policy for 5 virtual functions")
  2100  				sriovPolicy, err := network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, nic.Name, node, numVfs, resourceName, "netdevice")
  2101  				Expect(err).ToNot(HaveOccurred())
  2102  
  2103  				Eventually(func() int64 {
  2104  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  2105  					Expect(err).ToNot(HaveOccurred())
  2106  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  2107  					allocatable, _ := resNum.AsInt64()
  2108  					return allocatable
  2109  				}, 3*time.Minute, time.Second).Should(Equal(int64(numVfs)))
  2110  
  2111  				By("deleting the policy")
  2112  				err = clients.Delete(context.Background(), sriovPolicy, &runtimeclient.DeleteOptions{})
  2113  				Expect(err).ToNot(HaveOccurred())
  2114  				WaitForSRIOVStable()
  2115  
  2116  				Eventually(func() int64 {
  2117  					testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), node, metav1.GetOptions{})
  2118  					Expect(err).ToNot(HaveOccurred())
  2119  					resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  2120  					allocatable, _ := resNum.AsInt64()
  2121  					return allocatable
  2122  				}, 3*time.Minute, time.Second).Should(Equal(int64(0)))
  2123  
  2124  				By("checking the virtual functions don't exist anymore on the system")
  2125  				output, errOutput, err := runCommandOnConfigDaemon(node, "/bin/bash", "-c", fmt.Sprintf("cat /host/sys/class/net/%s/device/sriov_numvfs", nic.Name))
  2126  				Expect(err).ToNot(HaveOccurred())
  2127  				Expect(errOutput).To(Equal(""))
  2128  				Expect(output).To(ContainSubstring("0"))
  2129  			})
  2130  		})
  2131  	})
  2132  })
  2133  
  2134  func getDriver(ethtoolstdout string) string {
  2135  	lines := strings.Split(ethtoolstdout, "\n")
  2136  	Expect(len(lines)).To(BeNumerically(">", 0))
  2137  	for _, line := range lines {
  2138  		if strings.HasPrefix(line, "driver:") {
  2139  			return strings.TrimSpace(line[len("driver:"):])
  2140  		}
  2141  	}
  2142  	Fail("Could not find device driver")
  2143  	return ""
  2144  }
  2145  
  2146  func changeNodeInterfaceState(testNode string, ifcName string, enable bool) {
  2147  	state := "up"
  2148  	if !enable {
  2149  		state = "down"
  2150  	}
  2151  	podDefinition := pod.RedefineAsPrivileged(
  2152  		pod.RedefineWithRestartPolicy(
  2153  			pod.RedefineWithCommand(
  2154  				pod.DefineWithHostNetwork(testNode),
  2155  				[]string{"ip", "link", "set", "dev", ifcName, state}, []string{},
  2156  			),
  2157  			corev1.RestartPolicyNever,
  2158  		),
  2159  	)
  2160  	createdPod, err := clients.Pods(namespaces.Test).Create(context.Background(), podDefinition, metav1.CreateOptions{})
  2161  	Expect(err).ToNot(HaveOccurred())
  2162  	Eventually(func() corev1.PodPhase {
  2163  		runningPod, err := clients.Pods(namespaces.Test).Get(context.Background(), createdPod.Name, metav1.GetOptions{})
  2164  		Expect(err).ToNot(HaveOccurred())
  2165  		return runningPod.Status.Phase
  2166  	}, 3*time.Minute, 1*time.Second).Should(Equal(corev1.PodSucceeded))
  2167  }
  2168  
  2169  func discoverResourceForMainSriov(nodes *cluster.EnabledNodes) (*sriovv1.InterfaceExt, string, string, bool) {
  2170  	for _, node := range nodes.Nodes {
  2171  		nodeDevices, err := nodes.FindSriovDevices(node)
  2172  		Expect(err).ToNot(HaveOccurred())
  2173  		if len(nodeDevices) == 0 {
  2174  			continue
  2175  		}
  2176  
  2177  		executorPod := createCustomTestPod(node, []string{}, true, nil)
  2178  		mainDevice := findMainSriovDevice(executorPod, nodeDevices)
  2179  		if mainDevice == nil {
  2180  			return nil, "", "", false
  2181  		}
  2182  
  2183  		nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), node, metav1.GetOptions{})
  2184  		Expect(err).ToNot(HaveOccurred())
  2185  		resourceName, ok := findSuitableResourceForMain(mainDevice, nodeState)
  2186  		if ok {
  2187  			fmt.Printf("Using %s with resource %s for node %s", mainDevice.Name, resourceName, node)
  2188  			return mainDevice, resourceName, node, true
  2189  		}
  2190  	}
  2191  	return nil, "", "", false
  2192  }
  2193  
  2194  func findSuitableResourceForMain(mainIntf *sriovv1.InterfaceExt, networkState *sriovv1.SriovNetworkNodeState) (string, bool) {
  2195  	for _, intf := range networkState.Spec.Interfaces {
  2196  		if intf.Name == mainIntf.Name {
  2197  			for _, vfGroup := range intf.VfGroups {
  2198  				// we want to make sure that selecting the resource name means
  2199  				// selecting the primary interface
  2200  				if resourceOnlyForInterface(networkState, intf.Name, vfGroup.ResourceName) {
  2201  					return vfGroup.ResourceName, true
  2202  				}
  2203  			}
  2204  		}
  2205  	}
  2206  
  2207  	return "", false
  2208  }
  2209  
  2210  func resourceOnlyForInterface(networkState *sriovv1.SriovNetworkNodeState, resourceName, interfaceName string) bool {
  2211  	for _, intf := range networkState.Spec.Interfaces {
  2212  		if intf.Name != interfaceName {
  2213  			for _, vfGroup := range intf.VfGroups {
  2214  				if vfGroup.ResourceName == resourceName {
  2215  					return false
  2216  				}
  2217  			}
  2218  		}
  2219  	}
  2220  	return true
  2221  }
  2222  
  2223  func findMainSriovDevice(executorPod *corev1.Pod, sriovDevices []*sriovv1.InterfaceExt) *sriovv1.InterfaceExt {
  2224  	stdout, _, err := pod.ExecCommand(clients, executorPod, "ip", "route")
  2225  	Expect(err).ToNot(HaveOccurred())
  2226  	routes := strings.Split(stdout, "\n")
  2227  
  2228  	for _, device := range sriovDevices {
  2229  		if isDefaultRouteInterface(device.Name, routes) {
  2230  			fmt.Println("Chosen ", device.Name, " as it is the default gw")
  2231  			return device
  2232  		}
  2233  		stdout, _, err = pod.ExecCommand(clients, executorPod, "ip", "link", "show", device.Name)
  2234  		Expect(err).ToNot(HaveOccurred())
  2235  		Expect(len(stdout)).Should(Not(Equal(0)), "Unable to query link state")
  2236  		if strings.Contains(stdout, "state DOWN") {
  2237  			continue // The interface is not active
  2238  		}
  2239  		if strings.Contains(stdout, "master ovs-system") {
  2240  			fmt.Println("Chosen ", device.Name, " as it is used by ovs")
  2241  			return device
  2242  		}
  2243  	}
  2244  	return nil
  2245  }
  2246  
  2247  func findUnusedSriovDevices(testNode string, sriovDevices []*sriovv1.InterfaceExt) ([]*sriovv1.InterfaceExt, error) {
  2248  	createdPod := createCustomTestPod(testNode, []string{}, true, nil)
  2249  	filteredDevices := []*sriovv1.InterfaceExt{}
  2250  	stdout, _, err := pod.ExecCommand(clients, createdPod, "ip", "route")
  2251  	Expect(err).ToNot(HaveOccurred())
  2252  	routes := strings.Split(stdout, "\n")
  2253  
  2254  	for _, device := range sriovDevices {
  2255  		if isDefaultRouteInterface(device.Name, routes) {
  2256  			continue
  2257  		}
  2258  		stdout, _, err = pod.ExecCommand(clients, createdPod, "ip", "link", "show", device.Name)
  2259  		Expect(err).ToNot(HaveOccurred())
  2260  		Expect(len(stdout)).Should(Not(Equal(0)), "Unable to query link state")
  2261  		if strings.Contains(stdout, "master ovs-system") {
  2262  			continue // The interface is not active
  2263  		}
  2264  
  2265  		filteredDevices = append(filteredDevices, device)
  2266  	}
  2267  	if len(filteredDevices) == 0 {
  2268  		return nil, fmt.Errorf("unused sriov devices not found")
  2269  	}
  2270  	return filteredDevices, nil
  2271  }
  2272  
  2273  func isDefaultRouteInterface(intfName string, routes []string) bool {
  2274  	for _, route := range routes {
  2275  		if strings.HasPrefix(route, "default") && strings.Contains(route, "dev "+intfName) {
  2276  			return true
  2277  		}
  2278  	}
  2279  	return false
  2280  }
  2281  
  2282  // podVFIndexInHost retrieves the vf index on the host network namespace related to the given
  2283  // interface that was passed to the pod, using the name in the pod's namespace.
  2284  func podVFIndexInHost(hostNetPod *corev1.Pod, targetPod *corev1.Pod, interfaceName string) (int, error) {
  2285  	var stdout, stderr string
  2286  	var err error
  2287  	Eventually(func() error {
  2288  		stdout, stderr, err = pod.ExecCommand(clients, targetPod, "readlink", "-f", fmt.Sprintf("/sys/class/net/%s", interfaceName))
  2289  		if stdout == "" {
  2290  			return fmt.Errorf("empty response from pod exec")
  2291  		}
  2292  
  2293  		if err != nil {
  2294  			return fmt.Errorf("failed to find %s interface address %v - %s", interfaceName, err, stderr)
  2295  		}
  2296  
  2297  		return nil
  2298  	}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
  2299  
  2300  	// sysfs address looks like: /sys/devices/pci0000:17/0000:17:02.0/0000:19:00.5/net/net1
  2301  	pathSegments := strings.Split(stdout, "/")
  2302  	segNum := len(pathSegments)
  2303  
  2304  	if !strings.HasPrefix(pathSegments[segNum-1], "net1") { // not checking equality because of rubbish like new line
  2305  		return 0, fmt.Errorf("expecting net1 as last segment of %s", stdout)
  2306  	}
  2307  
  2308  	podVFAddr := pathSegments[segNum-3] // 0000:19:00.5
  2309  
  2310  	devicePath := strings.Join(pathSegments[0:segNum-2], "/") // /sys/devices/pci0000:17/0000:17:02.0/0000:19:00.5/
  2311  	findAllSiblingVfs := strings.Split(fmt.Sprintf("ls -gG %s/physfn/", devicePath), " ")
  2312  
  2313  	res := 0
  2314  	Eventually(func() error {
  2315  		stdout, stderr, err = pod.ExecCommand(clients, hostNetPod, findAllSiblingVfs...)
  2316  		if stdout == "" {
  2317  			return fmt.Errorf("empty response from pod exec")
  2318  		}
  2319  
  2320  		if err != nil {
  2321  			return fmt.Errorf("failed to find %s siblings %v - %s", devicePath, err, stderr)
  2322  		}
  2323  
  2324  		// lines of the format of
  2325  		// lrwxrwxrwx. 1        0 Mar  6 15:15 virtfn3 -> ../0000:19:00.5
  2326  		scanner := bufio.NewScanner(strings.NewReader(stdout))
  2327  		for scanner.Scan() {
  2328  			line := scanner.Text()
  2329  			if !strings.Contains(line, "virtfn") {
  2330  				continue
  2331  			}
  2332  
  2333  			columns := strings.Fields(line)
  2334  
  2335  			if len(columns) != 9 {
  2336  				return fmt.Errorf("expecting 9 columns in %s, found %d", line, len(columns))
  2337  			}
  2338  
  2339  			vfAddr := strings.TrimPrefix(columns[8], "../") // ../0000:19:00.2
  2340  
  2341  			if vfAddr == podVFAddr { // Found!
  2342  				vfName := columns[6] // virtfn0
  2343  				vfNumber := strings.TrimPrefix(vfName, "virtfn")
  2344  				res, err = strconv.Atoi(vfNumber)
  2345  				if err != nil {
  2346  					return fmt.Errorf("could not get vf number from vfname %s", vfName)
  2347  				}
  2348  				return nil
  2349  			}
  2350  		}
  2351  		return fmt.Errorf("could not find %s index in %s", podVFAddr, stdout)
  2352  	}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
  2353  
  2354  	return res, nil
  2355  }
  2356  
  2357  func daemonsScheduledOnNodes(selector string) bool {
  2358  	nn, err := clients.CoreV1Interface.Nodes().List(context.Background(), metav1.ListOptions{
  2359  		LabelSelector: selector,
  2360  	})
  2361  	Expect(err).ToNot(HaveOccurred())
  2362  	nodes := nn.Items
  2363  
  2364  	daemons, err := clients.Pods(operatorNamespace).List(context.Background(), metav1.ListOptions{LabelSelector: "app=sriov-network-config-daemon"})
  2365  	Expect(err).ToNot(HaveOccurred())
  2366  	for _, d := range daemons.Items {
  2367  		foundNode := false
  2368  		for i, n := range nodes {
  2369  			if d.Spec.NodeName == n.Name {
  2370  				foundNode = true
  2371  				// Removing the element from the list as we want to make sure
  2372  				// the daemons are running on different nodes
  2373  				nodes = append(nodes[:i], nodes[i+1:]...)
  2374  				break
  2375  			}
  2376  		}
  2377  		if !foundNode {
  2378  			return false
  2379  		}
  2380  	}
  2381  	return true
  2382  }
  2383  
  2384  func createSriovPolicy(sriovDevice string, testNode string, numVfs int, resourceName string) {
  2385  	_, err := network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, sriovDevice, testNode, numVfs, resourceName, "netdevice")
  2386  	Expect(err).ToNot(HaveOccurred())
  2387  	WaitForSRIOVStable()
  2388  
  2389  	Eventually(func() int64 {
  2390  		testedNode, err := clients.CoreV1Interface.Nodes().Get(context.Background(), testNode, metav1.GetOptions{})
  2391  		Expect(err).ToNot(HaveOccurred())
  2392  		resNum := testedNode.Status.Allocatable[corev1.ResourceName("openshift.io/"+resourceName)]
  2393  		capacity, _ := resNum.AsInt64()
  2394  		return capacity
  2395  	}, 10*time.Minute, time.Second).Should(Equal(int64(numVfs)))
  2396  }
  2397  
  2398  func createTestPod(node string, networks []string) *corev1.Pod {
  2399  	return createCustomTestPod(node, networks, false, nil)
  2400  }
  2401  
  2402  func createCustomTestPod(node string, networks []string, hostNetwork bool, podCapabilities []corev1.Capability) *corev1.Pod {
  2403  	var podDefinition *corev1.Pod
  2404  	if hostNetwork {
  2405  		podDefinition = pod.DefineWithHostNetwork(node)
  2406  	} else {
  2407  		podDefinition = pod.RedefineWithNodeSelector(
  2408  			pod.DefineWithNetworks(networks),
  2409  			node,
  2410  		)
  2411  	}
  2412  
  2413  	if len(podCapabilities) != 0 {
  2414  		if podDefinition.Spec.Containers[0].SecurityContext == nil {
  2415  			podDefinition.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{}
  2416  		}
  2417  		if podDefinition.Spec.Containers[0].SecurityContext.Capabilities == nil {
  2418  			podDefinition.Spec.Containers[0].SecurityContext.Capabilities = &corev1.Capabilities{}
  2419  		}
  2420  		podDefinition.Spec.Containers[0].SecurityContext.Capabilities.Add = podCapabilities
  2421  	}
  2422  
  2423  	createdPod, err := clients.Pods(namespaces.Test).Create(context.Background(), podDefinition, metav1.CreateOptions{})
  2424  	Expect(err).ToNot(HaveOccurred())
  2425  
  2426  	Eventually(func() corev1.PodPhase {
  2427  		runningPod, err := clients.Pods(namespaces.Test).Get(context.Background(), createdPod.Name, metav1.GetOptions{})
  2428  		Expect(err).ToNot(HaveOccurred())
  2429  		return runningPod.Status.Phase
  2430  	}, 5*time.Minute, 1*time.Second).Should(Equal(corev1.PodRunning))
  2431  	podObj, err := clients.Pods(namespaces.Test).Get(context.Background(), createdPod.Name, metav1.GetOptions{})
  2432  	Expect(err).ToNot(HaveOccurred())
  2433  	return podObj
  2434  }
  2435  
  2436  func pingPod(ip string, nodeSelector string, sriovNetworkAttachment string) {
  2437  	ipProtocolVersion := "6"
  2438  	if len(strings.Split(ip, ".")) == 4 {
  2439  		ipProtocolVersion = "4"
  2440  	}
  2441  	podDefinition := pod.RedefineWithNodeSelector(
  2442  		pod.RedefineWithCapabilities(
  2443  			pod.RedefineWithRestartPolicy(
  2444  				pod.RedefineWithCommand(
  2445  					pod.DefineWithNetworks([]string{sriovNetworkAttachment}),
  2446  					[]string{"sh", "-c", fmt.Sprintf("ping -%s -c 3 %s", ipProtocolVersion, ip)}, []string{},
  2447  				),
  2448  				corev1.RestartPolicyNever,
  2449  			),
  2450  			[]corev1.Capability{"NET_RAW"},
  2451  		),
  2452  		nodeSelector,
  2453  	)
  2454  
  2455  	createdPod, err := clients.Pods(namespaces.Test).Create(context.Background(), podDefinition, metav1.CreateOptions{})
  2456  	Expect(err).ToNot(HaveOccurred())
  2457  
  2458  	Eventually(func() corev1.PodPhase {
  2459  		runningPod, err := clients.Pods(namespaces.Test).Get(context.Background(), createdPod.Name, metav1.GetOptions{})
  2460  		Expect(err).ToNot(HaveOccurred())
  2461  		return runningPod.Status.Phase
  2462  	}, 3*time.Minute, 1*time.Second).Should(Equal(corev1.PodSucceeded))
  2463  }
  2464  
  2465  func WaitForSRIOVStable() {
  2466  	// This used to be to check for sriov not to be stable first,
  2467  	// then stable. The issue is that if no configuration is applied, then
  2468  	// the status won't never go to not stable and the test will fail.
  2469  	// TODO: find a better way to handle this scenario
  2470  
  2471  	time.Sleep((10 + snoTimeoutMultiplier*20) * time.Second)
  2472  
  2473  	fmt.Println("Waiting for the sriov state to stable")
  2474  	Eventually(func() bool {
  2475  		// ignoring the error. This can eventually be executed against a single node cluster,
  2476  		// and if a reconfiguration triggers a reboot then the api calls will return an error
  2477  		res, _ := cluster.SriovStable(operatorNamespace, clients)
  2478  		return res
  2479  	}, waitingTime, 1*time.Second).Should(BeTrue())
  2480  	fmt.Println("Sriov state is stable")
  2481  
  2482  	Eventually(func() bool {
  2483  		isClusterReady, err := cluster.IsClusterStable(clients)
  2484  		Expect(err).ToNot(HaveOccurred())
  2485  		return isClusterReady
  2486  	}, waitingTime, 1*time.Second).Should(BeTrue())
  2487  }
  2488  
  2489  func createVanillaNetworkPolicy(node string, sriovInfos *cluster.EnabledNodes, numVfs int, resourceName string) {
  2490  	// For the context of tests is better to use a Mellanox card
  2491  	// as they support all the virtual function flags
  2492  	// if we don't find a Mellanox card we fall back to any sriov
  2493  	// capability interface and skip the rate limit test.
  2494  	intf, err := sriovInfos.FindOneMellanoxSriovDevice(node)
  2495  	if err != nil {
  2496  		intf, err = sriovInfos.FindOneSriovDevice(node)
  2497  		Expect(err).ToNot(HaveOccurred())
  2498  	}
  2499  
  2500  	config := &sriovv1.SriovNetworkNodePolicy{
  2501  		ObjectMeta: metav1.ObjectMeta{
  2502  			GenerateName: "test-policy",
  2503  			Namespace:    operatorNamespace,
  2504  		},
  2505  
  2506  		Spec: sriovv1.SriovNetworkNodePolicySpec{
  2507  			NodeSelector: map[string]string{
  2508  				"kubernetes.io/hostname": node,
  2509  			},
  2510  			NumVfs:       numVfs,
  2511  			ResourceName: resourceName,
  2512  			Priority:     99,
  2513  			NicSelector: sriovv1.SriovNetworkNicSelector{
  2514  				PfNames: []string{intf.Name},
  2515  			},
  2516  			DeviceType: "netdevice",
  2517  		},
  2518  	}
  2519  	Eventually(func() error {
  2520  		return clients.Create(context.Background(), config)
  2521  	}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
  2522  
  2523  	Eventually(func() sriovv1.Interfaces {
  2524  		nodeState, err := clients.SriovNetworkNodeStates(operatorNamespace).Get(context.Background(), node, metav1.GetOptions{})
  2525  		Expect(err).ToNot(HaveOccurred())
  2526  		return nodeState.Spec.Interfaces
  2527  	}, 1*time.Minute, 1*time.Second).Should(ContainElement(MatchFields(
  2528  		IgnoreExtras,
  2529  		Fields{
  2530  			"Name":   Equal(intf.Name),
  2531  			"NumVfs": Equal(numVfs),
  2532  		})))
  2533  }
  2534  
  2535  func runCommandOnConfigDaemon(nodeName string, command ...string) (string, string, error) {
  2536  	pods := &corev1.PodList{}
  2537  	label, err := labels.Parse("app=sriov-network-config-daemon")
  2538  	Expect(err).ToNot(HaveOccurred())
  2539  	field, err := fields.ParseSelector(fmt.Sprintf("spec.nodeName=%s", nodeName))
  2540  	Expect(err).ToNot(HaveOccurred())
  2541  	err = clients.List(context.Background(), pods, &runtimeclient.ListOptions{Namespace: operatorNamespace, LabelSelector: label, FieldSelector: field})
  2542  	Expect(err).ToNot(HaveOccurred())
  2543  	Expect(len(pods.Items)).To(Equal(1))
  2544  
  2545  	output, errOutput, err := pod.ExecCommand(clients, &pods.Items[0], command...)
  2546  	return output, errOutput, err
  2547  }
  2548  
  2549  func defaultFilterPolicy(policy sriovv1.SriovNetworkNodePolicy) bool {
  2550  	return policy.Spec.DeviceType == "netdevice"
  2551  }
  2552  
  2553  func setSriovOperatorSpecFlag(flagName string, flagValue bool) {
  2554  	cfg := sriovv1.SriovOperatorConfig{}
  2555  	err := clients.Get(context.TODO(), runtimeclient.ObjectKey{
  2556  		Name:      "default",
  2557  		Namespace: operatorNamespace,
  2558  	}, &cfg)
  2559  
  2560  	Expect(err).ToNot(HaveOccurred())
  2561  	if flagName == operatorNetworkInjectorFlag && cfg.Spec.EnableInjector != flagValue {
  2562  		cfg.Spec.EnableInjector = flagValue
  2563  		err = clients.Update(context.TODO(), &cfg)
  2564  		Expect(err).ToNot(HaveOccurred())
  2565  		Expect(cfg.Spec.EnableInjector).To(Equal(flagValue))
  2566  	}
  2567  
  2568  	if flagName == operatorWebhookFlag && cfg.Spec.EnableOperatorWebhook != flagValue {
  2569  		cfg.Spec.EnableOperatorWebhook = flagValue
  2570  		clients.Update(context.TODO(), &cfg)
  2571  		Expect(err).ToNot(HaveOccurred())
  2572  		Expect(cfg.Spec.EnableOperatorWebhook).To(Equal(flagValue))
  2573  	}
  2574  
  2575  	if flagValue {
  2576  		Eventually(func(g Gomega) {
  2577  			podsList, err := clients.Pods(operatorNamespace).List(context.Background(), metav1.ListOptions{
  2578  				LabelSelector: fmt.Sprintf("app=%s", flagName)})
  2579  			g.Expect(err).ToNot(HaveOccurred())
  2580  
  2581  			g.Expect(len(podsList.Items)).To(BeNumerically(">", 0))
  2582  
  2583  			for _, pod := range podsList.Items {
  2584  				g.Expect(pod.Status.Phase).To(Equal(corev1.PodRunning))
  2585  			}
  2586  		}, 1*time.Minute, 10*time.Second).WithOffset(1).Should(Succeed())
  2587  	}
  2588  }
  2589  
  2590  func setOperatorConfigLogLevel(level int) {
  2591  	instantBeforeSettingLogLevel := time.Now()
  2592  
  2593  	Eventually(func(g Gomega) {
  2594  		cfg := sriovv1.SriovOperatorConfig{}
  2595  		err := clients.Get(context.TODO(), runtimeclient.ObjectKey{
  2596  			Name:      "default",
  2597  			Namespace: operatorNamespace,
  2598  		}, &cfg)
  2599  		g.Expect(err).ToNot(HaveOccurred())
  2600  
  2601  		if cfg.Spec.LogLevel == level {
  2602  			return
  2603  		}
  2604  
  2605  		cfg.Spec.LogLevel = level
  2606  
  2607  		err = clients.Update(context.TODO(), &cfg)
  2608  		g.Expect(err).ToNot(HaveOccurred())
  2609  
  2610  		logs := getOperatorLogs(instantBeforeSettingLogLevel)
  2611  		g.Expect(logs).To(
  2612  			ContainElement(
  2613  				ContainSubstring(fmt.Sprintf(`"new-level": %d`, level)),
  2614  			),
  2615  		)
  2616  	}, 1*time.Minute, 5*time.Second).Should(Succeed())
  2617  }
  2618  
  2619  func getOperatorConfigLogLevel() int {
  2620  	cfg := sriovv1.SriovOperatorConfig{}
  2621  	err := clients.Get(context.TODO(), runtimeclient.ObjectKey{
  2622  		Name:      "default",
  2623  		Namespace: operatorNamespace,
  2624  	}, &cfg)
  2625  	Expect(err).ToNot(HaveOccurred())
  2626  
  2627  	return cfg.Spec.LogLevel
  2628  }
  2629  
  2630  func getOperatorLogs(since time.Time) []string {
  2631  	podList, err := clients.Pods(operatorNamespace).List(context.Background(), metav1.ListOptions{
  2632  		LabelSelector: "name=sriov-network-operator",
  2633  	})
  2634  	ExpectWithOffset(1, err).ToNot(HaveOccurred())
  2635  	ExpectWithOffset(1, podList.Items).To(HaveLen(1), "One operator pod expected")
  2636  
  2637  	pod := podList.Items[0]
  2638  	logStart := metav1.NewTime(since)
  2639  	rawLogs, err := clients.Pods(pod.Namespace).
  2640  		GetLogs(pod.Name, &corev1.PodLogOptions{
  2641  			Container: pod.Spec.Containers[0].Name,
  2642  			SinceTime: &logStart,
  2643  		}).
  2644  		DoRaw(context.Background())
  2645  	ExpectWithOffset(1, err).ToNot(HaveOccurred())
  2646  
  2647  	return strings.Split(string(rawLogs), "\n")
  2648  }
  2649  
  2650  func assertObjectIsNotFound(name string, obj runtimeclient.Object) {
  2651  	Eventually(func() bool {
  2652  		err := clients.Get(context.Background(), runtimeclient.ObjectKey{Name: name, Namespace: operatorNamespace}, obj)
  2653  		return err != nil && k8serrors.IsNotFound(err)
  2654  	}, 2*time.Minute, 10*time.Second).Should(BeTrue())
  2655  }
  2656  
  2657  func assertDevicePluginConfigurationContains(node, configuration string) {
  2658  	Eventually(func(g Gomega) map[string]string {
  2659  		cfg := corev1.ConfigMap{}
  2660  		err := clients.Get(context.Background(), runtimeclient.ObjectKey{
  2661  			Name:      "device-plugin-config",
  2662  			Namespace: operatorNamespace,
  2663  		}, &cfg)
  2664  		g.Expect(err).ToNot(HaveOccurred())
  2665  
  2666  		return cfg.Data
  2667  	}, 30*time.Second, 2*time.Second).Should(
  2668  		HaveKeyWithValue(node, ContainSubstring(configuration)),
  2669  	)
  2670  }
  2671  
  2672  func getMultusPodLogs(nodeName string, since time.Time) []string {
  2673  	podList, err := clients.Pods("").List(context.Background(), metav1.ListOptions{
  2674  		LabelSelector: "app=multus",
  2675  		FieldSelector: "spec.nodeName=" + nodeName,
  2676  	})
  2677  	ExpectWithOffset(1, err).ToNot(HaveOccurred())
  2678  	ExpectWithOffset(1, podList.Items).To(HaveLen(1), "One multus pod expected")
  2679  
  2680  	multusPod := podList.Items[0]
  2681  	logStart := metav1.NewTime(since)
  2682  	rawLogs, err := clients.Pods(multusPod.Namespace).
  2683  		GetLogs(multusPod.Name, &corev1.PodLogOptions{
  2684  			Container: multusPod.Spec.Containers[0].Name,
  2685  			SinceTime: &logStart,
  2686  		}).
  2687  		DoRaw(context.Background())
  2688  	ExpectWithOffset(1, err).ToNot(HaveOccurred())
  2689  
  2690  	return strings.Split(string(rawLogs), "\n")
  2691  }
  2692  
  2693  func waitForNetAttachDef(name, namespace string) {
  2694  	Eventually(func() error {
  2695  		netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
  2696  		return clients.Get(context.Background(), runtimeclient.ObjectKey{Name: name, Namespace: namespace}, netAttDef)
  2697  	}, (10+snoTimeoutMultiplier*110)*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
  2698  }