Commit b15b5efa authored by ibuler's avatar ibuler

[Feature] 将recorde拆出来完成

parent e9a6daa7
...@@ -49,18 +49,27 @@ class Coco: ...@@ -49,18 +49,27 @@ class Coco:
self.clients = [] self.clients = []
self.lock = threading.Lock() self.lock = threading.Lock()
self.stop_evt = threading.Event() self.stop_evt = threading.Event()
self._service = None
self._sshd = None
self._httpd = None
@property @property
def service(self): def service(self):
return AppService(self) if self._service is None:
self._service = AppService(self)
return self._service
@property @property
def sshd(self): def sshd(self):
return SSHServer(self) if self._sshd is None:
self._sshd = SSHServer(self)
return self._sshd
@property @property
def httpd(self): def httpd(self):
return HttpServer(self) if self._httpd is None:
self._httpd = HttpServer(self)
return self._httpd
def make_logger(self): def make_logger(self):
create_logger(self) create_logger(self)
...@@ -73,10 +82,10 @@ class Coco: ...@@ -73,10 +82,10 @@ class Coco:
self.make_logger() self.make_logger()
self.service.initial() self.service.initial()
self.load_extra_conf_from_server() self.load_extra_conf_from_server()
self.heartbeat() self.keep_heartbeat()
self.monitor_sessions() self.monitor_sessions()
def heartbeat(self): def keep_heartbeat(self):
def func(): def func():
while not self.stop_evt.is_set(): while not self.stop_evt.is_set():
_sessions = [s.to_json() for s in self.sessions] _sessions = [s.to_json() for s in self.sessions]
...@@ -95,7 +104,7 @@ class Coco: ...@@ -95,7 +104,7 @@ class Coco:
def func(): def func():
while not self.stop_evt.is_set(): while not self.stop_evt.is_set():
for s in self.sessions: for s in self.sessions:
if not s.is_finished: if not s.stop_evt.is_set():
continue continue
if s.date_finished is None: if s.date_finished is None:
self.remove_session(s) self.remove_session(s)
......
# coding: utf-8 # coding: utf-8
import socket import socket
import threading import threading
import logging import logging
...@@ -9,11 +8,13 @@ import paramiko ...@@ -9,11 +8,13 @@ import paramiko
from .session import Session from .session import Session
from .models import Server from .models import Server
from .record import FileRecorder
from .utils import wrap_with_line_feed as wr from .utils import wrap_with_line_feed as wr
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
TIMEOUT = 8 TIMEOUT = 8
BUF_SIZE = 4096
class ProxyServer: class ProxyServer:
...@@ -32,7 +33,11 @@ class ProxyServer: ...@@ -32,7 +33,11 @@ class ProxyServer:
session = Session(self.client, self.server) session = Session(self.client, self.server)
self.app.add_session(session) self.app.add_session(session)
self.watch_win_size_change_async() self.watch_win_size_change_async()
session.add_recorder() recorder = FileRecorder(self.app, session)
session.add_recorder(recorder)
session.record_async()
self.server.add_recorder(recorder)
self.server.record_command_async()
session.bridge() session.bridge()
session.stop_evt.set() session.stop_evt.set()
...@@ -55,7 +60,7 @@ class ProxyServer: ...@@ -55,7 +60,7 @@ class ProxyServer:
self.app.service.get_system_user_auth_info(system_user) self.app.service.get_system_user_auth_info(system_user)
def get_server_conn(self, asset, system_user): def get_server_conn(self, asset, system_user):
logger.info("Connect to %s" % asset.hostname) logger.info("Connect to {}".format(asset.hostname))
if not self.validate_permission(asset, system_user): if not self.validate_permission(asset, system_user):
self.client.send(_('No permission')) self.client.send(_('No permission'))
return None return None
......
...@@ -39,7 +39,7 @@ class BaseWehSocketHandler: ...@@ -39,7 +39,7 @@ class BaseWehSocketHandler:
class InteractiveWehSocketHandler(BaseWehSocketHandler, tornado.websocket.WebSocketHandler): class InteractiveWehSocketHandler(BaseWehSocketHandler, tornado.websocket.WebSocketHandler):
@tornado.web.authenticated @tornado.web.authenticated
def open(self): def open(self):
InteractiveServer(self.app, self.client).activate_async() InteractiveServer(self.app, self.client).interact_async()
def on_message(self, message): def on_message(self, message):
try: try:
......
...@@ -3,7 +3,6 @@ import logging ...@@ -3,7 +3,6 @@ import logging
import socket import socket
import threading import threading
# Todo remove
from jms.models import Asset, AssetGroup from jms.models import Asset, AssetGroup
from . import char from . import char
...@@ -12,7 +11,6 @@ from .utils import wrap_with_line_feed as wr, wrap_with_title as title, \ ...@@ -12,7 +11,6 @@ from .utils import wrap_with_line_feed as wr, wrap_with_title as title, \
is_obj_attr_has, is_obj_attr_eq, sort_assets, TtyIOParser, \ is_obj_attr_has, is_obj_attr_eq, sort_assets, TtyIOParser, \
ugettext as _ ugettext as _
from .forward import ProxyServer from .forward import ProxyServer
from .session import Session
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
...@@ -172,9 +170,10 @@ class InteractiveServer: ...@@ -172,9 +170,10 @@ class InteractiveServer:
self.display_search_result() self.display_search_result()
def display_search_result(self): def display_search_result(self):
if len(self.search_result) == 0: # if len(self.search_result) == 0:
self.client.send(warning("Nothing match")) # self.client.send(warning("Nothing match"))
return # return
print("Total assets: ".format(len(self.assets)))
self.search_result = sort_assets(self.search_result, self.app.config["ASSET_LIST_SORT_BY"]) self.search_result = sort_assets(self.search_result, self.app.config["ASSET_LIST_SORT_BY"])
fake_asset = Asset(hostname=_("Hostname"), ip=_("IP"), system_users_join=_("LoginAs"), comment=_("Comment")) fake_asset = Asset(hostname=_("Hostname"), ip=_("IP"), system_users_join=_("LoginAs"), comment=_("Comment"))
...@@ -189,7 +188,9 @@ class InteractiveServer: ...@@ -189,7 +188,9 @@ class InteractiveServer:
self.client.send(wr(title(header.format(fake_asset, "ID")))) self.client.send(wr(title(header.format(fake_asset, "ID"))))
for index, asset in enumerate(self.search_result, 1): for index, asset in enumerate(self.search_result, 1):
self.client.send(wr(line.format(asset, index))) self.client.send(wr(line.format(asset, index)))
self.client.send(wr(_("Total: {}").format(len(self.search_result)), before=1)) self.client.send(wr(_("Total: {} Matched: {}").format(
len(self.assets), len(self.search_result)), before=1)
)
def search_and_display(self, q): def search_and_display(self, q):
self.search_assets(q) self.search_assets(q)
...@@ -204,6 +205,7 @@ class InteractiveServer: ...@@ -204,6 +205,7 @@ class InteractiveServer:
def get_user_assets(self): def get_user_assets(self):
self.assets = self.app.service.get_user_assets(self.client.user) self.assets = self.app.service.get_user_assets(self.client.user)
logger.debug("Get user {} assets total: {}".format(self.client.user, len(self.assets)))
def get_user_assets_async(self): def get_user_assets_async(self):
thread = threading.Thread(target=self.get_user_assets) thread = threading.Thread(target=self.get_user_assets)
...@@ -260,7 +262,7 @@ class InteractiveServer: ...@@ -260,7 +262,7 @@ class InteractiveServer:
break break
self.close() self.close()
def activate_async(self): def interact_async(self):
thread = threading.Thread(target=self.interact) thread = threading.Thread(target=self.interact)
thread.daemon = True thread.daemon = True
thread.start() thread.start()
......
...@@ -2,11 +2,13 @@ import json ...@@ -2,11 +2,13 @@ import json
import queue import queue
import threading import threading
import datetime import datetime
import logging
from . import char from . import char
from . import utils from . import utils
BUF_SIZE = 4096 BUF_SIZE = 4096
logger = logging.getLogger(__file__)
class Request: class Request:
...@@ -66,6 +68,7 @@ class Server: ...@@ -66,6 +68,7 @@ class Server:
self.system_user = system_user self.system_user = system_user
self.send_bytes = 0 self.send_bytes = 0
self.recv_bytes = 0 self.recv_bytes = 0
self.stop_evt = threading.Event()
self.input_data = [] self.input_data = []
self.output_data = [] self.output_data = []
...@@ -74,7 +77,10 @@ class Server: ...@@ -74,7 +77,10 @@ class Server:
self._in_vim_state = False self._in_vim_state = False
self.recorders = [] self.recorders = []
self.queue = queue.Queue() self.filters = []
self._input = ""
self._output = ""
self.command_queue = queue.Queue()
def add_recorder(self, recorder): def add_recorder(self, recorder):
self.recorders.append(recorder) self.recorders.append(recorder)
...@@ -82,13 +88,21 @@ class Server: ...@@ -82,13 +88,21 @@ class Server:
def remove_recorder(self, recorder): def remove_recorder(self, recorder):
self.recorders.remove(recorder) self.recorders.remove(recorder)
def record_command(self, _input, _output): def add_filter(self, _filter):
while True: self.filters.append(_filter)
_input, _output = self.queue.get()
for recorder in self.recorders: def remove_filter(self, _filter):
t = threading.Thread(target=recorder.record_command, self.filters.remove(_filter)
args=(_input, _output))
t.start() def record_command_async(self):
def func():
while not self.stop_evt.is_set():
_input, _output = self.command_queue.get()
logger.debug("Record command: ({},{})".format(_input, _output))
for recorder in self.recorders:
recorder.record_command(datetime.datetime.now(), _input, _output)
thread = threading.Thread(target=func)
thread.start()
def fileno(self): def fileno(self):
return self.chan.fileno() return self.chan.fileno()
...@@ -101,14 +115,18 @@ class Server: ...@@ -101,14 +115,18 @@ class Server:
if self._have_enter_char(b): if self._have_enter_char(b):
self._in_input_state = False self._in_input_state = False
self._input = self._parse_input()
else: else:
if not self._in_input_state: if not self._in_input_state:
print("#" * 30 + " 新周期 " + "#" * 30) print("#" * 30 + " 新周期 " + "#" * 30)
_input = self._parse_input() self._output = self._parse_output()
_output = self._parse_output() print(self._input)
self.record_command(_input, _output) print(self._output)
self.command_queue.put((self._input, self._output))
del self.input_data[:] del self.input_data[:]
del self.output_data[:] del self.output_data[:]
self._input = ""
self._output = ""
self._in_input_state = True self._in_input_state = True
return self.chan.send(b) return self.chan.send(b)
......
...@@ -2,11 +2,7 @@ ...@@ -2,11 +2,7 @@
# #
import abc import abc
import multiprocessing
import threading
import time import time
import datetime
import socket
import os import os
import logging import logging
...@@ -20,52 +16,17 @@ class Recorder(metaclass=abc.ABCMeta): ...@@ -20,52 +16,17 @@ class Recorder(metaclass=abc.ABCMeta):
def __init__(self, app, session): def __init__(self, app, session):
self.app = app self.app = app
self.session = session self.session = session
self.replay_queue = multiprocessing.Queue()
self.command_queue = multiprocessing.Queue()
self.stop_evt = multiprocessing.Event()
@abc.abstractmethod @abc.abstractmethod
def record_replay(self): def record_replay(self, now, timedelta, size, data):
pass pass
@abc.abstractmethod @abc.abstractmethod
def record_command(self, _input, _output): def record_command(self, now, _input, _output):
pass
class FileRecorder(Recorder):
def record_replay(self):
parent, child = socket.socketpair()
self.session.add_watcher(parent)
session_dir = self.app.config["SESSION_DIR"]
with open(os.path.join(session_dir, session.id + ".rec"), 'wb') as dataf, \
open(os.path.join(session_dir, session.id + ".time"), "w") as timef:
dataf.write("Script started on {}\n".format(time.asctime()).encode("utf-8"))
while not self.stop_evt.is_set():
start_t = time.time()
data = child.recv(BUF_SIZE)
end_t = time.time()
size = len(data)
if size == 0:
break
timef.write("%.4f %s\n" % (end_t - start_t, size))
dataf.write(data)
dataf.write("Script done on {}\n".format(time.asctime()).encode("utf-8"))
def record_command(self, _input, _output):
pass
class SessionReplay(metaclass=abc.ABCMeta):
@abc.abstractmethod
def write_meta(self, meta):
pass pass
@abc.abstractmethod @abc.abstractmethod
def write_data(self, data): def start(self):
pass pass
@abc.abstractmethod @abc.abstractmethod
...@@ -73,51 +34,107 @@ class SessionReplay(metaclass=abc.ABCMeta): ...@@ -73,51 +34,107 @@ class SessionReplay(metaclass=abc.ABCMeta):
pass pass
class SessionCommand(metaclass=abc.ABCMeta): class FileRecorder(Recorder):
@abc.abstractmethod
def write(self, cmd, output):
pass
class FileSessionReplay(SessionReplay):
def __init__(self, dataf, metaf):
self.dataf = dataf
self.metaf = metaf
self.playing = True
def write_data(self, data):
self.dataf.write(data)
def write_meta(self, meta):
self.metaf.write(meta)
def replay(self, sock): @property
sock.send(self.dataf.readline()) def session_dir(self):
for l in self.metaf: session_dir = os.path.join(
if not self.playing: self.app.config["SESSION_DIR"],
break self.session.date_created.strftime("%Y-%m-%d")
t, size = float(l.split()[0]), int(l.split()[1]) )
data = self.dataf.read(size) if not os.path.isdir(session_dir):
time.sleep(t) os.mkdir(session_dir)
sock.send(data) return session_dir
sock.send("Replay session end")
@property
def data_f(self):
filename = os.path.join(self.session_dir, str(self.session.id) + ".rec")
try:
f = open(filename, 'wb')
except IOError:
logger.error("Failed open file {} in recorder".format(filename))
raise
return f
@property
def time_f(self):
filename = os.path.join(self.session_dir, str(self.session.id) + ".time")
try:
f = open(filename, 'w')
except IOError:
logger.error("Failed open file {} in recorder".format(filename))
raise
return f
@property
def cmd_f(self):
filename = os.path.join(self.session_dir, str(self.session.id) + ".cmd")
try:
f = open(filename, "w")
except IOError:
logger.error("Failed open file {} in recorder".format(filename))
raise
return f
def record_replay(self, now, timedelta, size, data):
self.time_f.write("%.4f %s\n" % (timedelta, size))
self.data_f.write(data)
def record_command(self, now, _input, _output):
self.cmd_f.write("{}\n".format(now.strftime("%Y-%m-%d %H:%M:%S")))
self.cmd_f.write("$ {}\n".format(_input))
self.cmd_f.write("{}\n\n".format(_output))
def start(self):
self.data_f.write("Session started on {}\n".format(time.asctime()).encode("utf-8"))
def done(self): def done(self):
pass self.data_f.write("Session done on {}\n".format(time.asctime()).encode("utf-8"))
for f in [self.data_f, self.time_f, self.cmd_f]:
try:
class FileSessionCommand(SessionCommand): f.close()
except IOError:
def __init__(self, f): pass
self.f = f
def write(self, cmd, output):
self.f.write("{}\n".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
self.f.write("$ {}\n".format(cmd))
self.f.write("{}\n\n".format(output))
def done(self): # class FileSessionReplay(SessionReplay):
pass #
# def __init__(self, dataf, metaf):
# self.dataf = dataf
# self.metaf = metaf
# self.playing = True
#
# def write_data(self, data):
# self.dataf.write(data)
#
# def write_meta(self, meta):
# self.metaf.write(meta)
#
# def replay(self, sock):
# sock.send(self.dataf.readline())
# for l in self.metaf:
# if not self.playing:
# break
# t, size = float(l.split()[0]), int(l.split()[1])
# data = self.dataf.read(size)
# time.sleep(t)
# sock.send(data)
# sock.send("Replay session end")
#
# def done(self):
# pass
#
#
# class FileSessionCommand(SessionCommand):
#
# def __init__(self, f):
# self.f = f
#
# def write(self, cmd, output):
# self.f.write("{}\n".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
# self.f.write("$ {}\n".format(cmd))
# self.f.write("{}\n\n".format(output))
#
# def done(self):
# pass
...@@ -25,7 +25,7 @@ class Session: ...@@ -25,7 +25,7 @@ class Session:
self.replaying = True self.replaying = True
self.date_created = datetime.datetime.now() self.date_created = datetime.datetime.now()
self.date_finished = None self.date_finished = None
self.recorder = [] self.recorders = []
self.stop_evt = threading.Event() self.stop_evt = threading.Event()
self.sel = selectors.DefaultSelector() self.sel = selectors.DefaultSelector()
...@@ -70,11 +70,16 @@ class Session: ...@@ -70,11 +70,16 @@ class Session:
self.sel.unregister(sharer) self.sel.unregister(sharer)
self.sharers.remove(sharer) self.sharers.remove(sharer)
def add_recorder(self, recorder):
self.recorders.append(recorder)
def remove_recorder(self, recorder):
self.recorders.remove(recorder)
def bridge(self): def bridge(self):
""" """
Bridge clients with server Bridge clients with server
:return: :return:
""" """
logger.info("Start bridge session %s" % self.id) logger.info("Start bridge session %s" % self.id)
self.sel.register(self.client, selectors.EVENT_READ) self.sel.register(self.client, selectors.EVENT_READ)
...@@ -92,39 +97,42 @@ class Session: ...@@ -92,39 +97,42 @@ class Session:
elif sock == self.client: elif sock == self.client:
if len(data) == 0: if len(data) == 0:
for watcher in self.watchers + self.sharers: for watcher in self.watchers + self.sharers:
watcher.send("Client {} close the session" watcher.send("Client {} close the session".format(self.client).encode("utf-8"))
.format(self.client)
.encode("utf-8"))
self.close() self.close()
break break
self.server.send(data) self.server.send(data)
elif sock in self.sharers: elif sock in self.sharers:
if len(data) == 0: if len(data) == 0:
logger.info("Sharer {} leave session {}" logger.info("Sharer {} leave session {}".format(sock, self.id))
.format(sock, self.id))
self.remove_sharer(sock) self.remove_sharer(sock)
self.server.send(data) self.server.send(data)
elif sock in self.watchers: elif sock in self.watchers:
if len(data) == 0: if len(data) == 0:
logger.info("Watcher {} leave session {}" logger.info("Watcher {} leave session {}".format(sock, self.id))
.format(sock, self.id))
def set_size(self, width, height): def set_size(self, width, height):
self.server.resize_pty(width=width, height=height) self.server.resize_pty(width=width, height=height)
def add_recorder(self, recorder): def record_async(self):
self.recorder.append(recorder) def func():
parent, child = socket.socketpair()
def remove_recorder(self, recorder): for recorder in self.recorders:
self.recorder.remove(recorder) recorder.start()
while not self.stop_evt.is_set():
def record(self): start_t = time.time()
""" data = child.recv(BUF_SIZE)
Record the session end_t = time.time()
:return: size = len(data)
""" now = datetime.datetime.now()
for recorder in self.recorder: timedelta = '{.4f}'.format(end_t - start_t)
recorder.record(self) if size == 0:
break
for recorder in self.recorders:
recorder.record_replay(now, timedelta, size, data)
for recorder in self.recorders:
recorder.done()
thread = threading.Thread(target=func)
thread.start()
def close(self): def close(self):
self.stop_evt.set() self.stop_evt.set()
......
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