Skip to content

Commit

Permalink
OpenShift: enable OVS HW offload for multiple MCPs
Browse files Browse the repository at this point in the history
One node can only be added in one custom MCP pool by
design of MCO. Since current ovs-hw-offload MCs are
applied to hardcoded MCP ovs-hw-offload-worker, other
existing custome MCPs cannot be enabled with ovs hw
offload configuration.

This commits allows ovs-hw-offload MCs be applied
to multiple MCPs as specified in default SriovNetworkNodeConfig.
  • Loading branch information
zshi-redhat committed Jul 2, 2021
1 parent f08d16f commit bb82c48
Show file tree
Hide file tree
Showing 18 changed files with 357 additions and 466 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ _build-%:
_plugin-%: vet
@hack/build-plugins.sh $*

plugins: _plugin-intel _plugin-mellanox _plugin-generic _plugin-virtual _plugin-mco _plugin-k8s
plugins: _plugin-intel _plugin-mellanox _plugin-generic _plugin-virtual _plugin-k8s

clean:
@rm -rf $(TARGET_DIR)
Expand Down
9 changes: 5 additions & 4 deletions api/v1/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import (
)

const (
LASTNETWORKNAMESPACE = "operator.sriovnetwork.openshift.io/last-network-namespace"
FINALIZERNAME = "netattdef.finalizers.sriovnetwork.openshift.io"
ESWITCHMODE_LEGACY = "legacy"
ESWITCHMODE_SWITCHDEV = "switchdev"
LASTNETWORKNAMESPACE = "operator.sriovnetwork.openshift.io/last-network-namespace"
NETATTDEFFINALIZERNAME = "netattdef.finalizers.sriovnetwork.openshift.io"
POOLCONFIGFINALIZERNAME = "poolconfig.finalizers.sriovnetwork.openshift.io"
ESWITCHMODE_LEGACY = "legacy"
ESWITCHMODE_SWITCHDEV = "switchdev"
)

const invalidVfIndex = -1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ dropins:
- name: 10-hw-offload.conf
contents: |
[Service]
ExecStartPre=/bin/ovs-vsctl set Open_vSwitch . other_config:hw-offload=true
ExecStartPre=/bin/ovs-vsctl set Open_vSwitch . other_config:hw-offload=false
30 changes: 15 additions & 15 deletions controllers/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ import (
)

const (
ResyncPeriod = 5 * time.Minute
DEFAULT_CONFIG_NAME = "default"
CONFIG_DAEMON_PATH = "./bindata/manifests/daemon"
INJECTOR_WEBHOOK_PATH = "./bindata/manifests/webhook"
OPERATOR_WEBHOOK_PATH = "./bindata/manifests/operator-webhook"
SERVICE_CA_CONFIGMAP_ANNOTATION = "service.beta.openshift.io/inject-cabundle"
INJECTOR_WEBHOOK_NAME = "network-resources-injector-config"
OPERATOR_WEBHOOK_NAME = "sriov-operator-webhook-config"
DEPRECATED_OPERATOR_WEBHOOK_NAME = "operator-webhook-config"
PLUGIN_PATH = "./bindata/manifests/plugins"
DAEMON_PATH = "./bindata/manifests/daemon"
DEFAULT_POLICY_NAME = "default"
CONFIGMAP_NAME = "device-plugin-config"
DP_CONFIG_FILENAME = "config.json"
HwOffloadNodeLabel = "ovs-hw-offload-worker"
ResyncPeriod = 5 * time.Minute
DEFAULT_CONFIG_NAME = "default"
CONFIG_DAEMON_PATH = "./bindata/manifests/daemon"
INJECTOR_WEBHOOK_PATH = "./bindata/manifests/webhook"
OPERATOR_WEBHOOK_PATH = "./bindata/manifests/operator-webhook"
SERVICE_CA_CONFIGMAP_ANNOTATION = "service.beta.openshift.io/inject-cabundle"
INJECTOR_WEBHOOK_NAME = "network-resources-injector-config"
OPERATOR_WEBHOOK_NAME = "sriov-operator-webhook-config"
DEPRECATED_OPERATOR_WEBHOOK_NAME = "operator-webhook-config"
PLUGIN_PATH = "./bindata/manifests/plugins"
DAEMON_PATH = "./bindata/manifests/daemon"
DEFAULT_POLICY_NAME = "default"
CONFIGMAP_NAME = "device-plugin-config"
DP_CONFIG_FILENAME = "config.json"
OVS_HWOL_MACHINE_CONFIG_NAME_SUFFIX = "ovs-hw-offload"

linkTypeEthernet = "ether"
linkTypeInfiniband = "infiniband"
Expand Down
8 changes: 4 additions & 4 deletions controllers/sriovibnetwork_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ func (r *SriovIBNetworkReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !sriovnetworkv1.StringInArray(sriovnetworkv1.FINALIZERNAME, instance.ObjectMeta.Finalizers) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, sriovnetworkv1.FINALIZERNAME)
if !sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instance.ObjectMeta.Finalizers) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, sriovnetworkv1.NETATTDEFFINALIZERNAME)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
} else {
// The object is being deleted
if sriovnetworkv1.StringInArray(sriovnetworkv1.FINALIZERNAME, instance.ObjectMeta.Finalizers) {
if sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instance.ObjectMeta.Finalizers) {
// our finalizer is present, so lets handle any external dependency
reqLogger.Info("delete NetworkAttachmentDefinition CR", "Namespace", instance.Spec.NetworkNamespace, "Name", instance.Name)
if err := instance.DeleteNetAttDef(r); err != nil {
Expand All @@ -91,7 +91,7 @@ func (r *SriovIBNetworkReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
return reconcile.Result{}, err
}
// remove our finalizer from the list and update it.
instance.ObjectMeta.Finalizers = sriovnetworkv1.RemoveString(sriovnetworkv1.FINALIZERNAME, instance.ObjectMeta.Finalizers)
instance.ObjectMeta.Finalizers = sriovnetworkv1.RemoveString(sriovnetworkv1.NETATTDEFFINALIZERNAME, instance.ObjectMeta.Finalizers)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
Expand Down
8 changes: 4 additions & 4 deletions controllers/sriovnetwork_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ func (r *SriovNetworkReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !sriovnetworkv1.StringInArray(sriovnetworkv1.FINALIZERNAME, instance.ObjectMeta.Finalizers) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, sriovnetworkv1.FINALIZERNAME)
if !sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instance.ObjectMeta.Finalizers) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, sriovnetworkv1.NETATTDEFFINALIZERNAME)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
} else {
// The object is being deleted
if sriovnetworkv1.StringInArray(sriovnetworkv1.FINALIZERNAME, instance.ObjectMeta.Finalizers) {
if sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instance.ObjectMeta.Finalizers) {
// our finalizer is present, so lets handle any external dependency
reqLogger.Info("delete NetworkAttachmentDefinition CR", "Namespace", instance.Spec.NetworkNamespace, "Name", instance.Name)
if err := instance.DeleteNetAttDef(r); err != nil {
Expand All @@ -91,7 +91,7 @@ func (r *SriovNetworkReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error
return reconcile.Result{}, err
}
// remove our finalizer from the list and update it.
instance.ObjectMeta.Finalizers = sriovnetworkv1.RemoveString(sriovnetworkv1.FINALIZERNAME, instance.ObjectMeta.Finalizers)
instance.ObjectMeta.Finalizers = sriovnetworkv1.RemoveString(sriovnetworkv1.NETATTDEFFINALIZERNAME, instance.ObjectMeta.Finalizers)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
Expand Down
47 changes: 0 additions & 47 deletions controllers/sriovnetworknodeconfig_controller.go

This file was deleted.

205 changes: 205 additions & 0 deletions controllers/sriovnetworkpoolconfig_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package controllers

import (
"bytes"
"context"
"fmt"

"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
render "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
)

// SriovNetworkPoolConfigReconciler reconciles a SriovNetworkPoolConfig object
type SriovNetworkPoolConfigReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
}

//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworkpoolconfigs,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworkpoolconfigs/status,verbs=get;update;patch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the SriovNetworkPoolConfig object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *SriovNetworkPoolConfigReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
_ = context.Background()
reqLogger := r.Log.WithValues("sriovnetworkpoolconfig", req.NamespacedName)
reqLogger.Info("Reconciling")

// // Fetch SriovNetworkPoolConfig
instance := &sriovnetworkv1.SriovNetworkPoolConfig{}
err := r.Get(context.TODO(), req.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
// Return and don't requeue
reqLogger.Info("instance not found")
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}

// examine DeletionTimestamp to determine if object is under deletion
if instance.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !sriovnetworkv1.StringInArray(sriovnetworkv1.POOLCONFIGFINALIZERNAME, instance.ObjectMeta.Finalizers) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, sriovnetworkv1.POOLCONFIGFINALIZERNAME)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
if utils.ClusterType == utils.ClusterTypeOpenshift {
if err = r.syncOvsHardwareOffloadMachineConfigs(instance, false); err != nil {
return reconcile.Result{}, err
}
}
} else {
// The object is being deleted
if sriovnetworkv1.StringInArray(sriovnetworkv1.POOLCONFIGFINALIZERNAME, instance.ObjectMeta.Finalizers) {
// our finalizer is present, so lets handle any external dependency
reqLogger.Info("delete SriovNetworkPoolConfig CR", "Namespace", instance.Namespace, "Name", instance.Name)
if utils.ClusterType == utils.ClusterTypeOpenshift {
if err = r.syncOvsHardwareOffloadMachineConfigs(instance, true); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
return reconcile.Result{}, err
}
}
// remove our finalizer from the list and update it.
instance.ObjectMeta.Finalizers = sriovnetworkv1.RemoveString(sriovnetworkv1.POOLCONFIGFINALIZERNAME, instance.ObjectMeta.Finalizers)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
return reconcile.Result{}, err
}

return reconcile.Result{RequeueAfter: ResyncPeriod}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *SriovNetworkPoolConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&sriovnetworkv1.SriovNetworkPoolConfig{}).
Complete(r)
}

func (r *SriovNetworkPoolConfigReconciler) syncOvsHardwareOffloadMachineConfigs(nc *sriovnetworkv1.SriovNetworkPoolConfig, deletion bool) error {
logger := r.Log.WithName("syncOvsHardwareOffloadMachineConfigs")

data := render.MakeRenderData()
mcpMap := make(map[string]bool)

mcpList := &mcfgv1.MachineConfigPoolList{}
err := r.List(context.TODO(), mcpList, &client.ListOptions{})
if err != nil {
return fmt.Errorf("Failed to get MachineConfigPoolList: %v", err)
}

for _, mcp := range mcpList.Items {
// MachineConfigPool is selected when MachineConfigPool name matches with OvsHardwareOffloadConfig.Name
if mcp.GetName() == nc.Spec.OvsHardwareOffloadConfig.Name {
if mcp.GetName() == "master" {
logger.Info("Master nodes are selected viby OvsHardwareOffloadConfig.Name, ignoring.")
continue
}
if deletion {
mcpMap[mcp.GetName()] = false
} else {
mcpMap[mcp.GetName()] = true
}
break
}
}

for mcpName, enable := range mcpMap {
mcName := "00-" + mcpName + "-" + OVS_HWOL_MACHINE_CONFIG_NAME_SUFFIX
mc, err := render.GenerateMachineConfig("bindata/manifests/switchdev-config", mcName, mcpName, true, &data)
if err != nil {
return err
}

foundMC := &mcfgv1.MachineConfig{}
err = r.Get(context.TODO(), types.NamespacedName{Name: mcName}, foundMC)
if err != nil {
if errors.IsNotFound(err) {
if enable {
err = r.Create(context.TODO(), mc)
if err != nil {
return fmt.Errorf("Couldn't create MachineConfig: %v", err)
}
logger.Info("Created MachineConfig CR in MachineConfigPool", mcName, mcpName)
}
} else {
return fmt.Errorf("Failed to get MachineConfig: %v", err)
}
} else {
if enable {
if bytes.Compare(foundMC.Spec.Config.Raw, mc.Spec.Config.Raw) == 0 {
logger.Info("MachineConfig already exists, updating")
err = r.Update(context.TODO(), foundMC)
if err != nil {
return fmt.Errorf("Couldn't update MachineConfig: %v", err)
}
} else {
logger.Info("No content change, skip updating MC")
}
} else {
logger.Info("offload disabled, delete MachineConfig")
err = r.Delete(context.TODO(), foundMC)
if err != nil {
return fmt.Errorf("Couldn't delete MachineConfig: %v", err)
}
}
}
}

// Remove stale MCs for MCP that no longer exists in OvsHardwareOffloadConfig
for _, mcp := range mcpList.Items {
found := false
for mcpName := range mcpMap {
if mcp.Name == mcpName {
found = true
break
}
}
if !found {
mcName := "00-" + mcp.Name + "-" + OVS_HWOL_MACHINE_CONFIG_NAME_SUFFIX
foundMC := &mcfgv1.MachineConfig{}
err = r.Get(context.TODO(), types.NamespacedName{Name: mcName}, foundMC)
if err == nil {
delErr := r.Delete(context.TODO(), foundMC)
if delErr != nil {
return fmt.Errorf("Couldn't delete MachineConfig: %v", delErr)
}
} else {
if !errors.IsNotFound(err) {
return fmt.Errorf("Failed to get MachineConfig: %v", err)
}
}
}
}

return nil
}
Loading

0 comments on commit bb82c48

Please sign in to comment.