a simple command line msgpack-rpc client
Signed-off-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
220b9e2ca7
commit
3e3cb12c06
20
bin/rpc-cli
Executable file
20
bin/rpc-cli
Executable file
@ -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()
|
@ -90,7 +90,7 @@ run_tests() {
|
|||||||
run_pylint() {
|
run_pylint() {
|
||||||
echo "Running pylint ..."
|
echo "Running pylint ..."
|
||||||
PYLINT_OPTIONS="--rcfile=.pylintrc --output-format=parseable"
|
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
|
export PYTHONPATH=$PYTHONPATH:.ryu
|
||||||
PYLINT_LOG=pylint.log
|
PYLINT_LOG=pylint.log
|
||||||
|
|
||||||
|
237
ryu/cmd/rpc_cli.py
Executable file
237
ryu/cmd/rpc_cli.py
Executable file
@ -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()
|
Loading…
Reference in New Issue
Block a user