5ce45ad2f3a9a7f476d4c14d9b200f18a6d6e823
[sliver-openvswitch.git] / tests / test-jsonrpc.py
1 # Copyright (c) 2009, 2010, 2011 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
27 def handle_rpc(rpc, msg):
28     done = False
29     reply = None
30
31     if msg.type == ovs.jsonrpc.Message.T_REQUEST:
32         if msg.method == "echo":
33             reply = ovs.jsonrpc.Message.create_reply(msg.params, msg.id)
34         else:
35             reply = ovs.jsonrpc.Message.create_error(
36                 {"error": "unknown method"}, msg.id)
37             sys.stderr.write("unknown request %s" % msg.method)
38     elif msg.type == ovs.jsonrpc.Message.T_NOTIFY:
39         if msg.method == "shutdown":
40             done = True
41         else:
42             rpc.error(errno.ENOTTY)
43             sys.stderr.write("unknown notification %s" % msg.method)
44     else:
45         rpc.error(errno.EPROTO)
46         sys.stderr.write("unsolicited JSON-RPC reply or error\n")
47
48     if reply:
49         rpc.send(reply)
50     return done
51
52
53 def do_listen(name):
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
104 def do_request(name, method, params_string):
105     params = ovs.json.from_string(params_string)
106     msg = ovs.jsonrpc.Message.create_request(method, params)
107     s = msg.is_valid()
108     if s:
109         sys.stderr.write("not a valid JSON-RPC request: %s\n" % s)
110         sys.exit(1)
111
112     error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
113     if error:
114         sys.stderr.write("could not open \"%s\": %s\n"
115                          % (name, os.strerror(error)))
116         sys.exit(1)
117
118     rpc = ovs.jsonrpc.Connection(stream)
119
120     error = rpc.send(msg)
121     if error:
122         sys.stderr.write("could not send request: %s\n" % os.strerror(error))
123         sys.exit(1)
124
125     error, msg = rpc.recv_block()
126     if error:
127         sys.stderr.write("error waiting for reply: %s\n" % os.strerror(error))
128         sys.exit(1)
129
130     print ovs.json.to_string(msg.to_json())
131
132     rpc.close()
133
134
135 def do_notify(name, method, params_string):
136     params = ovs.json.from_string(params_string)
137     msg = ovs.jsonrpc.Message.create_notify(method, params)
138     s = msg.is_valid()
139     if s:
140         sys.stderr.write("not a valid JSON-RPC notification: %s\n" % s)
141         sys.exit(1)
142
143     error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
144     if error:
145         sys.stderr.write("could not open \"%s\": %s\n"
146                          % (name, os.strerror(error)))
147         sys.exit(1)
148
149     rpc = ovs.jsonrpc.Connection(stream)
150
151     error = rpc.send_block(msg)
152     if error:
153         sys.stderr.write("could not send notification: %s\n"
154                          % os.strerror(error))
155         sys.exit(1)
156
157     rpc.close()
158
159
160 def main(argv):
161     try:
162         options, args = getopt.gnu_getopt(
163             argv[1:], 'h', ["help"] + ovs.daemon.LONG_OPTIONS)
164     except getopt.GetoptError, geo:
165         sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
166         sys.exit(1)
167
168     for key, value in options:
169         if key in ['-h', '--help']:
170             usage()
171         elif not ovs.daemon.parse_opt(key, value):
172             sys.stderr.write("%s: unhandled option %s\n"
173                              % (ovs.util.PROGRAM_NAME, key))
174             sys.exit(1)
175
176     commands = {"listen": (do_listen, 1),
177                 "request": (do_request, 3),
178                 "notify": (do_notify, 3),
179                 "help": (usage, (0,))}
180
181     command_name = args[0]
182     args = args[1:]
183     if not command_name in commands:
184         sys.stderr.write("%s: unknown command \"%s\" "
185                          "(use --help for help)\n" % (argv[0], command_name))
186         sys.exit(1)
187
188     func, n_args = commands[command_name]
189     if type(n_args) == tuple:
190         if len(args) < n_args[0]:
191             sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
192                              "only %d provided\n"
193                              % (argv[0], command_name, n_args, len(args)))
194             sys.exit(1)
195     elif type(n_args) == int:
196         if len(args) != n_args:
197             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
198                              "provided\n"
199                              % (argv[0], command_name, n_args, len(args)))
200             sys.exit(1)
201     else:
202         assert False
203
204     func(*args)
205
206
207 def usage():
208     sys.stdout.write("""\
209 %s: JSON-RPC test utility for Python
210 usage: %s [OPTIONS] COMMAND [ARG...]
211   listen LOCAL             listen for connections on LOCAL
212   request REMOTE METHOD PARAMS   send request, print reply
213   notify REMOTE METHOD PARAMS  send notification and exit
214 """ % (ovs.util.PROGRAM_NAME, ovs.util.PROGRAM_NAME))
215     sys.stdout.write(ovs.stream.usage("JSON-RPC") + "\n")
216     ovs.daemon.usage()
217     sys.stdout.write("""
218 Other options:
219   -h, --help              display this help message
220 """)
221     sys.exit(0)
222
223
224 if __name__ == '__main__':
225     main(sys.argv)