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  }