[lighty.io] OVSDB & OpenFlow

OVSDB and OpenFlow controller based on lighty.io

PANTHEON.tech has recently published an example application of an SDN controller, using RESTCONF Northbound module and OVSDB + OpenFlow Southbound modules.

In this blog we are going to describe, how to use the SDN controller with an Open vSwitch instance running in OpenStack.

Open vSwitch, OVSDB & OpenFlow

Open vSwitch is open source virtual switch which uses OVSDB (Open vSwitch Database) and OVSDB management protocol for management of virtual OpenFlow switches referred to as bridges.

The bridges are configurable by OpenFlow protocol according to OpenFlow switch specification.

We have already written a blog about OpenFlow protocol and its support by lighty.io. There’s also example SDN controller application called lighty-community-restconf-ofp-app. It utilizes RESTCONF northbound module and OpenFlow southbound module only. You can find it at our GitHub repository.

Connecting to Open vSwitch of OpenStack

In this blog, we will show you some example setup and sequence of requests using both OVSDB and OpenFlow protocols implemented as SB modules in lighty.io.

We have published an example SDN controller called lighty-community-restconf-ovsdb-app, which utilizes RESTCONF northbound module and OVSDB southbound module only. You can check its README.md file and Postman collection for more details.

As we have already mentioned, Open vSwitch used in the testing setup is running in OpenStack. Here is a picture and description of the setup:

The SDN controller is running on a machine with IP address 10.14.0.160 with RESTCONF NB plugin & opening Port 8888.

  • Postman or curl requests are submitted from the same machine where SDN controller is running, so URLs use localhost address (127.0.0.1)
  • Open vSwitch instance is running on a machine with IP address 10.14.0.103 (the same address used in ovs-vsctl command explained above) The tested instance has been set up by DevStack scripts but it can be any Open vSwitch instance running in OpenStack Network node, compute node or outside of OpenStack.
  • TCP port used for OVSDB server(s) is 6640
  • TCP port used by OpenFlow server(s) is 6633

Example workflow

Subsequent requests can be found in the repository at GitHub, in the form of Postman collection. In the README.md & in this blog, we are using a curl command to send the RESTCONF requests. We are also using the Python module json.tool for printing of JSON responses.

1. Configure OVSDB manager of Open vSwitch

The following piece of the ovsvsctl show command output shows the initial configuration of the Open vSwitch and its state, where we can see that the Neutron service is connected as OVSDB manager (Manager “ptcp:6640:127.0.0.1”). The Neutron service is also configured as OpenFlow controller for the bridge br-tun (Controller “tcp:127.0.0.1:6633”) and br-tun is connected to the controller.

sudo ovs-vsctl show
729321e6-991d-4ae5-a4f9-96b1e2919596
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
    Bridge br-tun
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        Port br-tun
            Interface br-tun
                type: internal
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}

Using this ovs-vsctl command we set up Open vSwitch to listen to second OVSDB manager connection at TCP port 6640 and an interface with IP address 10.14.0.103. The command keeps the configuration for Neutron service as OVSDB manager (ptcp:6640:127.0.0.1).

sudo ovs-vsctl set-manager ptcp:6640:127.0.0.1 ptcp:6640:10.14.0.103

The output of “ovs-vsctl show” command must be changed, as a result of the command above. There should be two managers configured, but only one of them is connected. The connected one is Neutron service and we have to start and configure the SDN controller, in order to initiate OVSDB connection from the controller’s side.

sudo ovs-vsctl show
729321e6-991d-4ae5-a4f9-96b1e2919596
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
    Manager "ptcp:6640:10.14.0.103"
    Bridge br-tun
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        Port br-tun
            Interface br-tun
                type: internal
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}

2. Setup OVSDB connection

This RESTCONF request results in OVSDB session initiation to the pre-configured OVSDB server in the Open vSwitch.

curl -v --request PUT \
  --url http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1/node=ovsdb%3A%2F%2FHOST1 \
  --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
  --header 'Content-Type: application/json' \
  --data '{
        "network-topology:node": [
          {
            "node-id": "ovsdb://HOST1",
            "connection-info": {
              "ovsdb:remote-port": "6640",
              "ovsdb:remote-ip": "10.14.0.103"
            }
          }
        ]
      }'

You can check whether the session has been established using the “ovs-vsctl” show command. Both OVSDB managers should be connected now. You can also use the next RESTCONF request.

sudo ovs-vsctl show
729321e6-991d-4ae5-a4f9-96b1e2919596
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
    Manager "ptcp:6640:10.14.0.103"
        is_connected: true
    Bridge br-tun
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        Port br-tun
            Interface br-tun
                type: internal
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}

3. Retrieve OVSDB network topology data (all nodes)

Now, we are going to ask lighty.io (meaning the SDN controller) about the state of OVS. In this step, we are going to access the SDN controller through a RESTCONF request. The controller will then create a request via the OVSDB protocol and OVS will create the same request, as shown in OVS-VSCTL show.

This RPC request returns the same data as an output of the ovs-vsctl show command. But the show command returns text output. In case of RPC request, the output is formatted as JSON or XML (depending on the Accept header) what is more appropriate for API between software layers of SDN solutions (i.e.: RPCs returning JSON or XML formatted output of commands are SDN ready).

NOTE: In this state, the OVSDB connection between SDN controller and Open vSwitch is established. There’s also another connection which is used by Neutron service. You can check this in the output of ovs-vsctl show:

Manager "ptcp:6640:127.0.0.1"
    is_connected: true
Manager "ptcp:6640:10.14.0.103"
    is_connected: true

… and also in the output of RESTCONF request:

"ovsdb:manager-entry": [
    {
        "target": "ptcp:6640:127.0.0.1",
        "connected": true,
        "number_of_connections": 5
    },
    {
        "target": "ptcp:6640:10.14.0.103",
        "connected": true,
        "number_of_connections": 1
    }
]

The only OpenFlow connection(s) in this state are used by Neutron service, you can see it in the output of ovs-vsctl show:

    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
    Bridge br-ex
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
... and the same for other bridges

… and also in the output of RESTCONF request:

... all bridges should have:
                    "ovsdb:controller-entry": [
                        {
                            "target": "tcp:127.0.0.1:6633",
                            "controller-uuid": "de378546-d727-4631-8d46-fa57d78737d9",
                            "is-connected": true
                        }
                    ],

Here is an example of complete ovs-vsctl show command output:

sudo ovs-vsctl show
729321e6-991d-4ae5-a4f9-96b1e2919596
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
    Manager "ptcp:6640:10.14.0.103"
        is_connected: true
    Bridge br-tun
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        Port br-tun
            Interface br-tun
                type: internal
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}
    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        Port "tap2f509846-a3"
            tag: 4
            Interface "tap2f509846-a3"
                type: internal
        Port "qr-40a33ce6-dd"
            tag: 6
            Interface "qr-40a33ce6-dd"
                type: internal
        Port "tape9302402-e4"
            tag: 1
            Interface "tape9302402-e4"
                type: internal
        Port "tap63c483cb-87"
            tag: 6
            Interface "tap63c483cb-87"
                type: internal
        Port "tap960bd59d-2e"
            tag: 5
            Interface "tap960bd59d-2e"
                type: internal
        Port "tap74a59f96-94"
            tag: 3
            Interface "tap74a59f96-94"
                type: internal
        Port "qg-9285bad8-81"
            tag: 2
            Interface "qg-9285bad8-81"
                type: internal
        Port "tap3792b4af-27"
            tag: 7
            Interface "tap3792b4af-27"
                type: internal
        Port int-br-infra
            Interface int-br-infra
                type: patch
                options: {peer=phy-br-infra}
        Port "qr-9da1a177-1a"
            tag: 7
            Interface "qr-9da1a177-1a"
                type: internal
        Port "qg-7f8467e0-a4"
            tag: 2
            Interface "qg-7f8467e0-a4"
                type: internal
        Port "qr-7da2c452-59"
            tag: 1
            Interface "qr-7da2c452-59"
                type: internal
        Port "qg-5a4bd0e5-a0"
            tag: 2
            Interface "qg-5a4bd0e5-a0"
                type: internal
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port int-br-ex
            Interface int-br-ex
                type: patch
                options: {peer=phy-br-ex}
        Port "qr-91d9970c-ef"
            tag: 1
            Interface "qr-91d9970c-ef"
                type: internal
        Port br-int
            Interface br-int
                type: internal
    Bridge br-ex
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        Port br-ex
            Interface br-ex
                type: internal
        Port phy-br-ex
            Interface phy-br-ex
                type: patch
                options: {peer=int-br-ex}
    Bridge br-infra
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        Port br-infra
            Interface br-infra
                type: internal
        Port phy-br-infra
            Interface phy-br-infra
                type: patch
                options: {peer=int-br-infra}
    ovs_version: "2.9.2"

The related RESTCONF request:

curl -v --request GET \
  --url http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1 \
  --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
  --header 'Accept: application/json' \
  | python -m json.tool

The example output of the request can be found here. 

4. Retrieve specific node from OVSDB topology data (node-id: “ovsdb://HOST1”)

curl -v --request GET \
  --url http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1/node=ovsdb%3A%2F%2FHOST1 \
  --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
  --header 'Accept: application/json' \
  | python -m json.tool

Since there’s only one OVSDB topology node in the SDN controller, the output contains the same data as in case of the previous request.

5. Retrieve OVSDB data of specific bridge (br-int):

curl -v --request GET \
--url http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1/node=ovsdb%3A%2F%2FHOST1%2Fbridge%2Fbr-int \
--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
--header 'Accept: application/json' \
| python -m json.tool

This request returns only a subset of data related to the bridge br-int.

6. Setup SDN controller as OpenFlow controller for bridge br-int:

curl -v --request PUT \
  --url http://localhost:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1/node=ovsdb%3A%2F%2FHOST1%2Fbridge%2Fbr-int \
  --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
  --header 'Content-Type: application/json' \
  --data '{
            "network-topology:node": [
                  {
                    "node-id": "ovsdb://HOST1/bridge/br-int",
                       "ovsdb:bridge-name": "br-int",
                        "ovsdb:controller-entry": [
                          {
                            "target": "tcp:10.14.0.160:6633"
                          }
                        ]
                  }
              ]
          }'

 7. Check the state of the OpenFlow connection – retrieve the controller-entry list of br-int:

curl -v --request GET \
  --url http://localhost:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1/node=ovsdb%3A%2F%2FHOST1%2Fbridge%2Fbr-int/controller-entry=tcp%3A10.14.0.160%3A6633 \
  --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
  --header 'Accept: application/json' \
  | python -m json.tool

The GET request above only retrieves OVSDB data of OpenFlow connection between Open vSwitch and SDN controller. The output only contains one entry (if the OpenFlow connection is established then the item “is-connected” is set to true):

{
    "ovsdb:controller-entry": [
        {
            "controller-uuid": "5bfe55c9-70da-4e3b-b1ff-c6ecc8b6e62c",
            "is-connected": true,
            "target": "tcp:10.14.0.160:6633"
        }
    ]
}

In the output of ovs-vsctl show command or previous get requests to OVSDB, you will also see a connection between Open vSwitch and Neutron service:

"ovsdb:controller-entry": [
    {
        "target": "tcp:127.0.0.1:6633",
        "controller-uuid": "57b4a453-5ee5-40ea-953a-4132319ad1eb",
        "is-connected": true
    },
    {
        "target": "tcp:10.14.0.160:6633",
        "controller-uuid": "bc0af587-fc76-44d9-ab24-fe926b1099e6",
        "is-connected": true
    }
],

8. Retrieve OpenFlow network topology:

curl -v --request GET \
--url http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=flow%3A1 \
--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
--header 'Accept: application/json' \
| python -m json.tool

Here’s an example of the output, where you can find a node-id of the Open vSwitch instance. This can be used in subsequent requests.

9. Retrieve OpenFlow data of all nodes (reply includes also OpenFlow flow tables):

curl -v --request GET \
  --url http://127.0.0.1:8888/restconf/data/opendaylight-inventory:nodes \
  --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
  --header 'Accept: application/json' \
  | python -m json.tool

Here’s is the output from our example.

10. Retrieve OpenFlow data of a specific node (reply includes also OpenFlow flow tables):

This request uses node-id in the URL. The node-id can be found in reply to requests 8. and 9.

curl -v --request GET \
  --url http://127.0.0.1:8888/restconf/data/opendaylight-inventory:nodes/node=openflow%3A143423481343818 \
  --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
  --header 'Accept: application/json' \
  | python -m json.tool

11. Retrieve specific OpenFlow table of specific node:

curl -v --request GET \
--url http://127.0.0.1:8888/restconf/data/opendaylight-inventory:nodes/node=openflow%3A143423481343818/table=0 \
--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
--header 'Accept: application/json' \
| python -m json.tool

12. Delete OpenFlow controller connection from Open vSwitch configuration of bridge br-int:

curl -v --request DELETE \
--url http://localhost:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1/node=ovsdb%3A%2F%2FHOST1%2Fbridge%2Fbr-int/controller-entry=tcp%3A10.14.0.160%3A6633 \
--header 'Authorization: Basic YWRtaW46YWRtaW4='

13. Close OVSDB connection to the Open vSwitch instance:

curl -v --request DELETE \
--url http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=ovsdb%3A1/node=ovsdb%3A%2F%2FHOST1 \
--header 'Authorization: Basic YWRtaW46YWRtaW4='

OpenFlow manager (OFM)

For this setup, you can also use the OFM application, which provides GUI for management of OpenFlow switches. You can connect OFM to the controller and retrieve OpenFlow tables of a specific switch – plus the flows will be graphically displayed. You can also modify existing flows, or add new using OFM. See our blog about OpenFlow integration.

Conclusion

In this blog, we have described the use of an SDN controller with OVSDB and OpenFlow support. It can be used to manage Open vSwitch virtual switches. We have used Open vSwitch running in an OpenStack environment and we described the sequence of requests needed to connect SDN controller to OVSDB and OpenFlow interfaces of Open vSwitch.

This approach can be used to manage Open vSwitch instances, running in OpenStack Network nodes and Compute nodes without breaking the connection between Neutron service and the Open vSwitch instances. But the SDN controller example and described requests can be used with any virtual or physical network device supporting OpenFlow or OVSDB or both protocols.

The usage of RESTCONF SB plugin in the SDN controller means, that any application can implement RESTCONF (HTTP/REST/API) client and communicate with the controller, as we have demonstrated by Postman application and OpenFlow manager (OFM) in the previous blog.