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 }