string: strerror_r() for windows.
[sliver-openvswitch.git] / tests / test-jsonrpc.py
1 # Copyright (c) 2009, 2010, 2011 Nicira, Inc.
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 argparse
16 import errno
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
162     parser = argparse.ArgumentParser(
163             description="JSON-RPC test utility for Python.",
164             formatter_class=argparse.RawDescriptionHelpFormatter)
165
166     commands = {"listen": (do_listen, 1),
167                 "request": (do_request, 3),
168                 "notify": (do_notify, 3),
169                 "help": (parser.print_help, (0,))}
170
171     group_description = """\
172 listen LOCAL             listen for connections on LOCAL
173 request REMOTE METHOD PARAMS   send request, print reply
174 notify REMOTE METHOD PARAMS  send notification and exit
175 """ + ovs.stream.usage("JSON-RPC")
176
177     group = parser.add_argument_group(title="Commands",
178                                       description=group_description)
179     group.add_argument('command', metavar="COMMAND", nargs=1,
180                         choices=commands, help="Command to use.")
181     group.add_argument('command_args', metavar="ARG", nargs='*',
182                        help="Arguments to COMMAND.")
183
184     ovs.daemon.add_args(parser)
185     args = parser.parse_args()
186     ovs.daemon.handle_args(args)
187
188     command_name = args.command[0]
189     args = args.command_args
190     if not command_name in commands:
191         sys.stderr.write("%s: unknown command \"%s\" "
192                          "(use --help for help)\n" % (argv[0], command_name))
193         sys.exit(1)
194
195     func, n_args = commands[command_name]
196     if type(n_args) == tuple:
197         if len(args) < n_args[0]:
198             sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
199                              "only %d provided\n"
200                              % (argv[0], command_name, n_args, len(args)))
201             sys.exit(1)
202     elif type(n_args) == int:
203         if len(args) != n_args:
204             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
205                              "provided\n"
206                              % (argv[0], command_name, n_args, len(args)))
207             sys.exit(1)
208     else:
209         assert False
210
211     func(*args)
212
213
214 if __name__ == '__main__':
215     main(sys.argv)