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

k8snetworkplumbingwg / sriov-network-operator / 13677347230

05 Mar 2025 01:40PM UTC coverage: 48.413% (-0.5%) from 48.917%
13677347230

Pull #853

github

web-flow
Merge bbc18ece8 into ab7d36758
Pull Request #853: Move systemd to host package

142 of 318 new or added lines in 6 files covered. (44.65%)

11 existing lines in 4 files now uncovered.

7352 of 15186 relevant lines covered (48.41%)

0.53 hits per line

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

58.66
/controllers/sriovnetworknodepolicy_controller.go
1
/*
2
Copyright 2021.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package controllers
18

19
import (
20
        "context"
21
        "encoding/json"
22
        "fmt"
23
        "os"
24
        "reflect"
25
        "sort"
26
        "strconv"
27
        "strings"
28
        "time"
29

30
        corev1 "k8s.io/api/core/v1"
31
        "k8s.io/apimachinery/pkg/api/equality"
32
        "k8s.io/apimachinery/pkg/api/errors"
33
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34
        "k8s.io/apimachinery/pkg/runtime"
35
        "k8s.io/apimachinery/pkg/types"
36
        "k8s.io/client-go/util/workqueue"
37
        ctrl "sigs.k8s.io/controller-runtime"
38
        "sigs.k8s.io/controller-runtime/pkg/client"
39
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
40
        "sigs.k8s.io/controller-runtime/pkg/event"
41
        "sigs.k8s.io/controller-runtime/pkg/handler"
42
        "sigs.k8s.io/controller-runtime/pkg/log"
43
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
44
        "sigs.k8s.io/controller-runtime/pkg/source"
45

46
        dptypes "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types"
47

48
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
49
        constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
50
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
51
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
52
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
53
)
54

55
const nodePolicySyncEventName = "node-policy-sync-event"
56

57
// SriovNetworkNodePolicyReconciler reconciles a SriovNetworkNodePolicy object
58
type SriovNetworkNodePolicyReconciler struct {
59
        client.Client
60
        Scheme      *runtime.Scheme
61
        FeatureGate featuregate.FeatureGate
62
}
63

64
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworknodepolicies,verbs=get;list;watch;create;update;patch;delete
65
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworknodepolicies/status,verbs=get;update;patch
66
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworknodepolicies/finalizers,verbs=update
67

68
// Reconcile is part of the main kubernetes reconciliation loop which aims to
69
// move the current state of the cluster closer to the desired state.
70
// TODO(user): Modify the Reconcile function to compare the state specified by
71
// the SriovNetworkNodePolicy object against the actual cluster state, and then
72
// perform operations to make the cluster state reflect the state specified by
73
// the user.
74
//
75
// For more details, check Reconcile and its Result here:
76
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile
77
func (r *SriovNetworkNodePolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
1✔
78
        // Only handle node-policy-sync-event
1✔
79
        if req.Name != nodePolicySyncEventName || req.Namespace != "" {
2✔
80
                return reconcile.Result{}, nil
1✔
81
        }
1✔
82

83
        reqLogger := log.FromContext(ctx)
1✔
84
        reqLogger.Info("Reconciling")
1✔
85

1✔
86
        // Fetch the default SriovOperatorConfig
1✔
87
        defaultOpConf := &sriovnetworkv1.SriovOperatorConfig{}
1✔
88
        if err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: constants.DefaultConfigName}, defaultOpConf); err != nil {
1✔
89
                if errors.IsNotFound(err) {
×
90
                        reqLogger.Info("default SriovOperatorConfig object not found, cannot reconcile SriovNetworkNodePolicies. Requeue.")
×
91
                        return reconcile.Result{RequeueAfter: constants.DrainControllerRequeueTime}, nil
×
92
                }
×
93
                return reconcile.Result{}, err
×
94
        }
95

96
        // Fetch the SriovNetworkNodePolicyList
97
        policyList := &sriovnetworkv1.SriovNetworkNodePolicyList{}
1✔
98
        err := r.List(ctx, policyList, &client.ListOptions{})
1✔
99
        if err != nil {
1✔
100
                if errors.IsNotFound(err) {
×
101
                        // Request object not found, could have been deleted after reconcile request.
×
102
                        // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
×
103
                        // Return and don't requeue
×
104
                        return reconcile.Result{}, nil
×
105
                }
×
106
                // Error reading the object - requeue the request.
107
                return reconcile.Result{}, err
×
108
        }
109
        // Fetch the Nodes
110
        nodeList := &corev1.NodeList{}
1✔
111
        lo := &client.MatchingLabels{
1✔
112
                "node-role.kubernetes.io/worker": "",
1✔
113
                "kubernetes.io/os":               "linux",
1✔
114
        }
1✔
115
        if len(defaultOpConf.Spec.ConfigDaemonNodeSelector) > 0 {
1✔
116
                labels := client.MatchingLabels(defaultOpConf.Spec.ConfigDaemonNodeSelector)
×
117
                lo = &labels
×
118
        }
×
119
        err = r.List(ctx, nodeList, lo)
1✔
120
        if err != nil {
1✔
121
                // Error reading the object - requeue the request.
×
122
                reqLogger.Error(err, "Fail to list nodes")
×
123
                return reconcile.Result{}, err
×
124
        }
×
125

126
        // Sort the policies with priority, higher priority ones is applied later
127
        // We need to use the sort so we always get the policies in the same order
128
        // That is needed so when we create the node Affinity for the sriov-device plugin
129
        // it will remain in the same order and not trigger a pod recreation
130
        sort.Sort(sriovnetworkv1.ByPriority(policyList.Items))
1✔
131
        // Sync SriovNetworkNodeState objects
1✔
132
        if err = r.syncAllSriovNetworkNodeStates(ctx, defaultOpConf, policyList, nodeList); err != nil {
1✔
133
                return reconcile.Result{}, err
×
134
        }
×
135
        // Sync Sriov device plugin ConfigMap object
136
        if err = r.syncDevicePluginConfigMap(ctx, defaultOpConf, policyList, nodeList); err != nil {
1✔
137
                return reconcile.Result{}, err
×
138
        }
×
139

140
        // All was successful. Request that this be re-triggered after ResyncPeriod,
141
        // so we can reconcile state again.
142
        return reconcile.Result{RequeueAfter: constants.ResyncPeriod}, nil
1✔
143
}
144

145
// SetupWithManager sets up the controller with the Manager.
146
func (r *SriovNetworkNodePolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
147
        qHandler := func(q workqueue.RateLimitingInterface) {
2✔
148
                q.AddAfter(reconcile.Request{NamespacedName: types.NamespacedName{
1✔
149
                        Namespace: "",
1✔
150
                        Name:      nodePolicySyncEventName,
1✔
151
                }}, time.Second)
1✔
152
        }
1✔
153

154
        delayedEventHandler := handler.Funcs{
1✔
155
                CreateFunc: func(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
2✔
156
                        log.Log.WithName("SriovNetworkNodePolicy").
1✔
157
                                Info("Enqueuing sync for create event", "resource", e.Object.GetName(), "type", e.Object.GetObjectKind().GroupVersionKind().String())
1✔
158
                        qHandler(q)
1✔
159
                },
1✔
160
                UpdateFunc: func(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
×
161
                        log.Log.WithName("SriovNetworkNodePolicy").
×
162
                                Info("Enqueuing sync for update event", "resource", e.ObjectNew.GetName(), "type", e.ObjectNew.GetObjectKind().GroupVersionKind().String())
×
163
                        qHandler(q)
×
164
                },
×
165
                DeleteFunc: func(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
1✔
166
                        log.Log.WithName("SriovNetworkNodePolicy").
1✔
167
                                Info("Enqueuing sync for delete event", "resource", e.Object.GetName(), "type", e.Object.GetObjectKind().GroupVersionKind().String())
1✔
168
                        qHandler(q)
1✔
169
                },
1✔
170
                GenericFunc: func(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
1✔
171
                        log.Log.WithName("SriovNetworkNodePolicy").
1✔
172
                                Info("Enqueuing sync for generic event", "resource", e.Object.GetName(), "type", e.Object.GetObjectKind().GroupVersionKind().String())
1✔
173
                        qHandler(q)
1✔
174
                },
1✔
175
        }
176

177
        // we want to act fast on new or deleted nodes
178
        nodeEvenHandler := handler.Funcs{
1✔
179
                CreateFunc: func(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
2✔
180
                        log.Log.WithName("SriovNetworkNodePolicy").
1✔
181
                                Info("Enqueuing sync for create event", "resource", e.Object.GetName())
1✔
182
                        qHandler(q)
1✔
183
                },
1✔
184
                UpdateFunc: func(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
1✔
185
                        reflect.DeepEqual(e.ObjectOld.GetLabels(), e.ObjectNew.GetLabels())
1✔
186
                        log.Log.WithName("SriovNetworkNodePolicy").
1✔
187
                                Info("Enqueuing sync for create event", "resource", e.ObjectNew.GetName())
1✔
188
                        qHandler(q)
1✔
189
                },
1✔
190
                DeleteFunc: func(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
1✔
191
                        log.Log.WithName("SriovNetworkNodePolicy").
1✔
192
                                Info("Enqueuing sync for delete event", "resource", e.Object.GetName())
1✔
193
                        qHandler(q)
1✔
194
                },
1✔
195
        }
196

197
        // send initial sync event to trigger reconcile when controller is started
198
        var eventChan = make(chan event.GenericEvent, 1)
1✔
199
        eventChan <- event.GenericEvent{Object: &sriovnetworkv1.SriovNetworkNodePolicy{
1✔
200
                ObjectMeta: metav1.ObjectMeta{Name: nodePolicySyncEventName, Namespace: ""}}}
1✔
201
        close(eventChan)
1✔
202

1✔
203
        return ctrl.NewControllerManagedBy(mgr).
1✔
204
                For(&sriovnetworkv1.SriovNetworkNodePolicy{}).
1✔
205
                Watches(&corev1.Node{}, nodeEvenHandler).
1✔
206
                Watches(&sriovnetworkv1.SriovNetworkNodePolicy{}, delayedEventHandler).
1✔
207
                Watches(&sriovnetworkv1.SriovNetworkPoolConfig{}, delayedEventHandler).
1✔
208
                WatchesRawSource(&source.Channel{Source: eventChan}, delayedEventHandler).
1✔
209
                Complete(r)
1✔
210
}
211

212
func (r *SriovNetworkNodePolicyReconciler) syncDevicePluginConfigMap(ctx context.Context, dc *sriovnetworkv1.SriovOperatorConfig,
213
        pl *sriovnetworkv1.SriovNetworkNodePolicyList, nl *corev1.NodeList) error {
1✔
214
        logger := log.Log.WithName("syncDevicePluginConfigMap")
1✔
215
        logger.V(1).Info("Start to sync device plugin ConfigMap")
1✔
216

1✔
217
        configData := make(map[string]string)
1✔
218
        for _, node := range nl.Items {
2✔
219
                data, err := r.renderDevicePluginConfigData(ctx, pl, &node)
1✔
220
                if err != nil {
1✔
221
                        return err
×
222
                }
×
223
                config, err := json.Marshal(data)
1✔
224
                if err != nil {
1✔
225
                        return err
×
226
                }
×
227
                configData[node.Name] = string(config)
1✔
228

1✔
229
                if len(data.ResourceList) == 0 {
2✔
230
                        // if we don't have policies we should add the disabled label for the device plugin
1✔
231
                        err = utils.LabelNode(ctx, node.Name, constants.SriovDevicePluginLabel, constants.SriovDevicePluginLabelDisabled, r.Client)
1✔
232
                        if err != nil {
1✔
233
                                logger.Error(err, "failed to label node for device plugin label",
×
234
                                        "labelKey",
×
235
                                        constants.SriovDevicePluginLabel,
×
236
                                        "labelValue",
×
237
                                        constants.SriovDevicePluginLabelDisabled)
×
238
                                return err
×
239
                        }
×
240
                } else {
1✔
241
                        // if we have policies we should add the enabled label for the device plugin
1✔
242
                        err = utils.LabelNode(ctx, node.Name, constants.SriovDevicePluginLabel, constants.SriovDevicePluginLabelEnabled, r.Client)
1✔
243
                        if err != nil {
1✔
244
                                logger.Error(err, "failed to label node for device plugin label",
×
245
                                        "labelKey",
×
246
                                        constants.SriovDevicePluginLabel,
×
247
                                        "labelValue",
×
248
                                        constants.SriovDevicePluginLabelEnabled)
×
249
                                return err
×
250
                        }
×
251
                }
252
        }
253

254
        cm := &corev1.ConfigMap{
1✔
255
                TypeMeta: metav1.TypeMeta{
1✔
256
                        APIVersion: "v1",
1✔
257
                        Kind:       "ConfigMap",
1✔
258
                },
1✔
259
                ObjectMeta: metav1.ObjectMeta{
1✔
260
                        Name:      constants.ConfigMapName,
1✔
261
                        Namespace: vars.Namespace,
1✔
262
                },
1✔
263
                Data: configData,
1✔
264
        }
1✔
265

1✔
266
        if err := controllerutil.SetControllerReference(dc, cm, r.Scheme); err != nil {
1✔
267
                return err
×
268
        }
×
269

270
        found := &corev1.ConfigMap{}
1✔
271
        err := r.Get(ctx, types.NamespacedName{Namespace: cm.Namespace, Name: cm.Name}, found)
1✔
272
        if err != nil {
2✔
273
                if errors.IsNotFound(err) {
2✔
274
                        err = r.Create(ctx, cm)
1✔
275
                        if err != nil {
1✔
276
                                return fmt.Errorf("couldn't create ConfigMap: %v", err)
×
277
                        }
×
278
                        logger.V(1).Info("Created ConfigMap for", cm.Namespace, cm.Name)
1✔
279
                } else {
×
280
                        return fmt.Errorf("failed to get ConfigMap: %v", err)
×
281
                }
×
282
        } else {
1✔
283
                logger.V(1).Info("ConfigMap already exists, updating")
1✔
284
                err = r.Update(ctx, cm)
1✔
285
                if err != nil {
1✔
286
                        return fmt.Errorf("couldn't update ConfigMap: %v", err)
×
287
                }
×
288
        }
289
        return nil
1✔
290
}
291

292
func (r *SriovNetworkNodePolicyReconciler) syncAllSriovNetworkNodeStates(ctx context.Context, dc *sriovnetworkv1.SriovOperatorConfig, npl *sriovnetworkv1.SriovNetworkNodePolicyList, nl *corev1.NodeList) error {
1✔
293
        logger := log.Log.WithName("syncAllSriovNetworkNodeStates")
1✔
294
        logger.V(1).Info("Start to sync all SriovNetworkNodeState custom resource")
1✔
295
        found := &corev1.ConfigMap{}
1✔
296
        if err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: constants.ConfigMapName}, found); err != nil {
2✔
297
                logger.V(1).Info("Fail to get", "ConfigMap", constants.ConfigMapName)
1✔
298
        }
1✔
299
        for _, node := range nl.Items {
2✔
300
                logger.V(1).Info("Sync SriovNetworkNodeState CR", "name", node.Name)
1✔
301
                ns := &sriovnetworkv1.SriovNetworkNodeState{}
1✔
302
                ns.Name = node.Name
1✔
303
                ns.Namespace = vars.Namespace
1✔
304
                netPoolConfig, _, err := findNodePoolConfig(ctx, &node, r.Client)
1✔
305
                if err != nil {
1✔
306
                        logger.Error(err, "failed to get SriovNetworkPoolConfig for the current node")
×
307
                }
×
308
                if netPoolConfig != nil {
2✔
309
                        ns.Spec.System.RdmaMode = netPoolConfig.Spec.RdmaMode
1✔
310
                }
1✔
311
                j, _ := json.Marshal(ns)
1✔
312
                logger.V(2).Info("SriovNetworkNodeState CR", "content", j)
1✔
313
                if err := r.syncSriovNetworkNodeState(ctx, dc, npl, ns, &node); err != nil {
1✔
314
                        logger.Error(err, "Fail to sync", "SriovNetworkNodeState", ns.Name)
×
315
                        return err
×
316
                }
×
317
        }
318
        logger.V(1).Info("Remove SriovNetworkNodeState custom resource for unselected node")
1✔
319
        nsList := &sriovnetworkv1.SriovNetworkNodeStateList{}
1✔
320
        err := r.List(ctx, nsList, &client.ListOptions{})
1✔
321
        if err != nil {
1✔
322
                if !errors.IsNotFound(err) {
×
323
                        logger.Error(err, "Fail to list SriovNetworkNodeState CRs")
×
324
                        return err
×
325
                }
×
326
        } else {
1✔
327
                for _, ns := range nsList.Items {
2✔
328
                        found := false
1✔
329
                        for _, node := range nl.Items {
2✔
330
                                if ns.GetName() == node.GetName() {
2✔
331
                                        found = true
1✔
332
                                        break
1✔
333
                                }
334
                        }
335
                        if !found {
2✔
336
                                // remove device plugin labels
1✔
337
                                logger.Info("removing device plugin label from node as SriovNetworkNodeState doesn't exist", "nodeStateName", ns.Name)
1✔
338
                                err = utils.RemoveLabelFromNode(ctx, ns.Name, constants.SriovDevicePluginLabel, r.Client)
1✔
339
                                if err != nil {
1✔
340
                                        logger.Error(err, "Fail to remove device plugin label from node", "node", ns.Name)
×
341
                                        return err
×
342
                                }
×
343
                                if err := r.handleStaleNodeState(ctx, &ns); err != nil {
1✔
344
                                        return err
×
345
                                }
×
346
                        }
347
                }
348
        }
349
        return nil
1✔
350
}
351

352
// handleStaleNodeState handles stale SriovNetworkNodeState CR (the CR which no longer have a corresponding node with the daemon).
353
// If the CR has the "keep until time" annotation, indicating the earliest time the state object can be removed,
354
// this function will compare it to the current time to determine if deletion is permissible and do deletion if allowed.
355
// If the annotation is absent, the function will create one with a timestamp in future, using either the default or a configured offset.
356
// If STALE_NODE_STATE_CLEANUP_DELAY_MINUTES env variable is set to 0, removes the CR immediately
357
func (r *SriovNetworkNodePolicyReconciler) handleStaleNodeState(ctx context.Context, ns *sriovnetworkv1.SriovNetworkNodeState) error {
1✔
358
        logger := log.Log.WithName("handleStaleNodeState")
1✔
359

1✔
360
        var delayMinutes int
1✔
361
        var err error
1✔
362

1✔
363
        envValue, found := os.LookupEnv("STALE_NODE_STATE_CLEANUP_DELAY_MINUTES")
1✔
364
        if found {
2✔
365
                delayMinutes, err = strconv.Atoi(envValue)
1✔
366
                if err != nil || delayMinutes < 0 {
2✔
367
                        delayMinutes = constants.DefaultNodeStateCleanupDelayMinutes
1✔
368
                        logger.Error(err, "invalid value in STALE_NODE_STATE_CLEANUP_DELAY_MINUTES env variable, use default delay",
1✔
369
                                "delay", delayMinutes)
1✔
370
                }
1✔
UNCOV
371
        } else {
×
UNCOV
372
                delayMinutes = constants.DefaultNodeStateCleanupDelayMinutes
×
UNCOV
373
        }
×
374

375
        if delayMinutes != 0 {
2✔
376
                now := time.Now().UTC()
1✔
377
                keepUntilTime := ns.GetKeepUntilTime()
1✔
378
                if keepUntilTime.IsZero() {
2✔
379
                        keepUntilTime = now.Add(time.Minute * time.Duration(delayMinutes))
1✔
380
                        logger.V(2).Info("SriovNetworkNodeState has no matching node, configure cleanup delay for the state object",
1✔
381
                                "nodeStateName", ns.Name, "delay", delayMinutes, "keepUntilTime", keepUntilTime.String())
1✔
382
                        ns.SetKeepUntilTime(keepUntilTime)
1✔
383
                        if err := r.Update(ctx, ns); err != nil {
1✔
384
                                logger.Error(err, "Fail to update SriovNetworkNodeState CR", "name", ns.GetName())
×
385
                                return err
×
386
                        }
×
387
                        return nil
1✔
388
                }
389
                if now.Before(keepUntilTime) {
2✔
390
                        return nil
1✔
391
                }
1✔
392
        }
393
        // remove the object if delayMinutes is 0 or if keepUntilTime is already passed
394
        logger.Info("Deleting SriovNetworkNodeState as node with that name doesn't exist", "nodeStateName", ns.Name)
1✔
395
        if err := r.Delete(ctx, ns, &client.DeleteOptions{}); err != nil {
1✔
396
                logger.Error(err, "Fail to delete SriovNetworkNodeState CR", "name", ns.GetName())
×
397
                return err
×
398
        }
×
399
        return nil
1✔
400
}
401

402
func (r *SriovNetworkNodePolicyReconciler) syncSriovNetworkNodeState(ctx context.Context,
403
        dc *sriovnetworkv1.SriovOperatorConfig,
404
        npl *sriovnetworkv1.SriovNetworkNodePolicyList,
405
        ns *sriovnetworkv1.SriovNetworkNodeState,
406
        node *corev1.Node) error {
1✔
407
        logger := log.Log.WithName("syncSriovNetworkNodeState")
1✔
408
        logger.V(1).Info("Start to sync SriovNetworkNodeState", "Name", ns.Name)
1✔
409

1✔
410
        if err := controllerutil.SetControllerReference(dc, ns, r.Scheme); err != nil {
1✔
411
                return err
×
412
        }
×
413
        found := &sriovnetworkv1.SriovNetworkNodeState{}
1✔
414
        err := r.Get(ctx, types.NamespacedName{Namespace: ns.Namespace, Name: ns.Name}, found)
1✔
415
        if err != nil {
2✔
416
                logger.Error(err, "Fail to get SriovNetworkNodeState", "namespace", ns.Namespace, "name", ns.Name)
1✔
417
                if errors.IsNotFound(err) {
2✔
418
                        err = r.Create(ctx, ns)
1✔
419
                        if err != nil {
1✔
420
                                return fmt.Errorf("couldn't create SriovNetworkNodeState: %v", err)
×
421
                        }
×
422
                        logger.Info("Created SriovNetworkNodeState for", ns.Namespace, ns.Name)
1✔
423
                } else {
×
424
                        return fmt.Errorf("failed to get SriovNetworkNodeState: %v", err)
×
425
                }
×
426
        } else {
1✔
427
                keepUntilAnnotationUpdated := found.ResetKeepUntilTime()
1✔
428

1✔
429
                if len(found.Status.Interfaces) == 0 {
1✔
430
                        logger.Info("SriovNetworkNodeState Status Interfaces are empty. Skip update of policies in spec",
×
431
                                "namespace", ns.Namespace, "name", ns.Name)
×
432
                        if keepUntilAnnotationUpdated {
×
433
                                if err := r.Update(ctx, found); err != nil {
×
434
                                        return fmt.Errorf("couldn't update SriovNetworkNodeState: %v", err)
×
435
                                }
×
436
                        }
437
                        return nil
×
438
                }
439

440
                logger.V(1).Info("SriovNetworkNodeState already exists, updating")
1✔
441
                newVersion := found.DeepCopy()
1✔
442
                newVersion.Spec = ns.Spec
1✔
443
                newVersion.OwnerReferences = ns.OwnerReferences
1✔
444

1✔
445
                // Previous Policy Priority(ppp) records the priority of previous evaluated policy in node policy list.
1✔
446
                // Since node policy list is already sorted with priority number, comparing current priority with ppp shall
1✔
447
                // be sufficient.
1✔
448
                // ppp is set to 100 as initial value to avoid matching with the first policy in policy list, although
1✔
449
                // it should not matter since the flag used in p.Apply() will only be applied when VF partition is detected.
1✔
450
                ppp := 100
1✔
451
                for _, p := range npl.Items {
2✔
452
                        // Note(adrianc): default policy is deprecated and ignored.
1✔
453
                        if p.Name == constants.DefaultPolicyName {
1✔
454
                                continue
×
455
                        }
456
                        if p.Selected(node) {
2✔
457
                                logger.Info("apply", "policy", p.Name, "node", node.Name)
1✔
458
                                // Merging only for policies with the same priority (ppp == p.Spec.Priority)
1✔
459
                                // This boolean flag controls merging of PF configuration (e.g. mtu, numvfs etc)
1✔
460
                                // when VF partition is configured.
1✔
461
                                err = p.Apply(newVersion, ppp == p.Spec.Priority)
1✔
462
                                if err != nil {
1✔
463
                                        return err
×
464
                                }
×
465
                                if r.FeatureGate.IsEnabled(constants.ManageSoftwareBridgesFeatureGate) {
1✔
466
                                        err = p.ApplyBridgeConfig(newVersion)
×
467
                                        if err != nil {
×
468
                                                return err
×
469
                                        }
×
470
                                }
471
                                // record the evaluated policy priority for next loop
472
                                ppp = p.Spec.Priority
1✔
473
                        }
474
                }
475

476
                // Note(adrianc): we check same ownerReferences since SriovNetworkNodeState
477
                // was owned by a default SriovNetworkNodePolicy. if we encounter a descripancy
478
                // we need to update.
479
                if !keepUntilAnnotationUpdated && reflect.DeepEqual(newVersion.OwnerReferences, found.OwnerReferences) &&
1✔
480
                        equality.Semantic.DeepEqual(newVersion.Spec, found.Spec) {
1✔
481
                        logger.V(1).Info("SriovNetworkNodeState did not change, not updating")
×
482
                        return nil
×
483
                }
×
484
                err = r.Update(ctx, newVersion)
1✔
485
                if err != nil {
1✔
486
                        return fmt.Errorf("couldn't update SriovNetworkNodeState: %v", err)
×
487
                }
×
488
        }
489
        return nil
1✔
490
}
491

492
func (r *SriovNetworkNodePolicyReconciler) renderDevicePluginConfigData(ctx context.Context, pl *sriovnetworkv1.SriovNetworkNodePolicyList, node *corev1.Node) (dptypes.ResourceConfList, error) {
1✔
493
        logger := log.Log.WithName("renderDevicePluginConfigData")
1✔
494
        logger.V(1).Info("Start to render device plugin config data", "node", node.Name)
1✔
495
        rcl := dptypes.ResourceConfList{}
1✔
496
        for _, p := range pl.Items {
2✔
497
                // Note(adrianc): default policy is deprecated and ignored.
1✔
498
                if p.Name == constants.DefaultPolicyName {
1✔
499
                        continue
×
500
                }
501

502
                // render node specific data for device plugin config
503
                if !p.Selected(node) {
1✔
504
                        continue
×
505
                }
506

507
                nodeState := &sriovnetworkv1.SriovNetworkNodeState{}
1✔
508
                err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: node.Name}, nodeState)
1✔
509
                if err != nil {
1✔
510
                        return rcl, err
×
511
                }
×
512

513
                found, i := resourceNameInList(p.Spec.ResourceName, &rcl)
1✔
514

1✔
515
                if found {
1✔
516
                        err := updateDevicePluginResource(&rcl.ResourceList[i], &p, nodeState)
×
517
                        if err != nil {
×
518
                                return rcl, err
×
519
                        }
×
520
                        logger.V(1).Info("Update resource", "Resource", rcl.ResourceList[i])
×
521
                } else {
1✔
522
                        rc, err := createDevicePluginResource(&p, nodeState)
1✔
523
                        if err != nil {
1✔
524
                                return rcl, err
×
525
                        }
×
526
                        rcl.ResourceList = append(rcl.ResourceList, *rc)
1✔
527
                        logger.V(1).Info("Add resource", "Resource", *rc)
1✔
528
                }
529
        }
530
        return rcl, nil
1✔
531
}
532

533
func resourceNameInList(name string, rcl *dptypes.ResourceConfList) (bool, int) {
1✔
534
        for i, rc := range rcl.ResourceList {
1✔
535
                if rc.ResourceName == name {
×
536
                        return true, i
×
537
                }
×
538
        }
539
        return false, 0
1✔
540
}
541

542
func createDevicePluginResource(
543
        p *sriovnetworkv1.SriovNetworkNodePolicy,
544
        nodeState *sriovnetworkv1.SriovNetworkNodeState) (*dptypes.ResourceConfig, error) {
1✔
545
        netDeviceSelectors := dptypes.NetDeviceSelectors{}
1✔
546

1✔
547
        rc := &dptypes.ResourceConfig{
1✔
548
                ResourceName: p.Spec.ResourceName,
1✔
549
        }
1✔
550
        netDeviceSelectors.IsRdma = p.Spec.IsRdma
1✔
551
        netDeviceSelectors.NeedVhostNet = p.Spec.NeedVhostNet
1✔
552
        netDeviceSelectors.VdpaType = dptypes.VdpaType(p.Spec.VdpaType)
1✔
553

1✔
554
        if p.Spec.NicSelector.Vendor != "" {
2✔
555
                netDeviceSelectors.Vendors = append(netDeviceSelectors.Vendors, p.Spec.NicSelector.Vendor)
1✔
556
        }
1✔
557
        if p.Spec.NicSelector.DeviceID != "" {
1✔
558
                var deviceID string
×
559
                if p.Spec.NumVfs == 0 {
×
560
                        deviceID = p.Spec.NicSelector.DeviceID
×
561
                } else {
×
562
                        deviceID = sriovnetworkv1.GetVfDeviceID(p.Spec.NicSelector.DeviceID)
×
563
                }
×
564

565
                if !sriovnetworkv1.StringInArray(deviceID, netDeviceSelectors.Devices) && deviceID != "" {
×
566
                        netDeviceSelectors.Devices = append(netDeviceSelectors.Devices, deviceID)
×
567
                }
×
568
        }
569
        if len(p.Spec.NicSelector.PfNames) > 0 {
1✔
570
                netDeviceSelectors.PfNames = append(netDeviceSelectors.PfNames, p.Spec.NicSelector.PfNames...)
×
571
        }
×
572
        // vfio-pci device link type is not detectable
573
        if p.Spec.DeviceType != constants.DeviceTypeVfioPci {
2✔
574
                if p.Spec.LinkType != "" {
1✔
575
                        linkType := constants.LinkTypeEthernet
×
576
                        if strings.EqualFold(p.Spec.LinkType, constants.LinkTypeIB) {
×
577
                                linkType = constants.LinkTypeInfiniband
×
578
                        }
×
579
                        netDeviceSelectors.LinkTypes = sriovnetworkv1.UniqueAppend(netDeviceSelectors.LinkTypes, linkType)
×
580
                }
581
        }
582
        if len(p.Spec.NicSelector.RootDevices) > 0 {
1✔
583
                netDeviceSelectors.RootDevices = append(netDeviceSelectors.RootDevices, p.Spec.NicSelector.RootDevices...)
×
584
        }
×
585
        // Removed driver constraint for "netdevice" DeviceType
586
        if p.Spec.DeviceType == constants.DeviceTypeVfioPci {
1✔
587
                netDeviceSelectors.Drivers = append(netDeviceSelectors.Drivers, p.Spec.DeviceType)
×
588
        }
×
589
        // Enable the selection of devices using NetFilter
590
        if p.Spec.NicSelector.NetFilter != "" {
1✔
591
                // Loop through interfaces status to find a match for NetworkID or NetworkTag
×
592
                if len(nodeState.Status.Interfaces) == 0 {
×
593
                        return nil, fmt.Errorf("node state %s doesn't contain interfaces data", nodeState.Name)
×
594
                }
×
595
                for _, intf := range nodeState.Status.Interfaces {
×
596
                        if sriovnetworkv1.NetFilterMatch(p.Spec.NicSelector.NetFilter, intf.NetFilter) {
×
597
                                // Found a match add the Interfaces PciAddress
×
598
                                netDeviceSelectors.PciAddresses = sriovnetworkv1.UniqueAppend(netDeviceSelectors.PciAddresses, intf.PciAddress)
×
599
                        }
×
600
                }
601
        }
602

603
        netDeviceSelectorsMarshal, err := json.Marshal(netDeviceSelectors)
1✔
604
        if err != nil {
1✔
605
                return nil, err
×
606
        }
×
607
        rawNetDeviceSelectors := json.RawMessage(netDeviceSelectorsMarshal)
1✔
608
        rc.Selectors = &rawNetDeviceSelectors
1✔
609

1✔
610
        rc.ExcludeTopology = p.Spec.ExcludeTopology
1✔
611

1✔
612
        return rc, nil
1✔
613
}
614

615
func updateDevicePluginResource(
616
        rc *dptypes.ResourceConfig,
617
        p *sriovnetworkv1.SriovNetworkNodePolicy,
618
        nodeState *sriovnetworkv1.SriovNetworkNodeState) error {
×
619
        netDeviceSelectors := dptypes.NetDeviceSelectors{}
×
620

×
621
        if err := json.Unmarshal(*rc.Selectors, &netDeviceSelectors); err != nil {
×
622
                return err
×
623
        }
×
624

625
        if p.Spec.NicSelector.Vendor != "" && !sriovnetworkv1.StringInArray(p.Spec.NicSelector.Vendor, netDeviceSelectors.Vendors) {
×
626
                netDeviceSelectors.Vendors = append(netDeviceSelectors.Vendors, p.Spec.NicSelector.Vendor)
×
627
        }
×
628
        if p.Spec.NicSelector.DeviceID != "" {
×
629
                var deviceID string
×
630
                if p.Spec.NumVfs == 0 {
×
631
                        deviceID = p.Spec.NicSelector.DeviceID
×
632
                } else {
×
633
                        deviceID = sriovnetworkv1.GetVfDeviceID(p.Spec.NicSelector.DeviceID)
×
634
                }
×
635

636
                if !sriovnetworkv1.StringInArray(deviceID, netDeviceSelectors.Devices) && deviceID != "" {
×
637
                        netDeviceSelectors.Devices = append(netDeviceSelectors.Devices, deviceID)
×
638
                }
×
639
        }
640
        if len(p.Spec.NicSelector.PfNames) > 0 {
×
641
                netDeviceSelectors.PfNames = sriovnetworkv1.UniqueAppend(netDeviceSelectors.PfNames, p.Spec.NicSelector.PfNames...)
×
642
        }
×
643
        // vfio-pci device link type is not detectable
644
        if p.Spec.DeviceType != constants.DeviceTypeVfioPci {
×
645
                if p.Spec.LinkType != "" {
×
646
                        linkType := constants.LinkTypeEthernet
×
647
                        if strings.EqualFold(p.Spec.LinkType, constants.LinkTypeIB) {
×
648
                                linkType = constants.LinkTypeInfiniband
×
649
                        }
×
650
                        if !sriovnetworkv1.StringInArray(linkType, netDeviceSelectors.LinkTypes) {
×
651
                                netDeviceSelectors.LinkTypes = sriovnetworkv1.UniqueAppend(netDeviceSelectors.LinkTypes, linkType)
×
652
                        }
×
653
                }
654
        }
655
        if len(p.Spec.NicSelector.RootDevices) > 0 {
×
656
                netDeviceSelectors.RootDevices = sriovnetworkv1.UniqueAppend(netDeviceSelectors.RootDevices, p.Spec.NicSelector.RootDevices...)
×
657
        }
×
658
        // Removed driver constraint for "netdevice" DeviceType
659
        if p.Spec.DeviceType == constants.DeviceTypeVfioPci {
×
660
                netDeviceSelectors.Drivers = sriovnetworkv1.UniqueAppend(netDeviceSelectors.Drivers, p.Spec.DeviceType)
×
661
        }
×
662
        // Enable the selection of devices using NetFilter
663
        if p.Spec.NicSelector.NetFilter != "" {
×
664
                // Loop through interfaces status to find a match for NetworkID or NetworkTag
×
665
                for _, intf := range nodeState.Status.Interfaces {
×
666
                        if sriovnetworkv1.NetFilterMatch(p.Spec.NicSelector.NetFilter, intf.NetFilter) {
×
667
                                // Found a match add the Interfaces PciAddress
×
668
                                netDeviceSelectors.PciAddresses = sriovnetworkv1.UniqueAppend(netDeviceSelectors.PciAddresses, intf.PciAddress)
×
669
                        }
×
670
                }
671
        }
672

673
        netDeviceSelectorsMarshal, err := json.Marshal(netDeviceSelectors)
×
674
        if err != nil {
×
675
                return err
×
676
        }
×
677
        rawNetDeviceSelectors := json.RawMessage(netDeviceSelectorsMarshal)
×
678
        rc.Selectors = &rawNetDeviceSelectors
×
679

×
680
        rc.ExcludeTopology = p.Spec.ExcludeTopology
×
681

×
682
        return nil
×
683
}
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