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

k8snetworkplumbingwg / sriov-network-operator / 9059795374

13 May 2024 08:34AM UTC coverage: 39.941% (+0.3%) from 39.666%
9059795374

Pull #696

github

web-flow
Merge b18f1cb0e into 8326f5035
Pull Request #696: Followup pr 646

106 of 168 new or added lines in 4 files covered. (63.1%)

3 existing lines in 1 file now uncovered.

5112 of 12799 relevant lines covered (39.94%)

0.44 hits per line

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

70.69
/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
        "sort"
11
        "strconv"
12
        "strings"
13

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

21
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
22
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
23
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
24
)
25

26
const (
27
        LASTNETWORKNAMESPACE    = "operator.sriovnetwork.openshift.io/last-network-namespace"
28
        NETATTDEFFINALIZERNAME  = "netattdef.finalizers.sriovnetwork.openshift.io"
29
        POOLCONFIGFINALIZERNAME = "poolconfig.finalizers.sriovnetwork.openshift.io"
30
        ESwithModeLegacy        = "legacy"
31
        ESwithModeSwitchDev     = "switchdev"
32

33
        SriovCniStateEnable  = "enable"
34
        SriovCniStateDisable = "disable"
35
        SriovCniStateAuto    = "auto"
36
        SriovCniStateOff     = "off"
37
        SriovCniStateOn      = "on"
38
        SriovCniIpam         = "\"ipam\""
39
        SriovCniIpamEmpty    = SriovCniIpam + ":{}"
40
)
41

42
const invalidVfIndex = -1
43

44
var ManifestsPath = "./bindata/manifests/cni-config"
45
var log = logf.Log.WithName("sriovnetwork")
46

47
// NicIDMap contains supported mapping of IDs with each in the format of:
48
// Vendor ID, Physical Function Device ID, Virtual Function Device ID
49
var NicIDMap = []string{}
50

51
var InitialState SriovNetworkNodeState
52

53
// NetFilterType Represents the NetFilter tags to be used
54
type NetFilterType int
55

56
const (
57
        // OpenstackNetworkID network UUID
58
        OpenstackNetworkID NetFilterType = iota
59

60
        SupportedNicIDConfigmap = "supported-nic-ids"
61
)
62

63
type ConfigurationModeType string
64

65
const (
66
        DaemonConfigurationMode  ConfigurationModeType = "daemon"
67
        SystemdConfigurationMode ConfigurationModeType = "systemd"
68
)
69

70
func (e NetFilterType) String() string {
×
71
        switch e {
×
72
        case OpenstackNetworkID:
×
73
                return "openstack/NetworkID"
×
74
        default:
×
75
                return fmt.Sprintf("%d", int(e))
×
76
        }
77
}
78

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

93
        return nil
1✔
94
}
95

96
func InitNicIDMapFromList(idList []string) {
1✔
97
        NicIDMap = append(NicIDMap, idList...)
1✔
98
}
1✔
99

100
func IsSupportedVendor(vendorID string) bool {
1✔
101
        for _, n := range NicIDMap {
2✔
102
                ids := strings.Split(n, " ")
1✔
103
                if vendorID == ids[0] {
2✔
104
                        return true
1✔
105
                }
1✔
106
        }
107
        return false
1✔
108
}
109

110
func IsSupportedDevice(deviceID string) bool {
1✔
111
        for _, n := range NicIDMap {
2✔
112
                ids := strings.Split(n, " ")
1✔
113
                if deviceID == ids[1] {
1✔
114
                        return true
×
115
                }
×
116
        }
117
        return false
1✔
118
}
119

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

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

142
func IsEnabledUnsupportedVendor(vendorID string, unsupportedNicIDMap map[string]string) bool {
×
143
        for _, n := range unsupportedNicIDMap {
×
144
                if IsValidPciString(n) {
×
145
                        ids := strings.Split(n, " ")
×
146
                        if vendorID == ids[0] {
×
147
                                return true
×
148
                        }
×
149
                }
150
        }
151
        return false
×
152
}
153

154
func IsValidPciString(nicIDString string) bool {
×
155
        ids := strings.Split(nicIDString, " ")
×
156

×
157
        if len(ids) != 3 {
×
158
                log.Info("IsValidPciString(): ", nicIDString)
×
159
                return false
×
160
        }
×
161

162
        if len(ids[0]) != 4 {
×
163
                log.Info("IsValidPciString():", "Invalid vendor PciId ", ids[0])
×
164
                return false
×
165
        }
×
166
        if _, err := strconv.ParseInt(ids[0], 16, 32); err != nil {
×
167
                log.Info("IsValidPciString():", "Invalid vendor PciId ", ids[0])
×
168
        }
×
169

170
        if len(ids[1]) != 4 {
×
171
                log.Info("IsValidPciString():", "Invalid PciId of PF ", ids[1])
×
172
                return false
×
173
        }
×
174
        if _, err := strconv.ParseInt(ids[1], 16, 32); err != nil {
×
175
                log.Info("IsValidPciString():", "Invalid PciId of PF ", ids[1])
×
176
        }
×
177

178
        if len(ids[2]) != 4 {
×
179
                log.Info("IsValidPciString():", "Invalid PciId of VF ", ids[2])
×
180
                return false
×
181
        }
×
182
        if _, err := strconv.ParseInt(ids[2], 16, 32); err != nil {
×
183
                log.Info("IsValidPciString():", "Invalid PciId of VF ", ids[2])
×
184
        }
×
185

186
        return true
×
187
}
188

189
func GetSupportedVfIds() []string {
1✔
190
        var vfIds []string
1✔
191
        for _, n := range NicIDMap {
2✔
192
                ids := strings.Split(n, " ")
1✔
193
                vfID := "0x" + ids[2]
1✔
194
                if !StringInArray(vfID, vfIds) {
2✔
195
                        vfIds = append(vfIds, vfID)
1✔
196
                }
1✔
197
        }
198
        // return a sorted slice so that udev rule is stable
199
        sort.Slice(vfIds, func(i, j int) bool {
2✔
200
                ip, _ := strconv.ParseInt(vfIds[i], 0, 32)
1✔
201
                jp, _ := strconv.ParseInt(vfIds[j], 0, 32)
1✔
202
                return ip < jp
1✔
203
        })
1✔
204
        return vfIds
1✔
205
}
206

207
func GetVfDeviceID(deviceID string) string {
×
208
        for _, n := range NicIDMap {
×
209
                ids := strings.Split(n, " ")
×
210
                if deviceID == ids[1] {
×
211
                        return ids[2]
×
212
                }
×
213
        }
214
        return ""
×
215
}
216

217
func IsSwitchdevModeSpec(spec SriovNetworkNodeStateSpec) bool {
1✔
218
        return ContainsSwitchdevInterface(spec.Interfaces)
1✔
219
}
1✔
220

221
// ContainsSwitchdevInterface returns true if provided interface list contains interface
222
// with switchdev configuration
223
func ContainsSwitchdevInterface(interfaces []Interface) bool {
1✔
224
        for _, iface := range interfaces {
2✔
225
                if iface.EswitchMode == ESwithModeSwitchDev {
2✔
226
                        return true
1✔
227
                }
1✔
228
        }
229
        return false
1✔
230
}
231

232
func FindInterface(interfaces Interfaces, name string) (iface Interface, err error) {
×
233
        for _, i := range interfaces {
×
234
                if i.Name == name {
×
235
                        return i, nil
×
236
                }
×
237
        }
238
        return Interface{}, fmt.Errorf("unable to find interface: %v", name)
×
239
}
240

241
// GetEswitchModeFromSpec returns ESwitchMode from the interface spec, returns legacy if not set
242
func GetEswitchModeFromSpec(ifaceSpec *Interface) string {
1✔
243
        if ifaceSpec.EswitchMode == "" {
2✔
244
                return ESwithModeLegacy
1✔
245
        }
1✔
246
        return ifaceSpec.EswitchMode
1✔
247
}
248

249
// GetEswitchModeFromStatus returns ESwitchMode from the interface status, returns legacy if not set
250
func GetEswitchModeFromStatus(ifaceStatus *InterfaceExt) string {
1✔
251
        if ifaceStatus.EswitchMode == "" {
2✔
252
                return ESwithModeLegacy
1✔
253
        }
1✔
254
        return ifaceStatus.EswitchMode
1✔
255
}
256

257
func NeedToUpdateSriov(ifaceSpec *Interface, ifaceStatus *InterfaceExt) bool {
1✔
258
        if ifaceSpec.Mtu > 0 {
2✔
259
                mtu := ifaceSpec.Mtu
1✔
260
                if mtu != ifaceStatus.Mtu {
2✔
261
                        log.V(2).Info("NeedToUpdateSriov(): MTU needs update", "desired", mtu, "current", ifaceStatus.Mtu)
1✔
262
                        return true
1✔
263
                }
1✔
264
        }
265
        currentEswitchMode := GetEswitchModeFromStatus(ifaceStatus)
1✔
266
        desiredEswitchMode := GetEswitchModeFromSpec(ifaceSpec)
1✔
267
        if currentEswitchMode != desiredEswitchMode {
2✔
268
                log.V(2).Info("NeedToUpdateSriov(): EswitchMode needs update", "desired", desiredEswitchMode, "current", currentEswitchMode)
1✔
269
                return true
1✔
270
        }
1✔
271
        if ifaceSpec.NumVfs != ifaceStatus.NumVfs {
2✔
272
                log.V(2).Info("NeedToUpdateSriov(): NumVfs needs update", "desired", ifaceSpec.NumVfs, "current", ifaceStatus.NumVfs)
1✔
273
                return true
1✔
274
        }
1✔
275
        if ifaceSpec.NumVfs > 0 {
2✔
276
                for _, vfStatus := range ifaceStatus.VFs {
2✔
277
                        ingroup := false
1✔
278
                        for _, groupSpec := range ifaceSpec.VfGroups {
2✔
279
                                if IndexInRange(vfStatus.VfID, groupSpec.VfRange) {
2✔
280
                                        ingroup = true
1✔
281
                                        if vfStatus.Driver == "" {
1✔
282
                                                log.V(2).Info("NeedToUpdateSriov(): Driver needs update - has no driver",
×
283
                                                        "desired", groupSpec.DeviceType)
×
284
                                                return true
×
285
                                        }
×
286
                                        if groupSpec.DeviceType != "" && groupSpec.DeviceType != consts.DeviceTypeNetDevice {
2✔
287
                                                if groupSpec.DeviceType != vfStatus.Driver {
1✔
288
                                                        log.V(2).Info("NeedToUpdateSriov(): Driver needs update",
×
289
                                                                "desired", groupSpec.DeviceType, "current", vfStatus.Driver)
×
290
                                                        return true
×
291
                                                }
×
292
                                        } else {
1✔
293
                                                if StringInArray(vfStatus.Driver, vars.DpdkDrivers) {
1✔
294
                                                        log.V(2).Info("NeedToUpdateSriov(): Driver needs update",
×
295
                                                                "desired", groupSpec.DeviceType, "current", vfStatus.Driver)
×
296
                                                        return true
×
297
                                                }
×
298
                                                if vfStatus.Mtu != 0 && groupSpec.Mtu != 0 && vfStatus.Mtu != groupSpec.Mtu {
2✔
299
                                                        log.V(2).Info("NeedToUpdateSriov(): VF MTU needs update",
1✔
300
                                                                "vf", vfStatus.VfID, "desired", groupSpec.Mtu, "current", vfStatus.Mtu)
1✔
301
                                                        return true
1✔
302
                                                }
1✔
303

304
                                                // this is needed to be sure the admin mac address is configured as expected
305
                                                if ifaceSpec.ExternallyManaged {
1✔
306
                                                        log.V(2).Info("NeedToUpdateSriov(): need to update the device as it's externally manage",
×
307
                                                                "device", ifaceStatus.PciAddress)
×
308
                                                        return true
×
309
                                                }
×
310
                                        }
311
                                        if groupSpec.VdpaType != vfStatus.VdpaType {
1✔
312
                                                log.V(2).Info("NeedToUpdateSriov(): VF VdpaType mismatch",
×
313
                                                        "desired", groupSpec.VdpaType, "current", vfStatus.VdpaType)
×
314
                                                return true
×
315
                                        }
×
316
                                        break
1✔
317
                                }
318
                        }
319
                        if !ingroup && (StringInArray(vfStatus.Driver, vars.DpdkDrivers) || vfStatus.VdpaType != "") {
1✔
320
                                // need to reset VF if it is not a part of a group and:
×
321
                                // a. has DPDK driver loaded
×
322
                                // b. has VDPA device
×
323
                                return true
×
324
                        }
×
325
                }
326
        }
327
        return false
1✔
328
}
329

330
type ByPriority []SriovNetworkNodePolicy
331

332
func (a ByPriority) Len() int {
×
333
        return len(a)
×
334
}
×
335

336
func (a ByPriority) Less(i, j int) bool {
×
337
        if a[i].Spec.Priority != a[j].Spec.Priority {
×
338
                return a[i].Spec.Priority > a[j].Spec.Priority
×
339
        }
×
340
        return a[i].GetName() < a[j].GetName()
×
341
}
342

343
func (a ByPriority) Swap(i, j int) {
×
344
        a[i], a[j] = a[j], a[i]
×
345
}
×
346

347
// Match check if node is selected by NodeSelector
348
func (p *SriovNetworkNodePolicy) Selected(node *corev1.Node) bool {
1✔
349
        for k, v := range p.Spec.NodeSelector {
1✔
350
                if nv, ok := node.Labels[k]; ok && nv == v {
×
351
                        continue
×
352
                }
353
                return false
×
354
        }
355
        return true
1✔
356
}
357

358
func StringInArray(val string, array []string) bool {
1✔
359
        for i := range array {
2✔
360
                if array[i] == val {
2✔
361
                        return true
1✔
362
                }
1✔
363
        }
364
        return false
1✔
365
}
366

367
func RemoveString(s string, slice []string) (result []string, found bool) {
1✔
368
        if len(slice) != 0 {
2✔
369
                for _, item := range slice {
2✔
370
                        if item == s {
2✔
371
                                found = true
1✔
372
                                continue
1✔
373
                        }
374
                        result = append(result, item)
×
375
                }
376
        }
377
        return
1✔
378
}
379

380
func UniqueAppend(inSlice []string, strings ...string) []string {
×
381
        for _, s := range strings {
×
382
                if !StringInArray(s, inSlice) {
×
383
                        inSlice = append(inSlice, s)
×
384
                }
×
385
        }
386
        return inSlice
×
387
}
388

389
// Apply policy to SriovNetworkNodeState CR
390
func (p *SriovNetworkNodePolicy) Apply(state *SriovNetworkNodeState, equalPriority bool) error {
1✔
391
        s := p.Spec.NicSelector
1✔
392
        if s.Vendor == "" && s.DeviceID == "" && len(s.RootDevices) == 0 && len(s.PfNames) == 0 &&
1✔
393
                len(s.NetFilter) == 0 {
2✔
394
                // Empty NicSelector match none
1✔
395
                return nil
1✔
396
        }
1✔
397
        for _, iface := range state.Status.Interfaces {
2✔
398
                if s.Selected(&iface) {
2✔
399
                        log.Info("Update interface", "name:", iface.Name)
1✔
400
                        result := Interface{
1✔
401
                                PciAddress:        iface.PciAddress,
1✔
402
                                Mtu:               p.Spec.Mtu,
1✔
403
                                Name:              iface.Name,
1✔
404
                                LinkType:          p.Spec.LinkType,
1✔
405
                                EswitchMode:       p.Spec.EswitchMode,
1✔
406
                                NumVfs:            p.Spec.NumVfs,
1✔
407
                                ExternallyManaged: p.Spec.ExternallyManaged,
1✔
408
                        }
1✔
409
                        if p.Spec.NumVfs > 0 {
2✔
410
                                group, err := p.generatePfNameVfGroup(&iface)
1✔
411
                                if err != nil {
2✔
412
                                        return err
1✔
413
                                }
1✔
414
                                result.VfGroups = []VfGroup{*group}
1✔
415
                                found := false
1✔
416
                                for i := range state.Spec.Interfaces {
2✔
417
                                        if state.Spec.Interfaces[i].PciAddress == result.PciAddress {
2✔
418
                                                found = true
1✔
419
                                                state.Spec.Interfaces[i].mergeConfigs(&result, equalPriority)
1✔
420
                                                state.Spec.Interfaces[i] = result
1✔
421
                                                break
1✔
422
                                        }
423
                                }
424
                                if !found {
2✔
425
                                        state.Spec.Interfaces = append(state.Spec.Interfaces, result)
1✔
426
                                }
1✔
427
                        }
428
                }
429
        }
430
        return nil
1✔
431
}
432

433
// mergeConfigs merges configs from multiple polices where the last one has the
434
// highest priority. This merge is dependent on: 1. SR-IOV partition is
435
// configured with the #-notation in pfName, 2. The VF groups are
436
// non-overlapping or SR-IOV policies have the same priority.
437
func (iface Interface) mergeConfigs(input *Interface, equalPriority bool) {
1✔
438
        m := false
1✔
439
        // merge VF groups (input.VfGroups already contains the highest priority):
1✔
440
        // - skip group with same ResourceName,
1✔
441
        // - skip overlapping groups (use only highest priority)
1✔
442
        for _, gr := range iface.VfGroups {
2✔
443
                if gr.ResourceName == input.VfGroups[0].ResourceName || gr.isVFRangeOverlapping(input.VfGroups[0]) {
2✔
444
                        continue
1✔
445
                }
446
                m = true
1✔
447
                input.VfGroups = append(input.VfGroups, gr)
1✔
448
        }
449

450
        if !equalPriority && !m {
2✔
451
                return
1✔
452
        }
1✔
453

454
        // mtu configuration we take the highest value
455
        if input.Mtu < iface.Mtu {
2✔
456
                input.Mtu = iface.Mtu
1✔
457
        }
1✔
458
        if input.NumVfs < iface.NumVfs {
2✔
459
                input.NumVfs = iface.NumVfs
1✔
460
        }
1✔
461
}
462

463
func (gr VfGroup) isVFRangeOverlapping(group VfGroup) bool {
1✔
464
        rngSt, rngEnd, err := parseRange(gr.VfRange)
1✔
465
        if err != nil {
1✔
466
                return false
×
467
        }
×
468
        rngSt2, rngEnd2, err := parseRange(group.VfRange)
1✔
469
        if err != nil {
1✔
470
                return false
×
471
        }
×
472
        // compare minimal range has overlap
473
        if rngSt < rngSt2 {
2✔
474
                return IndexInRange(rngSt2, gr.VfRange) || IndexInRange(rngEnd2, gr.VfRange)
1✔
475
        }
1✔
476
        return IndexInRange(rngSt, group.VfRange) || IndexInRange(rngEnd, group.VfRange)
1✔
477
}
478

479
func (p *SriovNetworkNodePolicy) generatePfNameVfGroup(iface *InterfaceExt) (*VfGroup, error) {
1✔
480
        var err error
1✔
481
        pfName := ""
1✔
482
        var rngStart, rngEnd int
1✔
483
        found := false
1✔
484
        for _, selector := range p.Spec.NicSelector.PfNames {
2✔
485
                pfName, rngStart, rngEnd, err = ParseVfRange(selector)
1✔
486
                if err != nil {
2✔
487
                        log.Error(err, "Unable to parse PF Name.")
1✔
488
                        return nil, err
1✔
489
                }
1✔
490
                if pfName == iface.Name {
2✔
491
                        found = true
1✔
492
                        if rngStart == invalidVfIndex && rngEnd == invalidVfIndex {
2✔
493
                                rngStart, rngEnd = 0, p.Spec.NumVfs-1
1✔
494
                        }
1✔
495
                        break
1✔
496
                }
497
        }
498
        if !found {
1✔
499
                // assign the default vf index range if the pfName is not specified by the nicSelector
×
500
                rngStart, rngEnd = 0, p.Spec.NumVfs-1
×
501
        }
×
502
        rng := strconv.Itoa(rngStart) + "-" + strconv.Itoa(rngEnd)
1✔
503
        return &VfGroup{
1✔
504
                ResourceName: p.Spec.ResourceName,
1✔
505
                DeviceType:   p.Spec.DeviceType,
1✔
506
                VfRange:      rng,
1✔
507
                PolicyName:   p.GetName(),
1✔
508
                Mtu:          p.Spec.Mtu,
1✔
509
                IsRdma:       p.Spec.IsRdma,
1✔
510
                VdpaType:     p.Spec.VdpaType,
1✔
511
        }, nil
1✔
512
}
513

514
func IndexInRange(i int, r string) bool {
1✔
515
        rngSt, rngEnd, err := parseRange(r)
1✔
516
        if err != nil {
1✔
517
                return false
×
518
        }
×
519
        if i <= rngEnd && i >= rngSt {
2✔
520
                return true
1✔
521
        }
1✔
522
        return false
1✔
523
}
524

525
func parseRange(r string) (rngSt, rngEnd int, err error) {
1✔
526
        rng := strings.Split(r, "-")
1✔
527
        rngSt, err = strconv.Atoi(rng[0])
1✔
528
        if err != nil {
2✔
529
                return
1✔
530
        }
1✔
531
        rngEnd, err = strconv.Atoi(rng[1])
1✔
532
        if err != nil {
1✔
533
                return
×
534
        }
×
535
        return
1✔
536
}
537

538
// SplitDeviceFromRange return the device name and the range.
539
// the split is base on #
540
func SplitDeviceFromRange(device string) (string, string) {
1✔
541
        if strings.Contains(device, "#") {
2✔
542
                fields := strings.Split(device, "#")
1✔
543
                return fields[0], fields[1]
1✔
544
        }
1✔
545

546
        return device, ""
1✔
547
}
548

549
// ParseVfRange: parse a device with VF range
550
// this can be rootDevices or PFName
551
// if no range detect we just return the device name
552
func ParseVfRange(device string) (rootDeviceName string, rngSt, rngEnd int, err error) {
1✔
553
        rngSt, rngEnd = invalidVfIndex, invalidVfIndex
1✔
554
        rootDeviceName, splitRange := SplitDeviceFromRange(device)
1✔
555
        if splitRange != "" {
2✔
556
                rngSt, rngEnd, err = parseRange(splitRange)
1✔
557
        } else {
2✔
558
                rootDeviceName = device
1✔
559
        }
1✔
560
        return
1✔
561
}
562

563
func (selector *SriovNetworkNicSelector) Selected(iface *InterfaceExt) bool {
1✔
564
        if selector.Vendor != "" && selector.Vendor != iface.Vendor {
1✔
565
                return false
×
566
        }
×
567
        if selector.DeviceID != "" && selector.DeviceID != iface.DeviceID {
1✔
568
                return false
×
569
        }
×
570
        if len(selector.RootDevices) > 0 && !StringInArray(iface.PciAddress, selector.RootDevices) {
2✔
571
                return false
1✔
572
        }
1✔
573
        if len(selector.PfNames) > 0 {
2✔
574
                var pfNames []string
1✔
575
                for _, p := range selector.PfNames {
2✔
576
                        if strings.Contains(p, "#") {
2✔
577
                                fields := strings.Split(p, "#")
1✔
578
                                pfNames = append(pfNames, fields[0])
1✔
579
                        } else {
2✔
580
                                pfNames = append(pfNames, p)
1✔
581
                        }
1✔
582
                }
583
                if !StringInArray(iface.Name, pfNames) {
1✔
584
                        return false
×
585
                }
×
586
        }
587
        if selector.NetFilter != "" && !NetFilterMatch(selector.NetFilter, iface.NetFilter) {
1✔
588
                return false
×
589
        }
×
590

591
        return true
1✔
592
}
593

594
func (s *SriovNetworkNodeState) GetInterfaceStateByPciAddress(addr string) *InterfaceExt {
1✔
595
        for _, iface := range s.Status.Interfaces {
1✔
596
                if addr == iface.PciAddress {
×
597
                        return &iface
×
598
                }
×
599
        }
600
        return nil
1✔
601
}
602

603
func (s *SriovNetworkNodeState) GetDriverByPciAddress(addr string) string {
×
604
        for _, iface := range s.Status.Interfaces {
×
605
                if addr == iface.PciAddress {
×
606
                        return iface.Driver
×
607
                }
×
608
        }
609
        return ""
×
610
}
611

612
// RenderNetAttDef renders a net-att-def for ib-sriov CNI
613
func (cr *SriovIBNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
614
        logger := log.WithName("RenderNetAttDef")
1✔
615
        logger.Info("Start to render IB SRIOV CNI NetworkAttachmentDefinition")
1✔
616

1✔
617
        // render RawCNIConfig manifests
1✔
618
        data := render.MakeRenderData()
1✔
619
        data.Data["CniType"] = "ib-sriov"
1✔
620
        data.Data["SriovNetworkName"] = cr.Name
1✔
621
        if cr.Spec.NetworkNamespace == "" {
2✔
622
                data.Data["SriovNetworkNamespace"] = cr.Namespace
1✔
623
        } else {
2✔
624
                data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
625
        }
1✔
626
        data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
627

1✔
628
        data.Data["StateConfigured"] = true
1✔
629
        switch cr.Spec.LinkState {
1✔
630
        case SriovCniStateEnable:
1✔
631
                data.Data["SriovCniState"] = SriovCniStateEnable
1✔
632
        case SriovCniStateDisable:
×
633
                data.Data["SriovCniState"] = SriovCniStateDisable
×
634
        case SriovCniStateAuto:
×
635
                data.Data["SriovCniState"] = SriovCniStateAuto
×
636
        default:
1✔
637
                data.Data["StateConfigured"] = false
1✔
638
        }
639

640
        if cr.Spec.Capabilities == "" {
2✔
641
                data.Data["CapabilitiesConfigured"] = false
1✔
642
        } else {
2✔
643
                data.Data["CapabilitiesConfigured"] = true
1✔
644
                data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities
1✔
645
        }
1✔
646

647
        if cr.Spec.IPAM != "" {
2✔
648
                data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
649
        } else {
2✔
650
                data.Data["SriovCniIpam"] = SriovCniIpamEmpty
1✔
651
        }
1✔
652

653
        // metaplugins for the infiniband cni
654
        data.Data["MetaPluginsConfigured"] = false
1✔
655
        if cr.Spec.MetaPluginsConfig != "" {
1✔
656
                data.Data["MetaPluginsConfigured"] = true
×
657
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
×
658
        }
×
659

660
        // logLevel and logFile are currently not supports by the ip-sriov-cni -> hardcode them to false.
661
        data.Data["LogLevelConfigured"] = false
1✔
662
        data.Data["LogFileConfigured"] = false
1✔
663

1✔
664
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
1✔
665
        if err != nil {
1✔
666
                return nil, err
×
667
        }
×
668
        for _, obj := range objs {
2✔
669
                raw, _ := json.Marshal(obj)
1✔
670
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
671
        }
1✔
672
        return objs[0], nil
1✔
673
}
674

675
// NetworkNamespace returns target network namespace for the network
676
func (cr *SriovIBNetwork) NetworkNamespace() string {
1✔
677
        return cr.Spec.NetworkNamespace
1✔
678
}
1✔
679

680
// RenderNetAttDef renders a net-att-def for sriov CNI
681
func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
682
        logger := log.WithName("RenderNetAttDef")
1✔
683
        logger.Info("Start to render SRIOV CNI NetworkAttachmentDefinition")
1✔
684

1✔
685
        // render RawCNIConfig manifests
1✔
686
        data := render.MakeRenderData()
1✔
687
        data.Data["CniType"] = "sriov"
1✔
688
        data.Data["SriovNetworkName"] = cr.Name
1✔
689
        if cr.Spec.NetworkNamespace == "" {
2✔
690
                data.Data["SriovNetworkNamespace"] = cr.Namespace
1✔
691
        } else {
2✔
692
                data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
693
        }
1✔
694
        data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
695
        data.Data["SriovCniVlan"] = cr.Spec.Vlan
1✔
696

1✔
697
        if cr.Spec.VlanQoS <= 7 && cr.Spec.VlanQoS >= 0 {
2✔
698
                data.Data["VlanQoSConfigured"] = true
1✔
699
                data.Data["SriovCniVlanQoS"] = cr.Spec.VlanQoS
1✔
700
        } else {
1✔
701
                data.Data["VlanQoSConfigured"] = false
×
702
        }
×
703

704
        data.Data["VlanProtoConfigured"] = false
1✔
705
        if cr.Spec.VlanProto != "" {
2✔
706
                data.Data["VlanProtoConfigured"] = true
1✔
707
                data.Data["SriovCniVlanProto"] = cr.Spec.VlanProto
1✔
708
        }
1✔
709

710
        if cr.Spec.Capabilities == "" {
2✔
711
                data.Data["CapabilitiesConfigured"] = false
1✔
712
        } else {
1✔
713
                data.Data["CapabilitiesConfigured"] = true
×
714
                data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities
×
715
        }
×
716

717
        data.Data["SpoofChkConfigured"] = true
1✔
718
        switch cr.Spec.SpoofChk {
1✔
719
        case SriovCniStateOff:
×
720
                data.Data["SriovCniSpoofChk"] = SriovCniStateOff
×
721
        case SriovCniStateOn:
1✔
722
                data.Data["SriovCniSpoofChk"] = SriovCniStateOn
1✔
723
        default:
1✔
724
                data.Data["SpoofChkConfigured"] = false
1✔
725
        }
726

727
        data.Data["TrustConfigured"] = true
1✔
728
        switch cr.Spec.Trust {
1✔
729
        case SriovCniStateOn:
1✔
730
                data.Data["SriovCniTrust"] = SriovCniStateOn
1✔
731
        case SriovCniStateOff:
×
732
                data.Data["SriovCniTrust"] = SriovCniStateOff
×
733
        default:
1✔
734
                data.Data["TrustConfigured"] = false
1✔
735
        }
736

737
        data.Data["StateConfigured"] = true
1✔
738
        switch cr.Spec.LinkState {
1✔
739
        case SriovCniStateEnable:
×
740
                data.Data["SriovCniState"] = SriovCniStateEnable
×
741
        case SriovCniStateDisable:
×
742
                data.Data["SriovCniState"] = SriovCniStateDisable
×
743
        case SriovCniStateAuto:
×
744
                data.Data["SriovCniState"] = SriovCniStateAuto
×
745
        default:
1✔
746
                data.Data["StateConfigured"] = false
1✔
747
        }
748

749
        data.Data["MinTxRateConfigured"] = false
1✔
750
        if cr.Spec.MinTxRate != nil {
1✔
751
                if *cr.Spec.MinTxRate >= 0 {
×
752
                        data.Data["MinTxRateConfigured"] = true
×
753
                        data.Data["SriovCniMinTxRate"] = *cr.Spec.MinTxRate
×
754
                }
×
755
        }
756

757
        data.Data["MaxTxRateConfigured"] = false
1✔
758
        if cr.Spec.MaxTxRate != nil {
1✔
759
                if *cr.Spec.MaxTxRate >= 0 {
×
760
                        data.Data["MaxTxRateConfigured"] = true
×
761
                        data.Data["SriovCniMaxTxRate"] = *cr.Spec.MaxTxRate
×
762
                }
×
763
        }
764

765
        if cr.Spec.IPAM != "" {
2✔
766
                data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
767
        } else {
2✔
768
                data.Data["SriovCniIpam"] = SriovCniIpamEmpty
1✔
769
        }
1✔
770

771
        data.Data["MetaPluginsConfigured"] = false
1✔
772
        if cr.Spec.MetaPluginsConfig != "" {
2✔
773
                data.Data["MetaPluginsConfigured"] = true
1✔
774
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
1✔
775
        }
1✔
776

777
        data.Data["LogLevelConfigured"] = (cr.Spec.LogLevel != "")
1✔
778
        data.Data["LogLevel"] = cr.Spec.LogLevel
1✔
779
        data.Data["LogFileConfigured"] = (cr.Spec.LogFile != "")
1✔
780
        data.Data["LogFile"] = cr.Spec.LogFile
1✔
781

1✔
782
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
1✔
783
        if err != nil {
1✔
784
                return nil, err
×
785
        }
×
786
        for _, obj := range objs {
2✔
787
                raw, _ := json.Marshal(obj)
1✔
788
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
789
        }
1✔
790
        return objs[0], nil
1✔
791
}
792

793
// NetworkNamespace returns target network namespace for the network
794
func (cr *SriovNetwork) NetworkNamespace() string {
1✔
795
        return cr.Spec.NetworkNamespace
1✔
796
}
1✔
797

798
// RenderNetAttDef renders a net-att-def for sriov CNI
799
func (cr *OVSNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
800
        logger := log.WithName("RenderNetAttDef")
1✔
801
        logger.Info("Start to render OVS CNI NetworkAttachmentDefinition")
1✔
802

1✔
803
        // render RawCNIConfig manifests
1✔
804
        data := render.MakeRenderData()
1✔
805
        data.Data["CniType"] = "ovs"
1✔
806
        data.Data["NetworkName"] = cr.Name
1✔
807
        if cr.Spec.NetworkNamespace == "" {
2✔
808
                data.Data["NetworkNamespace"] = cr.Namespace
1✔
809
        } else {
2✔
810
                data.Data["NetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
811
        }
1✔
812
        data.Data["CniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
813

1✔
814
        if cr.Spec.Capabilities == "" {
2✔
815
                data.Data["CapabilitiesConfigured"] = false
1✔
816
        } else {
2✔
817
                data.Data["CapabilitiesConfigured"] = true
1✔
818
                data.Data["CniCapabilities"] = cr.Spec.Capabilities
1✔
819
        }
1✔
820

821
        data.Data["Bridge"] = cr.Spec.Bridge
1✔
822
        data.Data["VlanTag"] = cr.Spec.Vlan
1✔
823
        data.Data["MTU"] = cr.Spec.MTU
1✔
824
        if len(cr.Spec.Trunk) > 0 {
2✔
825
                trunkConfRaw, _ := json.Marshal(cr.Spec.Trunk)
1✔
826
                data.Data["Trunk"] = string(trunkConfRaw)
1✔
827
        } else {
2✔
828
                data.Data["Trunk"] = ""
1✔
829
        }
1✔
830
        data.Data["InterfaceType"] = cr.Spec.InterfaceType
1✔
831

1✔
832
        if cr.Spec.IPAM != "" {
2✔
833
                data.Data["CniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
834
        } else {
2✔
835
                data.Data["CniIpam"] = SriovCniIpamEmpty
1✔
836
        }
1✔
837

838
        data.Data["MetaPluginsConfigured"] = false
1✔
839
        if cr.Spec.MetaPluginsConfig != "" {
2✔
840
                data.Data["MetaPluginsConfigured"] = true
1✔
841
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
1✔
842
        }
1✔
843

844
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "ovs"), &data)
1✔
845
        if err != nil {
1✔
NEW
846
                return nil, err
×
NEW
847
        }
×
848
        for _, obj := range objs {
2✔
849
                raw, _ := json.Marshal(obj)
1✔
850
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
851
        }
1✔
852
        return objs[0], nil
1✔
853
}
854

855
// NetworkNamespace returns target network namespace for the network
856
func (cr *OVSNetwork) NetworkNamespace() string {
1✔
857
        return cr.Spec.NetworkNamespace
1✔
858
}
1✔
859

860
// NetFilterMatch -- parse netFilter and check for a match
861
func NetFilterMatch(netFilter string, netValue string) (isMatch bool) {
×
862
        logger := log.WithName("NetFilterMatch")
×
863

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

×
866
        netFilterResult := re.FindAllStringSubmatch(netFilter, -1)
×
867

×
868
        if netFilterResult == nil {
×
869
                logger.Info("Invalid NetFilter spec...", "netFilter", netFilter)
×
870
                return false
×
871
        }
×
872

873
        netValueResult := re.FindAllStringSubmatch(netValue, -1)
×
874

×
875
        if netValueResult == nil {
×
876
                logger.Info("Invalid netValue...", "netValue", netValue)
×
877
                return false
×
878
        }
×
879

880
        return netFilterResult[0][1] == netValueResult[0][1] && netFilterResult[0][2] == netValueResult[0][2]
×
881
}
882

883
// MaxUnavailable calculate the max number of unavailable nodes to represent the number of nodes
884
// we can drain in parallel
885
func (s *SriovNetworkPoolConfig) MaxUnavailable(numOfNodes int) (int, error) {
1✔
886
        // this means we want to drain all the nodes in parallel
1✔
887
        if s.Spec.MaxUnavailable == nil {
2✔
888
                return -1, nil
1✔
889
        }
1✔
890
        intOrPercent := *s.Spec.MaxUnavailable
1✔
891

1✔
892
        if intOrPercent.Type == intstrutil.String {
2✔
893
                if strings.HasSuffix(intOrPercent.StrVal, "%") {
2✔
894
                        i := strings.TrimSuffix(intOrPercent.StrVal, "%")
1✔
895
                        v, err := strconv.Atoi(i)
1✔
896
                        if err != nil {
1✔
897
                                return 0, fmt.Errorf("invalid value %q: %v", intOrPercent.StrVal, err)
×
898
                        }
×
899
                        if v > 100 || v < 1 {
2✔
900
                                return 0, fmt.Errorf("invalid value: percentage needs to be between 1 and 100")
1✔
901
                        }
1✔
902
                } else {
1✔
903
                        return 0, fmt.Errorf("invalid type: strings needs to be a percentage")
1✔
904
                }
1✔
905
        }
906

907
        maxunavail, err := intstrutil.GetScaledValueFromIntOrPercent(&intOrPercent, numOfNodes, false)
1✔
908
        if err != nil {
1✔
909
                return 0, err
×
910
        }
×
911

912
        if maxunavail < 0 {
2✔
913
                return 0, fmt.Errorf("negative number is not allowed")
1✔
914
        }
1✔
915

916
        return maxunavail, nil
1✔
917
}
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