github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/controllers/sriovnetworknodepolicy_controller.go (about) 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 "reflect" 24 "sort" 25 "strings" 26 "time" 27 28 errs "github.com/pkg/errors" 29 appsv1 "k8s.io/api/apps/v1" 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 uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 35 "k8s.io/apimachinery/pkg/runtime" 36 "k8s.io/apimachinery/pkg/types" 37 "k8s.io/client-go/util/workqueue" 38 ctrl "sigs.k8s.io/controller-runtime" 39 "sigs.k8s.io/controller-runtime/pkg/client" 40 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 41 "sigs.k8s.io/controller-runtime/pkg/event" 42 "sigs.k8s.io/controller-runtime/pkg/handler" 43 "sigs.k8s.io/controller-runtime/pkg/log" 44 "sigs.k8s.io/controller-runtime/pkg/reconcile" 45 "sigs.k8s.io/controller-runtime/pkg/source" 46 47 dptypes "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 48 49 sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" 50 constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" 51 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate" 52 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render" 53 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" 54 ) 55 56 const nodePolicySyncEventName = "node-policy-sync-event" 57 58 // SriovNetworkNodePolicyReconciler reconciles a SriovNetworkNodePolicy object 59 type SriovNetworkNodePolicyReconciler struct { 60 client.Client 61 Scheme *runtime.Scheme 62 FeatureGate featuregate.FeatureGate 63 } 64 65 //+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworknodepolicies,verbs=get;list;watch;create;update;patch;delete 66 //+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworknodepolicies/status,verbs=get;update;patch 67 //+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworknodepolicies/finalizers,verbs=update 68 69 // Reconcile is part of the main kubernetes reconciliation loop which aims to 70 // move the current state of the cluster closer to the desired state. 71 // TODO(user): Modify the Reconcile function to compare the state specified by 72 // the SriovNetworkNodePolicy object against the actual cluster state, and then 73 // perform operations to make the cluster state reflect the state specified by 74 // the user. 75 // 76 // For more details, check Reconcile and its Result here: 77 // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile 78 func (r *SriovNetworkNodePolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 79 // Only handle node-policy-sync-event 80 if req.Name != nodePolicySyncEventName || req.Namespace != "" { 81 return reconcile.Result{}, nil 82 } 83 84 reqLogger := log.FromContext(ctx) 85 reqLogger.Info("Reconciling") 86 87 // Fetch the default SriovOperatorConfig 88 defaultOpConf := &sriovnetworkv1.SriovOperatorConfig{} 89 if err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: constants.DefaultConfigName}, defaultOpConf); err != nil { 90 if errors.IsNotFound(err) { 91 reqLogger.Info("default SriovOperatorConfig object not found, cannot reconcile SriovNetworkNodePolicies. Requeue.") 92 return reconcile.Result{RequeueAfter: 5 * time.Second}, nil 93 } 94 return reconcile.Result{}, err 95 } 96 97 // Fetch the SriovNetworkNodePolicyList 98 policyList := &sriovnetworkv1.SriovNetworkNodePolicyList{} 99 err := r.List(ctx, policyList, &client.ListOptions{}) 100 if err != nil { 101 if errors.IsNotFound(err) { 102 // Request object not found, could have been deleted after reconcile request. 103 // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. 104 // Return and don't requeue 105 return reconcile.Result{}, nil 106 } 107 // Error reading the object - requeue the request. 108 return reconcile.Result{}, err 109 } 110 // Fetch the Nodes 111 nodeList := &corev1.NodeList{} 112 lo := &client.MatchingLabels{ 113 "node-role.kubernetes.io/worker": "", 114 "kubernetes.io/os": "linux", 115 } 116 if len(defaultOpConf.Spec.ConfigDaemonNodeSelector) > 0 { 117 labels := client.MatchingLabels(defaultOpConf.Spec.ConfigDaemonNodeSelector) 118 lo = &labels 119 } 120 err = r.List(ctx, nodeList, lo) 121 if err != nil { 122 // Error reading the object - requeue the request. 123 reqLogger.Error(err, "Fail to list nodes") 124 return reconcile.Result{}, err 125 } 126 127 // Sort the policies with priority, higher priority ones is applied later 128 sort.Sort(sriovnetworkv1.ByPriority(policyList.Items)) 129 // Sync SriovNetworkNodeState objects 130 if err = r.syncAllSriovNetworkNodeStates(ctx, defaultOpConf, policyList, nodeList); err != nil { 131 return reconcile.Result{}, err 132 } 133 // Sync Sriov device plugin ConfigMap object 134 if err = r.syncDevicePluginConfigMap(ctx, defaultOpConf, policyList, nodeList); err != nil { 135 return reconcile.Result{}, err 136 } 137 // Render and sync Daemon objects 138 if err = syncPluginDaemonObjs(ctx, r.Client, r.Scheme, defaultOpConf, policyList); err != nil { 139 return reconcile.Result{}, err 140 } 141 142 // All was successful. Request that this be re-triggered after ResyncPeriod, 143 // so we can reconcile state again. 144 return reconcile.Result{RequeueAfter: constants.ResyncPeriod}, nil 145 } 146 147 // SetupWithManager sets up the controller with the Manager. 148 func (r *SriovNetworkNodePolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { 149 qHandler := func(q workqueue.RateLimitingInterface) { 150 q.AddAfter(reconcile.Request{NamespacedName: types.NamespacedName{ 151 Namespace: "", 152 Name: nodePolicySyncEventName, 153 }}, time.Second) 154 } 155 156 delayedEventHandler := handler.Funcs{ 157 CreateFunc: func(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) { 158 log.Log.WithName("SriovNetworkNodePolicy"). 159 Info("Enqueuing sync for create event", "resource", e.Object.GetName()) 160 qHandler(q) 161 }, 162 UpdateFunc: func(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) { 163 log.Log.WithName("SriovNetworkNodePolicy"). 164 Info("Enqueuing sync for update event", "resource", e.ObjectNew.GetName()) 165 qHandler(q) 166 }, 167 DeleteFunc: func(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) { 168 log.Log.WithName("SriovNetworkNodePolicy"). 169 Info("Enqueuing sync for delete event", "resource", e.Object.GetName()) 170 qHandler(q) 171 }, 172 GenericFunc: func(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) { 173 log.Log.WithName("SriovNetworkNodePolicy"). 174 Info("Enqueuing sync for generic event", "resource", e.Object.GetName()) 175 qHandler(q) 176 }, 177 } 178 179 // send initial sync event to trigger reconcile when controller is started 180 var eventChan = make(chan event.GenericEvent, 1) 181 eventChan <- event.GenericEvent{Object: &sriovnetworkv1.SriovNetworkNodePolicy{ 182 ObjectMeta: metav1.ObjectMeta{Name: nodePolicySyncEventName, Namespace: ""}}} 183 close(eventChan) 184 185 return ctrl.NewControllerManagedBy(mgr). 186 For(&sriovnetworkv1.SriovNetworkNodePolicy{}). 187 Watches(&sriovnetworkv1.SriovNetworkNodePolicy{}, delayedEventHandler). 188 WatchesRawSource(&source.Channel{Source: eventChan}, delayedEventHandler). 189 Complete(r) 190 } 191 192 func (r *SriovNetworkNodePolicyReconciler) syncDevicePluginConfigMap(ctx context.Context, dc *sriovnetworkv1.SriovOperatorConfig, 193 pl *sriovnetworkv1.SriovNetworkNodePolicyList, nl *corev1.NodeList) error { 194 logger := log.Log.WithName("syncDevicePluginConfigMap") 195 logger.V(1).Info("Start to sync device plugin ConfigMap") 196 197 configData := make(map[string]string) 198 for _, node := range nl.Items { 199 data, err := r.renderDevicePluginConfigData(ctx, pl, &node) 200 if err != nil { 201 return err 202 } 203 config, err := json.Marshal(data) 204 if err != nil { 205 return err 206 } 207 configData[node.Name] = string(config) 208 } 209 210 cm := &corev1.ConfigMap{ 211 TypeMeta: metav1.TypeMeta{ 212 APIVersion: "v1", 213 Kind: "ConfigMap", 214 }, 215 ObjectMeta: metav1.ObjectMeta{ 216 Name: constants.ConfigMapName, 217 Namespace: vars.Namespace, 218 }, 219 Data: configData, 220 } 221 222 if err := controllerutil.SetControllerReference(dc, cm, r.Scheme); err != nil { 223 return err 224 } 225 226 found := &corev1.ConfigMap{} 227 err := r.Get(ctx, types.NamespacedName{Namespace: cm.Namespace, Name: cm.Name}, found) 228 if err != nil { 229 if errors.IsNotFound(err) { 230 err = r.Create(ctx, cm) 231 if err != nil { 232 return fmt.Errorf("couldn't create ConfigMap: %v", err) 233 } 234 logger.V(1).Info("Created ConfigMap for", cm.Namespace, cm.Name) 235 } else { 236 return fmt.Errorf("failed to get ConfigMap: %v", err) 237 } 238 } else { 239 logger.V(1).Info("ConfigMap already exists, updating") 240 err = r.Update(ctx, cm) 241 if err != nil { 242 return fmt.Errorf("couldn't update ConfigMap: %v", err) 243 } 244 } 245 return nil 246 } 247 248 func (r *SriovNetworkNodePolicyReconciler) syncAllSriovNetworkNodeStates(ctx context.Context, dc *sriovnetworkv1.SriovOperatorConfig, npl *sriovnetworkv1.SriovNetworkNodePolicyList, nl *corev1.NodeList) error { 249 logger := log.Log.WithName("syncAllSriovNetworkNodeStates") 250 logger.V(1).Info("Start to sync all SriovNetworkNodeState custom resource") 251 found := &corev1.ConfigMap{} 252 if err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: constants.ConfigMapName}, found); err != nil { 253 logger.V(1).Info("Fail to get", "ConfigMap", constants.ConfigMapName) 254 } 255 for _, node := range nl.Items { 256 logger.V(1).Info("Sync SriovNetworkNodeState CR", "name", node.Name) 257 ns := &sriovnetworkv1.SriovNetworkNodeState{} 258 ns.Name = node.Name 259 ns.Namespace = vars.Namespace 260 j, _ := json.Marshal(ns) 261 logger.V(2).Info("SriovNetworkNodeState CR", "content", j) 262 if err := r.syncSriovNetworkNodeState(ctx, dc, npl, ns, &node); err != nil { 263 logger.Error(err, "Fail to sync", "SriovNetworkNodeState", ns.Name) 264 return err 265 } 266 } 267 logger.V(1).Info("Remove SriovNetworkNodeState custom resource for unselected node") 268 nsList := &sriovnetworkv1.SriovNetworkNodeStateList{} 269 err := r.List(ctx, nsList, &client.ListOptions{}) 270 if err != nil { 271 if !errors.IsNotFound(err) { 272 logger.Error(err, "Fail to list SriovNetworkNodeState CRs") 273 return err 274 } 275 } else { 276 for _, ns := range nsList.Items { 277 found := false 278 for _, node := range nl.Items { 279 if ns.GetName() == node.GetName() { 280 found = true 281 break 282 } 283 } 284 if !found { 285 err := r.Delete(ctx, &ns, &client.DeleteOptions{}) 286 if err != nil { 287 logger.Error(err, "Fail to Delete", "SriovNetworkNodeState CR:", ns.GetName()) 288 return err 289 } 290 } 291 } 292 } 293 return nil 294 } 295 296 func (r *SriovNetworkNodePolicyReconciler) syncSriovNetworkNodeState(ctx context.Context, 297 dc *sriovnetworkv1.SriovOperatorConfig, 298 npl *sriovnetworkv1.SriovNetworkNodePolicyList, 299 ns *sriovnetworkv1.SriovNetworkNodeState, 300 node *corev1.Node) error { 301 logger := log.Log.WithName("syncSriovNetworkNodeState") 302 logger.V(1).Info("Start to sync SriovNetworkNodeState", "Name", ns.Name) 303 304 if err := controllerutil.SetControllerReference(dc, ns, r.Scheme); err != nil { 305 return err 306 } 307 found := &sriovnetworkv1.SriovNetworkNodeState{} 308 err := r.Get(ctx, types.NamespacedName{Namespace: ns.Namespace, Name: ns.Name}, found) 309 if err != nil { 310 logger.Error(err, "Fail to get SriovNetworkNodeState", "namespace", ns.Namespace, "name", ns.Name) 311 if errors.IsNotFound(err) { 312 err = r.Create(ctx, ns) 313 if err != nil { 314 return fmt.Errorf("couldn't create SriovNetworkNodeState: %v", err) 315 } 316 logger.Info("Created SriovNetworkNodeState for", ns.Namespace, ns.Name) 317 } else { 318 return fmt.Errorf("failed to get SriovNetworkNodeState: %v", err) 319 } 320 } else { 321 if len(found.Status.Interfaces) == 0 { 322 logger.Info("SriovNetworkNodeState Status Interfaces are empty. Skip update of policies in spec", 323 "namespace", ns.Namespace, "name", ns.Name) 324 return nil 325 } 326 327 logger.V(1).Info("SriovNetworkNodeState already exists, updating") 328 newVersion := found.DeepCopy() 329 newVersion.Spec = ns.Spec 330 newVersion.OwnerReferences = ns.OwnerReferences 331 332 // Previous Policy Priority(ppp) records the priority of previous evaluated policy in node policy list. 333 // Since node policy list is already sorted with priority number, comparing current priority with ppp shall 334 // be sufficient. 335 // ppp is set to 100 as initial value to avoid matching with the first policy in policy list, although 336 // it should not matter since the flag used in p.Apply() will only be applied when VF partition is detected. 337 ppp := 100 338 for _, p := range npl.Items { 339 // Note(adrianc): default policy is deprecated and ignored. 340 if p.Name == constants.DefaultPolicyName { 341 continue 342 } 343 if p.Selected(node) { 344 logger.Info("apply", "policy", p.Name, "node", node.Name) 345 // Merging only for policies with the same priority (ppp == p.Spec.Priority) 346 // This boolean flag controls merging of PF configuration (e.g. mtu, numvfs etc) 347 // when VF partition is configured. 348 err = p.Apply(newVersion, ppp == p.Spec.Priority) 349 if err != nil { 350 return err 351 } 352 // record the evaluated policy priority for next loop 353 ppp = p.Spec.Priority 354 } 355 } 356 357 // Note(adrianc): we check same ownerReferences since SriovNetworkNodeState 358 // was owned by a default SriovNetworkNodePolicy. if we encounter a descripancy 359 // we need to update. 360 if reflect.DeepEqual(newVersion.OwnerReferences, found.OwnerReferences) && 361 equality.Semantic.DeepEqual(newVersion.Spec, found.Spec) { 362 logger.V(1).Info("SriovNetworkNodeState did not change, not updating") 363 return nil 364 } 365 err = r.Update(ctx, newVersion) 366 if err != nil { 367 return fmt.Errorf("couldn't update SriovNetworkNodeState: %v", err) 368 } 369 } 370 return nil 371 } 372 373 func setDsNodeAffinity(pl *sriovnetworkv1.SriovNetworkNodePolicyList, ds *appsv1.DaemonSet) error { 374 terms := nodeSelectorTermsForPolicyList(pl.Items) 375 if len(terms) > 0 { 376 ds.Spec.Template.Spec.Affinity = &corev1.Affinity{ 377 NodeAffinity: &corev1.NodeAffinity{ 378 RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ 379 NodeSelectorTerms: terms, 380 }, 381 }, 382 } 383 } 384 return nil 385 } 386 387 func nodeSelectorTermsForPolicyList(policies []sriovnetworkv1.SriovNetworkNodePolicy) []corev1.NodeSelectorTerm { 388 terms := []corev1.NodeSelectorTerm{} 389 for _, p := range policies { 390 // Note(adrianc): default policy is deprecated and ignored. 391 if p.Name == constants.DefaultPolicyName { 392 continue 393 } 394 395 if len(p.Spec.NodeSelector) == 0 { 396 continue 397 } 398 expressions := []corev1.NodeSelectorRequirement{} 399 for k, v := range p.Spec.NodeSelector { 400 exp := corev1.NodeSelectorRequirement{ 401 Operator: corev1.NodeSelectorOpIn, 402 Key: k, 403 Values: []string{v}, 404 } 405 expressions = append(expressions, exp) 406 } 407 // sorting is needed to keep the daemon spec stable. 408 // the items are popped in a random order from the map 409 sort.Slice(expressions, func(i, j int) bool { 410 return expressions[i].Key < expressions[j].Key 411 }) 412 nodeSelector := corev1.NodeSelectorTerm{ 413 MatchExpressions: expressions, 414 } 415 terms = append(terms, nodeSelector) 416 } 417 418 return terms 419 } 420 421 // renderDsForCR returns a busybox pod with the same name/namespace as the cr 422 func renderDsForCR(path string, data *render.RenderData) ([]*uns.Unstructured, error) { 423 logger := log.Log.WithName("renderDsForCR") 424 logger.V(1).Info("Start to render objects") 425 426 objs, err := render.RenderDir(path, data) 427 if err != nil { 428 return nil, errs.Wrap(err, "failed to render SR-IOV Network Operator manifests") 429 } 430 return objs, nil 431 } 432 433 func (r *SriovNetworkNodePolicyReconciler) renderDevicePluginConfigData(ctx context.Context, pl *sriovnetworkv1.SriovNetworkNodePolicyList, node *corev1.Node) (dptypes.ResourceConfList, error) { 434 logger := log.Log.WithName("renderDevicePluginConfigData") 435 logger.V(1).Info("Start to render device plugin config data", "node", node.Name) 436 rcl := dptypes.ResourceConfList{} 437 for _, p := range pl.Items { 438 // Note(adrianc): default policy is deprecated and ignored. 439 if p.Name == constants.DefaultPolicyName { 440 continue 441 } 442 443 // render node specific data for device plugin config 444 if !p.Selected(node) { 445 continue 446 } 447 448 nodeState := &sriovnetworkv1.SriovNetworkNodeState{} 449 err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: node.Name}, nodeState) 450 if err != nil { 451 return rcl, err 452 } 453 454 found, i := resourceNameInList(p.Spec.ResourceName, &rcl) 455 456 if found { 457 err := updateDevicePluginResource(ctx, &rcl.ResourceList[i], &p, nodeState) 458 if err != nil { 459 return rcl, err 460 } 461 logger.V(1).Info("Update resource", "Resource", rcl.ResourceList[i]) 462 } else { 463 rc, err := createDevicePluginResource(ctx, &p, nodeState) 464 if err != nil { 465 return rcl, err 466 } 467 rcl.ResourceList = append(rcl.ResourceList, *rc) 468 logger.V(1).Info("Add resource", "Resource", *rc) 469 } 470 } 471 return rcl, nil 472 } 473 474 func resourceNameInList(name string, rcl *dptypes.ResourceConfList) (bool, int) { 475 for i, rc := range rcl.ResourceList { 476 if rc.ResourceName == name { 477 return true, i 478 } 479 } 480 return false, 0 481 } 482 483 func createDevicePluginResource( 484 ctx context.Context, 485 p *sriovnetworkv1.SriovNetworkNodePolicy, 486 nodeState *sriovnetworkv1.SriovNetworkNodeState) (*dptypes.ResourceConfig, error) { 487 netDeviceSelectors := dptypes.NetDeviceSelectors{} 488 489 rc := &dptypes.ResourceConfig{ 490 ResourceName: p.Spec.ResourceName, 491 } 492 netDeviceSelectors.IsRdma = p.Spec.IsRdma 493 netDeviceSelectors.NeedVhostNet = p.Spec.NeedVhostNet 494 netDeviceSelectors.VdpaType = dptypes.VdpaType(p.Spec.VdpaType) 495 496 if p.Spec.NicSelector.Vendor != "" { 497 netDeviceSelectors.Vendors = append(netDeviceSelectors.Vendors, p.Spec.NicSelector.Vendor) 498 } 499 if p.Spec.NicSelector.DeviceID != "" { 500 var deviceID string 501 if p.Spec.NumVfs == 0 { 502 deviceID = p.Spec.NicSelector.DeviceID 503 } else { 504 deviceID = sriovnetworkv1.GetVfDeviceID(p.Spec.NicSelector.DeviceID) 505 } 506 507 if !sriovnetworkv1.StringInArray(deviceID, netDeviceSelectors.Devices) && deviceID != "" { 508 netDeviceSelectors.Devices = append(netDeviceSelectors.Devices, deviceID) 509 } 510 } 511 if len(p.Spec.NicSelector.PfNames) > 0 { 512 netDeviceSelectors.PfNames = append(netDeviceSelectors.PfNames, p.Spec.NicSelector.PfNames...) 513 } 514 // vfio-pci device link type is not detectable 515 if p.Spec.DeviceType != constants.DeviceTypeVfioPci { 516 if p.Spec.LinkType != "" { 517 linkType := constants.LinkTypeEthernet 518 if strings.EqualFold(p.Spec.LinkType, constants.LinkTypeIB) { 519 linkType = constants.LinkTypeInfiniband 520 } 521 netDeviceSelectors.LinkTypes = sriovnetworkv1.UniqueAppend(netDeviceSelectors.LinkTypes, linkType) 522 } 523 } 524 if len(p.Spec.NicSelector.RootDevices) > 0 { 525 netDeviceSelectors.RootDevices = append(netDeviceSelectors.RootDevices, p.Spec.NicSelector.RootDevices...) 526 } 527 // Removed driver constraint for "netdevice" DeviceType 528 if p.Spec.DeviceType == constants.DeviceTypeVfioPci { 529 netDeviceSelectors.Drivers = append(netDeviceSelectors.Drivers, p.Spec.DeviceType) 530 } 531 // Enable the selection of devices using NetFilter 532 if p.Spec.NicSelector.NetFilter != "" { 533 // Loop through interfaces status to find a match for NetworkID or NetworkTag 534 if len(nodeState.Status.Interfaces) == 0 { 535 return nil, fmt.Errorf("node state %s doesn't contain interfaces data", nodeState.Name) 536 } 537 for _, intf := range nodeState.Status.Interfaces { 538 if sriovnetworkv1.NetFilterMatch(p.Spec.NicSelector.NetFilter, intf.NetFilter) { 539 // Found a match add the Interfaces PciAddress 540 netDeviceSelectors.PciAddresses = sriovnetworkv1.UniqueAppend(netDeviceSelectors.PciAddresses, intf.PciAddress) 541 } 542 } 543 } 544 545 netDeviceSelectorsMarshal, err := json.Marshal(netDeviceSelectors) 546 if err != nil { 547 return nil, err 548 } 549 rawNetDeviceSelectors := json.RawMessage(netDeviceSelectorsMarshal) 550 rc.Selectors = &rawNetDeviceSelectors 551 552 rc.ExcludeTopology = p.Spec.ExcludeTopology 553 554 return rc, nil 555 } 556 557 func updateDevicePluginResource( 558 ctx context.Context, 559 rc *dptypes.ResourceConfig, 560 p *sriovnetworkv1.SriovNetworkNodePolicy, 561 nodeState *sriovnetworkv1.SriovNetworkNodeState) error { 562 netDeviceSelectors := dptypes.NetDeviceSelectors{} 563 564 if err := json.Unmarshal(*rc.Selectors, &netDeviceSelectors); err != nil { 565 return err 566 } 567 568 if p.Spec.NicSelector.Vendor != "" && !sriovnetworkv1.StringInArray(p.Spec.NicSelector.Vendor, netDeviceSelectors.Vendors) { 569 netDeviceSelectors.Vendors = append(netDeviceSelectors.Vendors, p.Spec.NicSelector.Vendor) 570 } 571 if p.Spec.NicSelector.DeviceID != "" { 572 var deviceID string 573 if p.Spec.NumVfs == 0 { 574 deviceID = p.Spec.NicSelector.DeviceID 575 } else { 576 deviceID = sriovnetworkv1.GetVfDeviceID(p.Spec.NicSelector.DeviceID) 577 } 578 579 if !sriovnetworkv1.StringInArray(deviceID, netDeviceSelectors.Devices) && deviceID != "" { 580 netDeviceSelectors.Devices = append(netDeviceSelectors.Devices, deviceID) 581 } 582 } 583 if len(p.Spec.NicSelector.PfNames) > 0 { 584 netDeviceSelectors.PfNames = sriovnetworkv1.UniqueAppend(netDeviceSelectors.PfNames, p.Spec.NicSelector.PfNames...) 585 } 586 // vfio-pci device link type is not detectable 587 if p.Spec.DeviceType != constants.DeviceTypeVfioPci { 588 if p.Spec.LinkType != "" { 589 linkType := constants.LinkTypeEthernet 590 if strings.EqualFold(p.Spec.LinkType, constants.LinkTypeIB) { 591 linkType = constants.LinkTypeInfiniband 592 } 593 if !sriovnetworkv1.StringInArray(linkType, netDeviceSelectors.LinkTypes) { 594 netDeviceSelectors.LinkTypes = sriovnetworkv1.UniqueAppend(netDeviceSelectors.LinkTypes, linkType) 595 } 596 } 597 } 598 if len(p.Spec.NicSelector.RootDevices) > 0 { 599 netDeviceSelectors.RootDevices = sriovnetworkv1.UniqueAppend(netDeviceSelectors.RootDevices, p.Spec.NicSelector.RootDevices...) 600 } 601 // Removed driver constraint for "netdevice" DeviceType 602 if p.Spec.DeviceType == constants.DeviceTypeVfioPci { 603 netDeviceSelectors.Drivers = sriovnetworkv1.UniqueAppend(netDeviceSelectors.Drivers, p.Spec.DeviceType) 604 } 605 // Enable the selection of devices using NetFilter 606 if p.Spec.NicSelector.NetFilter != "" { 607 // Loop through interfaces status to find a match for NetworkID or NetworkTag 608 for _, intf := range nodeState.Status.Interfaces { 609 if sriovnetworkv1.NetFilterMatch(p.Spec.NicSelector.NetFilter, intf.NetFilter) { 610 // Found a match add the Interfaces PciAddress 611 netDeviceSelectors.PciAddresses = sriovnetworkv1.UniqueAppend(netDeviceSelectors.PciAddresses, intf.PciAddress) 612 } 613 } 614 } 615 616 netDeviceSelectorsMarshal, err := json.Marshal(netDeviceSelectors) 617 if err != nil { 618 return err 619 } 620 rawNetDeviceSelectors := json.RawMessage(netDeviceSelectorsMarshal) 621 rc.Selectors = &rawNetDeviceSelectors 622 623 rc.ExcludeTopology = p.Spec.ExcludeTopology 624 625 return nil 626 }