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  }