2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-22 09:32:21 +00:00

Have console sessions delete themselves after http client has gone away... probable

This commit is contained in:
Jarrod Johnson 2013-09-20 14:36:55 -04:00
parent 1d76dcf13d
commit 982b438279
2 changed files with 45 additions and 16 deletions

View File

@ -9,6 +9,8 @@
#there should be no more than one handler per node
import confluent.pluginapi as plugin
import confluent.util as util
import eventlet
import random
_handled_consoles = {}
@ -18,16 +20,24 @@ class _ConsoleHandler(object):
"create", configmanager)
self.buffer = bytearray()
self._console.connect(self.get_console_output)
self.rcpts = []
self.rcpts = {}
def unregister_rcpt(self, handle):
if handle in self.rcpts:
del self.rcpts[handle]
def register_rcpt(self, callback):
self.rcpts.append(callback)
hdl = random.random()
while hdl in self.rcpts:
hdl = random.random()
self.rcpts[hdl] = callback
return hdl
def flushbuffer(self):
#TODO:log the old stuff
if len(self.buffer) > 512:
self.buffer = bytearray(self.buffer[-512:])
if len(self.buffer) > 1024:
self.buffer = bytearray(self.buffer[-1024:])
#Will be interesting to keep track of logged but
#retained data, must only log data not already
#flushed
@ -38,28 +48,38 @@ class _ConsoleHandler(object):
#TODO: analyze buffer for registered events, examples:
# panics
# certificate signing request
if len(self.buffer) > 2048:
if len(self.buffer) > 8192:
#call to function to get generic data to log if applicable
#and shrink buffer
self.flushbuffer()
for rcpt in self.rcpts:
for rcpt in self.rcpts.itervalues():
rcpt(data)
def get_recent(self):
"""Retrieve 'recent' data
Replay data in the intent to perhaps reproduce the display.
"""
#For now, just try to seek back in buffer to find a clear screen
#If that fails, just return buffer
#a scheme always tracking the last clear screen would be too costly
#an alternative would be to emulate a VT100 to know what the
#whole screen would look like
#this is one scheme to clear screen, move cursor then clear
if len(self.buffer) > 512:
bgn = -512
else:
bgn = 0
bufidx = self.buffer[bgn:].rfind('\x1b[H\x1b[J')
bufidx = self.buffer.rfind('\x1b[H\x1b[J')
if bufidx >= 0:
return str(self.buffer[bufidx:])
#another scheme is the 2J scheme
bufidx = self.buffer[bgn:].rfind('\x1b[2J')
bufidx = self.buffer.rfind('\x1b[2J')
if bufidx >= 0:
# there was some sort of clear screen event
# somewhere in the buffer, replay from that point
# in hopes that it reproduces the screen
return str(self.buffer[bufidx:])
else:
return str(self.buffer[bgn:])
#we have no indication of last erase, play back last kibibyte
#to give some sense of context anyway
return str(self.buffer[-1024:])
def write(self, data):
#TODO.... take note of data coming in from audit/log perspective?
@ -84,15 +104,22 @@ class ConsoleSession(object):
def __init__(self, node, configmanager, datacallback=None):
if node not in _handled_consoles:
_handled_consoles[node] = _ConsoleHandler(node, configmanager)
self.node = node
self.conshdl = _handled_consoles[node]
self.write = _handled_consoles[node].write
if datacallback is None:
self.reaper = eventlet.spawn_after(15, self.destroy)
self.databuffer = _handled_consoles[node].get_recent()
_handled_consoles[node].register_rcpt(self.got_data)
self.reghdl = _handled_consoles[node].register_rcpt(self.got_data)
else:
_handled_consoles[node].register_rcpt(datacallback)
self.reghdl = _handled_consoles[node].register_rcpt(datacallback)
datacallback(_handled_consoles[node].get_recent())
def destroy(self):
_handled_consoles[self.node].unregister_rcpt(self.reghdl)
self.databuffer = None
self.reghdl = None
def got_data(self, data):
"""Receive data from console and buffer
@ -107,6 +134,7 @@ class ConsoleSession(object):
Ideally purely event driven scheme is perfect. AJAX over HTTP is
at least one case where we don't have that luxury
"""
self.reaper.cancel()
currtime = util.monotonic_time()
deadline = currtime + 45
while len(self.databuffer) == 0 and currtime < deadline:
@ -115,6 +143,7 @@ class ConsoleSession(object):
currtime = util.monotonic_time()
retval = self.databuffer
self.databuffer = ""
self.reaper = eventlet.spawn_after(15, self.destroy)
return retval

View File

@ -137,7 +137,7 @@ def resourcehandler(env, start_response):
try:
rsp = json.dumps({'session': querydict['session'], 'data': outdata})
except UnicodeDecodeError:
rsp = json.dumps({'session': querydict['session'], 'data': outdata}, encoding='cp1252')
rsp = json.dumps({'session': querydict['session'], 'data': outdata}, encoding='cp437')
except UnicodeDecodeError:
rsp = json.dumps({'session': querydict['session'], 'data': 'DECODEERROR'})
start_response('200 OK', [('Content-Type',