mirror of
https://github.com/xcat2/confluent.git
synced 2025-01-15 12:17:47 +00:00
Actually implement enough to get some console traffic going.
This implements a protocol that should be compatible with shellinabox's javascript client code.
This commit is contained in:
parent
a359ae5063
commit
1cbc16b22e
@ -8,6 +8,8 @@
|
||||
#we track nodes that are actively being logged, watched, or have attached
|
||||
#there should be no more than one handler per node
|
||||
import confluent.pluginapi as plugin
|
||||
import confluent.util as util
|
||||
|
||||
_handled_consoles = {}
|
||||
|
||||
class _ConsoleHandler(object):
|
||||
@ -15,16 +17,26 @@ class _ConsoleHandler(object):
|
||||
self._console = plugin.handle_path("/node/%s/_console/session" % node,
|
||||
"create", configmanager)
|
||||
self._console.connect(self.get_console_output)
|
||||
self.rcpts = []
|
||||
|
||||
def register_rcpt(self, callback):
|
||||
self.rcpts.append(callback)
|
||||
|
||||
def get_console_output(self, data):
|
||||
#TODO: logging, forwarding, etc
|
||||
print "huzzah"
|
||||
print data
|
||||
for rcpt in self.rcpts:
|
||||
rcpt(data)
|
||||
|
||||
def write(self, data):
|
||||
#TODO.... take note of data coming in from audit/log perspective?
|
||||
#or just let echo take care of it and then we can skip this stack
|
||||
#level?
|
||||
self._console.write(data)
|
||||
|
||||
#this represents some api view of a console handler. This handles things like
|
||||
#holding the caller specific queue data, for example, when http api should be
|
||||
#sending data, but there is no outstanding POST request to hold it,
|
||||
# this object has the job of halding the data
|
||||
# this object has the job of holding the data
|
||||
class ConsoleSession(object):
|
||||
"""Create a new socket to converse with node console
|
||||
|
||||
@ -36,10 +48,31 @@ class ConsoleSession(object):
|
||||
"""
|
||||
|
||||
def __init__(self, node, configmanager):
|
||||
self.databuffer = ""
|
||||
if node not in _handled_consoles:
|
||||
_handled_consoles[node] = _ConsoleHandler(node, configmanager)
|
||||
pass
|
||||
# TODO(jbjohnso): actually do the cool stuff
|
||||
self.conshdl = _handled_consoles[node]
|
||||
self.write = _handled_consoles[node].write
|
||||
_handled_consoles[node].register_rcpt(self.got_data)
|
||||
|
||||
def got_data(self, data):
|
||||
self.databuffer += data
|
||||
|
||||
def get_next_output(self, timeout=45):
|
||||
"""Poll for next available output on this console.
|
||||
|
||||
Ideally purely event driven scheme is perfect. AJAX over HTTP is
|
||||
at least one case where we don't have that luxury
|
||||
"""
|
||||
currtime = util.monotonic_time()
|
||||
deadline = currtime + 45
|
||||
while len(self.databuffer) == 0 and currtime < deadline:
|
||||
timeo = deadline - currtime
|
||||
self.conshdl.wait_for_data(timeout=timeo)
|
||||
currtime = util.monotonic_time()
|
||||
retval = self.databuffer
|
||||
self.databuffer = ""
|
||||
return retval
|
||||
|
||||
|
||||
def handle_request(request=None, connection=None, releaseconnection=False):
|
||||
|
@ -19,14 +19,14 @@ consolesessions = {}
|
||||
|
||||
def _get_query_dict(qstring, reqbody, reqtype):
|
||||
qdict = {}
|
||||
if not qstring:
|
||||
return qdict
|
||||
for qpair in qstring.split('&'):
|
||||
qkey, qvalue = qpair.split('=')
|
||||
qdict[qkey] = qvalue
|
||||
if qstring:
|
||||
for qpair in qstring.split('&'):
|
||||
qkey, qvalue = qpair.split('=')
|
||||
qdict[qkey] = qvalue
|
||||
if reqbody is not None:
|
||||
if reqtype == "application/x-www-form-urlencoded":
|
||||
if "application/x-www-form-urlencoded" in reqtype:
|
||||
print reqbody
|
||||
raise(Exception("TODO: must actually do url form encode parse here"))
|
||||
return qdict
|
||||
|
||||
|
||||
@ -120,6 +120,21 @@ def resourcehandler(env, start_response):
|
||||
start_response('200 OK', [('Content-Type',
|
||||
'application/json; charset=utf-8')])
|
||||
return ['{"session":"%s","data":""}' % sessid]
|
||||
elif 'keys' in querydict.keys():
|
||||
# client wishes to push some keys into the remote console
|
||||
input = ""
|
||||
for idx in xrange(0, len(querydict['keys'])):
|
||||
input += chr(int(querydict['keys'][idx:idx+2]))
|
||||
print "taking in "+input
|
||||
sessid = querydict['session']
|
||||
consolesessions[sessid].write(input)
|
||||
start_response('200 OK', [('Content-Type',
|
||||
'application/json; charset=utf-8')])
|
||||
return # client has requests to send or receive, not both...
|
||||
else: #no keys, but a session, means it's hooking to receive data
|
||||
outdata = consolesessions[sessid].get_next_output(timeout=45)
|
||||
json = '{"session":"%s","data":"%s"}'%(querydict['session'],
|
||||
outdata)
|
||||
start_response('404 Not Found', [])
|
||||
return ["Unrecognized directive (404)"]
|
||||
|
||||
|
@ -72,6 +72,20 @@ class Console(object):
|
||||
def write(self, data):
|
||||
self.solconnection.send_data(data)
|
||||
|
||||
def wait_for_data(self, timeout=600):
|
||||
"""Wait for some network event.
|
||||
|
||||
This is currently not guaranteed to actually have data when
|
||||
return. This is supposed to be something more appropriate
|
||||
than sleep(0), but only marginally so.
|
||||
"""
|
||||
# reason for this is that we currently nicely pass through the callback
|
||||
# straight to ipmi library. To implement this accurately, easiest path
|
||||
# would be to add a layer through the callback. IMO there isn't enough
|
||||
# value in assuring data coming back to bother with making the stack
|
||||
# taller than it has to be
|
||||
console.session.Session.wait_for_rsp(timeout=timeout)
|
||||
|
||||
|
||||
def create(nodes, element, configmanager):
|
||||
if element == '_console/session':
|
||||
|
Loading…
x
Reference in New Issue
Block a user