backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / cmd / rpc_cli.py
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
4 # Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #    http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15 # implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18
19 # a simple command line msgpack-rpc client
20 #
21 # a usage example:
22 #     % PYTHONPATH=. ./bin/rpc-cli \
23 #      --peers=echo-server=localhost:9999,hoge=localhost:9998
24 #     (Cmd) request echo-server echo ["hoge"]
25 #     RESULT hoge
26 #     (Cmd) request echo-server notify ["notify-method", ["param1","param2"]]
27 #     RESULT notify-method
28 #     (Cmd)
29 #     NOTIFICATION from echo-server ['notify-method', ['param1', 'param2']]
30 #     (Cmd)
31
32 from __future__ import print_function
33
34 import ast
35 import cmd
36 import signal
37 import socket
38 import sys
39 import termios
40
41 from ryu import cfg
42 from ryu.lib import rpc
43
44
45 CONF = cfg.CONF
46 CONF.register_cli_opts([
47     cfg.ListOpt('peers', default=[],
48                 help='List of peers, separated by commas. '
49                      '(e.g., "hoge=localhost:9998,fuga=localhost:9999")'),
50     cfg.StrOpt('command', short='c', default=None,
51                help='Command to be executed as single command. '
52                     'The default is None and opens interactive console.'),
53 ])
54
55
56 class Peer(object):
57     def __init__(self, name, addr):
58         self._name = name
59         self._addr = addr
60         self.socket = None
61         self.client = None
62         try:
63             self.connect()
64         except ConnectionError as e:
65             print('Exception when connecting to peer "%s": %s' % (name, e))
66             raise e
67
68     def connect(self):
69         self.socket = socket.create_connection(self._addr)
70         self.client = rpc.Client(self.socket,
71                                  notification_callback=self.notification)
72
73     def try_to_connect(self, verbose=False):
74         if self.client:
75             return
76         try:
77             self.connect()
78             assert self.client
79         except Exception as e:
80             if verbose:
81                 print("connection failure %s" % e)
82             raise EOFError
83
84     def notification(self, n):
85         print("NOTIFICATION from %s %s" % (self._name, n))
86
87     def call(self, method, params):
88         return self._do(lambda: self.client.call(method, params))
89
90     def send_notification(self, method, params):
91         self._do(lambda: self.client.send_notification(method, params))
92
93     def _do(self, f):
94         def g():
95             try:
96                 return f()
97             except EOFError:
98                 self.client = None
99                 raise
100
101         self.try_to_connect(verbose=True)
102         try:
103             return g()
104         except EOFError:
105             print("disconnected.  trying to connect...")
106             self.try_to_connect(verbose=True)
107             print("connected.  retrying the request...")
108             return g()
109
110     def close(self):
111         self.socket.close()
112
113
114 peers = {}
115
116
117 def add_peer(name, host, port):
118     try:
119         peer = Peer(name, (host, port))
120     except ConnectionError:
121         return
122
123     peers[name] = peer
124
125
126 def close_peers():
127     for peer in peers.values():
128         peer.socket.close()
129
130
131 class Cmd(cmd.Cmd):
132     def __init__(self, *args, **kwargs):
133         self._in_onecmd = False
134         self._notification_check_interval = 1  # worth to be configurable?
135         self._saved_termios = None
136         cmd.Cmd.__init__(self, *args, **kwargs)
137
138     def _request(self, line, f):
139         args = line.split(None, 2)
140         try:
141             peer = args[0]
142             method = args[1]
143             params = ast.literal_eval(args[2])
144         except (IndexError, ValueError) as e:
145             print("argument error: %s" % e)
146             return
147         try:
148             p = peers[peer]
149         except KeyError:
150             print("unknown peer %s" % peer)
151             return
152         try:
153             f(p, method, params)
154         except rpc.RPCError as e:
155             print("RPC ERROR %s" % e)
156         except EOFError:
157             print("disconnected")
158
159     def _complete_peer(self, text, line, _begidx, _endidx):
160         if len((line + 'x').split()) >= 3:
161             return []
162         return [name for name in peers if name.startswith(text)]
163
164     def do_request(self, line):
165         """request <peer> <method> <params>
166         send a msgpack-rpc request and print a response.
167         <params> is a python code snippet, it should be eval'ed to a list.
168         """
169
170         def f(p, method, params):
171             result = p.call(method, params)
172             print("RESULT %s" % result)
173
174         self._request(line, f)
175
176     def do_notify(self, line):
177         """notify <peer> <method> <params>
178         send a msgpack-rpc notification.
179         <params> is a python code snippet, it should be eval'ed to a list.
180         """
181
182         def f(p, method, params):
183             p.send_notification(method, params)
184
185         self._request(line, f)
186
187     def complete_request(self, text, line, begidx, endidx):
188         return self._complete_peer(text, line, begidx, endidx)
189
190     def complete_notify(self, text, line, begidx, endidx):
191         return self._complete_peer(text, line, begidx, endidx)
192
193     def do_EOF(self, _line=None):
194         close_peers()
195         sys.exit(0)
196
197     def emptyline(self):
198         self._peek_notification()
199
200     def postcmd(self, _stop, _line):
201         self._peek_notification()
202
203     def _peek_notification(self):
204         for k, p in peers.items():
205             if p.client:
206                 try:
207                     p.client.peek_notification()
208                 except EOFError:
209                     p.client = None
210                     print("disconnected %s" % k)
211
212     @staticmethod
213     def _save_termios():
214         return termios.tcgetattr(sys.stdin.fileno())
215
216     @staticmethod
217     def _restore_termios(t):
218         termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, t)
219
220     def preloop(self):
221         self._saved_termios = self._save_termios()
222         signal.signal(signal.SIGALRM, self._timeout)
223         signal.alarm(1)
224
225     def postloop(self):
226         close_peers()
227
228     def onecmd(self, string):
229         self._in_onecmd = True
230         try:
231             return cmd.Cmd.onecmd(self, string)
232         finally:
233             self._in_onecmd = False
234
235     def _timeout(self, _sig, _frame):
236         if not self._in_onecmd:
237             # restore terminal settings. (cooked/raw, ...)
238             # required for pypy at least.
239             # this doesn't seem to be needed for cpython readline
240             # module but i'm not sure if it's by spec or luck.
241             o = self._save_termios()
242             self._restore_termios(self._saved_termios)
243             self._peek_notification()
244             self._restore_termios(o)
245         signal.alarm(self._notification_check_interval)
246
247
248 def main(args=None, prog=None):
249     CONF(args=args, prog=prog, project='rpc-cli', version='rpc-cli')
250
251     for p_str in CONF.peers:
252         name, addr = p_str.split('=')
253         host, port = addr.rsplit(':', 1)
254         add_peer(name, host, port)
255
256     if CONF.command:
257         command = Cmd()
258         command.onecmd(CONF.command)
259         command.do_EOF()
260
261     Cmd().cmdloop()
262
263
264 if __name__ == "__main__":
265     main()