github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/api/v1/helper.go (about) 1 package v1 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "os" 8 "regexp" 9 "sort" 10 "strconv" 11 "strings" 12 13 corev1 "k8s.io/api/core/v1" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 16 intstrutil "k8s.io/apimachinery/pkg/util/intstr" 17 "k8s.io/client-go/kubernetes" 18 logf "sigs.k8s.io/controller-runtime/pkg/log" 19 20 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" 21 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render" 22 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" 23 ) 24 25 const ( 26 LASTNETWORKNAMESPACE = "operator.sriovnetwork.openshift.io/last-network-namespace" 27 NETATTDEFFINALIZERNAME = "netattdef.finalizers.sriovnetwork.openshift.io" 28 POOLCONFIGFINALIZERNAME = "poolconfig.finalizers.sriovnetwork.openshift.io" 29 ESwithModeLegacy = "legacy" 30 ESwithModeSwitchDev = "switchdev" 31 32 SriovCniStateEnable = "enable" 33 SriovCniStateDisable = "disable" 34 SriovCniStateAuto = "auto" 35 SriovCniStateOff = "off" 36 SriovCniStateOn = "on" 37 SriovCniIpam = "\"ipam\"" 38 SriovCniIpamEmpty = SriovCniIpam + ":{}" 39 ) 40 41 const invalidVfIndex = -1 42 43 var ManifestsPath = "./bindata/manifests/cni-config" 44 var log = logf.Log.WithName("sriovnetwork") 45 46 // NicIDMap contains supported mapping of IDs with each in the format of: 47 // Vendor ID, Physical Function Device ID, Virtual Function Device ID 48 var NicIDMap = []string{} 49 50 var InitialState SriovNetworkNodeState 51 52 // NetFilterType Represents the NetFilter tags to be used 53 type NetFilterType int 54 55 const ( 56 // OpenstackNetworkID network UUID 57 OpenstackNetworkID NetFilterType = iota 58 59 SupportedNicIDConfigmap = "supported-nic-ids" 60 ) 61 62 type ConfigurationModeType string 63 64 const ( 65 DaemonConfigurationMode ConfigurationModeType = "daemon" 66 SystemdConfigurationMode ConfigurationModeType = "systemd" 67 ) 68 69 func (e NetFilterType) String() string { 70 switch e { 71 case OpenstackNetworkID: 72 return "openstack/NetworkID" 73 default: 74 return fmt.Sprintf("%d", int(e)) 75 } 76 } 77 78 func InitNicIDMapFromConfigMap(client kubernetes.Interface, namespace string) error { 79 cm, err := client.CoreV1().ConfigMaps(namespace).Get( 80 context.Background(), 81 SupportedNicIDConfigmap, 82 metav1.GetOptions{}, 83 ) 84 // if the configmap does not exist, return false 85 if err != nil { 86 return err 87 } 88 for _, v := range cm.Data { 89 NicIDMap = append(NicIDMap, v) 90 } 91 92 return nil 93 } 94 95 func InitNicIDMapFromList(idList []string) { 96 NicIDMap = append(NicIDMap, idList...) 97 } 98 99 func IsSupportedVendor(vendorID string) bool { 100 for _, n := range NicIDMap { 101 ids := strings.Split(n, " ") 102 if vendorID == ids[0] { 103 return true 104 } 105 } 106 return false 107 } 108 109 func IsSupportedDevice(deviceID string) bool { 110 for _, n := range NicIDMap { 111 ids := strings.Split(n, " ") 112 if deviceID == ids[1] { 113 return true 114 } 115 } 116 return false 117 } 118 119 func IsSupportedModel(vendorID, deviceID string) bool { 120 for _, n := range NicIDMap { 121 ids := strings.Split(n, " ") 122 if vendorID == ids[0] && deviceID == ids[1] { 123 return true 124 } 125 } 126 log.Info("IsSupportedModel(): found unsupported model", "vendorId:", vendorID, "deviceId:", deviceID) 127 return false 128 } 129 130 func IsVfSupportedModel(vendorID, deviceID string) bool { 131 for _, n := range NicIDMap { 132 ids := strings.Split(n, " ") 133 if vendorID == ids[0] && deviceID == ids[2] { 134 return true 135 } 136 } 137 log.Info("IsVfSupportedModel(): found unsupported VF model", "vendorId:", vendorID, "deviceId:", deviceID) 138 return false 139 } 140 141 func IsEnabledUnsupportedVendor(vendorID string, unsupportedNicIDMap map[string]string) bool { 142 for _, n := range unsupportedNicIDMap { 143 if IsValidPciString(n) { 144 ids := strings.Split(n, " ") 145 if vendorID == ids[0] { 146 return true 147 } 148 } 149 } 150 return false 151 } 152 153 func IsValidPciString(nicIDString string) bool { 154 ids := strings.Split(nicIDString, " ") 155 156 if len(ids) != 3 { 157 log.Info("IsValidPciString(): ", nicIDString) 158 return false 159 } 160 161 if len(ids[0]) != 4 { 162 log.Info("IsValidPciString():", "Invalid vendor PciId ", ids[0]) 163 return false 164 } 165 if _, err := strconv.ParseInt(ids[0], 16, 32); err != nil { 166 log.Info("IsValidPciString():", "Invalid vendor PciId ", ids[0]) 167 } 168 169 if len(ids[1]) != 4 { 170 log.Info("IsValidPciString():", "Invalid PciId of PF ", ids[1]) 171 return false 172 } 173 if _, err := strconv.ParseInt(ids[1], 16, 32); err != nil { 174 log.Info("IsValidPciString():", "Invalid PciId of PF ", ids[1]) 175 } 176 177 if len(ids[2]) != 4 { 178 log.Info("IsValidPciString():", "Invalid PciId of VF ", ids[2]) 179 return false 180 } 181 if _, err := strconv.ParseInt(ids[2], 16, 32); err != nil { 182 log.Info("IsValidPciString():", "Invalid PciId of VF ", ids[2]) 183 } 184 185 return true 186 } 187 188 func GetSupportedVfIds() []string { 189 var vfIds []string 190 for _, n := range NicIDMap { 191 ids := strings.Split(n, " ") 192 vfID := "0x" + ids[2] 193 if !StringInArray(vfID, vfIds) { 194 vfIds = append(vfIds, vfID) 195 } 196 } 197 // return a sorted slice so that udev rule is stable 198 sort.Slice(vfIds, func(i, j int) bool { 199 ip, _ := strconv.ParseInt(vfIds[i], 0, 32) 200 jp, _ := strconv.ParseInt(vfIds[j], 0, 32) 201 return ip < jp 202 }) 203 return vfIds 204 } 205 206 func GetVfDeviceID(deviceID string) string { 207 for _, n := range NicIDMap { 208 ids := strings.Split(n, " ") 209 if deviceID == ids[1] { 210 return ids[2] 211 } 212 } 213 return "" 214 } 215 216 func IsSwitchdevModeSpec(spec SriovNetworkNodeStateSpec) bool { 217 return ContainsSwitchdevInterface(spec.Interfaces) 218 } 219 220 // ContainsSwitchdevInterface returns true if provided interface list contains interface 221 // with switchdev configuration 222 func ContainsSwitchdevInterface(interfaces []Interface) bool { 223 for _, iface := range interfaces { 224 if iface.EswitchMode == ESwithModeSwitchDev { 225 return true 226 } 227 } 228 return false 229 } 230 231 func FindInterface(interfaces Interfaces, name string) (iface Interface, err error) { 232 for _, i := range interfaces { 233 if i.Name == name { 234 return i, nil 235 } 236 } 237 return Interface{}, fmt.Errorf("unable to find interface: %v", name) 238 } 239 240 // GetEswitchModeFromSpec returns ESwitchMode from the interface spec, returns legacy if not set 241 func GetEswitchModeFromSpec(ifaceSpec *Interface) string { 242 if ifaceSpec.EswitchMode == "" { 243 return ESwithModeLegacy 244 } 245 return ifaceSpec.EswitchMode 246 } 247 248 // GetEswitchModeFromStatus returns ESwitchMode from the interface status, returns legacy if not set 249 func GetEswitchModeFromStatus(ifaceStatus *InterfaceExt) string { 250 if ifaceStatus.EswitchMode == "" { 251 return ESwithModeLegacy 252 } 253 return ifaceStatus.EswitchMode 254 } 255 256 func NeedToUpdateSriov(ifaceSpec *Interface, ifaceStatus *InterfaceExt) bool { 257 if ifaceSpec.Mtu > 0 { 258 mtu := ifaceSpec.Mtu 259 if mtu != ifaceStatus.Mtu { 260 log.V(2).Info("NeedToUpdateSriov(): MTU needs update", "desired", mtu, "current", ifaceStatus.Mtu) 261 return true 262 } 263 } 264 currentEswitchMode := GetEswitchModeFromStatus(ifaceStatus) 265 desiredEswitchMode := GetEswitchModeFromSpec(ifaceSpec) 266 if currentEswitchMode != desiredEswitchMode { 267 log.V(2).Info("NeedToUpdateSriov(): EswitchMode needs update", "desired", desiredEswitchMode, "current", currentEswitchMode) 268 return true 269 } 270 if ifaceSpec.NumVfs != ifaceStatus.NumVfs { 271 log.V(2).Info("NeedToUpdateSriov(): NumVfs needs update", "desired", ifaceSpec.NumVfs, "current", ifaceStatus.NumVfs) 272 return true 273 } 274 if ifaceSpec.NumVfs > 0 { 275 for _, vfStatus := range ifaceStatus.VFs { 276 ingroup := false 277 for _, groupSpec := range ifaceSpec.VfGroups { 278 if IndexInRange(vfStatus.VfID, groupSpec.VfRange) { 279 ingroup = true 280 if vfStatus.Driver == "" { 281 log.V(2).Info("NeedToUpdateSriov(): Driver needs update - has no driver", 282 "desired", groupSpec.DeviceType) 283 return true 284 } 285 if groupSpec.DeviceType != "" && groupSpec.DeviceType != consts.DeviceTypeNetDevice { 286 if groupSpec.DeviceType != vfStatus.Driver { 287 log.V(2).Info("NeedToUpdateSriov(): Driver needs update", 288 "desired", groupSpec.DeviceType, "current", vfStatus.Driver) 289 return true 290 } 291 } else { 292 if StringInArray(vfStatus.Driver, vars.DpdkDrivers) { 293 log.V(2).Info("NeedToUpdateSriov(): Driver needs update", 294 "desired", groupSpec.DeviceType, "current", vfStatus.Driver) 295 return true 296 } 297 if vfStatus.Mtu != 0 && groupSpec.Mtu != 0 && vfStatus.Mtu != groupSpec.Mtu { 298 log.V(2).Info("NeedToUpdateSriov(): VF MTU needs update", 299 "vf", vfStatus.VfID, "desired", groupSpec.Mtu, "current", vfStatus.Mtu) 300 return true 301 } 302 303 // this is needed to be sure the admin mac address is configured as expected 304 if ifaceSpec.ExternallyManaged { 305 log.V(2).Info("NeedToUpdateSriov(): need to update the device as it's externally manage", 306 "device", ifaceStatus.PciAddress) 307 return true 308 } 309 } 310 if groupSpec.VdpaType != vfStatus.VdpaType { 311 log.V(2).Info("NeedToUpdateSriov(): VF VdpaType mismatch", 312 "desired", groupSpec.VdpaType, "current", vfStatus.VdpaType) 313 return true 314 } 315 break 316 } 317 } 318 if !ingroup && (StringInArray(vfStatus.Driver, vars.DpdkDrivers) || vfStatus.VdpaType != "") { 319 // need to reset VF if it is not a part of a group and: 320 // a. has DPDK driver loaded 321 // b. has VDPA device 322 return true 323 } 324 } 325 } 326 return false 327 } 328 329 type ByPriority []SriovNetworkNodePolicy 330 331 func (a ByPriority) Len() int { 332 return len(a) 333 } 334 335 func (a ByPriority) Less(i, j int) bool { 336 if a[i].Spec.Priority != a[j].Spec.Priority { 337 return a[i].Spec.Priority > a[j].Spec.Priority 338 } 339 return a[i].GetName() < a[j].GetName() 340 } 341 342 func (a ByPriority) Swap(i, j int) { 343 a[i], a[j] = a[j], a[i] 344 } 345 346 // Match check if node is selected by NodeSelector 347 func (p *SriovNetworkNodePolicy) Selected(node *corev1.Node) bool { 348 for k, v := range p.Spec.NodeSelector { 349 if nv, ok := node.Labels[k]; ok && nv == v { 350 continue 351 } 352 return false 353 } 354 return true 355 } 356 357 func StringInArray(val string, array []string) bool { 358 for i := range array { 359 if array[i] == val { 360 return true 361 } 362 } 363 return false 364 } 365 366 func RemoveString(s string, slice []string) (result []string, found bool) { 367 if len(slice) != 0 { 368 for _, item := range slice { 369 if item == s { 370 found = true 371 continue 372 } 373 result = append(result, item) 374 } 375 } 376 return 377 } 378 379 func UniqueAppend(inSlice []string, strings ...string) []string { 380 for _, s := range strings { 381 if !StringInArray(s, inSlice) { 382 inSlice = append(inSlice, s) 383 } 384 } 385 return inSlice 386 } 387 388 // Apply policy to SriovNetworkNodeState CR 389 func (p *SriovNetworkNodePolicy) Apply(state *SriovNetworkNodeState, equalPriority bool) error { 390 s := p.Spec.NicSelector 391 if s.Vendor == "" && s.DeviceID == "" && len(s.RootDevices) == 0 && len(s.PfNames) == 0 && 392 len(s.NetFilter) == 0 { 393 // Empty NicSelector match none 394 return nil 395 } 396 for _, iface := range state.Status.Interfaces { 397 if s.Selected(&iface) { 398 log.Info("Update interface", "name:", iface.Name) 399 result := Interface{ 400 PciAddress: iface.PciAddress, 401 Mtu: p.Spec.Mtu, 402 Name: iface.Name, 403 LinkType: p.Spec.LinkType, 404 EswitchMode: p.Spec.EswitchMode, 405 NumVfs: p.Spec.NumVfs, 406 ExternallyManaged: p.Spec.ExternallyManaged, 407 } 408 if p.Spec.NumVfs > 0 { 409 group, err := p.generateVfGroup(&iface) 410 if err != nil { 411 return err 412 } 413 result.VfGroups = []VfGroup{*group} 414 found := false 415 for i := range state.Spec.Interfaces { 416 if state.Spec.Interfaces[i].PciAddress == result.PciAddress { 417 found = true 418 state.Spec.Interfaces[i].mergeConfigs(&result, equalPriority) 419 state.Spec.Interfaces[i] = result 420 break 421 } 422 } 423 if !found { 424 state.Spec.Interfaces = append(state.Spec.Interfaces, result) 425 } 426 } 427 } 428 } 429 return nil 430 } 431 432 // mergeConfigs merges configs from multiple polices where the last one has the 433 // highest priority. This merge is dependent on: 1. SR-IOV partition is 434 // configured with the #-notation in pfName, 2. The VF groups are 435 // non-overlapping or SR-IOV policies have the same priority. 436 func (iface Interface) mergeConfigs(input *Interface, equalPriority bool) { 437 m := false 438 // merge VF groups (input.VfGroups already contains the highest priority): 439 // - skip group with same ResourceName, 440 // - skip overlapping groups (use only highest priority) 441 for _, gr := range iface.VfGroups { 442 if gr.ResourceName == input.VfGroups[0].ResourceName || gr.isVFRangeOverlapping(input.VfGroups[0]) { 443 continue 444 } 445 m = true 446 input.VfGroups = append(input.VfGroups, gr) 447 } 448 449 if !equalPriority && !m { 450 return 451 } 452 453 // mtu configuration we take the highest value 454 if input.Mtu < iface.Mtu { 455 input.Mtu = iface.Mtu 456 } 457 if input.NumVfs < iface.NumVfs { 458 input.NumVfs = iface.NumVfs 459 } 460 } 461 462 func (gr VfGroup) isVFRangeOverlapping(group VfGroup) bool { 463 rngSt, rngEnd, err := parseRange(gr.VfRange) 464 if err != nil { 465 return false 466 } 467 rngSt2, rngEnd2, err := parseRange(group.VfRange) 468 if err != nil { 469 return false 470 } 471 // compare minimal range has overlap 472 if rngSt < rngSt2 { 473 return IndexInRange(rngSt2, gr.VfRange) || IndexInRange(rngEnd2, gr.VfRange) 474 } 475 return IndexInRange(rngSt, group.VfRange) || IndexInRange(rngEnd, group.VfRange) 476 } 477 478 func (p *SriovNetworkNodePolicy) generateVfGroup(iface *InterfaceExt) (*VfGroup, error) { 479 var err error 480 pfName := "" 481 var rngStart, rngEnd int 482 found := false 483 for _, selector := range p.Spec.NicSelector.PfNames { 484 pfName, rngStart, rngEnd, err = ParsePFName(selector) 485 if err != nil { 486 log.Error(err, "Unable to parse PF Name.") 487 return nil, err 488 } 489 if pfName == iface.Name { 490 found = true 491 if rngStart == invalidVfIndex && rngEnd == invalidVfIndex { 492 rngStart, rngEnd = 0, p.Spec.NumVfs-1 493 } 494 break 495 } 496 } 497 if !found { 498 // assign the default vf index range if the pfName is not specified by the nicSelector 499 rngStart, rngEnd = 0, p.Spec.NumVfs-1 500 } 501 rng := strconv.Itoa(rngStart) + "-" + strconv.Itoa(rngEnd) 502 return &VfGroup{ 503 ResourceName: p.Spec.ResourceName, 504 DeviceType: p.Spec.DeviceType, 505 VfRange: rng, 506 PolicyName: p.GetName(), 507 Mtu: p.Spec.Mtu, 508 IsRdma: p.Spec.IsRdma, 509 VdpaType: p.Spec.VdpaType, 510 }, nil 511 } 512 513 func IndexInRange(i int, r string) bool { 514 rngSt, rngEnd, err := parseRange(r) 515 if err != nil { 516 return false 517 } 518 if i <= rngEnd && i >= rngSt { 519 return true 520 } 521 return false 522 } 523 524 func parseRange(r string) (rngSt, rngEnd int, err error) { 525 rng := strings.Split(r, "-") 526 rngSt, err = strconv.Atoi(rng[0]) 527 if err != nil { 528 return 529 } 530 rngEnd, err = strconv.Atoi(rng[1]) 531 if err != nil { 532 return 533 } 534 return 535 } 536 537 // Parse PF name with VF range 538 func ParsePFName(name string) (ifName string, rngSt, rngEnd int, err error) { 539 rngSt, rngEnd = invalidVfIndex, invalidVfIndex 540 if strings.Contains(name, "#") { 541 fields := strings.Split(name, "#") 542 ifName = fields[0] 543 rngSt, rngEnd, err = parseRange(fields[1]) 544 } else { 545 ifName = name 546 } 547 return 548 } 549 550 func (selector *SriovNetworkNicSelector) Selected(iface *InterfaceExt) bool { 551 if selector.Vendor != "" && selector.Vendor != iface.Vendor { 552 return false 553 } 554 if selector.DeviceID != "" && selector.DeviceID != iface.DeviceID { 555 return false 556 } 557 if len(selector.RootDevices) > 0 && !StringInArray(iface.PciAddress, selector.RootDevices) { 558 return false 559 } 560 if len(selector.PfNames) > 0 { 561 var pfNames []string 562 for _, p := range selector.PfNames { 563 if strings.Contains(p, "#") { 564 fields := strings.Split(p, "#") 565 pfNames = append(pfNames, fields[0]) 566 } else { 567 pfNames = append(pfNames, p) 568 } 569 } 570 if !StringInArray(iface.Name, pfNames) { 571 return false 572 } 573 } 574 if selector.NetFilter != "" && !NetFilterMatch(selector.NetFilter, iface.NetFilter) { 575 return false 576 } 577 578 return true 579 } 580 581 func (s *SriovNetworkNodeState) GetInterfaceStateByPciAddress(addr string) *InterfaceExt { 582 for _, iface := range s.Status.Interfaces { 583 if addr == iface.PciAddress { 584 return &iface 585 } 586 } 587 return nil 588 } 589 590 func (s *SriovNetworkNodeState) GetDriverByPciAddress(addr string) string { 591 for _, iface := range s.Status.Interfaces { 592 if addr == iface.PciAddress { 593 return iface.Driver 594 } 595 } 596 return "" 597 } 598 599 // RenderNetAttDef renders a net-att-def for ib-sriov CNI 600 func (cr *SriovIBNetwork) RenderNetAttDef() (*uns.Unstructured, error) { 601 logger := log.WithName("RenderNetAttDef") 602 logger.Info("Start to render IB SRIOV CNI NetworkAttachmentDefinition") 603 604 // render RawCNIConfig manifests 605 data := render.MakeRenderData() 606 data.Data["CniType"] = "ib-sriov" 607 data.Data["SriovNetworkName"] = cr.Name 608 if cr.Spec.NetworkNamespace == "" { 609 data.Data["SriovNetworkNamespace"] = cr.Namespace 610 } else { 611 data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace 612 } 613 data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName 614 615 data.Data["StateConfigured"] = true 616 switch cr.Spec.LinkState { 617 case SriovCniStateEnable: 618 data.Data["SriovCniState"] = SriovCniStateEnable 619 case SriovCniStateDisable: 620 data.Data["SriovCniState"] = SriovCniStateDisable 621 case SriovCniStateAuto: 622 data.Data["SriovCniState"] = SriovCniStateAuto 623 default: 624 data.Data["StateConfigured"] = false 625 } 626 627 if cr.Spec.Capabilities == "" { 628 data.Data["CapabilitiesConfigured"] = false 629 } else { 630 data.Data["CapabilitiesConfigured"] = true 631 data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities 632 } 633 634 if cr.Spec.IPAM != "" { 635 data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "") 636 } else { 637 data.Data["SriovCniIpam"] = SriovCniIpamEmpty 638 } 639 640 // metaplugins for the infiniband cni 641 data.Data["MetaPluginsConfigured"] = false 642 if cr.Spec.MetaPluginsConfig != "" { 643 data.Data["MetaPluginsConfigured"] = true 644 data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig 645 } 646 647 // logLevel and logFile are currently not supports by the ip-sriov-cni -> hardcode them to false. 648 data.Data["LogLevelConfigured"] = false 649 data.Data["LogFileConfigured"] = false 650 651 objs, err := render.RenderDir(ManifestsPath, &data) 652 if err != nil { 653 return nil, err 654 } 655 for _, obj := range objs { 656 raw, _ := json.Marshal(obj) 657 logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw)) 658 } 659 return objs[0], nil 660 } 661 662 // NetworkNamespace returns target network namespace for the network 663 func (cr *SriovIBNetwork) NetworkNamespace() string { 664 return cr.Spec.NetworkNamespace 665 } 666 667 // RenderNetAttDef renders a net-att-def for sriov CNI 668 func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) { 669 logger := log.WithName("RenderNetAttDef") 670 logger.Info("Start to render SRIOV CNI NetworkAttachmentDefinition") 671 672 // render RawCNIConfig manifests 673 data := render.MakeRenderData() 674 data.Data["CniType"] = "sriov" 675 data.Data["SriovNetworkName"] = cr.Name 676 if cr.Spec.NetworkNamespace == "" { 677 data.Data["SriovNetworkNamespace"] = cr.Namespace 678 } else { 679 data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace 680 } 681 data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName 682 data.Data["SriovCniVlan"] = cr.Spec.Vlan 683 684 if cr.Spec.VlanQoS <= 7 && cr.Spec.VlanQoS >= 0 { 685 data.Data["VlanQoSConfigured"] = true 686 data.Data["SriovCniVlanQoS"] = cr.Spec.VlanQoS 687 } else { 688 data.Data["VlanQoSConfigured"] = false 689 } 690 691 data.Data["VlanProtoConfigured"] = false 692 if cr.Spec.VlanProto != "" { 693 data.Data["VlanProtoConfigured"] = true 694 data.Data["SriovCniVlanProto"] = cr.Spec.VlanProto 695 } 696 697 if cr.Spec.Capabilities == "" { 698 data.Data["CapabilitiesConfigured"] = false 699 } else { 700 data.Data["CapabilitiesConfigured"] = true 701 data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities 702 } 703 704 data.Data["SpoofChkConfigured"] = true 705 switch cr.Spec.SpoofChk { 706 case SriovCniStateOff: 707 data.Data["SriovCniSpoofChk"] = SriovCniStateOff 708 case SriovCniStateOn: 709 data.Data["SriovCniSpoofChk"] = SriovCniStateOn 710 default: 711 data.Data["SpoofChkConfigured"] = false 712 } 713 714 data.Data["TrustConfigured"] = true 715 switch cr.Spec.Trust { 716 case SriovCniStateOn: 717 data.Data["SriovCniTrust"] = SriovCniStateOn 718 case SriovCniStateOff: 719 data.Data["SriovCniTrust"] = SriovCniStateOff 720 default: 721 data.Data["TrustConfigured"] = false 722 } 723 724 data.Data["StateConfigured"] = true 725 switch cr.Spec.LinkState { 726 case SriovCniStateEnable: 727 data.Data["SriovCniState"] = SriovCniStateEnable 728 case SriovCniStateDisable: 729 data.Data["SriovCniState"] = SriovCniStateDisable 730 case SriovCniStateAuto: 731 data.Data["SriovCniState"] = SriovCniStateAuto 732 default: 733 data.Data["StateConfigured"] = false 734 } 735 736 data.Data["MinTxRateConfigured"] = false 737 if cr.Spec.MinTxRate != nil { 738 if *cr.Spec.MinTxRate >= 0 { 739 data.Data["MinTxRateConfigured"] = true 740 data.Data["SriovCniMinTxRate"] = *cr.Spec.MinTxRate 741 } 742 } 743 744 data.Data["MaxTxRateConfigured"] = false 745 if cr.Spec.MaxTxRate != nil { 746 if *cr.Spec.MaxTxRate >= 0 { 747 data.Data["MaxTxRateConfigured"] = true 748 data.Data["SriovCniMaxTxRate"] = *cr.Spec.MaxTxRate 749 } 750 } 751 752 if cr.Spec.IPAM != "" { 753 data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "") 754 } else { 755 data.Data["SriovCniIpam"] = SriovCniIpamEmpty 756 } 757 758 data.Data["MetaPluginsConfigured"] = false 759 if cr.Spec.MetaPluginsConfig != "" { 760 data.Data["MetaPluginsConfigured"] = true 761 data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig 762 } 763 764 data.Data["LogLevelConfigured"] = (cr.Spec.LogLevel != "") 765 data.Data["LogLevel"] = cr.Spec.LogLevel 766 data.Data["LogFileConfigured"] = (cr.Spec.LogFile != "") 767 data.Data["LogFile"] = cr.Spec.LogFile 768 769 objs, err := render.RenderDir(ManifestsPath, &data) 770 if err != nil { 771 return nil, err 772 } 773 for _, obj := range objs { 774 raw, _ := json.Marshal(obj) 775 logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw)) 776 } 777 return objs[0], nil 778 } 779 780 // NetworkNamespace returns target network namespace for the network 781 func (cr *SriovNetwork) NetworkNamespace() string { 782 return cr.Spec.NetworkNamespace 783 } 784 785 // NetFilterMatch -- parse netFilter and check for a match 786 func NetFilterMatch(netFilter string, netValue string) (isMatch bool) { 787 logger := log.WithName("NetFilterMatch") 788 789 var re = regexp.MustCompile(`(?m)^\s*([^\s]+)\s*:\s*([^\s]+)`) 790 791 netFilterResult := re.FindAllStringSubmatch(netFilter, -1) 792 793 if netFilterResult == nil { 794 logger.Info("Invalid NetFilter spec...", "netFilter", netFilter) 795 return false 796 } 797 798 netValueResult := re.FindAllStringSubmatch(netValue, -1) 799 800 if netValueResult == nil { 801 logger.Info("Invalid netValue...", "netValue", netValue) 802 return false 803 } 804 805 return netFilterResult[0][1] == netValueResult[0][1] && netFilterResult[0][2] == netValueResult[0][2] 806 } 807 808 // MaxUnavailable calculate the max number of unavailable nodes to represent the number of nodes 809 // we can drain in parallel 810 func (s *SriovNetworkPoolConfig) MaxUnavailable(numOfNodes int) (int, error) { 811 // this means we want to drain all the nodes in parallel 812 if s.Spec.MaxUnavailable == nil { 813 return -1, nil 814 } 815 intOrPercent := *s.Spec.MaxUnavailable 816 817 if intOrPercent.Type == intstrutil.String { 818 if strings.HasSuffix(intOrPercent.StrVal, "%") { 819 i := strings.TrimSuffix(intOrPercent.StrVal, "%") 820 v, err := strconv.Atoi(i) 821 if err != nil { 822 return 0, fmt.Errorf("invalid value %q: %v", intOrPercent.StrVal, err) 823 } 824 if v > 100 || v < 1 { 825 return 0, fmt.Errorf("invalid value: percentage needs to be between 1 and 100") 826 } 827 } else { 828 return 0, fmt.Errorf("invalid type: strings needs to be a percentage") 829 } 830 } 831 832 maxunavail, err := intstrutil.GetScaledValueFromIntOrPercent(&intOrPercent, numOfNodes, false) 833 if err != nil { 834 return 0, err 835 } 836 837 if maxunavail < 0 { 838 return 0, fmt.Errorf("negative number is not allowed") 839 } 840 841 return maxunavail, nil 842 }