relaxdiego

In my previous post, I briefly introduced the OVSDB Management Protocol in the process of talking about hardware VTEPs. In this post, I will focus on specific OVSDB Server features. And to do that, I will (mis)use it outside of SDN.

Huh??? But OVSDB == SDN!

Actually, no. OVSDB can exist independent of Open vSwitch. In fact, what’s actually happening under the covers is that Open vSwitch connects to OVSDB and monitors certain tables in the Open_vSwitch database for updates. When OVS receives update notifications from OVSDB, the former then gets to work creating and manipulating bridges, ports, etc.

Alright, so OVSDB is a Management Protocol!

Actually, no. OVSDB does not do anything to the host system. All it does is receive messages relating to data it has and responds accordingly. If anything needs to be done on the host system, that will be the job of external clients connecting to and monitoring tables inside OVSDB.

So…OVSDB is just a database engine?

Bingo! And here are its features:

  1. Native JSON RPC v1 support. In fact, this is the only way you can talk to it;
  2. Persistent plain TCP socket connections. No HTTP overhead. You can optionally wrap your connections with SSL/TLS for super secret message sending;
  3. Journaled in-memory datastore;
  4. Atomic transactions;
  5. Relational data with automatic garbage collection of orphaned rows;
  6. And more!

Hands-on Part 1: Using OVSDB as a To Do List database

I hope I got you intrigued about OVSDB’s features. Let’s dive deeper by creating our own database using the ovsdb-client CLI tool. The first database we will create is a To Do List database. But before everything else, let’s reload ovs-lab to a known good state.

$ cd path/to/ovs-lab

$ git pull

$ vagrant reload --provision

Once reloading is done, let’s ssh to each of the servers on different terminals via vagrant ssh server1, vagrant ssh internet, and vagrant ssh server2. Next, inside of server1, let’s login as root and ensure that Open vSwitch and OVSDB are not running:

[email protected]:~$ sudo su -

[email protected]:~# ovs-appctl -t ovs-vswitchd exit

[email protected]:~# ovs-appctl -t ovsdb-server exit

Next, we’ll create a new database using a schema definition file that I’ve included in the lesson03 directory named todo.ovsschema:

[email protected]:~# mkdir /etc/lesson03

[email protected]:~# ovsdb-tool create /etc/lesson03/todo.db /vagrant/shared/lesson03/todo.ovsschema

Let’s take a moment to read the schema definition we just loaded. I’ll focus on the part of the schema definition that talks about the tables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"List": {
"columns": {
"name": {
"type": "string"
},
"items": {
"type": {
"key": {
"type": "uuid",
"refTable": "Item"
},
"min": 0,
"max": "unlimited"
}
}
},
"isRoot": true,
"indexes": [
["name"]
]
},
"Item": {
"columns": {
"status": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}

In lines 2 and 23 are the names of our two tables, List and Item. The List table has two columns: name, and items. The name column is easy to read: it’s just a string datatype. The items column, on the other hand, is more involved, but it basically is a set of references to the UUID column of the Item table and it can have 0 or more references.

Yes, you read that right. In traditional database schemas, the child table references the parent table. In OVSDB, it’s the other way around and this primarily (probably exclusively) has something to do with its automatic garbage collection feature. Let me explain by pointing out the “isRoot” key in the schema (line 18). When a table’s isRoot key is set to true, that means that its records will never be garbage collected. However, if the isRoot key is false or not defined, then any record in that table that is not referenced by any other records in other tables will be automatically garbage collected. This feature is very crucial for environments like networking where communication between devices (e.g. OVS and controller) must be as efficient as possible.

We’ll see that in action shortly. First, let’s continue by starting OVSDB and loading our newly created database:

[email protected]:~# ovsdb-server --pidfile --detach --log-file --remote punix:/var/run/openvswitch/db.sock /etc/lesson03/todo.db

Let’s check that our database was loaded properly:

[email protected]:~# ovsdb-client list-dbs
todo

If that command didn’t print todo then you probably missed a step. Go back and review the steps. If you have it working properly, let’s proceed by having OVSDB listening for connections on port 6640.

[email protected]:~# ovs-appctl -t ovsdb-server ovsdb-server/add-remote ptcp:6640

SIDENOTE: The p in ptcp means passive. You can alternatively make OVSDB accept only SSL/TLS-based connections by using pssl. See man ovsdb-server for more info on that.

Alright, we’ve now set up our To Do List database. If at any point after this you need to reset the state of the database, just execute /vagrant/shared/lesson03/reset_todo_db.sh while logged in as root in server1:

[email protected]:~# /vagrant/shared/lesson03/reset_todo_db.sh
2014-09-22T04:57:34Z|00001|vlog|INFO|opened log file /var/log/openvswitch/ovsdb-server.log

Let’s log in to server2 and monitor the List table in server1 for changes:

$ vagrant ssh server2

[email protected]:~$ ovsdb-client monitor tcp:192.168.1.10:6640 todo List

That command should not return anything since it’s just waiting for the OVSDB server in server1 to send update notifications. So let’s login to internet and add a row on the List table:

$ vagrant ssh internet

[email protected]:~$ ovsdb-client transact tcp:192.168.1.10:6640 '["todo",{"op":"insert", "table":"List", "row":{"name":"List1"} }]'

[{"uuid":["uuid","cb706f22-ecae-432f-85ef-ecf7197849bc"]}]

In the above command, we used the ovsdb-client CLI tool to execute a transaction against the OVSDB server in server1. The last argument of the command specifies the parameters of that transaction. In the first element of that array, we say that we are performing the transaction against our todo database. The second element is our one and only operation which happens to be an insert on the List table with a row named List1. After the transaction has completed, OVSDB responds with the UUID of our newly created row.

Shifting back to our server2 terminal, we can see that OVSDB sent us an update notification:

row                                  action items name    _version
------------------------------------ ------ ----- ------- ------------------------------------
cb706f22-ecae-432f-85ef-ecf7197849bc insert [] "List1" 28055108-2115-42af-9224-67e794f3dd33

Neat! Now let’s try creating Item rows. Ctrl-C in server2 and issue the command below to start monitoring the Item table.

[email protected]:~$ ovsdb-client monitor tcp:192.168.1.10:6640 todo Item

Head back to internet and execute the following command:

[email protected]:~$ ovsdb-client transact tcp:192.168.1.10:6640 '["todo",{"op":"insert", "table":"Item", "row":{"description":"Sample Item1"} }]'

[{"uuid":["uuid","4f08243f-1dbe-4820-8ed9-4559bbc44474"]}]

Shift back to server2 and you will see…nothing. Why??? Remember our .ovsschema file above? We specified that the Item table is not a root table. Therefore, any row we create there that’s not referenced by a List row is automatically garbage collected. In the above command, as soon as OVSDB created the row, it was immediately deleted so OVSDB didn’t bother sending an update notification to server2. We can double check by running the select method against the Item table back in internet:

[email protected]:~$ ovsdb-client transact tcp:192.168.1.10:6640 '["todo",{"op":"select", "table":"Item", "where": []}]'

[{"rows":[]}]

Cool. Garbage collection works. Now let’s try and create an Item that won’t be garbage collected. Head back to internet and execute the following transaction against server1:

[email protected]:~$ ovsdb-client transact tcp:192.168.1.10:6640 '["todo",{"op":"insert", "table":"Item", "row":{"description":"Sample Item1", "status": "new"}, "uuid-name":"newitem" }, {"op":"insert", "table":"List", "row":{"name":"List2", "items":["set",[["named-uuid","newitem"]]]}}]'

[{"uuid":["uuid","8ccb47e3-c216-447d-8c8e-70854e8532b9"]},{"uuid":["uuid","7762efbf-ce69-4d71-b6e5-30315b5077f7"]}]

There’s a lot more going on above so let’s pretty-fy the last argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[
"todo",
{
"op": "insert",
"table": "Item",
"row": {
"description": "Sample Item1",
"status": "new"
},
"uuid-name": "newitem"
},
{
"op": "insert",
"table": "List",
"row": {
"name": "List2",
"items": [
"set",
[
["named-uuid", "newitem"]
]
]
}
}
]
  • In line 2, we can see that this transaction is still for the todo database
  • Lines 3 to 11 is our first operation and it inserts a new row in the Item table
  • Line 10 is something new and is useful if you want to reference the UUID of new Item in a subsequent operation within the same transaction. You’ll see this being used in the next operation.
  • Lines 12 to 24 is our second operation and it inserts a new row in the List table. To reference the item we created in the previous operation, we make use of its uuid-name.

NOTE: The uuid-name is temporary and is only valid within the transaction. If you ever want to reference the row after the transaction has completed, you can A) capture the UUID provided in OVSDB’s response or B) run a select against the table and capture the UUID from that response.

Head back to server2 to see the following update:

row                                  action description    status _version
------------------------------------ ------ -------------- ------ ------------------------------------
8ccb47e3-c216-447d-8c8e-70854e8532b9 insert "Sample Item1" new 4d8ba412-cb02-4156-af8b-9dc0dbc68f15

Let’s add one more Item to List2:

[email protected]:~$ ovsdb-client transact tcp:192.168.1.10:6640 '["todo",{"op":"insert", "table":"Item", "row":{"description":"Sample Item2", "status": "new"}, "uuid-name":"anotheritem" }, {"op":"mutate", "table":"List", "where":[["name", "==", "List2"]], "mutations":[["items", "insert", ["set",[["named-uuid", "anotheritem"]]]]] }]'

[{"uuid":["uuid","bf1573b8-3506-4b88-bf99-ab9c7f148cb5"]},{"count":1}]

Let’s look at the transaction parameters more closely:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[
"todo",
{
"op": "insert",
"table": "Item",
"row": {
"description": "Sample Item2",
"status": "new"
},
"uuid-name": "anotheritem"
},
{
"op": "mutate",
"table": "List",
"where": [
["name", "==", "List2"]
],
"mutations": [
[
"items",
"insert",
[
"set",
[
["named-uuid", "anotheritem"]
]
]
]
]
}
]
  • As with the previous transaction, this one operates on the todo database.
  • Lines 3 to 10 is just another insert operation on the Item table. Notice that we gave it a uuid-name of anotheritem.
  • Lines 12 to 30 is a mutate operation on the List table, specifically on our List2 row. This operation is useful for manipulating columns of type set or map where we want to add or remove references to other tables. In this case, we are inserting the newly created Item’s UUID.

Let’s check how our todo database looks like by using the dump command

[email protected]:~$ ovsdb-client dump tcp:192.168.1.10:6640 todo

Item table
_uuid description status
------------------------------------ -------------- ------
f49d6f47-2fbd-4f6c-b49c-8c57f36eb79f "Sample Item1" new
88bd4bcd-a199-41d6-9ff5-0eb406d9e776 "Sample Item2" new

List table
_uuid items name
------------------------------------ ---------------------------------------------------------------------------- -------
da864eed-b4e8-4bcb-b183-e8fc3622bbfb [] "List1"
f0d4fc1a-9862-4091-b07d-69f2b578bf80 [88bd4bcd-a199-41d6-9ff5-0eb406d9e776, f49d6f47-2fbd-4f6c-b49c-8c57f36eb79f] "List2"

You should have two Item rows and two List rows with List2 referencing both Item rows.

Hands-on Part 2: Using OVSDB as a Chat database

Here’s something a little bit more fun. I’ve created two python scripts that use OVSDB as a chat room. It’s a very limited implementation in that one script can only send a message and the other can only receive them but it should be enough to further showcase some of OVSDB’s features.

Go to server1 and execute the following:

[email protected]:~# /vagrant/shared/lesson03/reset_chat_db.sh

2014-09-22T06:44:38Z|00001|vlog|INFO|opened log file /var/log/openvswitch/ovsdb-server.log

You’ve just reset OVSDB and made it load a different database called chat. Start tailing its logs. The reset script above set the loglevel to debug so that you can see the messages passing between OVSDB and its clients.

[email protected]:~# tail -f /var/log/openvswitch/ovsdb-server.log

Now go to internet and execute the receiver script:

[email protected]:~$ python /vagrant/shared/lesson03/receiver.py 192.168.1.10
Connecting to chat room...
Connected.
(OVSDB) PING?
PONG!
(OVSDB) PING?
PONG!

You should see OVSDB sends pings regularly and the receiver responding with a pong. This is OVSDB’s inactivity probe sequence and is its way of checking if the remote client is still connected. If our receiver did not reply back in time, OVSDB promptly closes the socket.

Next, let’s go to server2 and run the sender script:

[email protected]:~$ python /vagrant/shared/lesson03/sender.py 192.168.1.10 relaxdiego

Your message:

Type a message and hit ENTER to see your message pop up on the receiver terminal. Also check the OVSDB logs to see your message being received and passed on to the receiver.

Feel free to poke around the code to understand how the scripts talk to OVSDB! If you want to learn some more about the various other methods that you can call against it, please consult IETF RFC 7047

Hands-on Part 3: Open vStamps Database

Finally, we can’t end this post without referencing this tweet:

So let’s track our stamp collection! First, do the following:

$ vagrant ssh server1

[email protected]:~$ sudo su -

[email protected]:~# cd /vagrant/shared/lesson03/stamps

[email protected]:/vagrant/shared/lesson03/stamps# ./reset_stamps_db.sh

You’ve just reset OVSDB in server1 and turned it into a pre-seeded stamp collection database! Look at the script for more information. When you’re done with that, let’s prepare to turn server1 into a web server by doing the following:

[email protected]:/vagrant/shared/lesson03/stamps# apt-get install -y python-pip

[email protected]:/vagrant/shared/lesson03/stamps# pip install flask

[email protected]:/vagrant/shared/lesson03/stamps# iptables -A INPUT -p tcp --dport 8080 -j ACCEPT

Then, let’s execute our web app:

[email protected]:/vagrant/shared/lesson03/stamps# ./main.py
* Running on http://192.168.101.10:8080/
* Restarting with reloader

In your local machine, browse on over to the above IP and port. Enjoy!

Parting Thoughts

OVSDB is a very interesting and versatile tool with potential applications independent of Open_vSwitch. In fact, in my previous post, we’ve seen that we can even use it as a front for hardware VTEPs. In this post, we’ve seen that it can even be applied to other things outside of SDN. Now, while I can’t vouch for its efficacy outside of SDN (since I have not really done any performance tests on it outside of SDN), I hope this post did its job of showing what you can do with the tool.

Also, this post talks about just a small fraction of what OVSDB can do. To learn more about the other methods available, please see IETF RFC 7047.

That’s it for now. Feel free to ask questions below!