Skip to content

Commit

Permalink
Add VendorPluginWithPhases interface
Browse files Browse the repository at this point in the history
The interface is for plugins which support phased configuration.
+ modify generic_plugin to support phased interface

Signed-off-by: Yury Kulazhenkov <[email protected]>
  • Loading branch information
ykulazhenkov committed Feb 2, 2024
1 parent 40bc84c commit e84e770
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 14 deletions.
69 changes: 55 additions & 14 deletions pkg/plugins/generic/generic_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type GenericPlugin struct {
DesiredKernelArgs map[string]bool
pfsToSkip map[string]bool
helpers helper.HostHelpersInterface
phase plugin.ConfigurationPhase
}

const scriptsPath = "bindata/scripts/enable-kargs.sh"
Expand Down Expand Up @@ -107,10 +108,38 @@ func (p *GenericPlugin) Spec() string {
return p.SpecVersion
}

// IsPhaseSupported checks if phase is supported by the plugin
func (p *GenericPlugin) IsPhaseSupported(phase plugin.ConfigurationPhase) bool {
switch phase {
case plugin.PreConfigurationPhase, plugin.PostConfigurationPhase:
return true
default:
return false
}
}

// SetPhase set configuration phase for the plugin.
func (p *GenericPlugin) SetPhase(phase plugin.ConfigurationPhase) {
if p.IsPhaseSupported(phase) {
p.phase = phase
}
}

// OnNodeStateChange Invoked when SriovNetworkNodeState CR is created or updated, return if need drain and/or reboot node
func (p *GenericPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeState) (needDrain bool, needReboot bool, err error) {
log.Log.Info("generic plugin OnNodeStateChange()")
log.Log.Info("generic plugin OnNodeStateChange()", "phase", p.phase)

// TODO drop this when removing support for legacy switchdev service
p.pfsToSkip, err = getPfsToSkip(new, p.helpers)
if err != nil {
return needDrain, needReboot, err
}

p.DesireState = new
if p.isPhasedMode() {
log.Log.Info("generic-plugin OnNodeStateChange() - no actions required is phased mode", "phase", p.phase)
return needDrain, needReboot, nil
}

needDrain = p.needDrainNode(new.Spec.Interfaces, new.Status.Interfaces)
needReboot, err = p.needRebootNode(new)
Expand All @@ -124,6 +153,13 @@ func (p *GenericPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeSt
return
}

// returns true if the plugin is running in the phased mode, meaning
// that the valid phase was configured bu SetPhase function before calling
// OnNodeStateChange and Apply
func (p *GenericPlugin) isPhasedMode() bool {
return p.phase != ""
}

func (p *GenericPlugin) syncDriverState() error {
for _, driverState := range p.DriverStateMap {
if !driverState.DriverLoaded && driverState.NeedDriverFunc(p.DesireState, driverState) {
Expand All @@ -142,14 +178,28 @@ func (p *GenericPlugin) syncDriverState() error {
func (p *GenericPlugin) Apply() error {
log.Log.Info("generic plugin Apply()", "desiredState", p.DesireState.Spec)

if p.isPhasedMode() {
// no need to save applied state when running in the phased mode, because
// the plugin is executed only once
return p.apply()
}

if p.LastState != nil {
log.Log.Info("generic plugin Apply()", "lastState", p.LastState.Spec)
if reflect.DeepEqual(p.LastState.Spec.Interfaces, p.DesireState.Spec.Interfaces) {
log.Log.Info("generic plugin Apply(): desired and latest state are the same, nothing to apply")
return nil
}
}
if err := p.apply(); err != nil {
return err
}
p.LastState = &sriovnetworkv1.SriovNetworkNodeState{}
*p.LastState = *p.DesireState
return nil
}

func (p *GenericPlugin) apply() error {
if err := p.syncDriverState(); err != nil {
return err
}
Expand All @@ -163,15 +213,16 @@ func (p *GenericPlugin) Apply() error {
defer exit()
}

if err := p.helpers.ConfigSriovInterfaces(p.helpers, p.DesireState.Spec.Interfaces, p.DesireState.Status.Interfaces, p.pfsToSkip, false); err != nil {
// on pre configuration phase we need to create VFs, but keep them unconfigured
skipConfigurationOfVFs := p.phase == plugin.PreConfigurationPhase
if err := p.helpers.ConfigSriovInterfaces(p.helpers, p.DesireState.Spec.Interfaces,
p.DesireState.Status.Interfaces, p.pfsToSkip, skipConfigurationOfVFs); err != nil {
// Catch the "cannot allocate memory" error and try to use PCI realloc
if errors.Is(err, syscall.ENOMEM) {
p.addToDesiredKernelArgs(consts.KernelArgPciRealloc)
}
return err
}
p.LastState = &sriovnetworkv1.SriovNetworkNodeState{}
*p.LastState = *p.DesireState
return nil
}

Expand Down Expand Up @@ -346,16 +397,6 @@ func (p *GenericPlugin) needRebootNode(state *sriovnetworkv1.SriovNetworkNodeSta
log.Log.V(2).Info("generic plugin needRebootNode(): need reboot for updating kernel arguments")
needReboot = true
}

// Create a map with all the PFs we will need to configure
// we need to create it here before we access the host file system using the chroot function
// because the skipConfigVf needs the mstconfig package that exist only inside the sriov-config-daemon file system
pfsToSkip, err := getPfsToSkip(p.DesireState, p.helpers)
if err != nil {
return false, err
}
p.pfsToSkip = pfsToSkip

updateNode, err = p.helpers.WriteSwitchdevConfFile(state, p.pfsToSkip)
if err != nil {
log.Log.Error(err, "generic plugin needRebootNode(): fail to write switchdev device config file")
Expand Down
108 changes: 108 additions & 0 deletions pkg/plugins/mock/mock_plugin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions pkg/plugins/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ import (
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
)

// ConfigurationPhase is the type to reflect configuration phase in the
// systemd mode
type ConfigurationPhase string

const (
// PreConfigurationPhase is executed on host boot before the NetworkManager or systemd-networkd
PreConfigurationPhase ConfigurationPhase = "pre"
// PostConfigurationPhase is executed on host boot after the NetworkManager or systemd-networkd
PostConfigurationPhase ConfigurationPhase = "post"
)

//go:generate ../../bin/mockgen -destination mock/mock_plugin.go -source plugin.go
type VendorPlugin interface {
// Return the name of plugin
Expand All @@ -15,3 +26,20 @@ type VendorPlugin interface {
// Apply config change
Apply() error
}

// defines interface for plugins which support phased configuration.
// such plugins must also support execution without setting a phase (daemon mode).
// now phase-based execution will happen only in systemd mode
type VendorPluginWithPhases interface {
VendorPlugin
// IsPhaseSupported checks if phase is supported by the plugin
IsPhaseSupported(phase ConfigurationPhase) bool
// SetPhase set configuration phase for the plugin.
// If SetPhase function was not called or was called
// with invalid(or unsupported) phase argument,
// then the plugin should run in the daemon mode (apply all phases).
// It is caller's responsibility to check if the phase is supported before calling
// the function.
// SetPhase must be called before OnNodeStateChange() and Apply() functions
SetPhase(phase ConfigurationPhase)
}

0 comments on commit e84e770

Please sign in to comment.