ovs-openflowd: Rename test-openflowd and move to "tests" directory.
[openvswitch] / 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 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     error, pstream = ovs.stream.PassiveStream.open(name)
53     if error:
54         sys.stderr.write("could not listen on \"%s\": %s\n"
55                          % (name, os.strerror(error)))
56         sys.exit(1)
57
58     ovs.daemon.daemonize()
59
60     rpcs = []
61     done = False
62     while True:
63         # Accept new connections.
64         error, stream = pstream.accept()
65         if stream:
66             rpcs.append(ovs.jsonrpc.Connection(stream))
67         elif error != errno.EAGAIN:
68             sys.stderr.write("PassiveStream.accept() failed\n")
69             sys.exit(1)
70
71         # Service existing connections.
72         dead_rpcs = []
73         for rpc in rpcs:
74             rpc.run()
75
76             error = 0
77             if not rpc.get_backlog():
78                 error, msg = rpc.recv()
79                 if not error:
80                     if handle_rpc(rpc, msg):
81                         done = True
82
83             error = rpc.get_status()
84             if error:
85                 rpc.close()
86                 dead_rpcs.append(rpc)
87         rpcs = [rpc for rpc in rpcs if not rpc in dead_rpcs]
88
89         if done and not rpcs:
90             break
91
92         poller = ovs.poller.Poller()
93         pstream.wait(poller)
94         for rpc in rpcs:
95             rpc.wait(poller)
96             if not rpc.get_backlog():
97                 rpc.recv_wait(poller)
98         poller.block()
99     pstream.close()
100
101 def do_request(name, method, params_string):
102     params = ovs.json.from_string(params_string)
103     msg = ovs.jsonrpc.Message.create_request(method, params)
104     s = msg.is_valid()
105     if s:
106         sys.stderr.write("not a valid JSON-RPC request: %s\n" % s)
107         sys.exit(1)
108
109     error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
110     if error:
111         sys.stderr.write("could not open \"%s\": %s\n"
112                          % (name, os.strerror(error)))
113         sys.exit(1)
114
115     rpc = ovs.jsonrpc.Connection(stream)
116
117     error = rpc.send(msg)
118     if error:
119         sys.stderr.write("could not send request: %s\n" % os.strerror(error))
120         sys.exit(1)
121
122     error, msg = rpc.recv_block()
123     if error:
124         sys.stderr.write("error waiting for reply: %s\n" % os.strerror(error))
125         sys.exit(1)
126     
127     print ovs.json.to_string(msg.to_json())
128
129     rpc.close()
130     
131 def do_notify(name, method, params_string):
132     params = ovs.json.from_string(params_string)
133     msg = ovs.jsonrpc.Message.create_notify(method, params)
134     s = msg.is_valid()
135     if s:
136         sys.stderr.write("not a valid JSON-RPC notification: %s\n" % s)
137         sys.exit(1)
138
139     error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
140     if error:
141         sys.stderr.write("could not open \"%s\": %s\n"
142                          % (name, os.strerror(error)))
143         sys.exit(1)
144
145     rpc = ovs.jsonrpc.Connection(stream)
146
147     error = rpc.send_block(msg)
148     if error:
149         sys.stderr.write("could not send notification: %s\n"
150                          % os.strerror(error))
151         sys.exit(1)
152
153     rpc.close()
154
155 def main(argv):
156     try:
157         options, args = getopt.gnu_getopt(
158             argv[1:], 'h', ["help"] + ovs.daemon.LONG_OPTIONS)
159     except getopt.GetoptError, geo:
160         sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
161         sys.exit(1)
162
163     for key, value in options:
164         if key in ['h', '--help']:
165             usage()
166         elif not ovs.daemon.parse_opt(key, value):
167             sys.stderr.write("%s: unhandled option %s\n"
168                              % (ovs.util.PROGRAM_NAME, key))
169             sys.exit(1)
170
171     commands = {"listen": (do_listen, 1),
172                 "request": (do_request, 3),
173                 "notify": (do_notify, 3),
174                 "help": (usage, (0,))}
175
176     command_name = args[0]
177     args = args[1:]
178     if not command_name in commands:
179         sys.stderr.write("%s: unknown command \"%s\" "
180                          "(use --help for help)\n" % (argv0, command_name))
181         sys.exit(1)
182
183     func, n_args = commands[command_name]
184     if type(n_args) == tuple:
185         if len(args) < n_args[0]:
186             sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
187                              "only %d provided\n"
188                              % (argv0, command_name, n_args, len(args)))
189             sys.exit(1)
190     elif type(n_args) == int:
191         if len(args) != n_args:
192             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
193                              "provided\n"
194                              % (argv0, command_name, n_args, len(args)))
195             sys.exit(1)
196     else:
197         assert False
198
199     func(*args)
200
201 def usage():
202     sys.stdout.write("""\
203 %s: JSON-RPC test utility for Python
204 usage: %s [OPTIONS] COMMAND [ARG...]
205   listen LOCAL             listen for connections on LOCAL
206   request REMOTE METHOD PARAMS   send request, print reply
207   notify REMOTE METHOD PARAMS  send notification and exit
208 """ % (ovs.util.PROGRAM_NAME, ovs.util.PROGRAM_NAME))
209     ovs.stream.usage("JSON-RPC", True, True, True)
210     ovs.daemon.usage()
211     sys.stdout.write("""
212 Other options:
213   -h, --help              display this help message
214 """)
215     sys.exit(0)
216
217 if __name__ == '__main__':
218     main(sys.argv)
219