Implement initial Python bindings for Open vSwitch database.
[sliver-openvswitch.git] / tests / test-jsonrpc.py
1 # Copyright (c) 2009, 2010 Nicira Networks
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import errno
16 import getopt
17 import os
18 import sys
19
20 import ovs.daemon
21 import ovs.json
22 import ovs.jsonrpc
23 import ovs.poller
24 import ovs.stream
25
26 def handle_rpc(rpc, msg):
27     done = False
28     reply = None
29
30     if msg.type == ovs.jsonrpc.Message.T_REQUEST:
31         if msg.method == "echo":
32             reply = ovs.jsonrpc.Message.create_reply(msg.params, msg.id)
33         else:
34             reply = ovs.jsonrpc.Message.create_error(
35                 {"error": "unknown method"}, msg.id)
36             sys.stderr.write("unknown request %s" % msg.method)
37     elif msg.type == ovs.jsonrpc.Message.T_NOTIFY:
38         if msg.method == "shutdown":
39             done = True
40         else:
41             rpc.error(errno.ENOTTY)
42             sys.stderr.write("unknown notification %s" % msg.method)
43     else:
44         rpc.error(errno.EPROTO)
45         sys.stderr.write("unsolicited JSON-RPC reply or error\n")
46         
47     if reply:
48         rpc.send(reply)
49     return done
50
51 def do_listen(name):
52     ovs.daemon.die_if_already_running()
53
54     error, pstream = ovs.stream.PassiveStream.open(name)
55     if error:
56         sys.stderr.write("could not listen on \"%s\": %s\n"
57                          % (name, os.strerror(error)))
58         sys.exit(1)
59
60     ovs.daemon.daemonize()
61
62     rpcs = []
63     done = False
64     while True:
65         # Accept new connections.
66         error, stream = pstream.accept()
67         if stream:
68             rpcs.append(ovs.jsonrpc.Connection(stream))
69         elif error != errno.EAGAIN:
70             sys.stderr.write("PassiveStream.accept() failed\n")
71             sys.exit(1)
72
73         # Service existing connections.
74         dead_rpcs = []
75         for rpc in rpcs:
76             rpc.run()
77
78             error = 0
79             if not rpc.get_backlog():
80                 error, msg = rpc.recv()
81                 if not error:
82                     if handle_rpc(rpc, msg):
83                         done = True
84
85             error = rpc.get_status()
86             if error:
87                 rpc.close()
88                 dead_rpcs.append(rpc)
89         rpcs = [rpc for rpc in rpcs if not rpc in dead_rpcs]
90
91         if done and not rpcs:
92             break
93
94         poller = ovs.poller.Poller()
95         pstream.wait(poller)
96         for rpc in rpcs:
97             rpc.wait(poller)
98             if not rpc.get_backlog():
99                 rpc.recv_wait(poller)
100         poller.block()
101     pstream.close()
102
103 def do_request(name, method, params_string):
104     params = ovs.json.from_string(params_string)
105     msg = ovs.jsonrpc.Message.create_request(method, params)
106     s = msg.is_valid()
107     if s:
108         sys.stderr.write("not a valid JSON-RPC request: %s\n" % s)
109         sys.exit(1)
110
111     error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
112     if error:
113         sys.stderr.write("could not open \"%s\": %s\n"
114                          % (name, os.strerror(error)))
115         sys.exit(1)
116
117     rpc = ovs.jsonrpc.Connection(stream)
118
119     error = rpc.send(msg)
120     if error:
121         sys.stderr.write("could not send request: %s\n" % os.strerror(error))
122         sys.exit(1)
123
124     error, msg = rpc.recv_block()
125     if error:
126         sys.stderr.write("error waiting for reply: %s\n" % os.strerror(error))
127         sys.exit(1)
128     
129     print ovs.json.to_string(msg.to_json())
130
131     rpc.close()
132     
133 def do_notify(name, method, params_string):
134     params = ovs.json.from_string(params_string)
135     msg = ovs.jsonrpc.Message.create_notify(method, params)
136     s = msg.is_valid()
137     if s:
138         sys.stderr.write("not a valid JSON-RPC notification: %s\n" % s)
139         sys.exit(1)
140
141     error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
142     if error:
143         sys.stderr.write("could not open \"%s\": %s\n"
144                          % (name, os.strerror(error)))
145         sys.exit(1)
146
147     rpc = ovs.jsonrpc.Connection(stream)
148
149     error = rpc.send_block(msg)
150     if error:
151         sys.stderr.write("could not send notification: %s\n"
152                          % os.strerror(error))
153         sys.exit(1)
154
155     rpc.close()
156
157 def main(argv):
158     try:
159         options, args = getopt.gnu_getopt(
160             argv[1:], 'h', ["help"] + ovs.daemon.LONG_OPTIONS)
161     except getopt.GetoptError, geo:
162         sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
163         sys.exit(1)
164
165     for key, value in options:
166         if key in ['h', '--help']:
167             usage()
168         elif not ovs.daemon.parse_opt(key, value):
169             sys.stderr.write("%s: unhandled option %s\n"
170                              % (ovs.util.PROGRAM_NAME, key))
171             sys.exit(1)
172
173     commands = {"listen": (do_listen, 1),
174                 "request": (do_request, 3),
175                 "notify": (do_notify, 3),
176                 "help": (usage, (0,))}
177
178     command_name = args[0]
179     args = args[1:]
180     if not command_name in commands:
181         sys.stderr.write("%s: unknown command \"%s\" "
182                          "(use --help for help)\n" % (argv0, command_name))
183         sys.exit(1)
184
185     func, n_args = commands[command_name]
186     if type(n_args) == tuple:
187         if len(args) < n_args[0]:
188             sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
189                              "only %d provided\n"
190                              % (argv0, command_name, n_args, len(args)))
191             sys.exit(1)
192     elif type(n_args) == int:
193         if len(args) != n_args:
194             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
195                              "provided\n"
196                              % (argv0, command_name, n_args, len(args)))
197             sys.exit(1)
198     else:
199         assert False
200
201     func(*args)
202
203 def usage():
204     sys.stdout.write("""\
205 %s: JSON-RPC test utility for Python
206 usage: %s [OPTIONS] COMMAND [ARG...]
207   listen LOCAL             listen for connections on LOCAL
208   request REMOTE METHOD PARAMS   send request, print reply
209   notify REMOTE METHOD PARAMS  send notification and exit
210 """ % (ovs.util.PROGRAM_NAME, ovs.util.PROGRAM_NAME))
211     ovs.stream.usage("JSON-RPC", True, True, True)
212     ovs.daemon.usage()
213     sys.stdout.write("""
214 Other options:
215   -h, --help              display this help message
216 """)
217     sys.exit(0)
218
219 if __name__ == '__main__':
220     main(sys.argv)
221