Signed-off-by: YAMAMOTO Takashi <yamam...@valinux.co.jp>
---
 bin/rpc-cli        |  20 +++++
 run_tests.sh       |   2 +-
 ryu/cmd/rpc_cli.py | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 setup.cfg          |   1 +
 4 files changed, 259 insertions(+), 1 deletion(-)
 create mode 100755 bin/rpc-cli
 create mode 100755 ryu/cmd/rpc_cli.py

diff --git a/bin/rpc-cli b/bin/rpc-cli
new file mode 100755
index 0000000..cdb832a
--- /dev/null
+++ b/bin/rpc-cli
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ryu.cmd.rpc_cli import main
+main()
diff --git a/run_tests.sh b/run_tests.sh
index e791ed9..89153a2 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -90,7 +90,7 @@ run_tests() {
 run_pylint() {
   echo "Running pylint ..."
   PYLINT_OPTIONS="--rcfile=.pylintrc --output-format=parseable"
-  PYLINT_INCLUDE="ryu ryu/tests/bin/ryu-client"
+  PYLINT_INCLUDE="ryu bin/rpc-cli ryu/tests/bin/ryu-client"
   export PYTHONPATH=$PYTHONPATH:.ryu
   PYLINT_LOG=pylint.log
 
diff --git a/ryu/cmd/rpc_cli.py b/ryu/cmd/rpc_cli.py
new file mode 100755
index 0000000..05696b3
--- /dev/null
+++ b/ryu/cmd/rpc_cli.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# a simple command line msgpack-rpc client
+#
+# a usage example:
+#     % PYTHONPATH=. ./bin/rpc-cli \
+#      --peers=echo-server=localhost:9999,hoge=localhost:9998
+#     (Cmd) request echo-server echo ["hoge"]
+#     RESULT hoge
+#     (Cmd) request echo-server notify ["notify-method", ["param1","param2"]]
+#     RESULT notify-method
+#     (Cmd)
+#     NOTIFICATION from echo-server ['notify-method', ['param1', 'param2']]
+#     (Cmd)
+
+import ryu.contrib
+
+from oslo.config import cfg
+
+import cmd
+import signal
+import socket
+import sys
+import termios
+
+from ryu.lib import rpc
+
+
+CONF = cfg.CONF
+CONF.register_cli_opts([
+    # eg. rpc-cli --peers=hoge=localhost:9998,fuga=localhost:9999
+    cfg.ListOpt('peers', default=[], help='list of peers')
+])
+
+
+class Peer(object):
+    def __init__(self, name, addr):
+        self._name = name
+        self._addr = addr
+        self.client = None
+        try:
+            self.connect()
+        except:
+            pass
+
+    def connect(self):
+        self.client = None
+        s = socket.create_connection(self._addr)
+        self.client = rpc.Client(s, notification_callback=self.notification)
+
+    def try_to_connect(self, verbose=False):
+        if self.client:
+            return
+        try:
+            self.connect()
+            assert self.client
+        except Exception, e:
+            if verbose:
+                print "connection failure", e
+            raise EOFError
+
+    def notification(self, n):
+        print "NOTIFICATION from", self._name, n
+
+    def call(self, method, params):
+        return self._do(lambda: self.client.call(method, params))
+
+    def send_notification(self, method, params):
+        self._do(lambda: self.client.send_notification(method, params))
+
+    def _do(self, f):
+        def g():
+            try:
+                return f()
+            except EOFError:
+                self.client = None
+                raise
+
+        self.try_to_connect(verbose=True)
+        try:
+            return g()
+        except EOFError:
+            print "disconnected.  trying to connect..."
+            self.try_to_connect(verbose=True)
+            print "connected.  retrying the request..."
+            return g()
+
+
+peers = {}
+
+
+def add_peer(name, host, port):
+    peers[name] = Peer(name, (host, port))
+
+
+class Cmd(cmd.Cmd):
+    def __init__(self, *args, **kwargs):
+        self._in_onecmd = False
+        self._notification_check_interval = 1  # worth to be configurable?
+        self._saved_termios = None
+        cmd.Cmd.__init__(self, *args, **kwargs)
+
+    def _request(self, line, f):
+        args = line.split(None, 2)
+        try:
+            peer = args[0]
+            method = args[1]
+            params = eval(args[2])
+        except:
+            print "argument error"
+            return
+        try:
+            p = peers[peer]
+        except KeyError:
+            print "unknown peer", peer
+            return
+        try:
+            f(p, method, params)
+        except rpc.RPCError, e:
+            print "RPC ERROR", e
+        except EOFError:
+            print "disconnected"
+
+    def _complete_peer(self, text, line, _begidx, _endidx):
+        if len((line + 'x').split()) >= 3:
+            return []
+        return [name for name in peers if name.startswith(text)]
+
+    def do_request(self, line):
+        """request <peer> <method> <params>
+        send a msgpack-rpc request and print a response.
+        <params> is a python code snippet, it should be eval'ed to a list.
+        """
+
+        def f(p, method, params):
+            result = p.call(method, params)
+            print "RESULT", result
+
+        self._request(line, f)
+
+    def do_notify(self, line):
+        """notify <peer> <method> <params>
+        send a msgpack-rpc notification.
+        <params> is a python code snippet, it should be eval'ed to a list.
+        """
+
+        def f(p, method, params):
+            p.send_notification(method, params)
+
+        self._request(line, f)
+
+    def complete_request(self, text, line, begidx, endidx):
+        return self._complete_peer(text, line, begidx, endidx)
+
+    def complete_notify(self, text, line, begidx, endidx):
+        return self._complete_peer(text, line, begidx, endidx)
+
+    def do_EOF(self, _line):
+        sys.exit(0)
+
+    def emptyline(self):
+        self._peek_notification()
+
+    def postcmd(self, _stop, _line):
+        self._peek_notification()
+
+    def _peek_notification(self):
+        for k, p in peers.iteritems():
+            if p.client:
+                try:
+                    p.client.peek_notification()
+                except EOFError:
+                    p.client = None
+                    print "disconnected", k
+
+    @staticmethod
+    def _save_termios():
+        return termios.tcgetattr(sys.stdin.fileno())
+
+    @staticmethod
+    def _restore_termios(t):
+        termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, t)
+
+    def preloop(self):
+        self._saved_termios = self._save_termios()
+        signal.signal(signal.SIGALRM, self._timeout)
+        signal.alarm(1)
+
+    def onecmd(self, string):
+        self._in_onecmd = True
+        try:
+            return cmd.Cmd.onecmd(self, string)
+        finally:
+            self._in_onecmd = False
+
+    def _timeout(self, _sig, _frame):
+        if not self._in_onecmd:
+            # restore terminal settings. (cooked/raw, ...)
+            # required for pypy at least.
+            # this doesn't seem to be needed for cpython readline
+            # module but i'm not sure if it's by spec or luck.
+            o = self._save_termios()
+            self._restore_termios(self._saved_termios)
+            self._peek_notification()
+            self._restore_termios(o)
+        signal.alarm(self._notification_check_interval)
+
+
+def main():
+    CONF(project='rpc-cli', version='rpc-cli')
+
+    for p_str in CONF.peers:
+        name, addr = p_str.split('=')
+        host, port = addr.rsplit(':', 1)
+        add_peer(name, host, port)
+
+    Cmd().cmdloop()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/setup.cfg b/setup.cfg
index de434ed..262dee2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -48,3 +48,4 @@ setup-hooks =
 [entry_points]
 console_scripts =
     ryu-manager = ryu.cmd.manager:main
+    rpc-cli = ryu.cmd.rpc_cli:main
-- 
1.8.3.1


------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT 
organizations don't have a clear picture of how application performance 
affects their revenue. With AppDynamics, you get 100% visibility into your 
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349351&iu=/4140/ostg.clktrk
_______________________________________________
Ryu-devel mailing list
Ryu-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to