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

k8snetworkplumbingwg / sriov-network-operator / 13526205039

25 Feb 2025 04:34PM UTC coverage: 48.882% (+0.9%) from 48.008%
13526205039

Pull #788

github

web-flow
Merge 3e6b91cf3 into d7c9458e0
Pull Request #788: Daemon redesign - using controller-runtime

273 of 615 new or added lines in 18 files covered. (44.39%)

199 existing lines in 12 files now uncovered.

7324 of 14983 relevant lines covered (48.88%)

0.54 hits per line

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

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

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

17
        corev1 "k8s.io/api/core/v1"
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
        logf "sigs.k8s.io/controller-runtime/pkg/log"
23

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

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

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

46
const invalidVfIndex = -1
47

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

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

55
var InitialState SriovNetworkNodeState
56

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

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

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

67
type ConfigurationModeType string
68

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

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

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

UNCOV
97
        return nil
×
98
}
99

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

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

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

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

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

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

158
func IsValidPciString(nicIDString string) bool {
×
159
        ids := strings.Split(nicIDString, " ")
×
160

×
161
        if len(ids) != 3 {
×
162
                log.Info("IsValidPciString(): ", nicIDString)
×
163
                return false
×
164
        }
×
165

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

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

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

190
        return true
×
191
}
192

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

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

221
func IsSwitchdevModeSpec(spec SriovNetworkNodeStateSpec) bool {
1✔
222
        return ContainsSwitchdevInterface(spec.Interfaces)
1✔
223
}
1✔
224

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

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

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

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

261
func NeedToUpdateSriov(ifaceSpec *Interface, ifaceStatus *InterfaceExt) bool {
1✔
262
        if ifaceSpec.Mtu > 0 {
2✔
263
                mtu := ifaceSpec.Mtu
1✔
264
                if mtu > ifaceStatus.Mtu {
2✔
265
                        log.V(0).Info("NeedToUpdateSriov(): MTU needs update", "desired", mtu, "current", ifaceStatus.Mtu)
1✔
266
                        return true
1✔
267
                }
1✔
268
        }
269
        currentEswitchMode := GetEswitchModeFromStatus(ifaceStatus)
1✔
270
        desiredEswitchMode := GetEswitchModeFromSpec(ifaceSpec)
1✔
271
        if currentEswitchMode != desiredEswitchMode {
2✔
272
                log.V(0).Info("NeedToUpdateSriov(): EswitchMode needs update", "desired", desiredEswitchMode, "current", currentEswitchMode)
1✔
273
                return true
1✔
274
        }
1✔
275
        if ifaceSpec.NumVfs != ifaceStatus.NumVfs {
2✔
276
                log.V(0).Info("NeedToUpdateSriov(): NumVfs needs update", "desired", ifaceSpec.NumVfs, "current", ifaceStatus.NumVfs)
1✔
277
                return true
1✔
278
        }
1✔
279

280
        if ifaceStatus.LinkAdminState == consts.LinkAdminStateDown {
2✔
281
                log.V(0).Info("NeedToUpdateSriov(): PF link status needs update", "desired to include", "up", "current", ifaceStatus.LinkAdminState)
1✔
282
                return true
1✔
283
        }
1✔
284

285
        if ifaceSpec.NumVfs > 0 {
2✔
286
                for _, vfStatus := range ifaceStatus.VFs {
2✔
287
                        for _, groupSpec := range ifaceSpec.VfGroups {
2✔
288
                                if IndexInRange(vfStatus.VfID, groupSpec.VfRange) {
2✔
289
                                        if vfStatus.Driver == "" {
1✔
290
                                                log.V(0).Info("NeedToUpdateSriov(): Driver needs update - has no driver",
×
291
                                                        "desired", groupSpec.DeviceType)
×
292
                                                return true
×
293
                                        }
×
294
                                        if groupSpec.DeviceType != "" && groupSpec.DeviceType != consts.DeviceTypeNetDevice {
2✔
295
                                                if groupSpec.DeviceType != vfStatus.Driver {
1✔
296
                                                        log.V(0).Info("NeedToUpdateSriov(): Driver needs update",
×
297
                                                                "desired", groupSpec.DeviceType, "current", vfStatus.Driver)
×
298
                                                        return true
×
299
                                                }
×
300
                                        } else {
1✔
301
                                                if StringInArray(vfStatus.Driver, vars.DpdkDrivers) {
2✔
302
                                                        log.V(0).Info("NeedToUpdateSriov(): Driver needs update",
1✔
303
                                                                "desired", groupSpec.DeviceType, "current", vfStatus.Driver)
1✔
304
                                                        return true
1✔
305
                                                }
1✔
306
                                                if vfStatus.Mtu != 0 && groupSpec.Mtu != 0 && vfStatus.Mtu != groupSpec.Mtu {
2✔
307
                                                        log.V(0).Info("NeedToUpdateSriov(): VF MTU needs update",
1✔
308
                                                                "vf", vfStatus.VfID, "desired", groupSpec.Mtu, "current", vfStatus.Mtu)
1✔
309
                                                        return true
1✔
310
                                                }
1✔
311

312
                                                if (strings.EqualFold(ifaceStatus.LinkType, consts.LinkTypeETH) && groupSpec.IsRdma) || strings.EqualFold(ifaceStatus.LinkType, consts.LinkTypeIB) {
2✔
313
                                                        // We do this check only if a Node GUID is set to ensure that we were able to read the
1✔
314
                                                        // Node GUID. We intentionally skip empty Node GUID in vfStatus because this may happen
1✔
315
                                                        // when the VF is allocated to a workload.
1✔
316
                                                        if vfStatus.GUID == consts.UninitializedNodeGUID {
2✔
317
                                                                log.V(0).Info("NeedToUpdateSriov(): VF GUID needs update",
1✔
318
                                                                        "vf", vfStatus.VfID, "current", vfStatus.GUID)
1✔
319
                                                                return true
1✔
320
                                                        }
1✔
321
                                                }
322
                                                // this is needed to be sure the admin mac address is configured as expected
323
                                                if ifaceSpec.ExternallyManaged {
1✔
324
                                                        log.V(0).Info("NeedToUpdateSriov(): need to update the device as it's externally manage",
×
325
                                                                "device", ifaceStatus.PciAddress)
×
326
                                                        return true
×
327
                                                }
×
328
                                        }
329
                                        if groupSpec.VdpaType != vfStatus.VdpaType {
1✔
330
                                                log.V(0).Info("NeedToUpdateSriov(): VF VdpaType mismatch",
×
331
                                                        "desired", groupSpec.VdpaType, "current", vfStatus.VdpaType)
×
332
                                                return true
×
333
                                        }
×
334
                                        break
1✔
335
                                }
336
                        }
337
                }
338
        }
339
        return false
1✔
340
}
341

342
type ByPriority []SriovNetworkNodePolicy
343

344
func (a ByPriority) Len() int {
1✔
345
        return len(a)
1✔
346
}
1✔
347

348
func (a ByPriority) Less(i, j int) bool {
×
349
        if a[i].Spec.Priority != a[j].Spec.Priority {
×
350
                return a[i].Spec.Priority > a[j].Spec.Priority
×
351
        }
×
352
        return a[i].GetName() < a[j].GetName()
×
353
}
354

355
func (a ByPriority) Swap(i, j int) {
×
356
        a[i], a[j] = a[j], a[i]
×
357
}
×
358

359
// Match check if node is selected by NodeSelector
360
func (p *SriovNetworkNodePolicy) Selected(node *corev1.Node) bool {
1✔
361
        for k, v := range p.Spec.NodeSelector {
2✔
362
                if nv, ok := node.Labels[k]; ok && nv == v {
2✔
363
                        continue
1✔
364
                }
365
                return false
×
366
        }
367
        return true
1✔
368
}
369

370
func StringInArray(val string, array []string) bool {
1✔
371
        for i := range array {
2✔
372
                if array[i] == val {
2✔
373
                        return true
1✔
374
                }
1✔
375
        }
376
        return false
1✔
377
}
378

379
func RemoveString(s string, slice []string) (result []string, found bool) {
1✔
380
        if len(slice) != 0 {
2✔
381
                for _, item := range slice {
2✔
382
                        if item == s {
2✔
383
                                found = true
1✔
384
                                continue
1✔
385
                        }
386
                        result = append(result, item)
×
387
                }
388
        }
389
        return
1✔
390
}
391

392
func UniqueAppend(inSlice []string, strings ...string) []string {
×
393
        for _, s := range strings {
×
394
                if !StringInArray(s, inSlice) {
×
395
                        inSlice = append(inSlice, s)
×
396
                }
×
397
        }
398
        return inSlice
×
399
}
400

401
// Apply policy to SriovNetworkNodeState CR
402
func (p *SriovNetworkNodePolicy) Apply(state *SriovNetworkNodeState, equalPriority bool) error {
1✔
403
        s := p.Spec.NicSelector
1✔
404
        if s.IsEmpty() {
2✔
405
                // Empty NicSelector match none
1✔
406
                return nil
1✔
407
        }
1✔
408
        for _, iface := range state.Status.Interfaces {
2✔
409
                if s.Selected(&iface) {
2✔
410
                        log.Info("Update interface", "name:", iface.Name)
1✔
411
                        result := Interface{
1✔
412
                                PciAddress:        iface.PciAddress,
1✔
413
                                Mtu:               p.Spec.Mtu,
1✔
414
                                Name:              iface.Name,
1✔
415
                                LinkType:          p.Spec.LinkType,
1✔
416
                                EswitchMode:       p.Spec.EswitchMode,
1✔
417
                                NumVfs:            p.Spec.NumVfs,
1✔
418
                                ExternallyManaged: p.Spec.ExternallyManaged,
1✔
419
                        }
1✔
420
                        if p.Spec.NumVfs > 0 {
2✔
421
                                group, err := p.generatePfNameVfGroup(&iface)
1✔
422
                                if err != nil {
2✔
423
                                        return err
1✔
424
                                }
1✔
425
                                result.VfGroups = []VfGroup{*group}
1✔
426
                                found := false
1✔
427
                                for i := range state.Spec.Interfaces {
2✔
428
                                        if state.Spec.Interfaces[i].PciAddress == result.PciAddress {
2✔
429
                                                found = true
1✔
430
                                                state.Spec.Interfaces[i].mergeConfigs(&result, equalPriority)
1✔
431
                                                state.Spec.Interfaces[i] = result
1✔
432
                                                break
1✔
433
                                        }
434
                                }
435
                                if !found {
2✔
436
                                        state.Spec.Interfaces = append(state.Spec.Interfaces, result)
1✔
437
                                }
1✔
438
                        }
439
                }
440
        }
441
        return nil
1✔
442
}
443

444
// ApplyBridgeConfig applies bridge configuration from the policy to the provided state
445
func (p *SriovNetworkNodePolicy) ApplyBridgeConfig(state *SriovNetworkNodeState) error {
1✔
446
        if p.Spec.NicSelector.IsEmpty() {
2✔
447
                // Empty NicSelector match none
1✔
448
                return nil
1✔
449
        }
1✔
450
        // sanity check the policy
451
        if !p.Spec.Bridge.IsEmpty() {
2✔
452
                if p.Spec.EswitchMode != ESwithModeSwitchDev {
2✔
453
                        return fmt.Errorf("eSwitchMode must be switchdev to use software bridge management")
1✔
454
                }
1✔
455
                if p.Spec.LinkType != "" && !strings.EqualFold(p.Spec.LinkType, consts.LinkTypeETH) {
2✔
456
                        return fmt.Errorf("linkType must be eth or ETH to use software bridge management")
1✔
457
                }
1✔
458
                if p.Spec.ExternallyManaged {
2✔
459
                        return fmt.Errorf("software bridge management can't be used when link is externally managed")
1✔
460
                }
1✔
461
        }
462
        for _, iface := range state.Status.Interfaces {
2✔
463
                if p.Spec.NicSelector.Selected(&iface) {
2✔
464
                        if p.Spec.Bridge.OVS == nil {
2✔
465
                                // 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✔
466
                                // 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✔
467
                                // config from the node's state if it has the interface (that matches "empty-bridge" policy) in the uplink section.
1✔
468
                                state.Spec.Bridges.OVS = slices.DeleteFunc(state.Spec.Bridges.OVS, func(br OVSConfigExt) bool {
2✔
469
                                        return slices.ContainsFunc(br.Uplinks, func(uplink OVSUplinkConfigExt) bool {
2✔
470
                                                return uplink.PciAddress == iface.PciAddress
1✔
471
                                        })
1✔
472
                                })
473
                                if len(state.Spec.Bridges.OVS) == 0 {
2✔
474
                                        state.Spec.Bridges.OVS = nil
1✔
475
                                }
1✔
476
                                continue
1✔
477
                        }
478
                        ovsBridge := OVSConfigExt{
1✔
479
                                Name:   GenerateBridgeName(&iface),
1✔
480
                                Bridge: p.Spec.Bridge.OVS.Bridge,
1✔
481
                                Uplinks: []OVSUplinkConfigExt{{
1✔
482
                                        PciAddress: iface.PciAddress,
1✔
483
                                        Name:       iface.Name,
1✔
484
                                        Interface:  p.Spec.Bridge.OVS.Uplink.Interface,
1✔
485
                                }},
1✔
486
                        }
1✔
487
                        if p.Spec.Mtu > 0 {
1✔
488
                                mtu := p.Spec.Mtu
×
489
                                ovsBridge.Uplinks[0].Interface.MTURequest = &mtu
×
490
                        }
×
491
                        log.Info("Update bridge for interface", "name", iface.Name, "bridge", ovsBridge.Name)
1✔
492

1✔
493
                        // We need to keep slices with bridges ordered to avoid unnecessary updates in the K8S API.
1✔
494
                        // Use binary search to insert (or update) the bridge config to the right place in the slice to keep it sorted.
1✔
495
                        pos, exist := slices.BinarySearchFunc(state.Spec.Bridges.OVS, ovsBridge, func(x, y OVSConfigExt) int {
2✔
496
                                return strings.Compare(x.Name, y.Name)
1✔
497
                        })
1✔
498
                        if exist {
2✔
499
                                state.Spec.Bridges.OVS[pos] = ovsBridge
1✔
500
                        } else {
2✔
501
                                state.Spec.Bridges.OVS = slices.Insert(state.Spec.Bridges.OVS, pos, ovsBridge)
1✔
502
                        }
1✔
503
                }
504
        }
505
        return nil
1✔
506
}
507

508
// mergeConfigs merges configs from multiple polices where the last one has the
509
// highest priority. This merge is dependent on: 1. SR-IOV partition is
510
// configured with the #-notation in pfName, 2. The VF groups are
511
// non-overlapping or SR-IOV policies have the same priority.
512
func (iface Interface) mergeConfigs(input *Interface, equalPriority bool) {
1✔
513
        m := false
1✔
514
        // merge VF groups (input.VfGroups already contains the highest priority):
1✔
515
        // - skip group with same ResourceName,
1✔
516
        // - skip overlapping groups (use only highest priority)
1✔
517
        for _, gr := range iface.VfGroups {
2✔
518
                if gr.ResourceName == input.VfGroups[0].ResourceName || gr.isVFRangeOverlapping(input.VfGroups[0]) {
2✔
519
                        continue
1✔
520
                }
521
                m = true
1✔
522
                input.VfGroups = append(input.VfGroups, gr)
1✔
523
        }
524

525
        if !equalPriority && !m {
2✔
526
                return
1✔
527
        }
1✔
528

529
        // mtu configuration we take the highest value
530
        if input.Mtu < iface.Mtu {
2✔
531
                input.Mtu = iface.Mtu
1✔
532
        }
1✔
533
        if input.NumVfs < iface.NumVfs {
2✔
534
                input.NumVfs = iface.NumVfs
1✔
535
        }
1✔
536
}
537

538
func (gr VfGroup) isVFRangeOverlapping(group VfGroup) bool {
1✔
539
        rngSt, rngEnd, err := parseRange(gr.VfRange)
1✔
540
        if err != nil {
1✔
541
                return false
×
542
        }
×
543
        rngSt2, rngEnd2, err := parseRange(group.VfRange)
1✔
544
        if err != nil {
1✔
545
                return false
×
546
        }
×
547
        // compare minimal range has overlap
548
        if rngSt < rngSt2 {
2✔
549
                return IndexInRange(rngSt2, gr.VfRange) || IndexInRange(rngEnd2, gr.VfRange)
1✔
550
        }
1✔
551
        return IndexInRange(rngSt, group.VfRange) || IndexInRange(rngEnd, group.VfRange)
1✔
552
}
553

554
func (p *SriovNetworkNodePolicy) generatePfNameVfGroup(iface *InterfaceExt) (*VfGroup, error) {
1✔
555
        var err error
1✔
556
        pfName := ""
1✔
557
        var rngStart, rngEnd int
1✔
558
        found := false
1✔
559
        for _, selector := range p.Spec.NicSelector.PfNames {
2✔
560
                pfName, rngStart, rngEnd, err = ParseVfRange(selector)
1✔
561
                if err != nil {
2✔
562
                        log.Error(err, "Unable to parse PF Name.")
1✔
563
                        return nil, err
1✔
564
                }
1✔
565
                if pfName == iface.Name {
2✔
566
                        found = true
1✔
567
                        if rngStart == invalidVfIndex && rngEnd == invalidVfIndex {
2✔
568
                                rngStart, rngEnd = 0, p.Spec.NumVfs-1
1✔
569
                        }
1✔
570
                        break
1✔
571
                }
572
        }
573
        if !found {
2✔
574
                // assign the default vf index range if the pfName is not specified by the nicSelector
1✔
575
                rngStart, rngEnd = 0, p.Spec.NumVfs-1
1✔
576
        }
1✔
577
        rng := strconv.Itoa(rngStart) + "-" + strconv.Itoa(rngEnd)
1✔
578
        return &VfGroup{
1✔
579
                ResourceName: p.Spec.ResourceName,
1✔
580
                DeviceType:   p.Spec.DeviceType,
1✔
581
                VfRange:      rng,
1✔
582
                PolicyName:   p.GetName(),
1✔
583
                Mtu:          p.Spec.Mtu,
1✔
584
                IsRdma:       p.Spec.IsRdma,
1✔
585
                VdpaType:     p.Spec.VdpaType,
1✔
586
        }, nil
1✔
587
}
588

589
func IndexInRange(i int, r string) bool {
1✔
590
        rngSt, rngEnd, err := parseRange(r)
1✔
591
        if err != nil {
2✔
592
                return false
1✔
593
        }
1✔
594
        if i <= rngEnd && i >= rngSt {
2✔
595
                return true
1✔
596
        }
1✔
597
        return false
1✔
598
}
599

600
func parseRange(r string) (rngSt, rngEnd int, err error) {
1✔
601
        rng := strings.Split(r, "-")
1✔
602
        rngSt, err = strconv.Atoi(rng[0])
1✔
603
        if err != nil {
2✔
604
                return
1✔
605
        }
1✔
606
        rngEnd, err = strconv.Atoi(rng[1])
1✔
607
        if err != nil {
1✔
608
                return
×
609
        }
×
610
        return
1✔
611
}
612

613
// SplitDeviceFromRange return the device name and the range.
614
// the split is base on #
615
func SplitDeviceFromRange(device string) (string, string) {
1✔
616
        if strings.Contains(device, "#") {
2✔
617
                fields := strings.Split(device, "#")
1✔
618
                return fields[0], fields[1]
1✔
619
        }
1✔
620

621
        return device, ""
1✔
622
}
623

624
// ParseVfRange: parse a device with VF range
625
// this can be rootDevices or PFName
626
// if no range detect we just return the device name
627
func ParseVfRange(device string) (rootDeviceName string, rngSt, rngEnd int, err error) {
1✔
628
        rngSt, rngEnd = invalidVfIndex, invalidVfIndex
1✔
629
        rootDeviceName, splitRange := SplitDeviceFromRange(device)
1✔
630
        if splitRange != "" {
2✔
631
                rngSt, rngEnd, err = parseRange(splitRange)
1✔
632
        } else {
2✔
633
                rootDeviceName = device
1✔
634
        }
1✔
635
        return
1✔
636
}
637

638
// IsEmpty returns true if nicSelector is empty
639
func (selector *SriovNetworkNicSelector) IsEmpty() bool {
1✔
640
        return selector.Vendor == "" &&
1✔
641
                selector.DeviceID == "" &&
1✔
642
                len(selector.RootDevices) == 0 &&
1✔
643
                len(selector.PfNames) == 0 &&
1✔
644
                len(selector.NetFilter) == 0
1✔
645
}
1✔
646

647
func (selector *SriovNetworkNicSelector) Selected(iface *InterfaceExt) bool {
1✔
648
        if selector.Vendor != "" && selector.Vendor != iface.Vendor {
1✔
649
                return false
×
650
        }
×
651
        if selector.DeviceID != "" && selector.DeviceID != iface.DeviceID {
1✔
652
                return false
×
653
        }
×
654
        if len(selector.RootDevices) > 0 && !StringInArray(iface.PciAddress, selector.RootDevices) {
2✔
655
                return false
1✔
656
        }
1✔
657
        if len(selector.PfNames) > 0 {
2✔
658
                var pfNames []string
1✔
659
                for _, p := range selector.PfNames {
2✔
660
                        if strings.Contains(p, "#") {
2✔
661
                                fields := strings.Split(p, "#")
1✔
662
                                pfNames = append(pfNames, fields[0])
1✔
663
                        } else {
2✔
664
                                pfNames = append(pfNames, p)
1✔
665
                        }
1✔
666
                }
667
                if !StringInArray(iface.Name, pfNames) {
1✔
668
                        return false
×
669
                }
×
670
        }
671
        if selector.NetFilter != "" && !NetFilterMatch(selector.NetFilter, iface.NetFilter) {
1✔
672
                return false
×
673
        }
×
674

675
        return true
1✔
676
}
677

678
func (s *SriovNetworkNodeState) GetInterfaceStateByPciAddress(addr string) *InterfaceExt {
1✔
679
        for _, iface := range s.Status.Interfaces {
1✔
680
                if addr == iface.PciAddress {
×
681
                        return &iface
×
682
                }
×
683
        }
684
        return nil
1✔
685
}
686

687
func (s *SriovNetworkNodeState) GetDriverByPciAddress(addr string) string {
×
688
        for _, iface := range s.Status.Interfaces {
×
689
                if addr == iface.PciAddress {
×
690
                        return iface.Driver
×
691
                }
×
692
        }
693
        return ""
×
694
}
695

696
// RenderNetAttDef renders a net-att-def for ib-sriov CNI
697
func (cr *SriovIBNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
698
        logger := log.WithName("RenderNetAttDef")
1✔
699
        logger.Info("Start to render IB SRIOV CNI NetworkAttachmentDefinition")
1✔
700

1✔
701
        // render RawCNIConfig manifests
1✔
702
        data := render.MakeRenderData()
1✔
703
        data.Data["CniType"] = "ib-sriov"
1✔
704
        data.Data["SriovNetworkName"] = cr.Name
1✔
705
        if cr.Spec.NetworkNamespace == "" {
2✔
706
                data.Data["SriovNetworkNamespace"] = cr.Namespace
1✔
707
        } else {
2✔
708
                data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
709
        }
1✔
710
        data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
711

1✔
712
        data.Data["StateConfigured"] = true
1✔
713
        switch cr.Spec.LinkState {
1✔
714
        case SriovCniStateEnable:
1✔
715
                data.Data["SriovCniState"] = SriovCniStateEnable
1✔
716
        case SriovCniStateDisable:
×
717
                data.Data["SriovCniState"] = SriovCniStateDisable
×
718
        case SriovCniStateAuto:
×
719
                data.Data["SriovCniState"] = SriovCniStateAuto
×
720
        default:
1✔
721
                data.Data["StateConfigured"] = false
1✔
722
        }
723

724
        if cr.Spec.Capabilities == "" {
2✔
725
                data.Data["CapabilitiesConfigured"] = false
1✔
726
        } else {
2✔
727
                data.Data["CapabilitiesConfigured"] = true
1✔
728
                data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities
1✔
729
        }
1✔
730

731
        if cr.Spec.IPAM != "" {
2✔
732
                data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
733
        } else {
2✔
734
                data.Data["SriovCniIpam"] = SriovCniIpamEmpty
1✔
735
        }
1✔
736

737
        // metaplugins for the infiniband cni
738
        data.Data["MetaPluginsConfigured"] = false
1✔
739
        if cr.Spec.MetaPluginsConfig != "" {
1✔
740
                data.Data["MetaPluginsConfigured"] = true
×
741
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
×
742
        }
×
743

744
        // logLevel and logFile are currently not supports by the ip-sriov-cni -> hardcode them to false.
745
        data.Data["LogLevelConfigured"] = false
1✔
746
        data.Data["LogFileConfigured"] = false
1✔
747

1✔
748
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
1✔
749
        if err != nil {
1✔
750
                return nil, err
×
751
        }
×
752
        for _, obj := range objs {
2✔
753
                raw, _ := json.Marshal(obj)
1✔
754
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
755
        }
1✔
756
        return objs[0], nil
1✔
757
}
758

759
// NetworkNamespace returns target network namespace for the network
760
func (cr *SriovIBNetwork) NetworkNamespace() string {
1✔
761
        return cr.Spec.NetworkNamespace
1✔
762
}
1✔
763

764
// RenderNetAttDef renders a net-att-def for sriov CNI
765
func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
766
        logger := log.WithName("RenderNetAttDef")
1✔
767
        logger.Info("Start to render SRIOV CNI NetworkAttachmentDefinition")
1✔
768

1✔
769
        // render RawCNIConfig manifests
1✔
770
        data := render.MakeRenderData()
1✔
771
        data.Data["CniType"] = "sriov"
1✔
772
        data.Data["SriovNetworkName"] = cr.Name
1✔
773
        if cr.Spec.NetworkNamespace == "" {
2✔
774
                data.Data["SriovNetworkNamespace"] = cr.Namespace
1✔
775
        } else {
2✔
776
                data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
777
        }
1✔
778
        data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
779
        data.Data["SriovCniVlan"] = cr.Spec.Vlan
1✔
780

1✔
781
        if cr.Spec.VlanQoS <= 7 && cr.Spec.VlanQoS >= 0 {
2✔
782
                data.Data["VlanQoSConfigured"] = true
1✔
783
                data.Data["SriovCniVlanQoS"] = cr.Spec.VlanQoS
1✔
784
        } else {
1✔
785
                data.Data["VlanQoSConfigured"] = false
×
786
        }
×
787

788
        data.Data["VlanProtoConfigured"] = false
1✔
789
        if cr.Spec.VlanProto != "" {
2✔
790
                data.Data["VlanProtoConfigured"] = true
1✔
791
                data.Data["SriovCniVlanProto"] = cr.Spec.VlanProto
1✔
792
        }
1✔
793

794
        if cr.Spec.Capabilities == "" {
2✔
795
                data.Data["CapabilitiesConfigured"] = false
1✔
796
        } else {
1✔
797
                data.Data["CapabilitiesConfigured"] = true
×
798
                data.Data["SriovCniCapabilities"] = cr.Spec.Capabilities
×
799
        }
×
800

801
        data.Data["SpoofChkConfigured"] = true
1✔
802
        switch cr.Spec.SpoofChk {
1✔
803
        case SriovCniStateOff:
×
804
                data.Data["SriovCniSpoofChk"] = SriovCniStateOff
×
805
        case SriovCniStateOn:
1✔
806
                data.Data["SriovCniSpoofChk"] = SriovCniStateOn
1✔
807
        default:
1✔
808
                data.Data["SpoofChkConfigured"] = false
1✔
809
        }
810

811
        data.Data["TrustConfigured"] = true
1✔
812
        switch cr.Spec.Trust {
1✔
813
        case SriovCniStateOn:
1✔
814
                data.Data["SriovCniTrust"] = SriovCniStateOn
1✔
815
        case SriovCniStateOff:
×
816
                data.Data["SriovCniTrust"] = SriovCniStateOff
×
817
        default:
1✔
818
                data.Data["TrustConfigured"] = false
1✔
819
        }
820

821
        data.Data["StateConfigured"] = true
1✔
822
        switch cr.Spec.LinkState {
1✔
823
        case SriovCniStateEnable:
×
824
                data.Data["SriovCniState"] = SriovCniStateEnable
×
825
        case SriovCniStateDisable:
×
826
                data.Data["SriovCniState"] = SriovCniStateDisable
×
827
        case SriovCniStateAuto:
×
828
                data.Data["SriovCniState"] = SriovCniStateAuto
×
829
        default:
1✔
830
                data.Data["StateConfigured"] = false
1✔
831
        }
832

833
        data.Data["MinTxRateConfigured"] = false
1✔
834
        if cr.Spec.MinTxRate != nil {
1✔
835
                if *cr.Spec.MinTxRate >= 0 {
×
836
                        data.Data["MinTxRateConfigured"] = true
×
837
                        data.Data["SriovCniMinTxRate"] = *cr.Spec.MinTxRate
×
838
                }
×
839
        }
840

841
        data.Data["MaxTxRateConfigured"] = false
1✔
842
        if cr.Spec.MaxTxRate != nil {
1✔
843
                if *cr.Spec.MaxTxRate >= 0 {
×
844
                        data.Data["MaxTxRateConfigured"] = true
×
845
                        data.Data["SriovCniMaxTxRate"] = *cr.Spec.MaxTxRate
×
846
                }
×
847
        }
848

849
        if cr.Spec.IPAM != "" {
2✔
850
                data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
851
        } else {
2✔
852
                data.Data["SriovCniIpam"] = SriovCniIpamEmpty
1✔
853
        }
1✔
854

855
        data.Data["MetaPluginsConfigured"] = false
1✔
856
        if cr.Spec.MetaPluginsConfig != "" {
2✔
857
                data.Data["MetaPluginsConfigured"] = true
1✔
858
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
1✔
859
        }
1✔
860

861
        data.Data["LogLevelConfigured"] = (cr.Spec.LogLevel != "")
1✔
862
        data.Data["LogLevel"] = cr.Spec.LogLevel
1✔
863
        data.Data["LogFileConfigured"] = (cr.Spec.LogFile != "")
1✔
864
        data.Data["LogFile"] = cr.Spec.LogFile
1✔
865

1✔
866
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
1✔
867
        if err != nil {
1✔
868
                return nil, err
×
869
        }
×
870
        for _, obj := range objs {
2✔
871
                raw, _ := json.Marshal(obj)
1✔
872
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
873
        }
1✔
874
        return objs[0], nil
1✔
875
}
876

877
// NetworkNamespace returns target network namespace for the network
878
func (cr *SriovNetwork) NetworkNamespace() string {
1✔
879
        return cr.Spec.NetworkNamespace
1✔
880
}
1✔
881

882
// RenderNetAttDef renders a net-att-def for sriov CNI
883
func (cr *OVSNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
1✔
884
        logger := log.WithName("RenderNetAttDef")
1✔
885
        logger.Info("Start to render OVS CNI NetworkAttachmentDefinition")
1✔
886

1✔
887
        // render RawCNIConfig manifests
1✔
888
        data := render.MakeRenderData()
1✔
889
        data.Data["CniType"] = "ovs"
1✔
890
        data.Data["NetworkName"] = cr.Name
1✔
891
        if cr.Spec.NetworkNamespace == "" {
2✔
892
                data.Data["NetworkNamespace"] = cr.Namespace
1✔
893
        } else {
2✔
894
                data.Data["NetworkNamespace"] = cr.Spec.NetworkNamespace
1✔
895
        }
1✔
896
        data.Data["CniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
1✔
897

1✔
898
        if cr.Spec.Capabilities == "" {
2✔
899
                data.Data["CapabilitiesConfigured"] = false
1✔
900
        } else {
2✔
901
                data.Data["CapabilitiesConfigured"] = true
1✔
902
                data.Data["CniCapabilities"] = cr.Spec.Capabilities
1✔
903
        }
1✔
904

905
        data.Data["Bridge"] = cr.Spec.Bridge
1✔
906
        data.Data["VlanTag"] = cr.Spec.Vlan
1✔
907
        data.Data["MTU"] = cr.Spec.MTU
1✔
908
        if len(cr.Spec.Trunk) > 0 {
2✔
909
                trunkConfRaw, _ := json.Marshal(cr.Spec.Trunk)
1✔
910
                data.Data["Trunk"] = string(trunkConfRaw)
1✔
911
        } else {
2✔
912
                data.Data["Trunk"] = ""
1✔
913
        }
1✔
914
        data.Data["InterfaceType"] = cr.Spec.InterfaceType
1✔
915

1✔
916
        if cr.Spec.IPAM != "" {
2✔
917
                data.Data["CniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
1✔
918
        } else {
2✔
919
                data.Data["CniIpam"] = SriovCniIpamEmpty
1✔
920
        }
1✔
921

922
        data.Data["MetaPluginsConfigured"] = false
1✔
923
        if cr.Spec.MetaPluginsConfig != "" {
2✔
924
                data.Data["MetaPluginsConfigured"] = true
1✔
925
                data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
1✔
926
        }
1✔
927

928
        objs, err := render.RenderDir(filepath.Join(ManifestsPath, "ovs"), &data)
1✔
929
        if err != nil {
1✔
930
                return nil, err
×
931
        }
×
932
        for _, obj := range objs {
2✔
933
                raw, _ := json.Marshal(obj)
1✔
934
                logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
1✔
935
        }
1✔
936
        return objs[0], nil
1✔
937
}
938

939
// NetworkNamespace returns target network namespace for the network
940
func (cr *OVSNetwork) NetworkNamespace() string {
1✔
941
        return cr.Spec.NetworkNamespace
1✔
942
}
1✔
943

944
// NetFilterMatch -- parse netFilter and check for a match
945
func NetFilterMatch(netFilter string, netValue string) (isMatch bool) {
×
946
        logger := log.WithName("NetFilterMatch")
×
947

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

×
950
        netFilterResult := re.FindAllStringSubmatch(netFilter, -1)
×
951

×
952
        if netFilterResult == nil {
×
953
                logger.Info("Invalid NetFilter spec...", "netFilter", netFilter)
×
954
                return false
×
955
        }
×
956

957
        netValueResult := re.FindAllStringSubmatch(netValue, -1)
×
958

×
959
        if netValueResult == nil {
×
960
                logger.Info("Invalid netValue...", "netValue", netValue)
×
961
                return false
×
962
        }
×
963

964
        return netFilterResult[0][1] == netValueResult[0][1] && netFilterResult[0][2] == netValueResult[0][2]
×
965
}
966

967
// MaxUnavailable calculate the max number of unavailable nodes to represent the number of nodes
968
// we can drain in parallel
969
func (s *SriovNetworkPoolConfig) MaxUnavailable(numOfNodes int) (int, error) {
1✔
970
        // this means we want to drain all the nodes in parallel
1✔
971
        if s.Spec.MaxUnavailable == nil {
2✔
972
                return -1, nil
1✔
973
        }
1✔
974
        intOrPercent := *s.Spec.MaxUnavailable
1✔
975

1✔
976
        if intOrPercent.Type == intstrutil.String {
2✔
977
                if strings.HasSuffix(intOrPercent.StrVal, "%") {
2✔
978
                        i := strings.TrimSuffix(intOrPercent.StrVal, "%")
1✔
979
                        v, err := strconv.Atoi(i)
1✔
980
                        if err != nil {
1✔
981
                                return 0, fmt.Errorf("invalid value %q: %v", intOrPercent.StrVal, err)
×
982
                        }
×
983
                        if v > 100 || v < 1 {
2✔
984
                                return 0, fmt.Errorf("invalid value: percentage needs to be between 1 and 100")
1✔
985
                        }
1✔
986
                } else {
1✔
987
                        return 0, fmt.Errorf("invalid type: strings needs to be a percentage")
1✔
988
                }
1✔
989
        }
990

991
        maxunavail, err := intstrutil.GetScaledValueFromIntOrPercent(&intOrPercent, numOfNodes, false)
1✔
992
        if err != nil {
1✔
993
                return 0, err
×
994
        }
×
995

996
        if maxunavail < 0 {
2✔
997
                return 0, fmt.Errorf("negative number is not allowed")
1✔
998
        }
1✔
999

1000
        return maxunavail, nil
1✔
1001
}
1002

1003
// GenerateBridgeName generate predictable name for the software bridge
1004
// current format is: br-0000_00_03.0
1005
func GenerateBridgeName(iface *InterfaceExt) string {
1✔
1006
        return fmt.Sprintf("br-%s", strings.ReplaceAll(iface.PciAddress, ":", "_"))
1✔
1007
}
1✔
1008

1009
// NeedToUpdateBridges returns true if bridge for the host requires update
1010
func NeedToUpdateBridges(bridgeSpec, bridgeStatus *Bridges) bool {
1✔
1011
        return !reflect.DeepEqual(bridgeSpec, bridgeStatus)
1✔
1012
}
1✔
1013

1014
// SetKeepUntilTime sets an annotation to hold the "keep until time" for the node’s state.
1015
// The "keep until time" specifies the earliest time at which the state object can be removed
1016
// if the daemon's pod is not found on the node.
1017
func (s *SriovNetworkNodeState) SetKeepUntilTime(t time.Time) {
1✔
1018
        ts := t.Format(time.RFC3339)
1✔
1019
        annotations := s.GetAnnotations()
1✔
1020
        if annotations == nil {
2✔
1021
                annotations = map[string]string{}
1✔
1022
        }
1✔
1023
        annotations[consts.NodeStateKeepUntilAnnotation] = ts
1✔
1024
        s.SetAnnotations(annotations)
1✔
1025
}
1026

1027
// GetKeepUntilTime returns the value that is stored in the "keep until time" annotation.
1028
// The "keep until time" specifies the earliest time at which the state object can be removed
1029
// if the daemon's pod is not found on the node.
1030
// Return zero time instant if annotaion is not found on the object or if it has a wrong format.
1031
func (s *SriovNetworkNodeState) GetKeepUntilTime() time.Time {
1✔
1032
        t, err := time.Parse(time.RFC3339, s.GetAnnotations()[consts.NodeStateKeepUntilAnnotation])
1✔
1033
        if err != nil {
2✔
1034
                return time.Time{}
1✔
1035
        }
1✔
1036
        return t
1✔
1037
}
1038

1039
// ResetKeepUntilTime removes "keep until time" annotation from the state object.
1040
// The "keep until time" specifies the earliest time at which the state object can be removed
1041
// if the daemon's pod is not found on the node.
1042
// Returns true if the value was removed, false otherwise.
1043
func (s *SriovNetworkNodeState) ResetKeepUntilTime() bool {
1✔
1044
        annotations := s.GetAnnotations()
1✔
1045
        _, exist := annotations[consts.NodeStateKeepUntilAnnotation]
1✔
1046
        if !exist {
2✔
1047
                return false
1✔
1048
        }
1✔
1049
        delete(annotations, consts.NodeStateKeepUntilAnnotation)
×
1050
        s.SetAnnotations(annotations)
×
1051
        return true
×
1052
}
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