github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/api/v1/helper_test.go (about) 1 package v1_test 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "flag" 8 "os" 9 "path/filepath" 10 "testing" 11 12 "github.com/google/go-cmp/cmp" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 intstrutil "k8s.io/apimachinery/pkg/util/intstr" 15 16 v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" 17 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" 18 ) 19 20 var update = flag.Bool("updategolden", false, "update .golden files") 21 22 func init() { 23 // when running go tests path is local to the file, overriding it. 24 v1.ManifestsPath = "../../bindata/manifests/cni-config" 25 } 26 27 func newNodeState() *v1.SriovNetworkNodeState { 28 return &v1.SriovNetworkNodeState{ 29 Spec: v1.SriovNetworkNodeStateSpec{}, 30 Status: v1.SriovNetworkNodeStateStatus{ 31 Interfaces: []v1.InterfaceExt{ 32 { 33 VFs: []v1.VirtualFunction{ 34 {}, 35 }, 36 DeviceID: "158b", 37 Driver: "i40e", 38 Mtu: 1500, 39 Name: "ens803f0", 40 PciAddress: "0000:86:00.0", 41 Vendor: "8086", 42 NumVfs: 4, 43 TotalVfs: 64, 44 }, 45 { 46 VFs: []v1.VirtualFunction{ 47 {}, 48 }, 49 DeviceID: "158b", 50 Driver: "i40e", 51 Mtu: 1500, 52 Name: "ens803f1", 53 PciAddress: "0000:86:00.1", 54 Vendor: "8086", 55 NumVfs: 4, 56 TotalVfs: 64, 57 }, 58 { 59 VFs: []v1.VirtualFunction{ 60 {}, 61 }, 62 DeviceID: "1015", 63 Driver: "i40e", 64 Mtu: 1500, 65 Name: "ens803f2", 66 PciAddress: "0000:86:00.2", 67 Vendor: "8086", 68 NumVfs: 4, 69 TotalVfs: 64, 70 }, 71 }, 72 }, 73 } 74 } 75 76 func newNodePolicy() *v1.SriovNetworkNodePolicy { 77 return &v1.SriovNetworkNodePolicy{ 78 ObjectMeta: metav1.ObjectMeta{ 79 Name: "p1", 80 }, 81 Spec: v1.SriovNetworkNodePolicySpec{ 82 DeviceType: consts.DeviceTypeNetDevice, 83 NicSelector: v1.SriovNetworkNicSelector{ 84 PfNames: []string{"ens803f1"}, 85 RootDevices: []string{"0000:86:00.1"}, 86 Vendor: "8086", 87 }, 88 NodeSelector: map[string]string{ 89 "feature.node.kubernetes.io/network-sriov.capable": "true", 90 }, 91 NumVfs: 2, 92 Priority: 99, 93 ResourceName: "p1res", 94 }, 95 } 96 } 97 98 func newVirtioVdpaNodePolicy() *v1.SriovNetworkNodePolicy { 99 return &v1.SriovNetworkNodePolicy{ 100 ObjectMeta: metav1.ObjectMeta{ 101 Name: "p1", 102 }, 103 Spec: v1.SriovNetworkNodePolicySpec{ 104 DeviceType: consts.DeviceTypeNetDevice, 105 VdpaType: consts.VdpaTypeVirtio, 106 NicSelector: v1.SriovNetworkNicSelector{ 107 PfNames: []string{"ens803f1#2-3"}, 108 RootDevices: []string{"0000:86:00.1"}, 109 Vendor: "8086", 110 }, 111 NodeSelector: map[string]string{ 112 "feature.node.kubernetes.io/network-sriov.capable": "true", 113 }, 114 NumVfs: 4, 115 Priority: 99, 116 ResourceName: "virtiovdpa", 117 }, 118 } 119 } 120 121 func newVhostVdpaNodePolicy() *v1.SriovNetworkNodePolicy { 122 return &v1.SriovNetworkNodePolicy{ 123 ObjectMeta: metav1.ObjectMeta{ 124 Name: "p1", 125 }, 126 Spec: v1.SriovNetworkNodePolicySpec{ 127 DeviceType: consts.DeviceTypeNetDevice, 128 VdpaType: consts.VdpaTypeVhost, 129 NicSelector: v1.SriovNetworkNicSelector{ 130 PfNames: []string{"ens803f1"}, 131 RootDevices: []string{"0000:86:00.1"}, 132 Vendor: "8086", 133 }, 134 NodeSelector: map[string]string{ 135 "feature.node.kubernetes.io/network-sriov.capable": "true", 136 }, 137 NumVfs: 2, 138 Priority: 99, 139 ResourceName: "vhostvdpa", 140 }, 141 } 142 } 143 144 func TestRendering(t *testing.T) { 145 testtable := []struct { 146 tname string 147 network v1.SriovNetwork 148 }{ 149 { 150 tname: "simple", 151 network: v1.SriovNetwork{ 152 Spec: v1.SriovNetworkSpec{ 153 NetworkNamespace: "testnamespace", 154 ResourceName: "testresource", 155 }, 156 }, 157 }, 158 { 159 tname: "chained", 160 network: v1.SriovNetwork{ 161 Spec: v1.SriovNetworkSpec{ 162 NetworkNamespace: "testnamespace", 163 ResourceName: "testresource", 164 MetaPluginsConfig: ` 165 { 166 "type": "vrf", 167 "vrfname": "blue" 168 } 169 `, 170 }, 171 }, 172 }, 173 } 174 for _, tc := range testtable { 175 t.Run(tc.tname, func(t *testing.T) { 176 var b bytes.Buffer 177 w := bufio.NewWriter(&b) 178 rendered, err := tc.network.RenderNetAttDef() 179 if err != nil { 180 t.Fatal("failed rendering network attachment definition", err) 181 } 182 encoder := json.NewEncoder(w) 183 encoder.SetIndent("", " ") 184 encoder.Encode(rendered) 185 w.Flush() 186 gp := filepath.Join("testdata", filepath.FromSlash(t.Name())+".golden") 187 if *update { 188 t.Log("update golden file") 189 if err := os.WriteFile(gp, b.Bytes(), 0644); err != nil { 190 t.Fatalf("failed to update golden file: %s", err) 191 } 192 } 193 g, err := os.ReadFile(gp) 194 if err != nil { 195 t.Fatalf("failed reading .golden: %s", err) 196 } 197 t.Log(b.String()) 198 if !bytes.Equal(b.Bytes(), g) { 199 t.Errorf("bytes do not match .golden file") 200 } 201 }) 202 } 203 } 204 205 func TestIBRendering(t *testing.T) { 206 testtable := []struct { 207 tname string 208 network v1.SriovIBNetwork 209 }{ 210 { 211 tname: "simpleib", 212 network: v1.SriovIBNetwork{ 213 Spec: v1.SriovIBNetworkSpec{ 214 NetworkNamespace: "testnamespace", 215 ResourceName: "testresource", 216 Capabilities: "foo", 217 }, 218 }, 219 }, 220 } 221 for _, tc := range testtable { 222 t.Run(tc.tname, func(t *testing.T) { 223 var b bytes.Buffer 224 w := bufio.NewWriter(&b) 225 rendered, err := tc.network.RenderNetAttDef() 226 if err != nil { 227 t.Fatal("failed rendering network attachment definition", err) 228 } 229 encoder := json.NewEncoder(w) 230 encoder.SetIndent("", " ") 231 encoder.Encode(rendered) 232 w.Flush() 233 gp := filepath.Join("testdata", filepath.FromSlash(t.Name())+".golden") 234 if *update { 235 t.Log("update golden file") 236 if err := os.WriteFile(gp, b.Bytes(), 0644); err != nil { 237 t.Fatalf("failed to update golden file: %s", err) 238 } 239 } 240 g, err := os.ReadFile(gp) 241 if err != nil { 242 t.Fatalf("failed reading .golden: %s", err) 243 } 244 t.Log(b.String()) 245 if !bytes.Equal(b.Bytes(), g) { 246 t.Errorf("bytes do not match .golden file") 247 } 248 }) 249 } 250 } 251 252 func TestSriovNetworkNodePolicyApply(t *testing.T) { 253 testtable := []struct { 254 tname string 255 currentState *v1.SriovNetworkNodeState 256 policy *v1.SriovNetworkNodePolicy 257 expectedInterfaces v1.Interfaces 258 equalP bool 259 expectedErr bool 260 }{ 261 { 262 tname: "starting config", 263 currentState: newNodeState(), 264 policy: newNodePolicy(), 265 equalP: false, 266 expectedInterfaces: []v1.Interface{ 267 { 268 Name: "ens803f1", 269 NumVfs: 2, 270 PciAddress: "0000:86:00.1", 271 VfGroups: []v1.VfGroup{ 272 { 273 DeviceType: consts.DeviceTypeNetDevice, 274 ResourceName: "p1res", 275 VfRange: "0-1", 276 PolicyName: "p1", 277 }, 278 }, 279 }, 280 }, 281 }, 282 { 283 tname: "one policy present different pf", 284 currentState: func() *v1.SriovNetworkNodeState { 285 st := newNodeState() 286 st.Spec.Interfaces = []v1.Interface{ 287 { 288 Name: "ens803f0", 289 NumVfs: 2, 290 PciAddress: "0000:86:00.0", 291 VfGroups: []v1.VfGroup{ 292 { 293 DeviceType: consts.DeviceTypeNetDevice, 294 ResourceName: "prevres", 295 VfRange: "0-1", 296 PolicyName: "p2", 297 }, 298 }, 299 }, 300 } 301 return st 302 }(), 303 policy: newNodePolicy(), 304 equalP: false, 305 expectedInterfaces: []v1.Interface{ 306 { 307 Name: "ens803f0", 308 NumVfs: 2, 309 PciAddress: "0000:86:00.0", 310 VfGroups: []v1.VfGroup{ 311 { 312 DeviceType: consts.DeviceTypeNetDevice, 313 ResourceName: "prevres", 314 VfRange: "0-1", 315 PolicyName: "p2", 316 }, 317 }, 318 }, 319 { 320 Name: "ens803f1", 321 NumVfs: 2, 322 PciAddress: "0000:86:00.1", 323 VfGroups: []v1.VfGroup{ 324 { 325 DeviceType: consts.DeviceTypeNetDevice, 326 ResourceName: "p1res", 327 VfRange: "0-1", 328 PolicyName: "p1", 329 }, 330 }, 331 }, 332 }, 333 }, 334 { 335 // policy overwrites (applied last has higher priority) what is inside the 336 // SriovNetworkNodeState 337 tname: "one policy present same pf different priority", 338 currentState: func() *v1.SriovNetworkNodeState { 339 st := newNodeState() 340 st.Spec.Interfaces = []v1.Interface{ 341 { 342 Name: "ens803f1", 343 NumVfs: 3, 344 PciAddress: "0000:86:00.1", 345 VfGroups: []v1.VfGroup{ 346 { 347 DeviceType: consts.DeviceTypeVfioPci, 348 ResourceName: "vfiores", 349 VfRange: "0-1", 350 PolicyName: "p2", 351 }, 352 }, 353 }, 354 } 355 return st 356 }(), 357 policy: newNodePolicy(), 358 equalP: false, 359 expectedInterfaces: []v1.Interface{ 360 { 361 Name: "ens803f1", 362 NumVfs: 2, 363 PciAddress: "0000:86:00.1", 364 VfGroups: []v1.VfGroup{ 365 { 366 DeviceType: consts.DeviceTypeNetDevice, 367 ResourceName: "p1res", 368 VfRange: "0-1", 369 PolicyName: "p1", 370 }, 371 }, 372 }, 373 }, 374 }, 375 { 376 // policy with same priority, but there is VfRange overlap so 377 // only the last applied stays, NumVfs and MTU is still merged 378 tname: "one policy present same pf same priority", 379 currentState: func() *v1.SriovNetworkNodeState { 380 st := newNodeState() 381 st.Spec.Interfaces = []v1.Interface{ 382 { 383 Name: "ens803f1", 384 NumVfs: 3, 385 PciAddress: "0000:86:00.1", 386 Mtu: 2000, 387 VfGroups: []v1.VfGroup{ 388 { 389 DeviceType: consts.DeviceTypeVfioPci, 390 ResourceName: "vfiores", 391 VfRange: "0-1", 392 PolicyName: "p2", 393 }, 394 }, 395 }, 396 } 397 return st 398 }(), 399 policy: newNodePolicy(), 400 equalP: true, 401 expectedInterfaces: []v1.Interface{ 402 { 403 Mtu: 2000, 404 Name: "ens803f1", 405 NumVfs: 3, 406 PciAddress: "0000:86:00.1", 407 VfGroups: []v1.VfGroup{ 408 { 409 DeviceType: consts.DeviceTypeNetDevice, 410 ResourceName: "p1res", 411 VfRange: "0-1", 412 PolicyName: "p1", 413 }, 414 }, 415 }, 416 }, 417 }, 418 { 419 // policy with same priority, VfRange's do not overlap so all is merged 420 tname: "one policy present same pf same priority partitioning", 421 currentState: func() *v1.SriovNetworkNodeState { 422 st := newNodeState() 423 st.Spec.Interfaces = []v1.Interface{ 424 { 425 Name: "ens803f1", 426 NumVfs: 5, 427 PciAddress: "0000:86:00.1", 428 VfGroups: []v1.VfGroup{ 429 { 430 DeviceType: consts.DeviceTypeVfioPci, 431 ResourceName: "vfiores", 432 VfRange: "2-4", 433 PolicyName: "p2", 434 }, 435 }, 436 }, 437 } 438 return st 439 }(), 440 policy: newNodePolicy(), 441 equalP: true, 442 expectedInterfaces: []v1.Interface{ 443 { 444 Name: "ens803f1", 445 NumVfs: 5, 446 PciAddress: "0000:86:00.1", 447 VfGroups: []v1.VfGroup{ 448 { 449 DeviceType: consts.DeviceTypeNetDevice, 450 ResourceName: "p1res", 451 VfRange: "0-1", 452 PolicyName: "p1", 453 }, 454 { 455 DeviceType: consts.DeviceTypeVfioPci, 456 ResourceName: "vfiores", 457 VfRange: "2-4", 458 PolicyName: "p2", 459 }, 460 }, 461 }, 462 }, 463 }, 464 { 465 // vdpa policy with same priority (both virtio and vhost), VfRange's do not overlap so all is merged 466 tname: "one vdpa policy present same pf same priority partitioning", 467 currentState: func() *v1.SriovNetworkNodeState { 468 st := newNodeState() 469 st.Spec.Interfaces = []v1.Interface{ 470 { 471 Name: "ens803f1", 472 NumVfs: 4, 473 PciAddress: "0000:86:00.1", 474 VfGroups: []v1.VfGroup{ 475 { 476 DeviceType: consts.DeviceTypeNetDevice, 477 VdpaType: consts.VdpaTypeVhost, 478 ResourceName: "vhostvdpa", 479 VfRange: "0-1", 480 PolicyName: "p2", 481 }, 482 }, 483 }, 484 } 485 return st 486 }(), 487 policy: newVirtioVdpaNodePolicy(), 488 equalP: true, 489 expectedInterfaces: []v1.Interface{ 490 { 491 Name: "ens803f1", 492 NumVfs: 4, 493 PciAddress: "0000:86:00.1", 494 VfGroups: []v1.VfGroup{ 495 { 496 DeviceType: consts.DeviceTypeNetDevice, 497 VdpaType: consts.VdpaTypeVirtio, 498 ResourceName: "virtiovdpa", 499 VfRange: "2-3", 500 PolicyName: "p1", 501 }, 502 { 503 DeviceType: consts.DeviceTypeNetDevice, 504 VdpaType: consts.VdpaTypeVhost, 505 ResourceName: "vhostvdpa", 506 VfRange: "0-1", 507 PolicyName: "p2", 508 }, 509 }, 510 }, 511 }, 512 }, 513 { 514 // policy with same priority that overwrites the 2 present groups in 515 // SriovNetworkNodeState because they overlap VfRange 516 tname: "two policy present same pf same priority overlap", 517 currentState: func() *v1.SriovNetworkNodeState { 518 st := newNodeState() 519 st.Spec.Interfaces = []v1.Interface{ 520 { 521 Name: "ens803f1", 522 NumVfs: 2, 523 PciAddress: "0000:86:00.1", 524 VfGroups: []v1.VfGroup{ 525 { 526 DeviceType: consts.DeviceTypeVfioPci, 527 ResourceName: "vfiores1", 528 VfRange: "0-0", 529 PolicyName: "p2", 530 }, 531 { 532 DeviceType: consts.DeviceTypeVfioPci, 533 ResourceName: "vfiores2", 534 VfRange: "1-1", 535 PolicyName: "p3", 536 }, 537 }, 538 }, 539 } 540 return st 541 }(), 542 policy: newNodePolicy(), 543 equalP: true, 544 expectedInterfaces: []v1.Interface{ 545 { 546 Name: "ens803f1", 547 NumVfs: 2, 548 PciAddress: "0000:86:00.1", 549 VfGroups: []v1.VfGroup{ 550 { 551 DeviceType: consts.DeviceTypeNetDevice, 552 ResourceName: "p1res", 553 VfRange: "0-1", 554 PolicyName: "p1", 555 }, 556 }, 557 }, 558 }, 559 }, 560 { 561 // policy with same priority that overwrites the present group in 562 // SriovNetworkNodeState because of same ResourceName 563 tname: "one policy present same pf same priority same ResourceName", 564 currentState: func() *v1.SriovNetworkNodeState { 565 st := newNodeState() 566 st.Spec.Interfaces = []v1.Interface{ 567 { 568 Name: "ens803f1", 569 NumVfs: 4, 570 PciAddress: "0000:86:00.1", 571 VfGroups: []v1.VfGroup{ 572 { 573 DeviceType: consts.DeviceTypeVfioPci, 574 ResourceName: "p1res", 575 VfRange: "2-3", 576 PolicyName: "p2", 577 }, 578 }, 579 }, 580 } 581 return st 582 }(), 583 policy: newNodePolicy(), 584 equalP: true, 585 expectedInterfaces: []v1.Interface{ 586 { 587 Name: "ens803f1", 588 NumVfs: 4, 589 PciAddress: "0000:86:00.1", 590 VfGroups: []v1.VfGroup{ 591 { 592 DeviceType: consts.DeviceTypeNetDevice, 593 ResourceName: "p1res", 594 VfRange: "0-1", 595 PolicyName: "p1", 596 }, 597 }, 598 }, 599 }, 600 }, 601 { 602 // policy with diff priority that have non-overlapping VF groups will be 603 // merged 604 tname: "one policy present same pf diff priority no overlap VFs", 605 currentState: func() *v1.SriovNetworkNodeState { 606 st := newNodeState() 607 st.Spec.Interfaces = []v1.Interface{ 608 { 609 Name: "ens803f1", 610 NumVfs: 4, 611 PciAddress: "0000:86:00.1", 612 VfGroups: []v1.VfGroup{ 613 { 614 DeviceType: consts.DeviceTypeVfioPci, 615 ResourceName: "p2res", 616 VfRange: "2-3", 617 PolicyName: "p2", 618 }, 619 }, 620 }, 621 } 622 return st 623 }(), 624 policy: newNodePolicy(), 625 equalP: false, 626 expectedInterfaces: []v1.Interface{ 627 { 628 Name: "ens803f1", 629 NumVfs: 4, 630 PciAddress: "0000:86:00.1", 631 VfGroups: []v1.VfGroup{ 632 { 633 DeviceType: consts.DeviceTypeNetDevice, 634 ResourceName: "p1res", 635 VfRange: "0-1", 636 PolicyName: "p1", 637 }, 638 { 639 DeviceType: consts.DeviceTypeVfioPci, 640 ResourceName: "p2res", 641 VfRange: "2-3", 642 PolicyName: "p2", 643 }, 644 }, 645 }, 646 }, 647 }, 648 { 649 tname: "no selectors", 650 currentState: newNodeState(), 651 policy: &v1.SriovNetworkNodePolicy{ 652 ObjectMeta: metav1.ObjectMeta{ 653 Name: "p1", 654 }, 655 Spec: v1.SriovNetworkNodePolicySpec{ 656 DeviceType: consts.DeviceTypeNetDevice, 657 NicSelector: v1.SriovNetworkNicSelector{ 658 PfNames: []string{}, 659 RootDevices: []string{}, 660 }, 661 NodeSelector: map[string]string{ 662 "feature.node.kubernetes.io/network-sriov.capable": "true", 663 }, 664 NumVfs: 2, 665 Priority: 99, 666 ResourceName: "p1res", 667 }, 668 }, 669 equalP: false, 670 expectedInterfaces: nil, 671 }, 672 { 673 tname: "bad pf partition", 674 currentState: newNodeState(), 675 policy: &v1.SriovNetworkNodePolicy{ 676 ObjectMeta: metav1.ObjectMeta{ 677 Name: "p1", 678 }, 679 Spec: v1.SriovNetworkNodePolicySpec{ 680 DeviceType: consts.DeviceTypeNetDevice, 681 NicSelector: v1.SriovNetworkNicSelector{ 682 PfNames: []string{"ens803f0#a-c"}, 683 RootDevices: []string{}, 684 }, 685 NodeSelector: map[string]string{ 686 "feature.node.kubernetes.io/network-sriov.capable": "true", 687 }, 688 NumVfs: 2, 689 Priority: 99, 690 ResourceName: "p1res", 691 }, 692 }, 693 equalP: false, 694 expectedInterfaces: nil, 695 expectedErr: true, 696 }, 697 } 698 for _, tc := range testtable { 699 t.Run(tc.tname, func(t *testing.T) { 700 err := tc.policy.Apply(tc.currentState, tc.equalP) 701 if tc.expectedErr && err == nil { 702 t.Errorf("Apply expecting error.") 703 } else if !tc.expectedErr && err != nil { 704 t.Errorf("Apply error:\n%s", err) 705 } 706 if diff := cmp.Diff(tc.expectedInterfaces, tc.currentState.Spec.Interfaces); diff != "" { 707 t.Errorf("SriovNetworkNodeState spec diff (-want +got):\n%s", diff) 708 } 709 }) 710 } 711 } 712 713 func TestVirtioVdpaNodePolicyApply(t *testing.T) { 714 testtable := []struct { 715 tname string 716 currentState *v1.SriovNetworkNodeState 717 policy *v1.SriovNetworkNodePolicy 718 expectedInterfaces v1.Interfaces 719 equalP bool 720 expectedErr bool 721 }{ 722 { 723 tname: "virtio/vdpa configuration", 724 currentState: newNodeState(), 725 policy: newVirtioVdpaNodePolicy(), 726 equalP: false, 727 expectedInterfaces: []v1.Interface{ 728 { 729 Name: "ens803f1", 730 NumVfs: 4, 731 PciAddress: "0000:86:00.1", 732 VfGroups: []v1.VfGroup{ 733 { 734 DeviceType: consts.DeviceTypeNetDevice, 735 VdpaType: consts.VdpaTypeVirtio, 736 ResourceName: "virtiovdpa", 737 VfRange: "2-3", 738 PolicyName: "p1", 739 }, 740 }, 741 }, 742 }, 743 }, 744 } 745 for _, tc := range testtable { 746 t.Run(tc.tname, func(t *testing.T) { 747 err := tc.policy.Apply(tc.currentState, tc.equalP) 748 if tc.expectedErr && err == nil { 749 t.Errorf("Apply expecting error.") 750 } else if !tc.expectedErr && err != nil { 751 t.Errorf("Apply error:\n%s", err) 752 } 753 if diff := cmp.Diff(tc.expectedInterfaces, tc.currentState.Spec.Interfaces); diff != "" { 754 t.Errorf("SriovNetworkNodeState spec diff (-want +got):\n%s", diff) 755 } 756 }) 757 } 758 } 759 760 func TestVhostVdpaNodePolicyApply(t *testing.T) { 761 testtable := []struct { 762 tname string 763 currentState *v1.SriovNetworkNodeState 764 policy *v1.SriovNetworkNodePolicy 765 expectedInterfaces v1.Interfaces 766 equalP bool 767 expectedErr bool 768 }{ 769 { 770 tname: "vhost/vdpa configuration", 771 currentState: newNodeState(), 772 policy: newVhostVdpaNodePolicy(), 773 equalP: false, 774 expectedInterfaces: []v1.Interface{ 775 { 776 Name: "ens803f1", 777 NumVfs: 2, 778 PciAddress: "0000:86:00.1", 779 VfGroups: []v1.VfGroup{ 780 { 781 DeviceType: consts.DeviceTypeNetDevice, 782 VdpaType: consts.VdpaTypeVhost, 783 ResourceName: "vhostvdpa", 784 VfRange: "0-1", 785 PolicyName: "p1", 786 }, 787 }, 788 }, 789 }, 790 }, 791 } 792 for _, tc := range testtable { 793 t.Run(tc.tname, func(t *testing.T) { 794 err := tc.policy.Apply(tc.currentState, tc.equalP) 795 if tc.expectedErr && err == nil { 796 t.Errorf("Apply expecting error.") 797 } else if !tc.expectedErr && err != nil { 798 t.Errorf("Apply error:\n%s", err) 799 } 800 if diff := cmp.Diff(tc.expectedInterfaces, tc.currentState.Spec.Interfaces); diff != "" { 801 t.Errorf("SriovNetworkNodeState spec diff (-want +got):\n%s", diff) 802 } 803 }) 804 } 805 } 806 807 func TestGetEswitchModeFromSpec(t *testing.T) { 808 testtable := []struct { 809 tname string 810 spec *v1.Interface 811 expectedResult string 812 }{ 813 { 814 tname: "set to legacy", 815 spec: &v1.Interface{EswitchMode: v1.ESwithModeLegacy}, 816 expectedResult: v1.ESwithModeLegacy, 817 }, 818 { 819 tname: "set to switchdev", 820 spec: &v1.Interface{EswitchMode: v1.ESwithModeSwitchDev}, 821 expectedResult: v1.ESwithModeSwitchDev, 822 }, 823 { 824 tname: "not set", 825 spec: &v1.Interface{}, 826 expectedResult: v1.ESwithModeLegacy, 827 }, 828 } 829 for _, tc := range testtable { 830 t.Run(tc.tname, func(t *testing.T) { 831 result := v1.GetEswitchModeFromSpec(tc.spec) 832 if diff := cmp.Diff(tc.expectedResult, result); diff != "" { 833 t.Errorf("unexpected result (-want +got):\n%s", diff) 834 } 835 }) 836 } 837 } 838 839 func TestGetEswitchModeFromStatus(t *testing.T) { 840 testtable := []struct { 841 tname string 842 spec *v1.InterfaceExt 843 expectedResult string 844 }{ 845 { 846 tname: "set to legacy", 847 spec: &v1.InterfaceExt{EswitchMode: v1.ESwithModeLegacy}, 848 expectedResult: v1.ESwithModeLegacy, 849 }, 850 { 851 tname: "set to switchdev", 852 spec: &v1.InterfaceExt{EswitchMode: v1.ESwithModeSwitchDev}, 853 expectedResult: v1.ESwithModeSwitchDev, 854 }, 855 { 856 tname: "not set", 857 spec: &v1.InterfaceExt{}, 858 expectedResult: v1.ESwithModeLegacy, 859 }, 860 } 861 for _, tc := range testtable { 862 t.Run(tc.tname, func(t *testing.T) { 863 result := v1.GetEswitchModeFromStatus(tc.spec) 864 if diff := cmp.Diff(tc.expectedResult, result); diff != "" { 865 t.Errorf("unexpected result (-want +got):\n%s", diff) 866 } 867 }) 868 } 869 } 870 871 func TestSriovNetworkPoolConfig_MaxUnavailable(t *testing.T) { 872 testtable := []struct { 873 tname string 874 maxUn intstrutil.IntOrString 875 maxUnNil bool 876 numOfNodes int 877 expectedNum int 878 expectedErr bool 879 }{ 880 { 881 tname: "valid int MaxUnavailable", 882 maxUn: intstrutil.FromInt32(1), 883 numOfNodes: 1, 884 expectedNum: 1, 885 expectedErr: false, 886 }, 887 { 888 tname: "invalid string MaxUnavailable", 889 maxUn: intstrutil.FromString("bla"), 890 numOfNodes: 1, 891 expectedNum: 0, 892 expectedErr: true, 893 }, 894 { 895 tname: "valid string percentage MaxUnavailable", 896 maxUn: intstrutil.FromString("33%"), 897 numOfNodes: 10, 898 expectedNum: 3, 899 expectedErr: false, 900 }, 901 { 902 tname: "negative int MaxUnavailable", 903 maxUn: intstrutil.FromInt32(-1), 904 numOfNodes: 10, 905 expectedNum: 0, 906 expectedErr: true, 907 }, 908 { 909 tname: "out of range int MaxUnavailable", 910 maxUn: intstrutil.FromString("99999999999999999."), 911 numOfNodes: 10, 912 expectedNum: 0, 913 expectedErr: true, 914 }, 915 { 916 tname: "over 100%", 917 maxUn: intstrutil.FromString("10000%"), 918 numOfNodes: 10, 919 expectedNum: 0, 920 expectedErr: true, 921 }, 922 { 923 tname: "parallel", 924 maxUn: intstrutil.FromInt32(-1), 925 maxUnNil: true, 926 numOfNodes: 10, 927 expectedNum: -1, 928 expectedErr: false, 929 }, 930 { 931 tname: "zero", 932 maxUn: intstrutil.FromString("30%"), 933 maxUnNil: false, 934 numOfNodes: 1, 935 expectedNum: 0, 936 expectedErr: false, 937 }, 938 } 939 for _, tc := range testtable { 940 t.Run(tc.tname, func(t *testing.T) { 941 pool := v1.SriovNetworkPoolConfig{ 942 Spec: v1.SriovNetworkPoolConfigSpec{ 943 MaxUnavailable: &tc.maxUn, 944 }, 945 } 946 947 if tc.maxUnNil { 948 pool.Spec.MaxUnavailable = nil 949 } 950 951 num, err := pool.MaxUnavailable(tc.numOfNodes) 952 if tc.expectedErr && err == nil { 953 t.Errorf("MaxUnavailable expecting error.") 954 } else if !tc.expectedErr && err != nil { 955 t.Errorf("MaxUnavailable error:\n%s", err) 956 } 957 958 if tc.expectedNum != num { 959 t.Errorf("unexpected number of MaxUnavailable.") 960 } 961 }) 962 } 963 }