diff --git a/confluent/console.py b/confluent/console.py index 84e20b54..0d3d94ab 100644 --- a/confluent/console.py +++ b/confluent/console.py @@ -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): diff --git a/confluent/httpapi.py b/confluent/httpapi.py index 43573074..b8e3d7b6 100644 --- a/confluent/httpapi.py +++ b/confluent/httpapi.py @@ -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)"] diff --git a/plugins/ipmi.py b/plugins/ipmi.py index ddeb3473..411d20a5 100644 --- a/plugins/ipmi.py +++ b/plugins/ipmi.py @@ -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':