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() {
|
||||
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
|
||||
|
||||
|
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