Skip to content

Commit

Permalink
Add simple tests for openvswitch bridge (#220)
Browse files Browse the repository at this point in the history
* Install nm and ovs on all the nodes.
Adding ovs installation and extending nm installation to all nodes.

Signed-off-by: Federico Paolinelli <[email protected]>

* Add ovs bridge state functions and rename linux bridge ones.
Now linux bridge ones have the linuxBr prefix.

Signed-off-by: Federico Paolinelli <[email protected]>

* Add simple ovs bridge tests.

Signed-off-by: Federico Paolinelli <[email protected]>

* Add tutorial on how to configure ovs bridge.

Signed-off-by: Federico Paolinelli <[email protected]>

* Skip ovs tests for non k8s providers.

Signed-off-by: Federico Paolinelli <[email protected]>

* Remove unused var()
Rename OVS tests as "Simple OVS bridge"

Signed-off-by: Federico Paolinelli <[email protected]>

* Remove unused var()
Rename OVS tests as "Simple OVS bridge"
Remove test with ovs bridge and no ports

Signed-off-by: Federico Paolinelli <[email protected]>

* Add test for ovs bridge with internal interface.

Signed-off-by: Federico Paolinelli <[email protected]>

* Refer to ovs as Open vSwitch

Signed-off-by: Federico Paolinelli <[email protected]>

* Add new line to install-ovs.sh

Signed-off-by: Federico Paolinelli <[email protected]>

* Mention the ovs tutorial in the readme and in the user guide

Signed-off-by: Federico Paolinelli <[email protected]>

* Use SSH and KUBECTL exported by the Makefile

Signed-off-by: Federico Paolinelli <[email protected]>

* Change ovs skipping comment, fix a typo in the readme

Signed-off-by: Federico Paolinelli <[email protected]>

* Remove unnecessary context

Signed-off-by: Federico Paolinelli <[email protected]>

* Extend ovs tutorial with internal interface

Signed-off-by: Federico Paolinelli <[email protected]>

* Remove unnecessary empty lines

Signed-off-by: Federico Paolinelli <[email protected]>

* Reword the internal interface part without mentioning the loose of connectivity

Signed-off-by: Federico Paolinelli <[email protected]>

* Install network manager only on k8s providers

Signed-off-by: Federico Paolinelli <[email protected]>

* Refactor skipping test behaviour.
Have a variable holding all the skips, append to it any local change and extras coming from outside,
and then add the skip to the args.

Signed-off-by: Federico Paolinelli <[email protected]>
  • Loading branch information
fedepaol authored and phoracek committed Oct 16, 2019
1 parent c90407b commit e39c79a
Show file tree
Hide file tree
Showing 13 changed files with 290 additions and 35 deletions.
18 changes: 14 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,21 @@ E2E_TEST_ARGS += -test.v -test.timeout=40m -ginkgo.v -ginkgo.slowSpecThreshold=6
ifdef E2E_TEST_FOCUS
E2E_TEST_ARGS += -ginkgo.focus $(E2E_TEST_FOCUS)
endif

# Unless explicitly focused, always skip the cleanup test (it removes a node)
ifdef E2E_TEST_SKIP
E2E_TEST_ARGS += -ginkgo.skip .*NNS.*cleanup.*|$(E2E_TEST_SKIP)
else
E2E_TEST_ARGS += -ginkgo.skip .*NNS.*cleanup.*
E2E_TEST_SKIP = .*NNS.*cleanup.*

# test ovs only for k8s providers
ifeq (,$(findstring k8s-,$(KUBEVIRT_PROVIDER)))
E2E_TEST_SKIP := $(E2E_TEST_SKIP)|.*OVS.*
endif

ifdef E2E_EXTRA_SKIP
E2E_TEST_SKIP := $(E2E_TEST_SKIP)|$(E2E_EXTRA_SKIP)
endif

E2E_TEST_ARGS += -ginkgo.skip $(E2E_TEST_SKIP)

ifdef E2E_TEST_EXTRA_ARGS
E2E_TEST_ARGS += $(E2E_TEST_EXTRA_ARGS)
endif
Expand Down Expand Up @@ -126,6 +135,7 @@ cluster-up: $(CLUSTER_UP)
$(CLUSTER_UP)
hack/install-nm.sh
hack/flush-secondary-nics.sh
hack/install-ovs.sh

cluster-down: $(CLUSTER_DOWN)
$(CLUSTER_DOWN)
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ After that, you can follow one of the following guides that will guide you
through node state reporting and interface configuration.
- [Report node network state](docs/user-guide-state-reporting.md)
- [Connect an Linux bridge to a node interface](docs/user-guide-policy-configure-linux-bridge.md)
- [Configure Linux bonding interface](docs/user-guide-policy-configure-linux-bond.md)
- [Connect a Linux bridge to a node interface](docs/user-guide-policy-configure-linux-bridge.md)
- [Configure an Open vSwitch bridge to a node interface](docs/user-guide-policy-configure-ovs-bridge.md)
- [Configure a Linux bonding interface](docs/user-guide-policy-configure-linux-bond.md)
## Development and Contributing
Expand Down
2 changes: 1 addition & 1 deletion automation/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ if [[ $SCRIPT_NAME =~ node-removal ]]; then
focus_tests=".*NNS.*cleanup.*"
fi

make E2E_TEST_EXTRA_ARGS="$test_args" E2E_TEST_FOCUS="$focus_tests" E2E_TEST_SKIP="$skip_tests" test/e2e
make E2E_TEST_EXTRA_ARGS="$test_args" E2E_TEST_FOCUS="$focus_tests" E2E_EXTRA_SKIP="$skip_tests" test/e2e
121 changes: 121 additions & 0 deletions docs/user-guide-policy-configure-ovs-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Tutorial: Create a Open vSwitch Bridge and Connect it to a Node Interface

Use Node Network Configuration Policy to configure a new ovs bridge `br1` connected
to node interface `eth1`.

## Requirements

Before we start, please make sure that you have your Kubernetes/OpenShift
cluster ready. In order to do that, you can follow the guides of deployment on
[local cluster](deployment-local-cluster.md) or your
[arbitrary cluster](deployment-arbitrary-cluster.md).

You must also be sure that [OpenVSwitch](https://www.openvswitch.org/) and [NetworkManager OpenVSwitch plugin](https://developer.gnome.org/NetworkManager/stable/nm-openvswitch.html) are installed in the nodes.

Please also note that on OpenShift `openvswitch` is already installed as a daemon, so only `nm-openvswitch` is required.

## Configure bridge

All you have to do in order to create the bridge on all nodes across cluster is
to apply the following policy:

```yaml
cat <<EOF | kubectl create -f -
apiVersion: nmstate.io/v1alpha1
kind: NodeNetworkConfigurationPolicy
metadata:
name: br1-eth1-policy
spec:
desiredState:
interfaces:
- name: br1
description: ovs bridge with eth1 as a port
type: ovs-bridge
state: up
bridge:
options:
stp: false
port:
- name: eth1
EOF
```

By doing this though, we will be able to create an Open vSwitch bridge but the bridge won't have an IP endpoint.
In order to provide an ip address an Open vSwitch internal interface must be used.

```yaml
cat <<EOF | kubectl create -f -
apiVersion: nmstate.io/v1alpha1
kind: NodeNetworkConfigurationPolicy
metadata:
name: br1-eth1-policy
spec:
desiredState:
interfaces:
- name: ovs0
type: ovs-interface
state: up
ipv4:
enabled: true
address:
- ip: 192.0.2.1
prefix-length: 24
- name: br1
description: ovs bridge with eth1 as a port and ovs0 as an internal interface
type: ovs-bridge
state: up
bridge:
options:
stp: true
port:
- name: eth1
type: system
- name: ovs0
type: internal
EOF
```

You can also remove the bridge with the following command:

```yaml
cat <<EOF | kubectl create -f -
apiVersion: nmstate.io/v1alpha1
kind: NodeNetworkConfigurationPolicy
metadata:
name: br1-eth1-policy
spec:
desiredState:
interfaces:
- name: br1
type: ovs-bridge
state: absent
EOF
```

## Selecting nodes

`NodeNetworkConfigurationPolicy` supports node selectors. Thanks to them you can
select a subset of nodes or a specific node by its name:

```yaml
cat <<EOF | kubectl create -f -
apiVersion: nmstate.io/v1alpha1
kind: NodeNetworkConfigurationPolicy
metadata:
name: br1-eth1-policy
spec:
nodeSelector:
kubernetes.io/hostname: node01
desiredState:
interfaces:
- name: ovs-br0
description: Ovs bridge with eth1 as a port
type: ovs-bridge
state: up
bridge:
options:
stp: false
port:
- name: eth1
EOF
```
3 changes: 2 additions & 1 deletion docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ the tutorials.

- [Report node network state](user-guide-state-reporting.md)
- [Create a Linux bridge and connect it to a node interface](user-guide-policy-configure-linux-bridge.md)
- [Configure Linux bonding interface](user-guide-policy-configure-linux-bond.md)
- [Create an Open vSwitch bridge and connect it to a node interface](user-guide-policy-configure-ovs-bridge.md)
- [Configure a Linux bonding interface](user-guide-policy-configure-linux-bond.md)
26 changes: 14 additions & 12 deletions hack/install-nm.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
#!/bin/bash -e

# TODO: Iterate all the nodes
script_dir=$(dirname "$(readlink -f "$0")")
ssh=$script_dir/../kubevirtci/cluster-up/ssh.sh
function install_nm_on_node() {
node=$1
$SSH $node sudo -- yum install -y NetworkManager NetworkManager-ovs
$SSH $node sudo -- systemctl daemon-reload
$SSH $node sudo -- systemctl restart NetworkManager
echo "Check NetworkManager is working fine on node $node"
$SSH $node -- nmcli device show > /dev/null
}

if [[ "$KUBEVIRT_PROVIDER" =~ k8s ]]; then
echo 'Install NetworkManager on the node'
$ssh node01 -- sudo yum install -y NetworkManager
$ssh node01 -- sudo systemctl daemon-reload
$ssh node01 -- sudo systemctl restart NetworkManager

echo 'Check NetworkManager is working fine'
$ssh node01 -- nmcli device show > /dev/null
fi
if [[ "$KUBEVIRT_PROVIDER" =~ k8s- ]]; then
echo 'Install NetworkManager on nodes'
for node in $($KUBECTL get nodes --no-headers | awk '{print $1}'); do
install_nm_on_node "$node"
done
fi
18 changes: 18 additions & 0 deletions hack/install-ovs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash -e


function install_ovs_on_node() {
node=$1
$SSH $node -- sudo yum install -y http://cbs.centos.org/kojifiles/packages/openvswitch/2.9.2/1.el7/x86_64/openvswitch-2.9.2-1.el7.x86_64.rpm http://cbs.centos.org/kojifiles/packages/openvswitch/2.9.2/1.el7/x86_64/openvswitch-devel-2.9.2-1.el7.x86_64.rpm http://cbs.centos.org/kojifiles/packages/dpdk/17.11/3.el7/x86_64/dpdk-17.11-3.el7.x86_64.rpm
$SSH $node -- sudo systemctl daemon-reload
$SSH $node -- sudo systemctl restart openvswitch
}

# we currently skip ovs for non k8s providers.
if [[ "$KUBEVIRT_PROVIDER" =~ k8s- ]]; then
echo 'Installing Open vSwitch on nodes'

for node in $($KUBECTL get nodes --no-headers | awk '{print $1}'); do
install_ovs_on_node "$node"
done
fi
4 changes: 2 additions & 2 deletions test/e2e/nns_conditions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ func invalidConfig(bridgeName string) nmstatev1alpha1.State {
var _ = Describe("NodeNetworkStateCondition", func() {
Context("when applying valid config", func() {
BeforeEach(func() {
updateDesiredState(brUp(bridge1))
updateDesiredState(linuxBrUp(bridge1))
})
AfterEach(func() {
updateDesiredState(brAbsent(bridge1))
updateDesiredState(linuxBrAbsent(bridge1))
for _, node := range nodes {
interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1))
}
Expand Down
6 changes: 3 additions & 3 deletions test/e2e/node_selector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ var _ = Describe("NodeSelector", func() {

Context("when policy is set with node selector not matching any nodes", func() {
BeforeEach(func() {
setDesiredStateWithPolicyAndNodeSelector(bridge1, brUp(bridge1), nonexistentNodeSelector)
setDesiredStateWithPolicyAndNodeSelector(bridge1, linuxBrUp(bridge1), nonexistentNodeSelector)
})

AfterEach(func() {
setDesiredStateWithPolicy(bridge1, brAbsent(bridge1))
setDesiredStateWithPolicy(bridge1, linuxBrAbsent(bridge1))
for _, node := range nodes {
interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1))
}
Expand All @@ -31,7 +31,7 @@ var _ = Describe("NodeSelector", func() {

Context("and we remove the node selector", func() {
BeforeEach(func() {
setDesiredStateWithPolicyAndNodeSelector(bridge1, brUp(bridge1), map[string]string{})
setDesiredStateWithPolicyAndNodeSelector(bridge1, linuxBrUp(bridge1), map[string]string{})
})

It("should update all nodes", func() {
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/rollback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ var _ = Describe("rollback", func() {
AfterEach(func() {
By("Rename vlan-filtering.bak to vlan-filtering to leave it as it was")
runAtPods("sudo", "mv", "/usr/local/bin/vlan-filtering.bak", "/usr/local/bin/vlan-filtering")
updateDesiredState(brAbsent(bridge1))
updateDesiredState(linuxBrAbsent(bridge1))
for _, node := range nodes {
interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1))
}
resetDesiredStateForNodes()
})
It("should rollback failed state configuration", func() {
updateDesiredState(brUpNoPorts(bridge1))
updateDesiredState(linuxBrUpNoPorts(bridge1))
for _, node := range nodes {
By("Wait for reconcile to fail")
nodeNetworkStateFailingConditionStatusEventually(node).
Expand Down
9 changes: 4 additions & 5 deletions test/e2e/simple_bridge_and_bond.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,13 @@ func bondUpWithEth1AndEth2(bondName string) nmstatev1alpha1.State {
}

var _ = Describe("NodeNetworkState", func() {
var ()
Context("when desiredState is configured", func() {
Context("with a linux bridge up with no ports", func() {
BeforeEach(func() {
updateDesiredState(brUpNoPorts(bridge1))
updateDesiredState(linuxBrUpNoPorts(bridge1))
})
AfterEach(func() {
updateDesiredState(brAbsent(bridge1))
updateDesiredState(linuxBrAbsent(bridge1))
for _, node := range nodes {
interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1))
}
Expand All @@ -108,10 +107,10 @@ var _ = Describe("NodeNetworkState", func() {
})
Context("with a linux bridge up", func() {
BeforeEach(func() {
updateDesiredState(brUp(bridge1))
updateDesiredState(linuxBrUp(bridge1))
})
AfterEach(func() {
updateDesiredState(brAbsent(bridge1))
updateDesiredState(linuxBrAbsent(bridge1))
for _, node := range nodes {
interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1))
}
Expand Down
58 changes: 58 additions & 0 deletions test/e2e/simple_ovs_bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package e2e

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Simple OVS bridge", func() {
Context("when desiredState is configured with an ovs bridge up", func() {
BeforeEach(func() {
updateDesiredState(ovsBrUp(bridge1))
})
AfterEach(func() {
updateDesiredState(ovsBrAbsent(bridge1))
for _, node := range nodes {
interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1))
}
resetDesiredStateForNodes()
})
It("should have the ovs bridge at currentState", func() {
for _, node := range nodes {
interfacesForNode(node).Should(ContainElement(SatisfyAll(
HaveKeyWithValue("name", bridge1),
HaveKeyWithValue("type", "ovs-bridge"),
HaveKeyWithValue("state", "up"),
)))
}
})
})
Context("when desiredState is configured with an ovs bridge with internal port up", func() {
BeforeEach(func() {
updateDesiredState(ovsbBrWithInternalInterface(bridge1))
})
AfterEach(func() {
updateDesiredState(ovsBrAbsent(bridge1))
for _, node := range nodes {
interfacesNameForNodeEventually(node).ShouldNot(ContainElement(bridge1))
}
resetDesiredStateForNodes()
})
It("should have the ovs bridge at currentState", func() {
for _, node := range nodes {
interfacesForNode(node).Should(SatisfyAll(
ContainElement(SatisfyAll(
HaveKeyWithValue("name", bridge1),
HaveKeyWithValue("type", "ovs-bridge"),
HaveKeyWithValue("state", "up"),
)),
ContainElement(SatisfyAll(
HaveKeyWithValue("name", "ovs0"),
HaveKeyWithValue("type", "ovs-interface"),
HaveKeyWithValue("state", "up"),
)),
))
}
})
})
})
Loading

0 comments on commit e39c79a

Please sign in to comment.