Commit 6e214f42 authored by zheng liu's avatar zheng liu

Merged in master (pull request #29)

Master
parents d8035482 d20c77da
...@@ -5,7 +5,7 @@ COPY requirements /opt/coco/requirements ...@@ -5,7 +5,7 @@ COPY requirements /opt/coco/requirements
WORKDIR /opt/coco WORKDIR /opt/coco
RUN cd requirements && yum -y install $(cat rpm_requirements.txt) && \ RUN cd requirements && yum -y install $(cat rpm_requirements.txt) && \
pip install -r requirements.txt pip install -r requirements.txt --download-cache=/opt/coco/.pip-cache/
COPY . /opt/coco COPY . /opt/coco
VOLUME /opt/coco/logs VOLUME /opt/coco/logs
VOLUME /opt/coco/keys VOLUME /opt/coco/keys
......
...@@ -205,6 +205,7 @@ class Coco: ...@@ -205,6 +205,7 @@ class Coco:
for client in self.clients: for client in self.clients:
self.remove_client(client) self.remove_client(client)
time.sleep(1) time.sleep(1)
self.heartbeat()
self.stop_evt.set() self.stop_evt.set()
self.sshd.shutdown() self.sshd.shutdown()
self.httpd.shutdown() self.httpd.shutdown()
...@@ -234,4 +235,4 @@ class Coco: ...@@ -234,4 +235,4 @@ class Coco:
with self.lock: with self.lock:
logger.info("Remove session: {}".format(session)) logger.info("Remove session: {}".format(session))
self.sessions.remove(session) self.sessions.remove(session)
self.service.finish_session(session.id) self.service.finish_session(session.to_json())
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
BACKSPACE_CHAR = {b'\x08': b'\x08\x1b[K', b'\x7f': b'\x08\x1b[K'} BACKSPACE_CHAR = {b'\x08': b'\x08\x1b[K', b'\x7f': b'\x08\x1b[K'}
ENTER_CHAR = [b'\r', b'\n', b'\r\n'] ENTER_CHAR = [b'\r', b'\n', b'\r\n']
ENTER_CHAR_ORDER = [ord(b'\r'), ord(b'\n')]
UNSUPPORTED_CHAR = {b'\x15': 'Ctrl-U', b'\x0c': 'Ctrl-L', b'\x05': 'Ctrl-E'} UNSUPPORTED_CHAR = {b'\x15': 'Ctrl-U', b'\x0c': 'Ctrl-L', b'\x05': 'Ctrl-E'}
CLEAR_CHAR = b'\x1b[H\x1b[2J' CLEAR_CHAR = b'\x1b[H\x1b[2J'
BELL_CHAR = b'\x07' BELL_CHAR = b'\x07'
NEW_LINE = b'\r\n' NEW_LINE = b'\r\n'
RZ_PROTOCOL_CHAR = b'**\x18B0900000000a87c\r\x8a\x11'
...@@ -11,7 +11,7 @@ from .models import Request, Client, WSProxy ...@@ -11,7 +11,7 @@ from .models import Request, Client, WSProxy
from .proxy import ProxyServer from .proxy import ProxyServer
from .utils import get_logger from .utils import get_logger
__version__ = '0.4.0' __version__ = '0.5.0'
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = os.path.dirname(os.path.dirname(__file__))
logger = get_logger(__file__) logger = get_logger(__file__)
......
...@@ -80,6 +80,7 @@ class InteractiveServer: ...@@ -80,6 +80,7 @@ class InteractiveServer:
input_data = [] input_data = []
parser = TtyIOParser() parser = TtyIOParser()
self.client.send(wr(prompt, before=1, after=0)) self.client.send(wr(prompt, before=1, after=0))
while True: while True:
data = self.client.recv(10) data = self.client.recv(10)
logger.debug(data) logger.debug(data)
...@@ -113,7 +114,7 @@ class InteractiveServer: ...@@ -113,7 +114,7 @@ class InteractiveServer:
# handle shell expect # handle shell expect
multi_char_with_enter = False multi_char_with_enter = False
if len(data) > 1 and data[-1] in char.ENTER_CHAR: if len(data) > 1 and data[-1] in char.ENTER_CHAR_ORDER:
self.client.send(data) self.client.send(data)
input_data.append(data[:-1]) input_data.append(data[:-1])
multi_char_with_enter = True multi_char_with_enter = True
......
...@@ -22,6 +22,18 @@ class Request: ...@@ -22,6 +22,18 @@ class Request:
self.date_start = datetime.datetime.now() self.date_start = datetime.datetime.now()
class SizedList(list):
def __init__(self, maxsize=0):
self.maxsize = maxsize
self.size = 0
super().__init__()
def append(self, b):
if self.maxsize == 0 or self.size < self.maxsize:
super().append(b)
self.size += len(b)
class Client: class Client:
""" """
Client is the request client. Nothing more to say Client is the request client. Nothing more to say
...@@ -78,8 +90,8 @@ class Server: ...@@ -78,8 +90,8 @@ class Server:
self.recv_bytes = 0 self.recv_bytes = 0
self.stop_evt = threading.Event() self.stop_evt = threading.Event()
self.input_data = [] self.input_data = SizedList(maxsize=1024)
self.output_data = [] self.output_data = SizedList(maxsize=1024)
self._in_input_state = True self._in_input_state = True
self._input_initial = False self._input_initial = False
self._in_vim_state = False self._in_vim_state = False
...@@ -118,6 +130,7 @@ class Server: ...@@ -118,6 +130,7 @@ class Server:
self._input, self._output, self._input, self._output,
"#" * 30 + " End " + "#" * 30, "#" * 30 + " End " + "#" * 30,
)) ))
if self._input:
self.session.put_command(self._input, self._output) self.session.put_command(self._input, self._output)
del self.input_data[:] del self.input_data[:]
del self.output_data[:] del self.output_data[:]
...@@ -152,10 +165,14 @@ class Server: ...@@ -152,10 +165,14 @@ class Server:
return False return False
def _parse_output(self): def _parse_output(self):
if not self.output_data:
return ''
parser = utils.TtyIOParser() parser = utils.TtyIOParser()
return parser.parse_output(self.output_data) return parser.parse_output(self.output_data)
def _parse_input(self): def _parse_input(self):
if not self.input_data or self.input_data[0] == char.RZ_PROTOCOL_CHAR:
return
parser = utils.TtyIOParser() parser = utils.TtyIOParser()
return parser.parse_input(self.input_data) return parser.parse_input(self.input_data)
...@@ -234,3 +251,7 @@ class WSProxy: ...@@ -234,3 +251,7 @@ class WSProxy:
self.child.close() self.child.close()
self.ws.logout(self.connection) self.ws.logout(self.connection)
logger.debug("Proxy {} closed".format(self)) logger.debug("Proxy {} closed".format(self))
...@@ -129,11 +129,45 @@ class ServerReplayRecorder(ReplayRecorder): ...@@ -129,11 +129,45 @@ class ServerReplayRecorder(ReplayRecorder):
logger.info("Succeed to push {}'s {}".format(session_id, "record")) logger.info("Succeed to push {}'s {}".format(session_id, "record"))
else: else:
logger.error("Failed to push {}'s {}".format(session_id, "record")) logger.error("Failed to push {}'s {}".format(session_id, "record"))
self.push_to_server(session_id)
def push_to_server(self, session_id): def push_to_server(self, session_id):
if self.upload_replay(3, session_id):
if self.finish_replay(3, session_id):
return True
else:
return False
else:
return False
def push_local(self, session_id):
return self.app.service.push_session_replay(os.path.join(self.app.config['LOG_DIR'], session_id + '.replay.gz'), return self.app.service.push_session_replay(os.path.join(self.app.config['LOG_DIR'], session_id + '.replay.gz'),
session_id) session_id)
def upload_replay(self, times, session_id):
if times > 0:
if self.push_local(session_id):
logger.info("success push session: {}'s replay log ".format(session_id))
return True
else:
logger.error("failed report session {}'s replay log, try {} times".format(session_id, times))
return self.upload_replay(times - 1, session_id)
else:
logger.error("failed report session {}'s replay log".format(session_id))
return False
def finish_replay(self, times, session_id):
if times > 0:
if self.app.service.finish_replay(session_id):
logger.info("success report session {}'s replay log ".format(session_id))
return True
else:
logger.error("failed report session {}'s replay log, try {} times".format(session_id, times))
return self.finish_replay(times - 1, session_id)
else:
logger.error("failed report session {}'s replay log".format(session_id))
return False
def __del__(self): def __del__(self):
print("{} has been gc".format(self)) print("{} has been gc".format(self))
del self.file del self.file
...@@ -254,18 +288,41 @@ class S3ReplayRecorder(ServerReplayRecorder): ...@@ -254,18 +288,41 @@ class S3ReplayRecorder(ServerReplayRecorder):
else: else:
self.s3 = boto3.client('s3') self.s3 = boto3.client('s3')
def push_to_server(self, session_id): def push_to_s3(self, session_id):
logger.debug("push to server") logger.debug("push to server")
try: try:
self.s3.upload_file( self.s3.upload_file(
os.path.join(self.app.config['LOG_DIR'], session_id + '.replay.gz'), os.path.join(self.app.config['LOG_DIR'], session_id + '.replay.gz'),
self.bucket, self.bucket,
self.app.config.get("NAME", "coco") + time.strftime('%Y-%m-%d', time.localtime( time.strftime('%Y-%m-%d', time.localtime(
self.starttime)) + '/' + session_id + '.replay.gz') self.starttime)) + '/' + session_id + '.replay.gz')
return True
except: except:
return self.app.service.push_session_replay( return False
os.path.join(self.app.config['LOG_DIR'], session_id + '.replay.gz'),
session_id) def upload_replay(self, times, session_id):
if times > 0:
if self.push_to_s3(session_id):
logger.info("success push session: {}'s replay log to S3 ".format(session_id))
return True
else:
logger.error("failed report session {}'s replay log to S3, try {} times".format(session_id, times))
return self.upload_replay(times - 1, session_id)
else:
logger.error("failed report session {}'s replay log S3, try to push to local".format(session_id))
return self.upload_replay_to_local(3, session_id)
def upload_replay_to_local(self, times, session_id):
if times > 0:
if self.push_local(session_id):
logger.info("success push session: {}'s replay log ".format(session_id))
return True
else:
logger.error("failed report session {}'s replay log, try {} times".format(session_id, times))
return self.upload_replay_to_local(times - 1, session_id)
else:
logger.error("failed report session {}'s replay log".format(session_id))
return False
def get_command_recorder_class(config): def get_command_recorder_class(config):
...@@ -280,6 +337,7 @@ def get_command_recorder_class(config): ...@@ -280,6 +337,7 @@ def get_command_recorder_class(config):
def get_replay_recorder_class(config): def get_replay_recorder_class(config):
replay_storage = config["REPLAY_STORAGE"] replay_storage = config["REPLAY_STORAGE"]
logger.debug(replay_storage)
storage_type = replay_storage.get('TYPE') storage_type = replay_storage.get('TYPE')
if storage_type == "s3": if storage_type == "s3":
return S3ReplayRecorder return S3ReplayRecorder
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment