Skip to content

Commit f061c11

Browse files
committed
Merge branch 'network-operator-26.1.x' into forked-master
# Conflicts: # pkg/daemon/daemon_test.go
2 parents d99aa9f + d8293c2 commit f061c11

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2253
-240
lines changed

.github/workflows/test.yml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -122,26 +122,6 @@ jobs:
122122
with:
123123
severity: error
124124

125-
hadolint:
126-
name: hadolint
127-
runs-on: ubuntu-24.04
128-
steps:
129-
- name: checkout PR
130-
uses: actions/checkout@v5
131-
- name: run hadolint on operator dockerfile
132-
# using commit sha of the hadolint-action to preserve immutability
133-
uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5
134-
with:
135-
dockerfile: Dockerfile
136-
- name: run hadolint on config daemon dockerfile
137-
uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5
138-
with:
139-
dockerfile: Dockerfile.sriov-network-config-daemon
140-
ignore: DL3033
141-
- name: run hadolint on webhook dockerfile
142-
uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5
143-
with:
144-
dockerfile: Dockerfile.webhook
145125
test-coverage:
146126
name: test-coverage
147127
runs-on: ubuntu-24.04

api/v1/helper.go

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,29 @@ func GetEswitchModeFromStatus(ifaceStatus *InterfaceExt) string {
204204
return ifaceStatus.EswitchMode
205205
}
206206

207+
func NeedToUpdateDevlinkParams(desired *DevlinkParams, current *DevlinkParams) bool {
208+
for _, dParam := range desired.Params {
209+
found := false
210+
for _, cParam := range current.Params {
211+
if dParam.Name == cParam.Name {
212+
found = true
213+
if dParam.Value != cParam.Value {
214+
log.V(0).Info("NeedToUpdateDevlinkParams(): DevlinkParam needs update",
215+
"name", dParam.Name, "desired", dParam.Value, "current", cParam.Value)
216+
return true
217+
}
218+
break
219+
}
220+
}
221+
if !found {
222+
log.V(0).Info("NeedToUpdateDevlinkParams(): DevlinkParam needs update - not found in status",
223+
"name", dParam.Name, "desired", dParam.Value)
224+
return true
225+
}
226+
}
227+
return false
228+
}
229+
207230
func NeedToUpdateSriov(ifaceSpec *Interface, ifaceStatus *InterfaceExt) bool {
208231
if ifaceSpec.Mtu > 0 {
209232
mtu := ifaceSpec.Mtu
@@ -282,6 +305,11 @@ func NeedToUpdateSriov(ifaceSpec *Interface, ifaceStatus *InterfaceExt) bool {
282305
}
283306
}
284307
}
308+
309+
if NeedToUpdateDevlinkParams(&ifaceSpec.DevlinkParams, &ifaceStatus.DevlinkParams) {
310+
return true
311+
}
312+
285313
return false
286314
}
287315

@@ -362,6 +390,7 @@ func (p *SriovNetworkNodePolicy) Apply(state *SriovNetworkNodeState, equalPriori
362390
EswitchMode: p.Spec.EswitchMode,
363391
NumVfs: p.Spec.NumVfs,
364392
ExternallyManaged: p.Spec.ExternallyManaged,
393+
DevlinkParams: p.Spec.DevlinkParams,
365394
}
366395
if p.Spec.NumVfs > 0 {
367396
group, err := p.generatePfNameVfGroup(&iface)
@@ -405,6 +434,18 @@ func (p *SriovNetworkNodePolicy) ApplyBridgeConfig(state *SriovNetworkNodeState)
405434
return fmt.Errorf("software bridge management can't be used when link is externally managed")
406435
}
407436
}
437+
438+
// Set GroupingPolicy from policy to state
439+
if p.Spec.Bridge.GroupingPolicy != "" {
440+
state.Spec.Bridges.GroupingPolicy = p.Spec.Bridge.GroupingPolicy
441+
}
442+
443+
// When groupingPolicy is "all", create a single bridge entry with all matching interfaces as uplinks.
444+
// The daemon will use this to create the grouped bridge.
445+
if p.Spec.Bridge.GroupingPolicy == consts.OvsGroupingPolicyAll {
446+
return p.applyGroupedBridgeConfig(state)
447+
}
448+
408449
for _, iface := range state.Status.Interfaces {
409450
if p.Spec.NicSelector.Selected(&iface) {
410451
if p.Spec.Bridge.OVS == nil {
@@ -451,6 +492,75 @@ func (p *SriovNetworkNodePolicy) ApplyBridgeConfig(state *SriovNetworkNodeState)
451492
return nil
452493
}
453494

495+
// applyGroupedBridgeConfig creates a single OVS bridge entry with all matching interfaces as uplinks
496+
// when groupingPolicy is "all". The first uplink will be used to create the bridge,
497+
// and the remaining uplinks will be added by the daemon using AddInterfaceToOVSBridge.
498+
func (p *SriovNetworkNodePolicy) applyGroupedBridgeConfig(state *SriovNetworkNodeState) error {
499+
if p.Spec.Bridge.OVS == nil {
500+
// No OVS config, nothing to do
501+
return nil
502+
}
503+
504+
// Collect all matching interfaces as uplinks
505+
var uplinks []OVSUplinkConfigExt
506+
for _, iface := range state.Status.Interfaces {
507+
if p.Spec.NicSelector.Selected(&iface) {
508+
uplink := OVSUplinkConfigExt{
509+
PciAddress: iface.PciAddress,
510+
Name: iface.Name,
511+
Interface: p.Spec.Bridge.OVS.Uplink.Interface,
512+
}
513+
if p.Spec.Mtu > 0 {
514+
mtu := p.Spec.Mtu
515+
uplink.Interface.MTURequest = &mtu
516+
}
517+
uplinks = append(uplinks, uplink)
518+
}
519+
}
520+
521+
if len(uplinks) == 0 {
522+
// No matching interfaces
523+
return nil
524+
}
525+
526+
// Sort uplinks by PciAddress for consistent ordering
527+
sort.Slice(uplinks, func(i, j int) bool {
528+
return uplinks[i].PciAddress < uplinks[j].PciAddress
529+
})
530+
531+
// Create a single bridge with all uplinks
532+
// Use the policy name as part of the bridge name for grouped bridges
533+
brName := "br-" + p.Name
534+
ovsBridge := OVSConfigExt{
535+
Name: brName,
536+
Bridge: p.Spec.Bridge.OVS.Bridge,
537+
Uplinks: uplinks,
538+
}
539+
540+
log.Info("Update grouped bridge for policy", "policy", p.Name, "bridge", brName, "uplinks", len(uplinks))
541+
542+
// Remove any existing individual per-PF bridges for the matching interfaces
543+
for _, uplink := range uplinks {
544+
state.Spec.Bridges.OVS = slices.DeleteFunc(state.Spec.Bridges.OVS, func(br OVSConfigExt) bool {
545+
return slices.ContainsFunc(br.Uplinks, func(u OVSUplinkConfigExt) bool {
546+
return u.PciAddress == uplink.PciAddress
547+
})
548+
})
549+
}
550+
551+
// Add or update the grouped bridge
552+
pos, exist := slices.BinarySearchFunc(state.Spec.Bridges.OVS, ovsBridge, func(x, y OVSConfigExt) int {
553+
return strings.Compare(x.Name, y.Name)
554+
})
555+
if exist {
556+
state.Spec.Bridges.OVS[pos] = ovsBridge
557+
} else {
558+
state.Spec.Bridges.OVS = slices.Insert(state.Spec.Bridges.OVS, pos, ovsBridge)
559+
}
560+
561+
return nil
562+
}
563+
454564
// mergeConfigs merges configs from multiple polices where the last one has the
455565
// highest priority. This merge is dependent on: 1. SR-IOV partition is
456566
// configured with the #-notation in pfName, 2. The VF groups are
@@ -957,7 +1067,9 @@ func GenerateBridgeName(iface *InterfaceExt) string {
9571067

9581068
// NeedToUpdateBridges returns true if bridge for the host requires update
9591069
func NeedToUpdateBridges(bridgeSpec, bridgeStatus *Bridges) bool {
960-
return !equality.Semantic.DeepEqual(bridgeSpec, bridgeStatus)
1070+
// Compare only OVS configurations, not GroupingPolicy which is a policy directive
1071+
// and not something that exists in the actual OVS state
1072+
return !equality.Semantic.DeepEqual(bridgeSpec.OVS, bridgeStatus.OVS)
9611073
}
9621074

9631075
// SetKeepUntilTime sets an annotation to hold the "keep until time" for the node’s state.

api/v1/helper_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,156 @@ func TestSriovNetworkNodePolicyApplyBridgeConfig(t *testing.T) {
14951495
},
14961496
}},
14971497
},
1498+
{
1499+
tname: "groupingPolicy all - creates single bridge with all uplinks",
1500+
currentState: newNodeState(),
1501+
policy: &v1.SriovNetworkNodePolicy{
1502+
ObjectMeta: metav1.ObjectMeta{
1503+
Name: "rail-1",
1504+
},
1505+
Spec: v1.SriovNetworkNodePolicySpec{
1506+
DeviceType: consts.DeviceTypeNetDevice,
1507+
NicSelector: v1.SriovNetworkNicSelector{
1508+
RootDevices: []string{"0000:86:00.0", "0000:86:00.2"},
1509+
},
1510+
NodeSelector: map[string]string{
1511+
"feature.node.kubernetes.io/network-sriov.capable": "true",
1512+
},
1513+
NumVfs: 2,
1514+
Priority: 99,
1515+
EswitchMode: "switchdev",
1516+
ResourceName: "p1res",
1517+
Bridge: v1.Bridge{
1518+
GroupingPolicy: consts.OvsGroupingPolicyAll,
1519+
OVS: &v1.OVSConfig{
1520+
Bridge: v1.OVSBridgeConfig{DatapathType: "netdev", FailMode: "secure"},
1521+
Uplink: v1.OVSUplinkConfig{
1522+
Interface: v1.OVSInterfaceConfig{
1523+
Type: "dpdk",
1524+
}},
1525+
}},
1526+
},
1527+
},
1528+
expectedBridges: v1.Bridges{
1529+
GroupingPolicy: consts.OvsGroupingPolicyAll,
1530+
OVS: []v1.OVSConfigExt{
1531+
{
1532+
Name: "br-rail-1",
1533+
Bridge: v1.OVSBridgeConfig{DatapathType: "netdev", FailMode: "secure"},
1534+
Uplinks: []v1.OVSUplinkConfigExt{
1535+
{
1536+
Name: "ens803f0",
1537+
PciAddress: "0000:86:00.0",
1538+
Interface: v1.OVSInterfaceConfig{Type: "dpdk"},
1539+
},
1540+
{
1541+
Name: "ens803f2",
1542+
PciAddress: "0000:86:00.2",
1543+
Interface: v1.OVSInterfaceConfig{Type: "dpdk"},
1544+
},
1545+
},
1546+
},
1547+
}},
1548+
},
1549+
{
1550+
tname: "groupingPolicy all - removes existing per-PF bridges",
1551+
currentState: &v1.SriovNetworkNodeState{
1552+
Spec: v1.SriovNetworkNodeStateSpec{
1553+
Bridges: v1.Bridges{OVS: []v1.OVSConfigExt{
1554+
{
1555+
Name: "br-0000_86_00.0",
1556+
Bridge: v1.OVSBridgeConfig{DatapathType: "old"},
1557+
Uplinks: []v1.OVSUplinkConfigExt{{
1558+
Name: "ens803f0",
1559+
PciAddress: "0000:86:00.0",
1560+
Interface: v1.OVSInterfaceConfig{Type: "old"},
1561+
}},
1562+
},
1563+
{
1564+
Name: "br-0000_86_00.2",
1565+
Bridge: v1.OVSBridgeConfig{DatapathType: "old"},
1566+
Uplinks: []v1.OVSUplinkConfigExt{{
1567+
Name: "ens803f2",
1568+
PciAddress: "0000:86:00.2",
1569+
Interface: v1.OVSInterfaceConfig{Type: "old"},
1570+
}},
1571+
},
1572+
}},
1573+
},
1574+
Status: v1.SriovNetworkNodeStateStatus{
1575+
Interfaces: []v1.InterfaceExt{
1576+
{
1577+
VFs: []v1.VirtualFunction{{}},
1578+
DeviceID: "158b",
1579+
Driver: "i40e",
1580+
Mtu: 1500,
1581+
Name: "ens803f0",
1582+
PciAddress: "0000:86:00.0",
1583+
Vendor: "8086",
1584+
NumVfs: 4,
1585+
TotalVfs: 64,
1586+
},
1587+
{
1588+
VFs: []v1.VirtualFunction{{}},
1589+
DeviceID: "158b",
1590+
Driver: "i40e",
1591+
Mtu: 1500,
1592+
Name: "ens803f2",
1593+
PciAddress: "0000:86:00.2",
1594+
Vendor: "8086",
1595+
NumVfs: 4,
1596+
TotalVfs: 64,
1597+
},
1598+
},
1599+
}},
1600+
policy: &v1.SriovNetworkNodePolicy{
1601+
ObjectMeta: metav1.ObjectMeta{
1602+
Name: "grouped-bridge",
1603+
},
1604+
Spec: v1.SriovNetworkNodePolicySpec{
1605+
DeviceType: consts.DeviceTypeNetDevice,
1606+
NicSelector: v1.SriovNetworkNicSelector{
1607+
RootDevices: []string{"0000:86:00.0", "0000:86:00.2"},
1608+
},
1609+
NodeSelector: map[string]string{
1610+
"feature.node.kubernetes.io/network-sriov.capable": "true",
1611+
},
1612+
NumVfs: 2,
1613+
Priority: 99,
1614+
EswitchMode: "switchdev",
1615+
ResourceName: "p1res",
1616+
Bridge: v1.Bridge{
1617+
GroupingPolicy: consts.OvsGroupingPolicyAll,
1618+
OVS: &v1.OVSConfig{
1619+
Bridge: v1.OVSBridgeConfig{DatapathType: "netdev"},
1620+
Uplink: v1.OVSUplinkConfig{
1621+
Interface: v1.OVSInterfaceConfig{
1622+
Type: "dpdk",
1623+
}},
1624+
}},
1625+
},
1626+
},
1627+
expectedBridges: v1.Bridges{
1628+
GroupingPolicy: consts.OvsGroupingPolicyAll,
1629+
OVS: []v1.OVSConfigExt{
1630+
{
1631+
Name: "br-grouped-bridge",
1632+
Bridge: v1.OVSBridgeConfig{DatapathType: "netdev"},
1633+
Uplinks: []v1.OVSUplinkConfigExt{
1634+
{
1635+
Name: "ens803f0",
1636+
PciAddress: "0000:86:00.0",
1637+
Interface: v1.OVSInterfaceConfig{Type: "dpdk"},
1638+
},
1639+
{
1640+
Name: "ens803f2",
1641+
PciAddress: "0000:86:00.2",
1642+
Interface: v1.OVSInterfaceConfig{Type: "dpdk"},
1643+
},
1644+
},
1645+
},
1646+
}},
1647+
},
14981648
}
14991649
for _, tc := range testtable {
15001650
t.Run(tc.tname, func(t *testing.T) {
@@ -1538,6 +1688,18 @@ func TestNeedToUpdateBridges(t *testing.T) {
15381688
statusBridge: &v1.Bridges{OVS: []v1.OVSConfigExt{}},
15391689
expectedResult: true,
15401690
},
1691+
{
1692+
tname: "no update required - groupingPolicy difference should be ignored",
1693+
specBridge: &v1.Bridges{
1694+
GroupingPolicy: "all",
1695+
OVS: []v1.OVSConfigExt{{Name: "br-test", Bridge: v1.OVSBridgeConfig{DatapathType: "netdev"}}},
1696+
},
1697+
statusBridge: &v1.Bridges{
1698+
// Status doesn't have GroupingPolicy since it's not stored in OVS
1699+
OVS: []v1.OVSConfigExt{{Name: "br-test", Bridge: v1.OVSBridgeConfig{DatapathType: "netdev"}}},
1700+
},
1701+
expectedResult: false,
1702+
},
15411703
}
15421704
for _, tc := range testtable {
15431705
t.Run(tc.tname, func(t *testing.T) {

0 commit comments

Comments
 (0)