github.com/nmstate/kubernetes-nmstate@v0.82.0/test/e2e/handler/simple_bridge_and_bond_test.go (about) 1 /* 2 Copyright The Kubernetes NMState Authors. 3 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 package handler 19 20 import ( 21 "fmt" 22 23 . "github.com/onsi/ginkgo/v2" 24 . "github.com/onsi/gomega" 25 26 nmstate "github.com/nmstate/kubernetes-nmstate/api/shared" 27 ) 28 29 func bondAbsent(bondName string) nmstate.State { 30 return nmstate.NewState(fmt.Sprintf(`interfaces: 31 - name: %s 32 type: bond 33 state: absent 34 `, bondName)) 35 } 36 37 func brAndBondAbsent(bridgeName, bondName string) nmstate.State { 38 return nmstate.NewState(fmt.Sprintf(`interfaces: 39 - name: %s 40 type: linux-bridge 41 state: absent 42 - name: %s 43 type: bond 44 state: absent 45 `, bridgeName, bondName)) 46 } 47 48 func bondUp(bondName string) nmstate.State { 49 return nmstate.NewState(fmt.Sprintf(`interfaces: 50 - name: %s 51 type: bond 52 state: up 53 link-aggregation: 54 mode: active-backup 55 %s: 56 - %s 57 options: 58 miimon: %s 59 `, bondName, portFieldName, firstSecondaryNic, fmt.Sprintf(miimonFormat, 120))) 60 } 61 62 func brWithBondUp(bridgeName, bondName string) nmstate.State { 63 return nmstate.NewState(fmt.Sprintf(`interfaces: 64 - name: %s 65 type: bond 66 state: up 67 link-aggregation: 68 mode: active-backup 69 %s: 70 - %s 71 options: 72 miimon: %s 73 - name: %s 74 type: linux-bridge 75 state: up 76 bridge: 77 options: 78 stp: 79 enabled: false 80 port: 81 - name: %s 82 `, bondName, portFieldName, firstSecondaryNic, fmt.Sprintf(miimonFormat, 120), bridgeName, bondName)) 83 } 84 85 func bondUpWithEth1AndEth2(bondName string) nmstate.State { 86 return nmstate.NewState(fmt.Sprintf(`interfaces: 87 - name: %s 88 type: bond 89 state: up 90 ipv4: 91 address: 92 - ip: 10.10.10.10 93 prefix-length: 24 94 enabled: true 95 link-aggregation: 96 mode: balance-rr 97 options: 98 miimon: %s 99 %s: 100 - %s 101 - %s 102 `, bondName, fmt.Sprintf(miimonFormat, 140), portFieldName, firstSecondaryNic, secondSecondaryNic)) 103 } 104 105 func bondUpWithEth1Eth2AndVlan(bondName string) nmstate.State { 106 return nmstate.NewState(fmt.Sprintf(`interfaces: 107 - name: %s 108 type: bond 109 state: up 110 ipv4: 111 address: 112 - ip: 10.10.10.10 113 prefix-length: 24 114 enabled: true 115 link-aggregation: 116 mode: balance-rr 117 options: 118 miimon: %s 119 %s: 120 - %s 121 - %s 122 - name: %s.102 123 type: vlan 124 state: up 125 ipv4: 126 address: 127 - ip: 10.102.10.10 128 prefix-length: 24 129 enabled: true 130 vlan: 131 base-iface: %s 132 id: 102 133 `, bondName, fmt.Sprintf(miimonFormat, 140), portFieldName, firstSecondaryNic, secondSecondaryNic, bondName, bondName)) 134 } 135 136 var _ = Describe("NodeNetworkState", func() { 137 Context("when desiredState is configured", func() { 138 Context("with a linux bridge up with no ports", func() { 139 BeforeEach(func() { 140 updateDesiredStateAndWait(linuxBrUpNoPorts(bridge1)) 141 }) 142 AfterEach(func() { 143 updateDesiredStateAndWait(linuxBrAbsent(bridge1)) 144 for _, node := range nodes { 145 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1)) 146 } 147 resetDesiredStateForNodes() 148 }) 149 It("should have the linux bridge at currentState with vlan_filtering 0", func() { 150 for _, node := range nodes { 151 interfacesNameForNodeEventually(node).Should(ContainElement(bridge1)) 152 bridgeDescription(node, bridge1).Should(ContainSubstring("vlan_filtering 0")) 153 } 154 }) 155 }) 156 Context("with a linux bridge up with a port with disabled VLAN", func() { 157 BeforeEach(func() { 158 updateDesiredStateAndWait(linuxBrUpWithDisabledVlan(bridge1)) 159 }) 160 AfterEach(func() { 161 updateDesiredStateAndWait(linuxBrAbsent(bridge1)) 162 for _, node := range nodes { 163 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1)) 164 } 165 resetDesiredStateForNodes() 166 }) 167 It("should have the linux bridge at currentState with vlan_filtering 0 and no default vlan range configured", func() { 168 for _, node := range nodes { 169 interfacesNameForNodeEventually(node).Should(ContainElement(bridge1)) 170 bridgeDescription(node, bridge1).Should(ContainSubstring("vlan_filtering 0")) 171 172 getVLANFlagsEventually(node, firstSecondaryNic, 1). 173 Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 174 vlansCardinality(node, firstSecondaryNic).Should(Equal(0)) 175 } 176 }) 177 }) 178 Context("with a linux bridge up", func() { 179 BeforeEach(func() { 180 updateDesiredStateAndWait(linuxBrUp(bridge1)) 181 }) 182 AfterEach(func() { 183 updateDesiredStateAndWait(linuxBrAbsent(bridge1)) 184 for _, node := range nodes { 185 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1)) 186 } 187 resetDesiredStateForNodes() 188 }) 189 It("should have the linux bridge at currentState with vlan_filtering 1", func() { 190 for _, node := range nodes { 191 interfacesNameForNodeEventually(node).Should(ContainElement(bridge1)) 192 bridgeDescription(node, bridge1).Should(ContainSubstring("vlan_filtering 1")) 193 } 194 }) 195 It("should have the linux bridge at currentState", func() { 196 for _, node := range nodes { 197 interfacesNameForNodeEventually(node).Should(ContainElement(bridge1)) 198 getVLANFlagsEventually(node, bridge1, 1).Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 199 getVLANFlagsEventually(node, firstSecondaryNic, 1). 200 Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 201 hasVlans(node, firstSecondaryNic, 2, 4094).Should(Succeed()) 202 getVLANFlagsEventually(node, secondSecondaryNic, 1). 203 Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 204 hasVlans(node, secondSecondaryNic, 2, 4094).Should(Succeed()) 205 } 206 }) 207 Context("and vlan field reset at ports", func() { 208 BeforeEach(func() { 209 updateDesiredStateAndWait(linuxBrUpWithDisabledVlan(bridge1)) 210 }) 211 AfterEach(func() { 212 updateDesiredStateAndWait(linuxBrAbsent(bridge1)) 213 for _, node := range nodes { 214 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1)) 215 } 216 resetDesiredStateForNodes() 217 }) 218 It("should have the linux bridge at currentState with vlan_filtering 0 and no default vlan range configured", func() { 219 Skip("Pending on https://bugzilla.redhat.com/show_bug.cgi?id=2067058 land centos stream 8") 220 for _, node := range nodes { 221 interfacesNameForNodeEventually(node).Should(ContainElement(bridge1)) 222 bridgeDescription(node, bridge1).Should(ContainSubstring("vlan_filtering 0")) 223 224 getVLANFlagsEventually(node, firstSecondaryNic, 1). 225 Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 226 vlansCardinality(node, firstSecondaryNic).Should(Equal(0)) 227 } 228 }) 229 }) 230 231 }) 232 Context("with a active-backup miimon 100 bond interface up", func() { 233 BeforeEach(func() { 234 updateDesiredStateAndWait(bondUp(bond1)) 235 }) 236 AfterEach(func() { 237 updateDesiredStateAndWait(bondAbsent(bond1)) 238 for _, node := range nodes { 239 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bond1)) 240 } 241 resetDesiredStateForNodes() 242 }) 243 It("should have the bond interface at currentState", func() { 244 var ( 245 expectedBond = interfaceByName(interfaces(bondUp(bond1)), bond1) 246 ) 247 248 for _, node := range nodes { 249 interfacesForNode(node).Should(ContainElement(matchingBond(expectedBond))) 250 } 251 }) 252 }) 253 Context("with the bond interface as linux bridge port", func() { 254 BeforeEach(func() { 255 updateDesiredStateAndWait(brWithBondUp(bridge1, bond1)) 256 }) 257 AfterEach(func() { 258 updateDesiredStateAndWait(brAndBondAbsent(bridge1, bond1)) 259 for _, node := range nodes { 260 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1)) 261 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bond1)) 262 } 263 resetDesiredStateForNodes() 264 }) 265 It("should have the bond in the linux bridge as port at currentState", func() { 266 var ( 267 expectedInterfaces = interfaces(brWithBondUp(bridge1, bond1)) 268 expectedBond = interfaceByName(expectedInterfaces, bond1) 269 expectedBridge = interfaceByName(expectedInterfaces, bridge1) 270 ) 271 for _, node := range nodes { 272 interfacesForNode(node).Should(SatisfyAll( 273 ContainElement(matchingBond(expectedBond)), 274 ContainElement(SatisfyAll( 275 HaveKeyWithValue("name", expectedBridge["name"]), 276 HaveKeyWithValue("type", expectedBridge["type"]), 277 HaveKeyWithValue("state", expectedBridge["state"]), 278 HaveKeyWithValue("bridge", HaveKeyWithValue("port", 279 ContainElement(HaveKeyWithValue("name", bond1)))), 280 )))) 281 282 getVLANFlagsEventually(node, bridge1, 1).Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 283 hasVlans(node, bond1, 2, 4094).Should(Succeed()) 284 getVLANFlagsEventually(node, bond1, 1).Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 285 vlansCardinality(node, firstSecondaryNic).Should(Equal(0)) 286 vlansCardinality(node, secondSecondaryNic).Should(Equal(0)) 287 } 288 }) 289 }) 290 Context("with bond interface that has 2 eths as ports", func() { 291 BeforeEach(func() { 292 updateDesiredStateAndWait(bondUpWithEth1AndEth2(bond1)) 293 }) 294 AfterEach(func() { 295 updateDesiredStateAndWait(bondAbsent(bond1)) 296 for _, node := range nodes { 297 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bond1)) 298 } 299 resetDesiredStateForNodes() 300 }) 301 It("should have the bond interface with 2 ports at currentState", func() { 302 var ( 303 expectedBond = interfaceByName(interfaces(bondUpWithEth1AndEth2(bond1)), bond1) 304 ) 305 306 for _, node := range nodes { 307 interfacesForNode(node).Should(ContainElement(matchingBond(expectedBond))) 308 } 309 }) 310 }) 311 Context("with bond interface that has 2 eths as ports and vlan tag on the bond", func() { 312 BeforeEach(func() { 313 updateDesiredStateAndWait(bondUpWithEth1Eth2AndVlan(bond1)) 314 }) 315 AfterEach(func() { 316 updateDesiredStateAndWait(bondAbsent(bond1)) 317 for _, node := range nodes { 318 interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bond1)) 319 } 320 resetDesiredStateForNodes() 321 }) 322 It("should have the bond interface with 2 ports at currentState", func() { 323 var ( 324 expectedBond = interfaceByName(interfaces(bondUpWithEth1Eth2AndVlan(bond1)), bond1) 325 expectedVlanBond102 = interfaceByName(interfaces(bondUpWithEth1Eth2AndVlan(bond1)), fmt.Sprintf("%s.102", bond1)) 326 ) 327 328 for _, node := range nodes { 329 interfacesForNode(node).Should(SatisfyAll( 330 ContainElement(matchingBond(expectedBond)), 331 ContainElement(SatisfyAll( 332 HaveKeyWithValue("name", expectedVlanBond102["name"]), 333 HaveKeyWithValue("type", expectedVlanBond102["type"]), 334 HaveKeyWithValue("state", expectedVlanBond102["state"]))))) 335 } 336 }) 337 }) 338 }) 339 })