• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

k8snetworkplumbingwg / sriov-network-operator / 19227470377

10 Nov 2025 09:48AM UTC coverage: 62.151% (-0.2%) from 62.366%
19227470377

Pull #902

github

web-flow
Merge f9637c189 into 3d1a472a6
Pull Request #902: Create platform and orchestrator packages

319 of 659 new or added lines in 25 files covered. (48.41%)

41 existing lines in 9 files now uncovered.

8772 of 14114 relevant lines covered (62.15%)

0.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.17
/api/v1/helper.go
1
package v1
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "fmt"
7
        "os"
8
        "path/filepath"
9
        "regexp"
10
        "slices"
11
        "sort"
12
        "strconv"
13
        "strings"
14
        "time"
15

16
        corev1 "k8s.io/api/core/v1"
17
        "k8s.io/apimachinery/pkg/api/equality"
18
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19
        uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
20
        intstrutil "k8s.io/apimachinery/pkg/util/intstr"
21
        "k8s.io/client-go/kubernetes"
22
        "sigs.k8s.io/controller-runtime/pkg/client"
23
        logf "sigs.k8s.io/controller-runtime/pkg/log"
24

25
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
26
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
27
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
28
)
29

30
const (
31
        LASTNETWORKNAMESPACE        = "operator.sriovnetwork.openshift.io/last-network-namespace"
32
        NETATTDEFFINALIZERNAME      = "netattdef.finalizers.sriovnetwork.openshift.io"
33
        POOLCONFIGFINALIZERNAME     = "poolconfig.finalizers.sriovnetwork.openshift.io"
34
        OPERATORCONFIGFINALIZERNAME = "operatorconfig.finalizers.sriovnetwork.openshift.io"
35
        ESwithModeLegacy            = "legacy"
36
        ESwithModeSwitchDev         = "switchdev"
37

38
        SriovCniStateEnable  = "enable"
39
        SriovCniStateDisable = "disable"
40
        SriovCniStateAuto    = "auto"
41
        SriovCniStateOff     = "off"
42
        SriovCniStateOn      = "on"
43
        SriovCniIpam         = "\"ipam\""
44
        SriovCniIpamEmpty    = SriovCniIpam + ":{}"
45
)
46

47
const invalidVfIndex = -1
48

49
var ManifestsPath = "./bindata/manifests/cni-config"
50
var log = logf.Log.WithName("sriovnetwork")
51

52
// NicIDMap contains supported mapping of IDs with each in the format of:
53
// Vendor ID, Physical Function Device ID, Virtual Function Device ID
54
var NicIDMap = []string{}
55

56
var InitialState SriovNetworkNodeState
57

58
// NetFilterType Represents the NetFilter tags to be used
59
type NetFilterType int
60

61
const (
62
        // OpenstackNetworkID network UUID
63
        OpenstackNetworkID NetFilterType = iota
64

65
        SupportedNicIDConfigmap = "supported-nic-ids"
66
)
67

68
type ConfigurationModeType string
69

70
const (
71
        DaemonConfigurationMode  ConfigurationModeType = "daemon"
72
        SystemdConfigurationMode ConfigurationModeType = "systemd"
73
)
74

75
func (e NetFilterType) String() string {
×
76
        switch e {
×
77
        case OpenstackNetworkID:
×
78
                return "openstack/NetworkID"
×
79
        default:
×
80
                return fmt.Sprintf("%d", int(e))
×
81
        }
82
}
83

84
func InitNicIDMapFromConfigMap(client kubernetes.Interface, namespace string) error {
×
85
        cm, err := client.CoreV1().ConfigMaps(namespace).Get(
×
86
                context.Background(),
×
87
                SupportedNicIDConfigmap,
×
88
                metav1.GetOptions{},
×
89
        )
×
90
        // if the configmap does not exist, return false
×
91
        if err != nil {
×
92
                return err
×
93
        }
×
94
        for _, v := range cm.Data {
×
95
                NicIDMap = append(NicIDMap, v)
×
96
        }
×
97

98
        return nil
×
99
}
100

101
func InitNicIDMapFromList(idList []string) {
1✔
102
        NicIDMap = append(NicIDMap, idList...)
1✔
103
}
1✔
104

105
func IsSupportedVendor(vendorID string) bool {
1✔
106
        for _, n := range NicIDMap {
2✔
107
                ids := strings.Split(n, " ")
1✔
108
                if vendorID == ids[0] {
2✔
109
                        return true
1✔
110
                }
1✔
111
        }
112
        return false
1✔
113
}
114

115
func IsSupportedDevice(deviceID string) bool {
1✔
116
        for _, n := range NicIDMap {
2✔
117
                ids := strings.Split(n, " ")
1✔
118
                if deviceID == ids[1] {
1✔
119
                        return true
×
120
                }
×
121
        }
122
        return false
1✔
123
}
124

125
func IsSupportedModel(vendorID, deviceID string) bool {
1✔
126
        for _, n := range NicIDMap {
2✔
127
                ids := strings.Split(n, " ")
1✔
128
                if vendorID == ids[0] && deviceID == ids[1] {
2✔
129
                        return true
1✔
130
                }
1✔
131
        }
132
        log.Info("IsSupportedModel(): found unsupported model", "vendorId:", vendorID, "deviceId:", deviceID)
1✔
133
        return false
1✔
134
}
135

136
func IsVfSupportedModel(vendorID, deviceID string) bool {
1✔
137
        for _, n := range NicIDMap {
2✔
138
                ids := strings.Split(n, " ")
1✔
139
                if vendorID == ids[0] && deviceID == ids[2] {
2✔
140
                        return true
1✔
141
                }
1✔
142
        }
143
        log.Info("IsVfSupportedModel(): found unsupported VF model", "vendorId:", vendorID, "deviceId:", deviceID)
×
144
        return false
×
145
}
146

UNCOV
147
func GetSupportedVfIds() []string {
×
UNCOV
148
        var vfIds []string
×
UNCOV
149
        for _, n := range NicIDMap {
×
150
                ids := strings.Split(n, " ")
×
151
                vfID := "0x" + ids[2]
×
152
                if !StringInArray(vfID, vfIds) {
×
153
                        vfIds = append(vfIds, vfID)
×
154
                }
×
155
        }
156
        // return a sorted slice so that udev rule is stable
UNCOV
157
        sort.Slice(vfIds, func(i, j int) bool {
×
158
                ip, _ := strconv.ParseInt(vfIds[i], 0, 32)
×
159
                jp, _ := strconv.ParseInt(vfIds[j], 0, 32)
×
160
                return ip < jp
×
161
        })
×
UNCOV
162
        return vfIds
×
163
}
164

165
func GetVfDeviceID(deviceID string) string {
1✔
166
        for _, n := range NicIDMap {
2✔
167
                ids := strings.Split(n, " ")
1✔
168
                if deviceID == ids[1] {
2✔
169
                        return ids[2]
1✔
170
                }
1✔
171
        }
172
        return ""
1✔
173
}
174

175
func IsSwitchdevModeSpec(spec SriovNetworkNodeStateSpec) bool {
1✔
176
        return ContainsSwitchdevInterface(spec.Interfaces)
1✔
177
}
1✔
178

179
// ContainsSwitchdevInterface returns true if provided interface list contains interface
180
// with switchdev configuration
181
func ContainsSwitchdevInterface(interfaces []Interface) bool {
1✔
182
        for _, iface := range interfaces {
2✔
183
                if iface.EswitchMode == ESwithModeSwitchDev {
2✔
184
                        return true
1✔
185
                }
1✔
186
        }
187
        return false
1✔
188
}
189

190
// GetEswitchModeFromSpec returns ESwitchMode from the interface spec, returns legacy if not set
191
func GetEswitchModeFromSpec(ifaceSpec *Interface) string {
1✔
192
        if ifaceSpec.EswitchMode == "" {
2✔
193
                return ESwithModeLegacy
1✔
194
        }
1✔
195
        return ifaceSpec.EswitchMode
1✔
196
}
197

198
// GetEswitchModeFromStatus returns ESwitchMode from the interface status, returns legacy if not set
199
func GetEswitchModeFromStatus(ifaceStatus *InterfaceExt) string {
1✔
200
        if ifaceStatus.EswitchMode == "" {
2✔
201
                return ESwithModeLegacy
1✔
202
        }
1✔
203
        return ifaceStatus.EswitchMode
1✔
204
}
205

206
func NeedToUpdateSriov(ifaceSpec *Interface, ifaceStatus *InterfaceExt) bool {
1✔
207
        if ifaceSpec.Mtu > 0 {
2✔
208
                mtu := ifaceSpec.Mtu
1✔
209
                if mtu > ifaceStatus.Mtu {
2✔
210
                        log.V(0).Info("NeedToUpdateSriov(): MTU needs update", "desired", mtu, "current", ifaceStatus.Mtu)
1✔
211
                        return true
1✔
212
                }
1✔
213
        }
214
        currentEswitchMode := GetEswitchModeFromStatus(ifaceStatus)
1✔
215
        desiredEswitchMode := GetEswitchModeFromSpec(ifaceSpec)
1✔
216
        if currentEswitchMode != desiredEswitchMode {
2✔
217
                log.V(0).Info("NeedToUpdateSriov(): EswitchMode needs update", "desired", desiredEswitchMode, "current", currentEswitchMode)
1✔
218
                return true
1✔
219
        }
1✔
220
        if ifaceSpec.NumVfs != ifaceStatus.NumVfs {
2✔
221
                log.V(0).Info("NeedToUpdateSriov(): NumVfs needs update", "desired", ifaceSpec.NumVfs, "current", ifaceStatus.NumVfs)
1✔
222
                return true
1✔
223
        }
1✔
224

225
        if ifaceStatus.LinkAdminState == consts.LinkAdminStateDown {
2✔
226
                log.V(0).Info("NeedToUpdateSriov(): PF link status needs update", "desired to include", "up", "current", ifaceStatus.LinkAdminState)
1✔
227
                return true
1✔
228
        }
1✔
229

230
        if ifaceSpec.NumVfs > 0 {
2✔
231
                for _, vfStatus := range ifaceStatus.VFs {
2✔
232
                        for _, groupSpec := range ifaceSpec.VfGroups {
2✔
233
                                if IndexInRange(vfStatus.VfID, groupSpec.VfRange) {
2✔
234
                                        if vfStatus.Driver == "" {
1✔
235
                                                log.V(0).Info("NeedToUpdateSriov(): Driver needs update - has no driver",
×
236
                                                        "desired", groupSpec.DeviceType)
×
237
                                                return true
×
238
                                        }
×
239
                                        if groupSpec.DeviceType != "" && groupSpec.DeviceType != consts.DeviceTypeNetDevice {
2✔
240
                                                if groupSpec.DeviceType != vfStatus.Driver {
1✔
241
                                                        log.V(0).Info("NeedToUpdateSriov(): Driver needs update",
×
242
                                                                "desired", groupSpec.DeviceType, "current", vfStatus.Driver)
×
243
                                                        return true
×
244
                                                }
×
245
                                        } else {
1✔
246
                                                if StringInArray(vfStatus.Driver, vars.DpdkDrivers) {
2✔
247
                                                        log.V(0).Info("NeedToUpdateSriov(): Driver needs update",
1✔
248
                                                                "desired", groupSpec.DeviceType, "current", vfStatus.Driver)
1✔
249
                                                        return true
1✔
250
                                                }
1✔
251
                                                if vfStatus.Mtu != 0 && groupSpec.Mtu != 0 && vfStatus.Mtu != groupSpec.Mtu {
2✔
252
                                                        log.V(0).Info("NeedToUpdateSriov(): VF MTU needs update",
1✔
253
                                                                "vf", vfStatus.VfID, "desired", groupSpec.Mtu, "current", vfStatus.Mtu)
1✔
254
                                                        return true
1✔
255
                                                }
1✔
256

257
                                                if (strings.EqualFold(ifaceStatus.LinkType, consts.LinkTypeETH) && groupSpec.IsRdma) || strings.EqualFold(ifaceStatus.LinkType, consts.LinkTypeIB) {
2✔
258
                                                        // We do this check only if a Node GUID is set to ensure that we were able to read the
1✔
259
                                                        // Node GUID. We intentionally skip empty Node GUID in vfStatus because this may happen
1✔
260
                                                        // when the VF is allocated to a workload.
1✔
261
                                                        if vfStatus.GUID == consts.UninitializedNodeGUID {
2✔
262
                                                                log.V(0).Info("NeedToUpdateSriov(): VF GUID needs update",
1✔
263
                                                                        "vf", vfStatus.VfID, "current", vfStatus.GUID)
1✔
264
                                                                return true
1✔
265
                                                        }
1✔
266
                                                }
267
                                                // this is needed to be sure the admin mac address is configured as expected
268
                                                if ifaceSpec.ExternallyManaged {
1✔
269
                                                        log.V(0).Info("NeedToUpdateSriov(): need to update the device as it's externally manage",
×
270
                                                                "device", ifaceStatus.PciAddress)
×
271
                                                        return true
×
272
                                                }
×
273
                                        }
274
                                        if groupSpec.VdpaType != vfStatus.VdpaType {
1✔
275
                                                log.V(0).Info("NeedToUpdateSriov(): VF VdpaType mismatch",
×
276
                                                        "desired", groupSpec.VdpaType, "current", vfStatus.VdpaType)
×
277
                                                return true
×
278
                                        }
×
279
                                        break
1✔
280
                                }
281
                        }
282
                }
283
        }
284
        return false
1✔
285
}
286

287
type ByPriority []SriovNetworkNodePolicy
288

289
func (a ByPriority) Len() int {
1✔
290
        return len(a)
1✔
291
}
1✔
292

293
func (a ByPriority) Less(i, j int) bool {
×
294
        if a[i].Spec.Priority != a[j].Spec.Priority {
×
295
                return a[i].Spec.Priority > a[j].Spec.Priority
×
296
        }
×
297
        return a[i].GetName() < a[j].GetName()
×
298
}
299

300
func (a ByPriority) Swap(i, j int) {
×
301
        a[i], a[j] = a[j], a[i]
×
302
}
×
303

304
// Match check if node is selected by NodeSelector
305
func (p *SriovNetworkNodePolicy) Selected(node *corev1.Node) bool {
1✔
306
        for k, v := range p.Spec.NodeSelector {
2✔
307
                if nv, ok := node.Labels[k]; ok && nv == v {
2✔
308
                        continue
1✔
309
                }
310
                return false
×
311
        }
312
        return true
1✔
313
}
314

315
func StringInArray(val string, array []string) bool {
1✔
316
        for i := range array {
2✔
317
                if array[i] == val {
2✔
318
                        return true
1✔
319
                }
1✔
320
        }
321
        return false
1✔
322
}
323

324
func RemoveString(s string, slice []string) (result []string, found bool) {
1✔
325
        if len(slice) != 0 {
2✔
326
                for _, item := range slice {
2✔
327
                        if item == s {
2✔
328
                                found = true
1✔
329
                                continue
1✔
330
                        }
331
                        result = append(result, item)
×
332
                }
333
        }
334
        return
1✔
335
}
336

337
func UniqueAppend(inSlice []string, strings ...string) []string {
1✔
338
        for _, s := range strings {
2✔
339
                if !StringInArray(s, inSlice) {
2✔
340
                        inSlice = append(inSlice, s)
1✔
341
                }
1✔
342
        }
343
        return inSlice
1✔
344
}
345

346
// Apply policy to SriovNetworkNodeState CR
347
func (p *SriovNetworkNodePolicy) Apply(state *SriovNetworkNodeState, equalPriority bool) error {
1✔
348
        s := p.Spec.NicSelector
1✔
349
        if s.IsEmpty() {
2✔
350
                // Empty NicSelector match none
1✔
351
                return nil
1✔
352
        }
1✔
353
        for _, iface := range state.Status.Interfaces {
2✔
354
                if s.Selected(&iface) {
2✔
355
                        log.Info("Update interface", "name:", iface.Name)
1✔
356
                        result := Interface{
1✔
357
                                PciAddress:        iface.PciAddress,
1✔
358
                                Mtu:               p.Spec.Mtu,
1✔
359
                                Name:              iface.Name,
1✔
360
                                LinkType:          p.Spec.LinkType,
1✔
361
                                EswitchMode:       p.Spec.EswitchMode,
1✔
362
                                NumVfs:            p.Spec.NumVfs,
1✔
363
                                ExternallyManaged: p.Spec.ExternallyManaged,
1✔
364
                        }
1✔
365
                        if p.Spec.NumVfs > 0 {
2✔
366
                                group, err := p.generatePfNameVfGroup(&iface)
1✔
367
                                if err != nil {
2✔
368
                                        return err
1✔
369
                                }
1✔
370
                                result.VfGroups = []VfGroup{*group}
1✔
371
                                found := false
1✔
372
                                for i := range state.Spec.Interfaces {
2✔
373
                                        if state.Spec.Interfaces[i].PciAddress == result.PciAddress {
2✔
374
                                                found = true
1✔
375
                                                state.Spec.Interfaces[i].mergeConfigs(&result, equalPriority)
1✔
376
                                                state.Spec.Interfaces[i] = result
1✔
377
                                                break
1✔
378
                                        }
379
                                }
380
                                if !found {
2✔
381
                                        state.Spec.Interfaces = append(state.Spec.Interfaces, result)
1✔
382
                                }
1✔
383
                        }
384
                }
385
        }
386
        return nil
1✔
387
}
388

389
// ApplyBridgeConfig applies bridge configuration from the policy to the provided state
390
func (p *SriovNetworkNodePolicy) ApplyBridgeConfig(state *SriovNetworkNodeState) error {
1✔
391
        if p.Spec.NicSelector.IsEmpty() {
2✔
392
                // Empty NicSelector match none
1✔
393
                return nil
1✔
394
        }
1✔
395
        // sanity check the policy
396
        if !p.Spec.Bridge.IsEmpty() {
2✔
397
                if p.Spec.EswitchMode != ESwithModeSwitchDev {
2✔
398
                        return fmt.Errorf("eSwitchMode must be switchdev to use software bridge management")
1✔
399
                }
1✔
400
                if p.Spec.LinkType != "" && !strings.EqualFold(p.Spec.LinkType, consts.LinkTypeETH) {
2✔
401
                        return fmt.Errorf("linkType must be eth or ETH to use software bridge management")
1✔
402
                }
1✔
403
                if p.Spec.ExternallyManaged {
2✔
404
                        return fmt.Errorf("software bridge management can't be used when link is externally managed")
1✔
405
                }
1✔
406
        }
407
        for _, iface := range state.Status.Interfaces {
2✔
408
                if p.Spec.NicSelector.Selected(&iface) {
2✔
409
                        if p.Spec.Bridge.OVS == nil {
2✔
410
                                // The policy has no OVS bridge config, this means that the node's state should have no managed OVS bridges for the interfaces that match the policy.
1✔
411
                                // Currently PF to OVS bridge mapping is always 1 to 1 (bonding is not supported at the moment), meaning we can remove the OVS bridge
1✔
412
                                // config from the node's state if it has the interface (that matches "empty-bridge" policy) in the uplink section.
1✔
413
                                state.Spec.Bridges.OVS = slices.DeleteFunc(state.Spec.Bridges.OVS, func(br OVSConfigExt) bool {
2✔
414
                                        return slices.ContainsFunc(br.Uplinks, func(uplink OVSUplinkConfigExt) bool {
2✔
415
                                                return uplink.PciAddress == iface.PciAddress
1✔
416
                                        })
1✔
417
                                })
418
                                if len(state.Spec.Bridges.OVS) == 0 {
2✔
419
                                        state.Spec.Bridges.OVS = nil
1✔
420
                                }
1✔
421
                                continue
1✔
422
                        }
423
                        ovsBridge := OVSConfigExt{
1✔
424
                                Name:   GenerateBridgeName(&iface),
1✔
425
                                Bridge: p.Spec.Bridge.OVS.Bridge,
1✔
426
                                Uplinks: []OVSUplinkConfigExt{{
1✔
427
                                        PciAddress: iface.PciAddress,
1✔
428
                                        Name:       iface.Name,
1✔
429
                                        Interface:  p.Spec.Bridge.OVS.Uplink.Interface,
1✔
430
                                }},
1✔
431
                        }
1✔
432
                        if p.Spec.Mtu > 0 {
1✔
433
                                mtu := p.Spec.Mtu
×
434
                                ovsBridge.Uplinks[0].Interface.MTURequest = &mtu
×
435
                        }
×
436
                        log.Info("Update bridge for interface", "name", iface.Name, "bridge", ovsBridge.Name)
1✔
437

1✔
438
                        // We need to keep slices with bridges ordered to avoid unnecessary updates in the K8S API.
1✔
439
                        // Use binary search to insert (or update) the bridge config to the right place in the slice to keep it sorted.
1✔
440
                        pos, exist := slices.BinarySearchFunc(state.Spec.Bridges.OVS, ovsBridge, func(x, y OVSConfigExt) int {
2✔
441
                                return strings.Compare(x.Name, y.Name)
1✔
442
                        })
1✔
443
                        if exist {
2✔
444
                                state.Spec.Bridges.OVS[pos] = ovsBridge
1✔
445
                        } else {
2✔
446
                                state.Spec.Bridges.OVS = slices.Insert(state.Spec.Bridges.OVS, pos, ovsBridge)
1✔
447
                        }
1✔
448
                }
449
        }
450
        return nil
1✔
451
}
452

453
// mergeConfigs merges configs from multiple polices where the last one has the
454
// highest priority. This merge is dependent on: 1. SR-IOV partition is
455
// configured with the #-notation in pfName, 2. The VF groups are
456
// non-overlapping or SR-IOV policies have the same priority.
457
func (iface Interface) mergeConfigs(input *Interface, equalPriority bool) {
1✔
458
        m := false
1✔
459
        // merge VF groups (input.VfGroups already contains the highest priority):
1✔
460
        // - skip group with same ResourceName,
1✔
461
        // - skip overlapping groups (use only highest priority)
1✔
462
        for _, gr := range iface.VfGroups {
2✔
463
                if gr.ResourceName == input.VfGroups[0].ResourceName || gr.isVFRangeOverlapping(input.VfGroups[0]) {
2✔
464
                        continue
1✔
465
                }
466
                m = true
1✔
467
                input.VfGroups = append(input.VfGroups, gr)
1✔
468
        }
469

470
        if !equalPriority && !m {
2✔
471
                return
1✔
472
        }
1✔
473

474
        // mtu configuration we take the highest value
475
        if input.Mtu < iface.Mtu {
2✔
476
                input.Mtu = iface.Mtu
1✔
477
        }
1✔
478
        if input.NumVfs < iface.NumVfs {
2✔
479
                input.NumVfs = iface.NumVfs
1✔
480
        }
1✔
481
}
482

483
func (gr VfGroup) isVFRangeOverlapping(group VfGroup) bool {
1✔
484
        rngSt, rngEnd, err := parseRange(gr.VfRange)
1✔
485
        if err != nil {
1✔
486
                return false
×
487
        }
×
488
        rngSt2, rngEnd2, err := parseRange(group.VfRange)
1✔
489
        if err != nil {
1✔
490
                return false
×
491
        }
×
492
        // compare minimal range has overlap
493
        if rngSt < rngSt2 {
2✔
494
                return IndexInRange(rngSt2, gr.VfRange) || IndexInRange(rngEnd2, gr.VfRange)
1✔
495
        }
1✔
496
        return IndexInRange(rngSt, group.VfRange) || IndexInRange(rngEnd, group.VfRange)
1✔
497
}
498

499
func (p *SriovNetworkNodePolicy) generatePfNameVfGroup(iface *InterfaceExt) (*VfGroup, error) {
1✔
500
        var err error
1✔
501
        pfName := ""
1✔
502
        var rngStart, rngEnd int
1✔
503
        found := false
1✔
504
        for _, selector := range p.Spec.NicSelector.PfNames {
2✔
505
                pfName, rngStart, rngEnd, err = ParseVfRange(selector)
1✔
506
                if err != nil {
2✔
507
                        log.Error(err, "Unable to parse PF Name.")
1✔
508
                        return nil, err
1✔
509
                }
1✔
510
                if pfName == iface.Name {
2✔
511
                        found = true
1✔
512
                        if rngStart == invalidVfIndex && rngEnd == invalidVfIndex {
2✔
513
                                rngStart, rngEnd = 0, p.Spec.NumVfs-1
1✔
514
                        }
1✔
515
                        break
1✔
516
                }
517
        }
518
        if !found {
2✔
519
                // assign the default vf index range if the pfName is not specified by the nicSelector
1✔
520
                rngStart, rngEnd = 0, p.Spec.NumVfs-1
1✔
521
        }
1✔
522
        rng := strconv.Itoa(rngStart) + "-" + strconv.Itoa(rngEnd)
1✔
523
        return &VfGroup{
1✔
524
                ResourceName: p.Spec.ResourceName,
1✔
525
                DeviceType:   p.Spec.DeviceType,
1✔
526
                VfRange:      rng,
1✔
527
                PolicyName:   p.GetName(),
1✔
528
                Mtu:          p.Spec.Mtu,
1✔
529
                IsRdma:       p.Spec.IsRdma,
1✔
530
                VdpaType:     p.Spec.VdpaType,
1✔
531
        }, nil
1✔
532
}
533

534
func IndexInRange(i int, r string) bool {
1✔
535
        rngSt, rngEnd, err := parseRange(r)
1✔
536
        if err != nil {
2✔
537
                return false
1✔
538
        }
1✔
539
        if i <= rngEnd && i >= rngSt {
2✔
540
                return true
1✔
541
        }
1✔
542
        return false
1✔
543
}
544

545
func parseRange(r string) (rngSt, rngEnd int, err error) {
1✔
546
        rng := strings.Split(r, "-")
1✔
547
        rngSt, err = strconv.Atoi(rng[0])
1✔
548
        if err != nil {
2✔
549
                return
1✔
550
        }
1✔
551
        rngEnd, err = strconv.Atoi(rng[1])
1✔
552
        if err != nil {
1✔
553
                return
×
554
        }
×
555
        return
1✔
556
}
557

558
// SplitDeviceFromRange return the device name and the range.
559
// the split is base on #
560
func SplitDeviceFromRange(device string) (string, string) {
1✔
561
        if strings.Contains(device, "#") {
2✔
562
                fields := strings.Split(device, "#")
1✔
563
                return fields[0], fields[1]
1✔
564
        }
1✔
565

566
        return device, ""
1✔
567
}
568

569
// ParseVfRange: parse a device with VF range
570
// this can be rootDevices or PFName
571
// if no range detect we just return the device name
572
func ParseVfRange(device string) (rootDeviceName string, rngSt, rngEnd int, err error) {
1✔
573
        rngSt, rngEnd = invalidVfIndex, invalidVfIndex
1✔
574
        rootDeviceName, splitRange := SplitDeviceFromRange(device)
1✔
575
        if splitRange != "" {
2✔
576
                rngSt, rngEnd, err = parseRange(splitRange)
1✔
577
        } else {
2✔
578
                rootDeviceName = device
1✔
579
        }
1✔
580
        return
1✔
581
}
582

583
// IsEmpty returns true if nicSelector is empty
584
func (selector *SriovNetworkNicSelector) IsEmpty() bool {
1✔
585
        return selector.Vendor == "" &&
1✔
586
                selector.DeviceID == "" &&
1✔
587
                len(selector.RootDevices) == 0 &&
1✔
588
                len(selector.PfNames) == 0 &&
1✔
589
                len(selector.NetFilter) == 0
1✔
590
}
1✔
591

592
func (selector *SriovNetworkNicSelector) Selected(iface *InterfaceExt) bool {
1✔
593
        if selector.Vendor != "" && selector.Vendor != iface.Vendor {
1✔
594
                return false
×
595
        }
×
596
        if selector.DeviceID != "" && selector.DeviceID != iface.DeviceID {
1✔
597
                return false
×
598
        }
×
599
        if len(selector.RootDevices) > 0 && !StringInArray(iface.PciAddress, selector.RootDevices) {
2✔
600
                return false
1✔
601
        }
1✔
602
        if len(selector.PfNames) > 0 {
2✔
603
                var pfNames []string
1✔
604
                for _, p := range selector.PfNames {
2✔
605
                        if strings.Contains(p, "#") {
2✔
606
                                fields := strings.Split(p, "#")
1✔
607
                                pfNames = append(pfNames, fields[0])
1✔
608
                        } else {
2✔
609
                                pfNames = append(pfNames, p)
1✔
610
                        }
1✔
611
                }
612
                if !StringInArray(iface.Name, pfNames) {
1✔
613
                        return false
×
614
                }
×
615
        }
616
        if selector.NetFilter != "" && !NetFilterMatch(selector.NetFilter, iface.NetFilter) {
1✔
617
                return false
×
618
        }
×
619

620
        return true
1✔
621
}
622

623
func (s *SriovNetworkNodeState) GetInterfaceStateByPciAddress(addr string) *InterfaceExt {
1✔
624
        for _, iface := range s.Status.Interfaces {
1✔
625
                if addr == iface.PciAddress {
×
626
                        return &iface
×
627
                }
×
628
        }
629
        return nil
1✔
630
}
631

632
func (s *SriovNetworkNodeState) GetDriverByPciAddress(addr string) string {
×
633
        for _, iface := range s.Status.Interfaces {
×
634
                if addr == iface.PciAddress {
×
635
                        return iface.Driver
×
636
                }
×
637
        }
638
        return ""
×
639
}
640

641
// RenderNetAttDef renders a net-att-def for ib-sriov CNI
642
func (cr *SriovIBNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
643
        logger := log.WithName("RenderNetAttDef")
1✔
644
        logger.Info("Start to render IB SRIOV CNI NetworkAttachmentDefinition")
1✔
645

1✔
646
        // render RawCNIConfig manifests
1✔
647
        data := render.MakeRenderData()
1✔
648
        data.Data["CniType"] = "ib-sriov"
1✔
649
        data.Data["SriovNetworkName"] = cr.Name
1✔
650
        if cr.Spec.NetworkNamespace == "" {
2✔
651
                data.Data["SriovNetworkNamespace"] = cr.Namespace
1✔
652
        } else {
2✔
653
                data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
654
        }
1✔
655
        data.Data["Owner"] = OwnerRefToString(cr)
1✔
656
        data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
657

1✔
658
        data.Data["StateConfigured"] = true
1✔
659
        switch cr.Spec.LinkState {
1✔
660
        case SriovCniStateEnable:
1✔
661
                data.Data["SriovCniState"] = SriovCniStateEnable
1✔
662
        case SriovCniStateDisable:
×
663
                data.Data["SriovCniState"] = SriovCniStateDisable
×
664
        case SriovCniStateAuto:
×
665
                data.Data["SriovCniState"] = SriovCniStateAuto
×
666
        default:
1✔
667
                data.Data["StateConfigured"] = false
1✔
668
        }
669

670
        if cr.Spec.Capabilities == "" {
2✔
671
                data.Data["CapabilitiesConfigured"] = false
1✔
672
        } else {
2✔
673
                data.Data["CapabilitiesConfigured"] = true
1✔
674
                data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities
1✔
675
        }
1✔
676

677
        if cr.Spec.IPAM != "" {
2✔
678
                data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
679
        } else {
2✔
680
                data.Data["SriovCniIpam"] = SriovCniIpamEmpty
1✔
681
        }
1✔
682

683
        // metaplugins for the infiniband cni
684
        data.Data["MetaPluginsConfigured"] = false
1✔
685
        if cr.Spec.MetaPluginsConfig != "" {
1✔
686
                data.Data["MetaPluginsConfigured"] = true
×
687
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
×
688
        }
×
689

690
        // logLevel and logFile are currently not supports by the ip-sriov-cni -> hardcode them to false.
691
        data.Data["LogLevelConfigured"] = false
1✔
692
        data.Data["LogFileConfigured"] = false
1✔
693

1✔
694
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
1✔
695
        if err != nil {
1✔
696
                return nil, err
×
697
        }
×
698
        for _, obj := range objs {
2✔
699
                raw, _ := json.Marshal(obj)
1✔
700
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
701
        }
1✔
702
        return objs[0], nil
1✔
703
}
704

705
// NetworkNamespace returns target network namespace for the network
706
func (cr *SriovIBNetwork) NetworkNamespace() string {
1✔
707
        return cr.Spec.NetworkNamespace
1✔
708
}
1✔
709

710
// RenderNetAttDef renders a net-att-def for sriov CNI
711
func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
712
        logger := log.WithName("RenderNetAttDef")
1✔
713
        logger.Info("Start to render SRIOV CNI NetworkAttachmentDefinition")
1✔
714

1✔
715
        // render RawCNIConfig manifests
1✔
716
        data := render.MakeRenderData()
1✔
717
        data.Data["CniType"] = "sriov"
1✔
718
        data.Data["SriovNetworkName"] = cr.Name
1✔
719
        if cr.Spec.NetworkNamespace == "" {
2✔
720
                data.Data["SriovNetworkNamespace"] = cr.Namespace
1✔
721
        } else {
2✔
722
                data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
723
        }
1✔
724
        data.Data["Owner"] = OwnerRefToString(cr)
1✔
725
        data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
726
        data.Data["SriovCniVlan"] = cr.Spec.Vlan
1✔
727

1✔
728
        if cr.Spec.VlanQoS <= 7 && cr.Spec.VlanQoS >= 0 {
2✔
729
                data.Data["VlanQoSConfigured"] = true
1✔
730
                data.Data["SriovCniVlanQoS"] = cr.Spec.VlanQoS
1✔
731
        } else {
1✔
732
                data.Data["VlanQoSConfigured"] = false
×
733
        }
×
734

735
        data.Data["VlanProtoConfigured"] = false
1✔
736
        if cr.Spec.VlanProto != "" {
2✔
737
                data.Data["VlanProtoConfigured"] = true
1✔
738
                data.Data["SriovCniVlanProto"] = cr.Spec.VlanProto
1✔
739
        }
1✔
740

741
        if cr.Spec.Capabilities == "" {
2✔
742
                data.Data["CapabilitiesConfigured"] = false
1✔
743
        } else {
1✔
744
                data.Data["CapabilitiesConfigured"] = true
×
745
                data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities
×
746
        }
×
747

748
        data.Data["SpoofChkConfigured"] = true
1✔
749
        switch cr.Spec.SpoofChk {
1✔
750
        case SriovCniStateOff:
×
751
                data.Data["SriovCniSpoofChk"] = SriovCniStateOff
×
752
        case SriovCniStateOn:
1✔
753
                data.Data["SriovCniSpoofChk"] = SriovCniStateOn
1✔
754
        default:
1✔
755
                data.Data["SpoofChkConfigured"] = false
1✔
756
        }
757

758
        data.Data["TrustConfigured"] = true
1✔
759
        switch cr.Spec.Trust {
1✔
760
        case SriovCniStateOn:
1✔
761
                data.Data["SriovCniTrust"] = SriovCniStateOn
1✔
762
        case SriovCniStateOff:
×
763
                data.Data["SriovCniTrust"] = SriovCniStateOff
×
764
        default:
1✔
765
                data.Data["TrustConfigured"] = false
1✔
766
        }
767

768
        data.Data["StateConfigured"] = true
1✔
769
        switch cr.Spec.LinkState {
1✔
770
        case SriovCniStateEnable:
×
771
                data.Data["SriovCniState"] = SriovCniStateEnable
×
772
        case SriovCniStateDisable:
×
773
                data.Data["SriovCniState"] = SriovCniStateDisable
×
774
        case SriovCniStateAuto:
×
775
                data.Data["SriovCniState"] = SriovCniStateAuto
×
776
        default:
1✔
777
                data.Data["StateConfigured"] = false
1✔
778
        }
779

780
        data.Data["MinTxRateConfigured"] = false
1✔
781
        if cr.Spec.MinTxRate != nil {
2✔
782
                if *cr.Spec.MinTxRate >= 0 {
2✔
783
                        data.Data["MinTxRateConfigured"] = true
1✔
784
                        data.Data["SriovCniMinTxRate"] = *cr.Spec.MinTxRate
1✔
785
                }
1✔
786
        }
787

788
        data.Data["MaxTxRateConfigured"] = false
1✔
789
        if cr.Spec.MaxTxRate != nil {
1✔
790
                if *cr.Spec.MaxTxRate >= 0 {
×
791
                        data.Data["MaxTxRateConfigured"] = true
×
792
                        data.Data["SriovCniMaxTxRate"] = *cr.Spec.MaxTxRate
×
793
                }
×
794
        }
795

796
        if cr.Spec.IPAM != "" {
2✔
797
                data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
798
        } else {
2✔
799
                data.Data["SriovCniIpam"] = SriovCniIpamEmpty
1✔
800
        }
1✔
801

802
        data.Data["MetaPluginsConfigured"] = false
1✔
803
        if cr.Spec.MetaPluginsConfig != "" {
2✔
804
                data.Data["MetaPluginsConfigured"] = true
1✔
805
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
1✔
806
        }
1✔
807

808
        data.Data["LogLevelConfigured"] = (cr.Spec.LogLevel != "")
1✔
809
        data.Data["LogLevel"] = cr.Spec.LogLevel
1✔
810
        data.Data["LogFileConfigured"] = (cr.Spec.LogFile != "")
1✔
811
        data.Data["LogFile"] = cr.Spec.LogFile
1✔
812

1✔
813
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
1✔
814
        if err != nil {
1✔
815
                return nil, err
×
816
        }
×
817
        for _, obj := range objs {
2✔
818
                raw, _ := json.Marshal(obj)
1✔
819
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
820
        }
1✔
821
        return objs[0], nil
1✔
822
}
823

824
// NetworkNamespace returns target network namespace for the network
825
func (cr *SriovNetwork) NetworkNamespace() string {
1✔
826
        return cr.Spec.NetworkNamespace
1✔
827
}
1✔
828

829
// RenderNetAttDef renders a net-att-def for sriov CNI
830
func (cr *OVSNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
831
        logger := log.WithName("RenderNetAttDef")
1✔
832
        logger.Info("Start to render OVS CNI NetworkAttachmentDefinition")
1✔
833

1✔
834
        // render RawCNIConfig manifests
1✔
835
        data := render.MakeRenderData()
1✔
836
        data.Data["CniType"] = "ovs"
1✔
837
        data.Data["NetworkName"] = cr.Name
1✔
838
        if cr.Spec.NetworkNamespace == "" {
2✔
839
                data.Data["NetworkNamespace"] = cr.Namespace
1✔
840
        } else {
2✔
841
                data.Data["NetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
842
        }
1✔
843
        data.Data["Owner"] = OwnerRefToString(cr)
1✔
844
        data.Data["CniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
845

1✔
846
        if cr.Spec.Capabilities == "" {
2✔
847
                data.Data["CapabilitiesConfigured"] = false
1✔
848
        } else {
2✔
849
                data.Data["CapabilitiesConfigured"] = true
1✔
850
                data.Data["CniCapabilities"] = cr.Spec.Capabilities
1✔
851
        }
1✔
852

853
        data.Data["Bridge"] = cr.Spec.Bridge
1✔
854
        data.Data["VlanTag"] = cr.Spec.Vlan
1✔
855
        data.Data["MTU"] = cr.Spec.MTU
1✔
856
        if len(cr.Spec.Trunk) > 0 {
2✔
857
                trunkConfRaw, _ := json.Marshal(cr.Spec.Trunk)
1✔
858
                data.Data["Trunk"] = string(trunkConfRaw)
1✔
859
        } else {
2✔
860
                data.Data["Trunk"] = ""
1✔
861
        }
1✔
862
        data.Data["InterfaceType"] = cr.Spec.InterfaceType
1✔
863

1✔
864
        if cr.Spec.IPAM != "" {
2✔
865
                data.Data["CniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
866
        } else {
2✔
867
                data.Data["CniIpam"] = SriovCniIpamEmpty
1✔
868
        }
1✔
869

870
        data.Data["MetaPluginsConfigured"] = false
1✔
871
        if cr.Spec.MetaPluginsConfig != "" {
2✔
872
                data.Data["MetaPluginsConfigured"] = true
1✔
873
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
1✔
874
        }
1✔
875

876
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "ovs"), &data)
1✔
877
        if err != nil {
1✔
878
                return nil, err
×
879
        }
×
880
        for _, obj := range objs {
2✔
881
                raw, _ := json.Marshal(obj)
1✔
882
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
883
        }
1✔
884
        return objs[0], nil
1✔
885
}
886

887
// NetworkNamespace returns target network namespace for the network
888
func (cr *OVSNetwork) NetworkNamespace() string {
1✔
889
        return cr.Spec.NetworkNamespace
1✔
890
}
1✔
891

892
// NetFilterMatch -- parse netFilter and check for a match
893
func NetFilterMatch(netFilter string, netValue string) (isMatch bool) {
×
894
        logger := log.WithName("NetFilterMatch")
×
895

×
896
        var re = regexp.MustCompile(`(?m)^\s*([^\s]+)\s*:\s*([^\s]+)`)
×
897

×
898
        netFilterResult := re.FindAllStringSubmatch(netFilter, -1)
×
899

×
900
        if netFilterResult == nil {
×
901
                logger.Info("Invalid NetFilter spec...", "netFilter", netFilter)
×
902
                return false
×
903
        }
×
904

905
        netValueResult := re.FindAllStringSubmatch(netValue, -1)
×
906

×
907
        if netValueResult == nil {
×
908
                logger.Info("Invalid netValue...", "netValue", netValue)
×
909
                return false
×
910
        }
×
911

912
        return netFilterResult[0][1] == netValueResult[0][1] && netFilterResult[0][2] == netValueResult[0][2]
×
913
}
914

915
// MaxUnavailable calculate the max number of unavailable nodes to represent the number of nodes
916
// we can drain in parallel
917
func (s *SriovNetworkPoolConfig) MaxUnavailable(numOfNodes int) (int, error) {
1✔
918
        // this means we want to drain all the nodes in parallel
1✔
919
        if s.Spec.MaxUnavailable == nil {
2✔
920
                return -1, nil
1✔
921
        }
1✔
922
        intOrPercent := *s.Spec.MaxUnavailable
1✔
923

1✔
924
        if intOrPercent.Type == intstrutil.String {
2✔
925
                if strings.HasSuffix(intOrPercent.StrVal, "%") {
2✔
926
                        i := strings.TrimSuffix(intOrPercent.StrVal, "%")
1✔
927
                        v, err := strconv.Atoi(i)
1✔
928
                        if err != nil {
1✔
929
                                return 0, fmt.Errorf("invalid value %q: %v", intOrPercent.StrVal, err)
×
930
                        }
×
931
                        if v > 100 || v < 1 {
2✔
932
                                return 0, fmt.Errorf("invalid value: percentage needs to be between 1 and 100")
1✔
933
                        }
1✔
934
                } else {
1✔
935
                        return 0, fmt.Errorf("invalid type: strings needs to be a percentage")
1✔
936
                }
1✔
937
        }
938

939
        maxunavail, err := intstrutil.GetScaledValueFromIntOrPercent(&intOrPercent, numOfNodes, false)
1✔
940
        if err != nil {
1✔
941
                return 0, err
×
942
        }
×
943

944
        if maxunavail < 0 {
2✔
945
                return 0, fmt.Errorf("negative number is not allowed")
1✔
946
        }
1✔
947

948
        return maxunavail, nil
1✔
949
}
950

951
// GenerateBridgeName generate predictable name for the software bridge
952
// current format is: br-0000_00_03.0
953
func GenerateBridgeName(iface *InterfaceExt) string {
1✔
954
        return fmt.Sprintf("br-%s", strings.ReplaceAll(iface.PciAddress, ":", "_"))
1✔
955
}
1✔
956

957
// NeedToUpdateBridges returns true if bridge for the host requires update
958
func NeedToUpdateBridges(bridgeSpec, bridgeStatus *Bridges) bool {
1✔
959
        return !equality.Semantic.DeepEqual(bridgeSpec, bridgeStatus)
1✔
960
}
1✔
961

962
// SetKeepUntilTime sets an annotation to hold the "keep until time" for the node’s state.
963
// The "keep until time" specifies the earliest time at which the state object can be removed
964
// if the daemon's pod is not found on the node.
965
func (s *SriovNetworkNodeState) SetKeepUntilTime(t time.Time) {
1✔
966
        ts := t.Format(time.RFC3339)
1✔
967
        annotations := s.GetAnnotations()
1✔
968
        if annotations == nil {
2✔
969
                annotations = map[string]string{}
1✔
970
        }
1✔
971
        annotations[consts.NodeStateKeepUntilAnnotation] = ts
1✔
972
        s.SetAnnotations(annotations)
1✔
973
}
974

975
// GetKeepUntilTime returns the value that is stored in the "keep until time" annotation.
976
// The "keep until time" specifies the earliest time at which the state object can be removed
977
// if the daemon's pod is not found on the node.
978
// Return zero time instant if annotaion is not found on the object or if it has a wrong format.
979
func (s *SriovNetworkNodeState) GetKeepUntilTime() time.Time {
1✔
980
        t, err := time.Parse(time.RFC3339, s.GetAnnotations()[consts.NodeStateKeepUntilAnnotation])
1✔
981
        if err != nil {
2✔
982
                return time.Time{}
1✔
983
        }
1✔
984
        return t
1✔
985
}
986

987
// ResetKeepUntilTime removes "keep until time" annotation from the state object.
988
// The "keep until time" specifies the earliest time at which the state object can be removed
989
// if the daemon's pod is not found on the node.
990
// Returns true if the value was removed, false otherwise.
991
func (s *SriovNetworkNodeState) ResetKeepUntilTime() bool {
1✔
992
        annotations := s.GetAnnotations()
1✔
993
        _, exist := annotations[consts.NodeStateKeepUntilAnnotation]
1✔
994
        if !exist {
2✔
995
                return false
1✔
996
        }
1✔
997
        delete(annotations, consts.NodeStateKeepUntilAnnotation)
×
998
        s.SetAnnotations(annotations)
×
999
        return true
×
1000
}
1001

1002
func OwnerRefToString(cr client.Object) string {
1✔
1003
        if cr == nil {
1✔
1004
                return "owner-object-not-found"
×
1005
        }
×
1006
        if cr.GetObjectKind().GroupVersionKind().Empty() {
1✔
1007
                return "unknown/" + cr.GetNamespace() + "/" + cr.GetName()
×
1008
        }
×
1009

1010
        return cr.GetObjectKind().GroupVersionKind().GroupKind().String() + "/" + cr.GetNamespace() + "/" + cr.GetName()
1✔
1011
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc