summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeepak Kodihalli <dkodihal@in.ibm.com>2018-04-23 03:26:38 -0500
committerDeepak Kodihalli <dkodihal@in.ibm.com>2018-04-30 09:17:07 -0500
commit5c518f63d7a169326b572cfa5886b15a6876f4f1 (patch)
tree3a58cc215aacdf47b1c085d7f72c8425f8cb7a30
parent97fe435fc04b401eab197adcd15d212971ed6e0c (diff)
downloadphosphor-rest-server-5c518f63d7a169326b572cfa5886b15a6876f4f1.zip
phosphor-rest-server-5c518f63d7a169326b572cfa5886b15a6876f4f1.tar.gz
Expose host serial console over a websocket
Expose host serial console over a "/console" route, using a websocket. An authenticated client can access the host serial console via reads/writes to this websocket. Change-Id: I0f63a3844e777d4f4c45194c85a63c9f10a91744 Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
-rw-r--r--module/obmc/wsgi/apps/rest_dbus.py83
1 files changed, 83 insertions, 0 deletions
diff --git a/module/obmc/wsgi/apps/rest_dbus.py b/module/obmc/wsgi/apps/rest_dbus.py
index 6374add..3eac4f4 100644
--- a/module/obmc/wsgi/apps/rest_dbus.py
+++ b/module/obmc/wsgi/apps/rest_dbus.py
@@ -44,6 +44,8 @@ if have_wsock:
except ImportError: # python 3
from gi.repository import GObject as gobject
import gevent
+ from gevent import socket
+ from gevent import Greenlet
DBUS_UNKNOWN_INTERFACE = 'org.freedesktop.DBus.Error.UnknownInterface'
DBUS_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod'
@@ -916,6 +918,85 @@ class EventHandler(RouteHandler):
notifier = EventNotifier(wsock, filters)
+class HostConsoleHandler(RouteHandler):
+ ''' Handles the /console route, for clients to be able
+ read/write the host serial console. The way this is
+ done is by exposing a websocket that's mirrored to an
+ abstract UNIX domain socket, which is the source for
+ the console data. '''
+
+ verbs = ['GET']
+ # Naming the route console0, because the numbering will help
+ # on multi-bmc/multi-host systems.
+ rules = ['/console0']
+
+ def __init__(self, app, bus):
+ super(HostConsoleHandler, self).__init__(
+ app, bus, self.verbs, self.rules)
+
+ def find(self, **kw):
+ pass
+
+ def setup(self, **kw):
+ pass
+
+ def read_wsock(self, wsock, sock):
+ while True:
+ try:
+ incoming = wsock.receive()
+ if incoming:
+ # Read websocket, write to UNIX socket
+ sock.send(incoming)
+ except Exception as e:
+ sock.close()
+ return
+
+ def read_sock(self, sock, wsock):
+ max_sock_read_len = 4096
+ while True:
+ try:
+ outgoing = sock.recv(max_sock_read_len)
+ if outgoing:
+ # Read UNIX socket, write to websocket
+ wsock.send(outgoing)
+ except Exception as e:
+ wsock.close()
+ return
+
+ def send_ping(self, wsock) :
+ # Most webservers close websockets after 60 seconds of
+ # inactivity. Make sure to send a ping before that.
+ timeout = 45
+ payload = "ping"
+ # the ping payload can be anything, the receiver has to just
+ # return the same back.
+ while True:
+ gevent.sleep(timeout)
+ wsock.send_frame(payload, wsock.OPCODE_PING)
+
+ def do_get(self):
+ wsock = request.environ.get('wsgi.websocket')
+ if not wsock:
+ abort(400, 'Expected WebSocket based request.')
+
+ # A UNIX domain socket structure defines a 108-byte pathname. The
+ # server in this case, obmc-console-server, expects a 108-byte path.
+ socket_name = "\0obmc-console"
+ trailing_bytes = "\0" * (108 - len(socket_name))
+ socket_path = socket_name + trailing_bytes
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+
+ try:
+ sock.connect(socket_path)
+ except Exception as e:
+ abort(500, str(e))
+
+ wsock_reader = Greenlet.spawn(self.read_wsock, wsock, sock)
+ sock_reader = Greenlet.spawn(self.read_sock, sock, wsock)
+ ping_sender = Greenlet.spawn(self.send_ping, wsock)
+ gevent.joinall([wsock_reader, sock_reader, ping_sender])
+
+
class ImagePutHandler(RouteHandler):
''' Handles the /upload/image/<filename> route. '''
@@ -1386,6 +1467,7 @@ class App(Bottle):
self.download_dump_get_handler = DownloadDumpHandler(self, self.bus)
if self.have_wsock:
self.event_handler = EventHandler(self, self.bus)
+ self.host_console_handler = HostConsoleHandler(self, self.bus)
self.instance_handler = InstanceHandler(self, self.bus)
def install_handlers(self):
@@ -1402,6 +1484,7 @@ class App(Bottle):
self.download_dump_get_handler.install()
if self.have_wsock:
self.event_handler.install()
+ self.host_console_handler.install()
# this has to come last, since it matches everything
self.instance_handler.install()
OpenPOWER on IntegriCloud