From b20cadfd05d87b6c1218a05fb5ed51ae94b72614 Mon Sep 17 00:00:00 2001 From: Felician Nemeth Date: Fri, 7 Mar 2014 08:46:15 -0800 Subject: [PATCH 01/14] add generate_id() --- ovsdb.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ovsdb.py b/ovsdb.py index bc5e570..3248785 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -1,3 +1,4 @@ +import os import sys import Queue import socket @@ -9,6 +10,19 @@ DEFAULT_DB = 'Open_vSwitch' BUFFER_SIZE = 4096 +pid = None +last_id = 0 + +def generate_id(): + global pid + global last_id + if pid is None: + pid = os.getpid() + last_id += 1 + return "%d-%d" % (pid, last_id) + +# ---------------------------------------------------------------------- + # TODO: Could start by getting the DB name and using that for ongoing requests # TODO: How to keep an eye out for montor, update, echo messages? def gather_reply(socket): From ddc6f8a62b426b1d78f2937b854a59c21802ddfd Mon Sep 17 00:00:00 2001 From: Felician Nemeth Date: Fri, 7 Mar 2014 08:51:35 -0800 Subject: [PATCH 02/14] implement transact method (without real error handling) --- ovsdb.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index 3248785..0b15acc 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -86,9 +86,28 @@ def list_tables(server, db): def list_columns(server, db): return -def transact(server, transactions): +def transact(s, db, operations): # Variants of this will add stuff - return + request = { "method": "transact", + "params": [db] + operations, + "id": generate_id(), + } + + s.send(json.dumps(request)) + response = gather_reply(s) + + #assumtion: no overlapping calls + assert( request['id'] == response['id'] ) + results = response['result'] + if len(operations) == len(results): + for i, val in enumerate(zip(operations, results)): + if 'error' in val[1]: + raise RuntimeError('Op failed (%d, %s): %s' % + (i, val[0], val[1])) + else: + raise RuntimeError('transact failed: %s' % results[-1]) + + return results def monitor(columns, monitor_id = None, db = DEFAULT_DB): msg = {"method":"monitor", "params":[db, monitor_id, columns], "id":0} From 77f2b40f79beb62333d8bd7af9e9d52ced0d6cf6 Mon Sep 17 00:00:00 2001 From: Felician Nemeth Date: Fri, 7 Mar 2014 08:53:10 -0800 Subject: [PATCH 03/14] add get_daemon_uuid() --- ovsdb.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ovsdb.py b/ovsdb.py index 0b15acc..b46a3a3 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -133,6 +133,27 @@ def list_bridges(db = DEFAULT_DB): # TODO: cancel the monitor after we're done? return monitor(columns, db) +daemon_uuid = None +def get_daemon_uuid(socket, db = DEFAULT_DB): + "Get the uuid from table Open_vSwitch" + global daemon_uuid + if daemon_uuid: + return daemon_uuid + op = {"op": "select", + "table": "Open_vSwitch", + "where": [], + "columns": ["_uuid"], + } + reply = transact(socket, db, [op]) + try: + if (len(reply[0]['rows']) != 1): + e = 'There must be exactly one record in the Open_vSwitch table.' + raise RuntimeError(e) + daemon_uuid = reply[0]['rows'][0]['_uuid'][1] + except (KeyError, TypeError): + raise RuntimeError("Database schema changed") + return daemon_uuid + if __name__ == '__main__': s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((OVSDB_IP, OVSDB_PORT)) From 04a50672fa419fe7326f3ff89d189fabf82aabde Mon Sep 17 00:00:00 2001 From: Felician Nemeth Date: Fri, 7 Mar 2014 08:55:30 -0800 Subject: [PATCH 04/14] fix gather_reply() in case of very long replies --- ovsdb.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index b46a3a3..8a0473f 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -28,11 +28,15 @@ def generate_id(): def gather_reply(socket): print "Waiting for reply" result = "" - # we got the whole thing if we received all the fields - while "error" not in result or "id" not in result or "result" not in result: + while True: reply = socket.recv(BUFFER_SIZE) result += reply - return json.loads(result) + # we got the whole thing if we received all the fields + if "error" in result and "id" in result and "result" in result: + try: + return json.loads(result) + except ValueError: + pass def listen_for_messages(sock, message_queues): # To send something, add a message to queue and append sock to outputs From 7f396d0dee2a8b4091b1ea72a4a01d992c53b2dc Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Wed, 14 Oct 2015 15:25:03 +0100 Subject: [PATCH 05/14] These are the changes I needed to make in order to make the tests run on my Centos 7 box running ovs 2.4.90 --- ovsdb_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ovsdb_test.py b/ovsdb_test.py index 76fab5e..c01b596 100644 --- a/ovsdb_test.py +++ b/ovsdb_test.py @@ -29,8 +29,8 @@ def test_list_br(self): bridge_list = ovsdb.gather_reply(self.sock) bridges = bridge_list['result']['Bridge'] #print bridges - print bridges.values() - self.assertTrue("br0" in bridges.values()) + #print bridges.values()[0]['new']['name'] + self.assertTrue("br0" in bridges.values()[0]['new']['name']) def test_choice(self): self.assertTrue(True) From 01ad2da061b7839c6a13eac513b9099424ec2f16 Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Wed, 14 Oct 2015 15:26:33 +0100 Subject: [PATCH 06/14] add some notes about getting ovs api running --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 01a32f0..f00c2e7 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ This is a client interface to the Open vSwitch DB (OVSDB) protocol. There are still many functions to be fleshed out and the code is pretty messy at this point. There is also a function to keep the connection open to get updates from the database using `select` to make it non-blocking. + +Start the api server in ovs by running: +ovs-appctl -t ovsdb-server ovsdb-server/add-remote ptcp:6632 From 0e14d4e2cec2ee3a6a7996ec5b7384737cfd3a9c Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Wed, 14 Oct 2015 16:03:22 +0100 Subject: [PATCH 07/14] Consistent handling of database name throughout. --- ovsdb.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index 8a0473f..d95b6b7 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -72,7 +72,7 @@ def list_dbs(): return json.dumps(list_dbs_query) def get_schema(socket, db = DEFAULT_DB, current_id = 0): - list_schema = {"method": "get_schema", "params":[db_name], "id": current_id} + list_schema = {"method": "get_schema", "params":[db], "id": current_id} socket.send(json.dumps(list_schema)) result = gather_reply(socket) return result @@ -81,16 +81,16 @@ def get_schema_version(socket, db = DEFAULT_DB): db_schema = get_schema(socket, db) return db_schema['version'] -def list_tables(server, db): +def list_tables(socket, db = DEFAULT_DB): # keys that are under 'tables' db_schema = get_schema(socket, db) # return db_schema['tables'].keys return json.loads() -def list_columns(server, db): +def list_columns(server, db = DEFAULT_DB): return -def transact(s, db, operations): +def transact(s, db = DEFAULT_DB, operations = ""): # Variants of this will add stuff request = { "method": "transact", "params": [db] + operations, @@ -127,7 +127,7 @@ def echo(): echo_msg = {"method":"echo","id":"echo","params":[]} return json.dumps(echo_msg) -def dump(server, db): +def dump(server, db = DEFAULT_DB): return def list_bridges(db = DEFAULT_DB): From 9ae058190615e2a1384aeafa4bd4fc381c9d20bf Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Wed, 14 Oct 2015 17:01:14 +0100 Subject: [PATCH 08/14] use integer uuids for ID numbers --- ovsdb.py | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index d95b6b7..347bce2 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -3,24 +3,14 @@ import Queue import socket import json +import uuid from select import select OVSDB_IP = '127.0.0.1' OVSDB_PORT = 6632 -DEFAULT_DB = 'Open_vSwitch' +DEFAULT_DB = "Open_vSwitch" BUFFER_SIZE = 4096 -pid = None -last_id = 0 - -def generate_id(): - global pid - global last_id - if pid is None: - pid = os.getpid() - last_id += 1 - return "%d-%d" % (pid, last_id) - # ---------------------------------------------------------------------- # TODO: Could start by getting the DB name and using that for ongoing requests @@ -68,11 +58,11 @@ def listen_for_messages(sock, message_queues): print "error" def list_dbs(): - list_dbs_query = {"method":"list_dbs", "params":[], "id": 0} + list_dbs_query = {"method":"list_dbs", "params":[], "id": uuid.uuid4().int} return json.dumps(list_dbs_query) -def get_schema(socket, db = DEFAULT_DB, current_id = 0): - list_schema = {"method": "get_schema", "params":[db], "id": current_id} +def get_schema(socket, db = DEFAULT_DB): + list_schema = {"method": "get_schema", "params":[db], "id": uuid.uuid4().int} socket.send(json.dumps(list_schema)) result = gather_reply(socket) return result @@ -94,7 +84,7 @@ def transact(s, db = DEFAULT_DB, operations = ""): # Variants of this will add stuff request = { "method": "transact", "params": [db] + operations, - "id": generate_id(), + "id": uuid.uuid4().int, } s.send(json.dumps(request)) @@ -114,7 +104,7 @@ def transact(s, db = DEFAULT_DB, operations = ""): return results def monitor(columns, monitor_id = None, db = DEFAULT_DB): - msg = {"method":"monitor", "params":[db, monitor_id, columns], "id":0} + msg = {"method":"monitor", "params":[db, monitor_id, columns], "id":uuid.uuid4().int} return json.dumps(msg) def monitor_cancel(): From 481d54e30abf3f569dd31a2e2aebdf424cba420c Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Wed, 14 Oct 2015 17:22:05 +0100 Subject: [PATCH 09/14] rename function that builds the query to list database --- ovsdb.py | 7 +++---- ovsdb.pyc | Bin 4682 -> 5428 bytes 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index 347bce2..0b5c8ae 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -57,9 +57,8 @@ def listen_for_messages(sock, message_queues): else: print "error" -def list_dbs(): - list_dbs_query = {"method":"list_dbs", "params":[], "id": uuid.uuid4().int} - return json.dumps(list_dbs_query) +def build_query_list_dbs(): + return json.dumps({"method":"list_dbs", "params":[], "id": uuid.uuid4().int}) def get_schema(socket, db = DEFAULT_DB): list_schema = {"method": "get_schema", "params":[db], "id": uuid.uuid4().int} @@ -154,7 +153,7 @@ def get_daemon_uuid(socket, db = DEFAULT_DB): current_id = 0 - s.send(list_dbs()) + s.send(build_query_list_dbs()) db_list = gather_reply(s) db_name = db_list['result'][0] print "list bridges:" diff --git a/ovsdb.pyc b/ovsdb.pyc index c15b9a9ec568e3b9f37143b175bf7b769bc087e0..c6c25643f64a40d5190e6e9b9ebb0996368416f2 100644 GIT binary patch literal 5428 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 bXZ^B2jlGljcL{$>3XQ`}O5T~#GS>bFqrA7< literal 4682 zcmcInZBrb_5$@f4fjeF$YyrlSL00TbRS=fqLW$#2SqKo6ZIJhnsa#pLS$2kFkG(zE z*#ne7DplxP{zQI7eu95UKBV$J`R+X3vv;Q?u2kYn;C8mBXQrp8`|0l4!e1w5kKD>^ebeLgq}8niOJ+)RYj@tR*j; zmQ^=N-4Ol^shdK)MQTQfw@JMv#4VZswn&~OJxA&m9lMnN$MFm`!J(W)+*_~Rfxn~g@V8Tg()WRN6*`X_!`Ijy|!ur!m+Rxw=k%7T_jkVk{H_+|}Q5Gf5#ludznCg@G zRR$(yB{VusJ1N%TfJ)AlYSWFg8mpOclj`s+8w0WSVsmrD^XuD<4H;;eP9h@WPM}PU z&t?tN36FK=v2Yod4QuF;G-d_V$_t}YvvPQ3x+_Ybt(?5djyefm%bhc$4wl0>(n+== zo%w!(ccqeB6YYU1}0t73kt` z%umN0oE-MMEHMANK;IPTsz_HQx+>GfHC+~I{pD}yYJ@KFBRU?X^!MTdgtB$PsJMV= zz6FPk3cew&1&>*L6+=aY|>>3T0Ew! z3SHu7wfIi77;IkQmGHX5n^$|ApMJ}jf}n3{|8$P_S+aO`M=)vFN9^BEJoa9Vy__*+ zUKw$n$yAhhV35aPlChI+CT%**+KsJfj9$^abz+?H-gE}H8!)nlZbYwoU&7K7MuxZ+?gufuy3HV z>J;6I^k$uDH^c(!rG%%w)sesokXZ#B;ki|00hEQ+s~(0esPfR3!?QBJIe<*Od(o?3@ip5 zzvaa1AOAla59ADBA!$=+YNzm3Fds6J--D8TAi3supmuxL{ATq;lnj#tb$d3##F;%_?3mwu} zA3(&<@ECRtId_~}z>Q=wwo~6$3@}&-=SIdyY2wrmGdVP07tiiUh@2s*0{Y@tk0nc1 zmJ@lbXU5eE2C(W6r+Ic0vN*D>FhAe>F}S(or@rSLsE>RO#A5#(*1gx9nm8bKBtePx zR&h3r$BMsEVswh_;DOX&3JZq=F34|hWVJ$G0#UOD7Zw3>yOTs&CmjUw_a!n3TzgUBz|O5DF`KVP z%v#)Y9JNhzILCNDg_K=9Ilk{XAv*H+Z^6MchTGXzM{5&543bdCbqMW&LR3KW|JH)7 z9HUs{D*5$4ZO>b?k;VIb*n#(vq}b_P(KxG1{3EN|6y8hTj2s;vb*#s^C%Jo2wXCyw z8!3A{*B{=aKZm?u;>kU_@ZSd5|03M^uw0H`vL1Pu^EsL1TbG#h*C3)2MX)7)$oz<; zA~o&x0N;$O7-0+JvVd<#?qLSkQ)Y}>2EHb%d+R3_{%3_#j+b<(ilUP48ncg!>$y7&7}QJ{i(Z z9P3m{(ROdK1ih7NFMBAuet6jQ#NoV8_>U_OgDCdf9cAyFCy;M%Kx?9zEr1-Z7C;P0 z(Pqdn>}Td&)n7rbg-)UE3NC%W9Yl%m%jb`oz&K~}#eDuJ-WIRmVph#rRl{Mkyu2({ zOZ2(N6|~01+vDYOTlF0iZ&)azNrFWZWvj%Wsf=_1e$1G!}$HjGoP2_#Z3`UqWjAb(R From e29ca1e8d3834dc051da0851895adbf48228a4d2 Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Thu, 15 Oct 2015 09:00:50 +0100 Subject: [PATCH 10/14] fix function that dumps database tables --- ovsdb.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index 0b5c8ae..fa602b4 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -73,8 +73,7 @@ def get_schema_version(socket, db = DEFAULT_DB): def list_tables(socket, db = DEFAULT_DB): # keys that are under 'tables' db_schema = get_schema(socket, db) - # return db_schema['tables'].keys - return json.loads() + return db_schema['result']['tables'].keys() def list_columns(server, db = DEFAULT_DB): return From 94a9d58005b99e6429b2fbfdc470a151cade16ee Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Thu, 15 Oct 2015 09:05:12 +0100 Subject: [PATCH 11/14] renamed list_dbs function --- ovsdb_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ovsdb_test.py b/ovsdb_test.py index c01b596..547fa80 100644 --- a/ovsdb_test.py +++ b/ovsdb_test.py @@ -11,7 +11,7 @@ def setUp(self): self.sock.connect((OVSDB_IP, OVSDB_PORT)) def test_list_dbs(self): - self.sock.send(ovsdb.list_dbs()) + self.sock.send(ovsdb.build_query_list_dbs()) db_list = ovsdb.gather_reply(self.sock) db_name = db_list['result'][0] self.assertEqual(db_name, 'Open_vSwitch') @@ -21,7 +21,6 @@ def test_monitor(self): self.sock.send(ovsdb.monitor(columns)) result = ovsdb.gather_reply(self.sock) self.assertEqual(result['error'], None) - self.assertEqual(result['id'], 0) self.assertTrue("Bridge" in result['result']) def test_list_br(self): From b4aede5c81a8f277592421ea9b85212bcff6d890 Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Thu, 15 Oct 2015 09:10:01 +0100 Subject: [PATCH 12/14] remove raw file --- ovsdb.pyc | Bin 5428 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ovsdb.pyc diff --git a/ovsdb.pyc b/ovsdb.pyc deleted file mode 100644 index c6c25643f64a40d5190e6e9b9ebb0996368416f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5428 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 bXZ^B2jlGljcL{$>3XQ`}O5T~#GS>bFqrA7< From 7d495ea3b1a10c9e7a19d285c22998b50eea4ef6 Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Thu, 15 Oct 2015 10:40:50 +0100 Subject: [PATCH 13/14] make py-ovs-client more object oriented --- ovsdb.py | 265 +++++++++++++++++++++++++------------------------- ovsdb_test.py | 22 +---- 2 files changed, 139 insertions(+), 148 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index fa602b4..411fc6c 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -11,119 +11,129 @@ DEFAULT_DB = "Open_vSwitch" BUFFER_SIZE = 4096 -# ---------------------------------------------------------------------- - -# TODO: Could start by getting the DB name and using that for ongoing requests -# TODO: How to keep an eye out for montor, update, echo messages? -def gather_reply(socket): - print "Waiting for reply" - result = "" - while True: - reply = socket.recv(BUFFER_SIZE) - result += reply - # we got the whole thing if we received all the fields - if "error" in result and "id" in result and "result" in result: - try: - return json.loads(result) - except ValueError: - pass - -def listen_for_messages(sock, message_queues): - # To send something, add a message to queue and append sock to outputs - inputs = [sock, sys.stdin] - outputs = [] - while sock: - readable, writable, exceptional = select(inputs, outputs, []) - for s in readable: - if s is sock: - data = sock.recv(4096) - # should test if its echo, if so, reply - # message_type = get_msg_type(data) - # if message_type is "echo": - # send_echo(message_ - message_queues[sock].put(data) - outputs.append(sock) - print "recv:" + data - elif s is sys.stdin: - print sys.stdin.readline() - sock.close() - return - else: - print "error" - for w in writable: - if w is sock: - sock.send(message_queues[sock].get_nowait()) - outputs.remove(sock) - else: - print "error" - -def build_query_list_dbs(): - return json.dumps({"method":"list_dbs", "params":[], "id": uuid.uuid4().int}) - -def get_schema(socket, db = DEFAULT_DB): - list_schema = {"method": "get_schema", "params":[db], "id": uuid.uuid4().int} - socket.send(json.dumps(list_schema)) - result = gather_reply(socket) - return result - -def get_schema_version(socket, db = DEFAULT_DB): - db_schema = get_schema(socket, db) - return db_schema['version'] - -def list_tables(socket, db = DEFAULT_DB): - # keys that are under 'tables' - db_schema = get_schema(socket, db) - return db_schema['result']['tables'].keys() - -def list_columns(server, db = DEFAULT_DB): - return - -def transact(s, db = DEFAULT_DB, operations = ""): - # Variants of this will add stuff - request = { "method": "transact", - "params": [db] + operations, - "id": uuid.uuid4().int, - } - - s.send(json.dumps(request)) - response = gather_reply(s) - - #assumtion: no overlapping calls - assert( request['id'] == response['id'] ) - results = response['result'] - if len(operations) == len(results): - for i, val in enumerate(zip(operations, results)): - if 'error' in val[1]: - raise RuntimeError('Op failed (%d, %s): %s' % - (i, val[0], val[1])) - else: - raise RuntimeError('transact failed: %s' % results[-1]) - - return results - -def monitor(columns, monitor_id = None, db = DEFAULT_DB): - msg = {"method":"monitor", "params":[db, monitor_id, columns], "id":uuid.uuid4().int} - return json.dumps(msg) - -def monitor_cancel(): - return - -def locking(): - return - -def echo(): - echo_msg = {"method":"echo","id":"echo","params":[]} - return json.dumps(echo_msg) - -def dump(server, db = DEFAULT_DB): - return - -def list_bridges(db = DEFAULT_DB): - # What if we replaced with a more specific query - # columns = {"Bridge":{"name"}} - columns = {"Port":{"columns":["fake_bridge","interfaces","name","tag"]},"Controller":{"columns":[]},"Interface":{"columns":["name"]},"Open_vSwitch":{"columns":["bridges","cur_cfg"]},"Bridge":{"columns":["controller","fail_mode","name","ports"]}} - # TODO: cancel the monitor after we're done? - return monitor(columns, db) +class OVSDB: + def __init__(self, db_ip = OVSDB_IP, db_port = OVSDB_PORT, db_name = DEFAULT_DB): + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect((db_ip,db_port)) + self.db_name = db_name + + # TODO: Could start by getting the DB name and using that for ongoing requests + # TODO: How to keep an eye out for montor, update, echo messages? + def gather_reply(self): + print "Waiting for reply" + result = "" + while True: + reply = self.socket.recv(BUFFER_SIZE) + result += reply + # we got the whole thing if we received all the fields + if "error" in result and "id" in result and "result" in result: + try: + return json.loads(result) + except ValueError: + pass + + def listen_for_messages(self, message_queues): + # To send something, add a message to queue and append sock to outputs + inputs = [self.socket, sys.stdin] + outputs = [] + while self.socket: + readable, writable, exceptional = select(inputs, outputs, []) + for s in readable: + if s is self.socket: + data = self.socket.recv(4096) + # should test if its echo, if so, reply + # message_type = get_msg_type(data) + # if message_type is "echo": + # send_echo(message_ + message_queues[self.socket].put(data) + outputs.append(self.socket) + print "recv:" + data + elif s is sys.stdin: + print sys.stdin.readline() + self.socket.close() + return + else: + print "error" + for w in writable: + if w is self.socket: + self.socket.send(message_queues[self.socket].get_nowait()) + outputs.remove(self.socket) + else: + print "error" + + def build_query_list_dbs(self): + return json.dumps({"method":"list_dbs", "params":[], "id": uuid.uuid4().int}) + + def get_schema(self, db = DEFAULT_DB): + list_schema = {"method": "get_schema", "params":[db], "id": uuid.uuid4().int} + self.socket.send(json.dumps(list_schema)) + result = self.gather_reply() + return result + + def get_dbs(self): + self.socket.send(self.build_query_list_dbs()) + result = self.gather_reply() + return result + + def get_schema_version(self, db = DEFAULT_DB): + db_schema = get_schema(self.socket, db) + return db_schema['version'] + + def list_tables(self, db = DEFAULT_DB): + # keys that are under 'tables' + db_schema = get_schema(self.socket, db) + return db_schema['result']['tables'].keys() + + def list_columns(server, db = DEFAULT_DB): + return + + def transact(self, db = DEFAULT_DB, operations = ""): + # Variants of this will add stuff + request = { "method": "transact", + "params": [db] + operations, + "id": uuid.uuid4().int, + } + + self.socket.send(json.dumps(request)) + response = self.gather_reply() + + #assumtion: no overlapping calls + assert( request['id'] == response['id'] ) + results = response['result'] + if len(operations) == len(results): + for i, val in enumerate(zip(operations, results)): + if 'error' in val[1]: + raise RuntimeError('Op failed (%d, %s): %s' % + (i, val[0], val[1])) + else: + raise RuntimeError('transact failed: %s' % results[-1]) + + return results + + def monitor(self, columns, monitor_id = None, db = DEFAULT_DB): + msg = {"method":"monitor", "params":[db, monitor_id, columns], "id":uuid.uuid4().int} + return json.dumps(msg) + + def monitor_cancel(): + return + + def locking(): + return + + def echo(): + echo_msg = {"method":"echo","id":"echo","params":[]} + return json.dumps(echo_msg) + + def dump(server, db = DEFAULT_DB): + return + + def list_bridges(self, db = DEFAULT_DB): + # What if we replaced with a more specific query + # columns = {"Bridge":{"name"}} + columns = {"Port":{"columns":["fake_bridge","interfaces","name","tag"]},"Controller":{"columns":[]},"Interface":{"columns":["name"]},"Open_vSwitch":{"columns":["bridges","cur_cfg"]},"Bridge":{"columns":["controller","fail_mode","name","ports"]}} + # TODO: cancel the monitor after we're done? + self.socket.send(self.monitor(columns)) + return self.gather_reply()['result'] daemon_uuid = None def get_daemon_uuid(socket, db = DEFAULT_DB): @@ -147,31 +157,24 @@ def get_daemon_uuid(socket, db = DEFAULT_DB): return daemon_uuid if __name__ == '__main__': - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((OVSDB_IP, OVSDB_PORT)) - - current_id = 0 + ovs = OVSDB() - s.send(build_query_list_dbs()) - db_list = gather_reply(s) - db_name = db_list['result'][0] + print "List dbs" + db_list = ovs.get_dbs() + print db_list['result'][0] + print "list bridges:" - s.send(list_bridges()) - bridge_list = gather_reply(s) + bridge_list = ovs.list_bridges() print bridge_list - bridges = bridge_list['result']['Bridge'] + bridges = bridge_list['Bridge'] print "\nbridges\n" print bridges.values() for bridge in bridges.values(): print "---" print bridge['new']['name'] - #db_schema = get_schema(s, db_name) - #print db_schema - - #columns = {"Bridge":{"columns":["name"]}} - #print monitor(s, columns, 1) + # TODO: Put this in a thread and use Queues to send/recv data from the thread - message_queues = {} - message_queues[s] = Queue.Queue() - listen_for_messages(s, message_queues) + # message_queues = {} + # message_queues[s] = Queue.Queue() + # listen_for_messages(s, message_queues) diff --git a/ovsdb_test.py b/ovsdb_test.py index 547fa80..509c7dd 100644 --- a/ovsdb_test.py +++ b/ovsdb_test.py @@ -1,32 +1,20 @@ -import ovsdb +from ovsdb import OVSDB import unittest import socket class TestFunctions(unittest.TestCase): def setUp(self): - OVSDB_IP = '127.0.0.1' - OVSDB_PORT = 6632 - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((OVSDB_IP, OVSDB_PORT)) + self.ovs = OVSDB() def test_list_dbs(self): - self.sock.send(ovsdb.build_query_list_dbs()) - db_list = ovsdb.gather_reply(self.sock) + db_list = self.ovs.get_dbs() db_name = db_list['result'][0] self.assertEqual(db_name, 'Open_vSwitch') - def test_monitor(self): - columns = {"Bridge":{"columns":["name"]}} - self.sock.send(ovsdb.monitor(columns)) - result = ovsdb.gather_reply(self.sock) - self.assertEqual(result['error'], None) - self.assertTrue("Bridge" in result['result']) - def test_list_br(self): - self.sock.send(ovsdb.list_bridges()) - bridge_list = ovsdb.gather_reply(self.sock) - bridges = bridge_list['result']['Bridge'] + bridge_list = self.ovs.list_bridges() + bridges = bridge_list['Bridge'] #print bridges #print bridges.values()[0]['new']['name'] self.assertTrue("br0" in bridges.values()[0]['new']['name']) From b33294e0a763231b6357dad036284dce69a0c188 Mon Sep 17 00:00:00 2001 From: Andy Davidson Date: Thu, 15 Oct 2015 13:17:59 +0100 Subject: [PATCH 14/14] move database used to object. --- ovsdb.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ovsdb.py b/ovsdb.py index 411fc6c..8dc1510 100644 --- a/ovsdb.py +++ b/ovsdb.py @@ -64,8 +64,8 @@ def listen_for_messages(self, message_queues): def build_query_list_dbs(self): return json.dumps({"method":"list_dbs", "params":[], "id": uuid.uuid4().int}) - def get_schema(self, db = DEFAULT_DB): - list_schema = {"method": "get_schema", "params":[db], "id": uuid.uuid4().int} + def get_schema(self): + list_schema = {"method": "get_schema", "params":[self.db_name], "id": uuid.uuid4().int} self.socket.send(json.dumps(list_schema)) result = self.gather_reply() return result @@ -75,22 +75,22 @@ def get_dbs(self): result = self.gather_reply() return result - def get_schema_version(self, db = DEFAULT_DB): - db_schema = get_schema(self.socket, db) + def get_schema_version(self): + db_schema = get_schema(self.socket, self.db_name) return db_schema['version'] - def list_tables(self, db = DEFAULT_DB): + def list_tables(self): # keys that are under 'tables' - db_schema = get_schema(self.socket, db) + db_schema = get_schema(self.socket, self.db_name) return db_schema['result']['tables'].keys() - def list_columns(server, db = DEFAULT_DB): + def list_columns(server): return - def transact(self, db = DEFAULT_DB, operations = ""): + def transact(self, operations = ""): # Variants of this will add stuff request = { "method": "transact", - "params": [db] + operations, + "params": [self.db_name] + operations, "id": uuid.uuid4().int, } @@ -110,8 +110,8 @@ def transact(self, db = DEFAULT_DB, operations = ""): return results - def monitor(self, columns, monitor_id = None, db = DEFAULT_DB): - msg = {"method":"monitor", "params":[db, monitor_id, columns], "id":uuid.uuid4().int} + def monitor(self, columns, monitor_id = None): + msg = {"method":"monitor", "params":[self.db_name, monitor_id, columns], "id":uuid.uuid4().int} return json.dumps(msg) def monitor_cancel(): @@ -124,10 +124,10 @@ def echo(): echo_msg = {"method":"echo","id":"echo","params":[]} return json.dumps(echo_msg) - def dump(server, db = DEFAULT_DB): + def dump(self): return - def list_bridges(self, db = DEFAULT_DB): + def list_bridges(self): # What if we replaced with a more specific query # columns = {"Bridge":{"name"}} columns = {"Port":{"columns":["fake_bridge","interfaces","name","tag"]},"Controller":{"columns":[]},"Interface":{"columns":["name"]},"Open_vSwitch":{"columns":["bridges","cur_cfg"]},"Bridge":{"columns":["controller","fail_mode","name","ports"]}}