Spdk/scripts/rpc/client.py
Pawel Wodkowski d5ac3eb190 scripts/rpc: add support for receiving multiple responses
Switch to raw_decode in recv() function. This allow to decode JSON
objects one by one. Decoded part is removed from from receive bufer.

Change-Id: Id0d78a2ace85bcbb9cc8e30d72da6c1c2cad753c
Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/435507
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Karol Latecki <karol.latecki@intel.com>
Reviewed-by: Pawel Kaminski <pawelx.kaminski@intel.com>
2019-03-15 05:01:27 +00:00

115 lines
3.7 KiB
Python

import json
import socket
import time
import os
def print_dict(d):
print(json.dumps(d, indent=2))
class JSONRPCException(Exception):
def __init__(self, message):
self.message = message
class JSONRPCClient(object):
def __init__(self, addr, port=None, verbose=False, timeout=60.0):
self.sock = None
self.verbose = verbose
self.timeout = timeout
self._request_id = 0
self._recv_buf = ""
self._reqs = []
try:
if os.path.exists(addr):
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.connect(addr)
elif ':' in addr:
for res in socket.getaddrinfo(addr, port, socket.AF_INET6, socket.SOCK_STREAM, socket.SOL_TCP):
af, socktype, proto, canonname, sa = res
self.sock = socket.socket(af, socktype, proto)
self.sock.connect(sa)
else:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((addr, port))
except socket.error as ex:
raise JSONRPCException("Error while connecting to %s\n"
"Error details: %s" % (addr, ex))
def __del__(self):
if self.sock:
self.sock.close()
def send(self, method, params=None):
self._request_id += 1
req = {
'jsonrpc': '2.0',
'method': method,
'id': self._request_id
}
if params:
req['params'] = params
reqstr = json.dumps(req)
if self.verbose:
print("request:")
print(json.dumps(req, indent=2))
self.sock.sendall(reqstr.encode("utf-8"))
return req
def decode_one_response(self):
try:
buf = self._recv_buf.lstrip()
obj, idx = json.JSONDecoder().raw_decode(buf)
self._recv_buf = buf[idx:]
return obj
except ValueError:
return None
def recv(self):
start_time = time.clock()
response = self.decode_one_response()
while not response:
try:
timeout = self.timeout - (time.clock() - start_time)
self.sock.settimeout(timeout)
newdata = self.sock.recv(4096)
if not newdata:
self.sock.close()
self.sock = None
raise JSONRPCException("Connection closed with partial response:\n%s\n" % self._recv_buf)
self._recv_buf += newdata.decode("utf-8")
response = self.decode_one_response()
except socket.timeout:
break # throw exception after loop to avoid Python freaking out about nested exceptions
except ValueError:
continue # incomplete response; keep buffering
if not response:
raise JSONRPCException("Timeout while waiting for response:\n%s\n" % self._recv_buf)
if self.verbose:
print("response:")
print(json.dumps(response, indent=2))
if 'error' in response:
msg = "\n".join(["Got JSON-RPC error response",
"response:",
json.dumps(response['error'], indent=2)])
raise JSONRPCException(msg)
return response
def call(self, method, params=None):
self.send(method, params)
try:
return self.recv()['result']
except JSONRPCException as e:
""" Don't expect response to kill """
if not self.sock and method == "kill_instance":
return {}
else:
raise e