Spdk/scripts/rpc_http_proxy.py
Konrad Sztyber 7610bc38dc scripts: move python modules to python directory
Up until now, importing an SPDK RPC python module was just a matter of
`import rpc`.  It's fine until there's another module called `rpc`
installed on the system, in which case it's impossible to import both of
them.  Therefore, to avoid this problem, all of the modules were moved
to a separate directory under the "spdk" namespace.

The decision to move to a location under a separate directory was
motivated by the fact that a directory called scripts/spdk would look
pretty confusing.  Moreover, it should make it also easier to package
these scripts as a python package.

Other than moving the packages, all of the imports were updated to
reflect these changes.  Files under python now use relative imports,
while those under scripts/ use the "spdk" namespace and have their
PYTHONPATH extended with python directory.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Ib43dee73921d590a551dd83885e22870e72451cf
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9692
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
2022-04-05 14:40:47 +00:00

152 lines
4.3 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import base64
import errno
import json
import os
import socket
import ssl
import sys
try:
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
except ImportError:
from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler
sys.path.append(os.path.dirname(__file__) + '/../python')
from spdk.rpc.client import print_json # noqa
rpc_sock = None
parser = argparse.ArgumentParser(description='http(s) proxy for SPDK RPC calls')
parser.add_argument('host', help='Host name / IP representing proxy server')
parser.add_argument('port', help='Port number', type=int)
parser.add_argument('user', help='User name used for authentication')
parser.add_argument('password', help='Password used for authentication')
parser.add_argument('-s', dest='sock', help='RPC domain socket path', default='/var/tmp/spdk.sock')
parser.add_argument('-c', dest='cert', help='SSL certificate')
def print_usage_and_exit(status):
print('Usage: rpc_http_proxy.py <server IP> <server port> <user name>' +
' <password> <SPDK RPC socket (optional, default: /var/tmp/spdk.sock)>')
sys.exit(status)
def rpc_call(req):
global rpc_sock
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(rpc_sock)
sock.sendall(req)
if 'id' not in json.loads(req.decode('ascii')):
sock.close()
return None
buf = ''
closed = False
response = None
print_json(req.decode('ascii'))
while not closed:
newdata = sock.recv(1024)
if (newdata == b''):
closed = True
buf += newdata.decode('ascii')
try:
response = json.loads(buf)
except ValueError:
continue # incomplete response; keep buffering
break
sock.close()
if not response and len(buf) > 0:
raise
print_json(buf)
return buf
class ServerHandler(BaseHTTPRequestHandler):
key = ""
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_AUTHHEAD(self):
self.send_response(401)
self.send_header('WWW-Authenticate', 'text/html')
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_INTERNALERROR(self):
self.send_response(500)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_POST(self):
if self.headers['Authorization'] != 'Basic ' + self.key:
self.do_AUTHHEAD()
else:
if "Content-Length" in self.headers:
data_string = self.rfile.read(int(self.headers['Content-Length']))
elif "chunked" in self.headers.get("Transfer-Encoding", ""):
data_string = b''
while True:
line = self.rfile.readline().strip()
chunk_length = int(line, 16)
if chunk_length != 0:
chunk = self.rfile.read(chunk_length)
data_string += chunk
# Each chunk is followed by an additional empty newline
# that we have to consume.
self.rfile.readline()
# Finally, a chunk size of 0 is an end indication
if chunk_length == 0:
break
try:
response = rpc_call(data_string)
if response is not None:
self.do_HEAD()
self.wfile.write(bytes(response.encode(encoding='ascii')))
except ValueError:
self.do_INTERNALERROR()
def main():
global rpc_sock
args = parser.parse_args()
rpc_sock = args.sock
# encoding user name and password
key = base64.b64encode((args.user+':'+args.password).encode(encoding='ascii')).decode('ascii')
try:
ServerHandler.key = key
httpd = HTTPServer((args.host, args.port), ServerHandler)
if args.cert is not None:
httpd.socket = ssl.wrap_socket(httpd.socket, certfile=args.cert, server_side=True)
print('Started RPC http proxy server')
httpd.serve_forever()
except KeyboardInterrupt:
print('Shutting down server')
httpd.socket.close()
if __name__ == '__main__':
main()