Skip to content

Commit

Permalink
Merge pull request #694 from ykulazhenkov/pr-autobridge-controller-part
Browse files Browse the repository at this point in the history
[software-bridges 4/x] Controller part
  • Loading branch information
adrianchiris authored Jul 23, 2024
2 parents 18c4695 + f10b9a6 commit 588abb4
Show file tree
Hide file tree
Showing 12 changed files with 603 additions and 10 deletions.
85 changes: 83 additions & 2 deletions api/v1/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"fmt"
"os"
"path/filepath"
"reflect"
"regexp"
"slices"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -397,8 +399,7 @@ func UniqueAppend(inSlice []string, strings ...string) []string {
// Apply policy to SriovNetworkNodeState CR
func (p *SriovNetworkNodePolicy) Apply(state *SriovNetworkNodeState, equalPriority bool) error {
s := p.Spec.NicSelector
if s.Vendor == "" && s.DeviceID == "" && len(s.RootDevices) == 0 && len(s.PfNames) == 0 &&
len(s.NetFilter) == 0 {
if s.IsEmpty() {
// Empty NicSelector match none
return nil
}
Expand Down Expand Up @@ -438,6 +439,66 @@ func (p *SriovNetworkNodePolicy) Apply(state *SriovNetworkNodeState, equalPriori
return nil
}

// ApplyBridgeConfig applies bridge configuration from the policy to the provided state
func (p *SriovNetworkNodePolicy) ApplyBridgeConfig(state *SriovNetworkNodeState) error {
if p.Spec.NicSelector.IsEmpty() {
// Empty NicSelector match none
return nil
}
// sanity check the policy
if !p.Spec.Bridge.IsEmpty() {
if p.Spec.EswitchMode != ESwithModeSwitchDev {
return fmt.Errorf("eSwitchMode must be switchdev to use software bridge management")
}
if p.Spec.LinkType != "" && !strings.EqualFold(p.Spec.LinkType, consts.LinkTypeETH) {
return fmt.Errorf("linkType must be eth or ETH to use software bridge management")
}
if p.Spec.ExternallyManaged {
return fmt.Errorf("software bridge management can't be used when link is externally managed")
}
}
for _, iface := range state.Status.Interfaces {
if p.Spec.NicSelector.Selected(&iface) {
if p.Spec.Bridge.OVS == nil {
// The policy has no OVS bridge config, this means that the node's state should have no managed OVS bridges for the interfaces that match the policy.
// Currently PF to OVS bridge mapping is always 1 to 1 (bonding is not supported at the moment), meaning we can remove the OVS bridge
// config from the node's state if it has the interface (that matches "empty-bridge" policy) in the uplink section.
state.Spec.Bridges.OVS = slices.DeleteFunc(state.Spec.Bridges.OVS, func(br OVSConfigExt) bool {
return slices.ContainsFunc(br.Uplinks, func(uplink OVSUplinkConfigExt) bool {
return uplink.PciAddress == iface.PciAddress
})
})
if len(state.Spec.Bridges.OVS) == 0 {
state.Spec.Bridges.OVS = nil
}
continue
}
ovsBridge := OVSConfigExt{
Name: GenerateBridgeName(&iface),
Bridge: p.Spec.Bridge.OVS.Bridge,
Uplinks: []OVSUplinkConfigExt{{
PciAddress: iface.PciAddress,
Name: iface.Name,
Interface: p.Spec.Bridge.OVS.Uplink.Interface,
}},
}
log.Info("Update bridge for interface", "name", iface.Name, "bridge", ovsBridge.Name)

// We need to keep slices with bridges ordered to avoid unnecessary updates in the K8S API.
// Use binary search to insert (or update) the bridge config to the right place in the slice to keep it sorted.
pos, exist := slices.BinarySearchFunc(state.Spec.Bridges.OVS, ovsBridge, func(x, y OVSConfigExt) int {
return strings.Compare(x.Name, y.Name)
})
if exist {
state.Spec.Bridges.OVS[pos] = ovsBridge
} else {
state.Spec.Bridges.OVS = slices.Insert(state.Spec.Bridges.OVS, pos, ovsBridge)
}
}
}
return nil
}

// mergeConfigs merges configs from multiple polices where the last one has the
// highest priority. This merge is dependent on: 1. SR-IOV partition is
// configured with the #-notation in pfName, 2. The VF groups are
Expand Down Expand Up @@ -568,6 +629,15 @@ func ParseVfRange(device string) (rootDeviceName string, rngSt, rngEnd int, err
return
}

// IsEmpty returns true if nicSelector is empty
func (selector *SriovNetworkNicSelector) IsEmpty() bool {
return selector.Vendor == "" &&
selector.DeviceID == "" &&
len(selector.RootDevices) == 0 &&
len(selector.PfNames) == 0 &&
len(selector.NetFilter) == 0
}

func (selector *SriovNetworkNicSelector) Selected(iface *InterfaceExt) bool {
if selector.Vendor != "" && selector.Vendor != iface.Vendor {
return false
Expand Down Expand Up @@ -923,3 +993,14 @@ func (s *SriovNetworkPoolConfig) MaxUnavailable(numOfNodes int) (int, error) {

return maxunavail, nil
}

// GenerateBridgeName generate predictable name for the software bridge
// current format is: br-0000_00_03.0
func GenerateBridgeName(iface *InterfaceExt) string {
return fmt.Sprintf("br-%s", strings.ReplaceAll(iface.PciAddress, ":", "_"))
}

// NeedToUpdateBridges returns true if bridge for the host requires update
func NeedToUpdateBridges(bridgeSpec, bridgeStatus *Bridges) bool {
return !reflect.DeepEqual(bridgeSpec, bridgeStatus)
}
Loading

0 comments on commit 588abb4

Please sign in to comment.