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  })