Skip to content

Commit

Permalink
Support changing Eswitch mode of SR-IOV NICs for kubernetes deployment
Browse files Browse the repository at this point in the history
Signed-off-by: Mamduh Alassi <[email protected]>
  • Loading branch information
Mmduh-483 committed Jan 17, 2021
1 parent 9d14395 commit 9bb80db
Show file tree
Hide file tree
Showing 11 changed files with 627 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ _build-%:
_plugin-%: vet
@hack/build-plugins.sh $*

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

clean:
@rm -rf $(TARGET_DIR)
Expand Down
7 changes: 7 additions & 0 deletions bindata/manifests/daemon/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ spec:
volumeMounts:
- name: host
mountPath: /host
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "touch /host/etc/sriov-netowrk-operator-run"]
preStop:
exec:
command: ["/bin/sh","-c","rm -f /host//etc/sriov-netowrk-operator-run"]
volumes:
- name: host
hostPath:
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require (
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
google.golang.org/genproto v0.0.0-20200610104632-a5b850bcf112 // indirect
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.19.0
k8s.io/apimachinery v0.19.0
k8s.io/client-go v0.19.0
Expand Down
2 changes: 2 additions & 0 deletions pkg/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,8 @@ func (dn *Daemon) loadVendorPlugins(ns *sriovnetworkv1.SriovNetworkNodeState) er
pl = registerPlugins(ns)
if utils.ClusterType == utils.ClusterTypeOpenshift {
pl = append(pl, McoPlugin)
} else {
pl = append(pl, K8sPlugin)
}
pl = append(pl, GenericPlugin)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/daemon/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
GenericPlugin = "generic_plugin"
VirtualPlugin = "virtual_plugin"
McoPlugin = "mco_plugin"
K8sPlugin = "k8s_plugin"
)

// loadPlugin loads a single plugin from a file path
Expand Down
269 changes: 269 additions & 0 deletions pkg/plugins/k8s/k8s_plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package main

import (
"io/ioutil"
"path"
"strings"

"github.com/golang/glog"
"gopkg.in/yaml.v2"

sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/service"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
)

type K8sPlugin struct {
PluginName string
SpecVersion string
}

type McoService struct {
Contents string
}

type McoFile struct {
Path string
Contents struct {
Inline string
}
}

type McoServiceInjection struct {
Dropins []struct {
Contents string
}
}

const (
mcoManifestPath = "bindata/manifests/machine-config/"
switchdevUnits = mcoManifestPath + "switchdev-units/"
switchdevUnitFile = switchdevUnits + "switchdev-configuration.yaml"
networkManagerUnitFile = switchdevUnits + "NetworkManager.service.yaml"
configuresSwitchdevScript = mcoManifestPath + "files/configure-switchdev.sh.yaml"
ovsVSwitchdUnitFile = mcoManifestPath + "ovs-units/ovs-vswitchd.service.yaml"

switchdevService = "switchdev-configuration"
networkManagerService = "NetworkManager"

switchdevServicePath = "/etc/systemd/system/" + switchdevService + ".service"
systemdDir = "/usr/lib/systemd/system"
)

var (
Plugin K8sPlugin

serviceManager service.ServiceManager

ovsServiceName = "openvswitch"

needServices bool
needInjectServices bool
)

// Initialize our plugin and set up initial values
func init() {
Plugin = K8sPlugin{
PluginName: "k8s_plugin",
SpecVersion: "1.0",
}

// ovs service name is different on ubuntu
serviceManager = service.NewServiceManager("/host")
data, err := ioutil.ReadFile("/host/etc/os-release")
if err != nil {
panic(err)
}

osInfo := string(data)
if strings.Contains(osInfo, "ubuntu") {
ovsServiceName = "openvswitch-switch"
}
}

// Name returns the name of the plugin
func (p *K8sPlugin) Name() string {
return p.PluginName
}

// Spec returns the version of the spec expected by the plugin
func (p *K8sPlugin) Spec() string {
return p.SpecVersion
}

// OnNodeStateAdd Invoked when SriovNetworkNodeState CR is created, return if need dain and/or reboot node
func (p *K8sPlugin) OnNodeStateAdd(state *sriovnetworkv1.SriovNetworkNodeState) (needDrain bool, needReboot bool, err error) {
glog.Info("k8s-plugin OnNodeStateAdd()")
return p.OnNodeStateChange(nil, state)
}

// OnNodeStateChange Invoked when SriovNetworkNodeState CR is updated, return if need dain and/or reboot node
func (p *K8sPlugin) OnNodeStateChange(old, new *sriovnetworkv1.SriovNetworkNodeState) (needDrain bool, needReboot bool, err error) {
glog.Info("k8s-plugin OnNodeStateChange()")
needDrain = false
needReboot = false

// Check services
needServices, err = handleServices()
if err != nil {
return
}

if !needServices {
needServices, err = serviceManager.CheckServicesLoaded()
if err != nil {
return
}
}
needDrain = needServices

needInjectServices, err = handleServiceInjections()
if err != nil {
return false, false, err
}
if !needInjectServices {
needInjectServices, err = serviceManager.CheckServiceInjections()
}
needDrain = needDrain || needInjectServices

// Check switchdev config
var update, remove bool
if new == nil {
return
}
if update, remove, err = utils.WriteSwitchdevConfFile(new); err != nil {
glog.Errorf("k8s-plugin OnNodeStateChange():fail to update switchdev.conf file: %v", err)
return
}
if remove {
glog.Info("k8s-plugin OnNodeStateChange(): need reboot node to clean switchdev VFs")
needDrain = true
needReboot = true
return
}
if update {
glog.Info("k8s-plugin OnNodeStateChange(): need reboot node to use the up-to-date switchdev.conf")
needDrain = true
needReboot = true
return
}
return
}

// Apply config change
func (p *K8sPlugin) Apply() error {
glog.Info("k8s-plugin Apply()")
if needServices {
if err := serviceManager.LoadMissingServices(); err != nil {
return err
}
needServices = false
}

if needInjectServices {
if err := serviceManager.InjectServices(); err != nil {
return err
}
needInjectServices = false
}

return nil
}

func handleServices() (bool, error) {
needChange := false
smServices := serviceManager.ServicesSet()
// Check switchdev service
if _, ok := smServices[switchdevService]; !ok {
needChange = true
data, err := ioutil.ReadFile(switchdevUnitFile)
if err != nil {
return false, err
}

var switchdevServiceContent McoService
if err := yaml.Unmarshal(data, &switchdevServiceContent); err != nil {
return false, err
}

data, err = ioutil.ReadFile(configuresSwitchdevScript)
if err != nil {
return false, err
}
var switchdevRunFile McoFile
if err := yaml.Unmarshal(data, &switchdevRunFile); err != nil {
return false, err
}

switchdevService := &service.Service{
Name: switchdevService,
Path: switchdevServicePath,
Body: switchdevServiceContent.Contents,
Run: switchdevRunFile.Path,
RunBody: switchdevRunFile.Contents.Inline,
}

if err = serviceManager.AddService(switchdevService); err != nil {
return false, err
}
}

return needChange, nil
}

func handleServiceInjections() (bool, error) {
needChange := false
smServices := serviceManager.InjectableServicesSet()

// Check ovs service exists
if _, ok := smServices[ovsServiceName]; !ok {
needChange = true

data, err := ioutil.ReadFile(ovsVSwitchdUnitFile)
if err != nil {
return false, err
}
var ovsServiceContent McoServiceInjection
if err := yaml.Unmarshal(data, &ovsServiceContent); err != nil {
return false, err
}

ovsService, err := service.NewInjectableService(ovsServiceName,
path.Join(systemdDir, ovsServiceName+".service"),
ovsServiceContent.Dropins[0].Contents)
if err != nil {
return false, err
}

if err = serviceManager.AddInjectableService(ovsService); err != nil {
return false, err
}
}

// Check NetworkManager service exists
if _, ok := smServices[networkManagerService]; !ok {
needChange = true

data, err := ioutil.ReadFile(networkManagerUnitFile)
if err != nil {
return false, err
}
var networkManagerServiceContent McoServiceInjection
if err := yaml.Unmarshal(data, &networkManagerServiceContent); err != nil {
return false, err
}

networkMgrService, err := service.NewInjectableService(networkManagerService,
path.Join(systemdDir, networkManagerService+".service"),
networkManagerServiceContent.Dropins[0].Contents)
if err != nil {
return false, err
}

if err = serviceManager.AddInjectableService(networkMgrService); err != nil {
return false, err
}
}

return needChange, nil
}
49 changes: 2 additions & 47 deletions pkg/plugins/mco/mco_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"

"github.com/golang/glog"
Expand All @@ -17,6 +15,7 @@ import (

sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
"github.com/k8snetworkplumbingwg/sriov-network-operator/controllers"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
)

type McoPlugin struct {
Expand All @@ -28,7 +27,6 @@ type McoPlugin struct {

const (
switchdevUnitPath = "/host/etc/systemd/system/switchdev-configuration.service"
switchDevConfPath = "/host/etc/switchdev.conf"
nodeLabelPrefix = "node-role.kubernetes.io/"
)

Expand Down Expand Up @@ -89,7 +87,7 @@ func (p *McoPlugin) OnNodeStateChange(old, new *sriovnetworkv1.SriovNetworkNodeS
}

var update, remove bool
if update, remove, err = writeSwitchdevConfFile(new); err != nil {
if update, remove, err = utils.WriteSwitchdevConfFile(new); err != nil {
glog.Errorf("mco-plugin OnNodeStateChange():fail to update switchdev.conf file: %v", err)
return
}
Expand Down Expand Up @@ -156,46 +154,3 @@ func (p *McoPlugin) Apply() error {
glog.Infof("Node %s is not in HW offload MachineConfigPool", node.Name)
return nil
}

func writeSwitchdevConfFile(newState *sriovnetworkv1.SriovNetworkNodeState) (update, remove bool, err error) {
_, err = os.Stat(switchDevConfPath)
if err != nil {
if os.IsNotExist(err) {
glog.V(2).Infof("writeSwitchdevConfFile(): file not existed, create it")
_, err = os.Create(switchDevConfPath)
if err != nil {
glog.Errorf("writeSwitchdevConfFile(): fail to create file: %v", err)
return
}
} else {
return
}
}
newContent := ""
for _, iface := range newState.Spec.Interfaces {
if iface.EswitchMode == sriovnetworkv1.ESWITCHMODE_SWITCHDEV {
newContent = newContent + fmt.Sprintln(iface.PciAddress, iface.NumVfs)
}
}
oldContent, err := ioutil.ReadFile(switchDevConfPath)
if err != nil {
glog.Errorf("writeSwitchdevConfFile(): fail to read file: %v", err)
return
}
if newContent == string(oldContent) {
glog.V(2).Info("writeSwitchdevConfFile(): no update")
return
}
if newContent == "" {
remove = true
glog.V(2).Info("writeSwitchdevConfFile(): remove content in switchdev.conf")
}
update = true
glog.V(2).Infof("writeSwitchdevConfFile(): write %s to switchdev.conf", newContent)
err = ioutil.WriteFile(switchDevConfPath, []byte(newContent), 0666)
if err != nil {
glog.Errorf("writeSwitchdevConfFile(): fail to write file: %v", err)
return
}
return
}
Loading

0 comments on commit 9bb80db

Please sign in to comment.