mirror of https://github.com/jumpserver/jumpserver
jlog
parent
72a18d6abf
commit
9366003f7b
71
connect.py
71
connect.py
|
@ -19,7 +19,6 @@ if django.get_version() != '1.6':
|
|||
django.setup()
|
||||
from jumpserver.api import ServerError, User, Asset, Jtty, get_object
|
||||
from jumpserver.api import logger
|
||||
from jumpserver.api import BisGroup as AssetGroup
|
||||
|
||||
login_user = get_object(User, username=getpass.getuser())
|
||||
|
||||
|
@ -98,76 +97,6 @@ def print_prompt():
|
|||
print textwrap.dedent(msg)
|
||||
|
||||
|
||||
# def remote_exec_cmd(ip, port, username, password, cmd):
|
||||
# try:
|
||||
# time.sleep(5)
|
||||
# ssh = paramiko.SSHClient()
|
||||
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
# ssh.connect(ip, port, username, password, timeout=5)
|
||||
# stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd)
|
||||
# out = stdout.readlines()
|
||||
# err = stderr.readlines()
|
||||
# color_print('%s:' % ip, 'blue')
|
||||
# for i in out:
|
||||
# color_print(" " * 4 + i.strip(), 'green')
|
||||
# for j in err:
|
||||
# color_print(" " * 4 + j.strip(), 'red')
|
||||
# ssh.close()
|
||||
# except Exception as e:
|
||||
# color_print(ip + ':', 'blue')
|
||||
# color_print(str(e), 'red')
|
||||
|
||||
|
||||
# def multi_remote_exec_cmd(hosts, username, cmd):
|
||||
# pool = Pool(processes=5)
|
||||
# for host in hosts:
|
||||
# username, password, ip, port = get_connect_item(username, host)
|
||||
# pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd))
|
||||
# pool.close()
|
||||
# pool.join()
|
||||
|
||||
|
||||
# def exec_cmd_servers(username):
|
||||
# color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green')
|
||||
# user.get_asset_info(printable=True)
|
||||
# while True:
|
||||
# hosts = []
|
||||
# inputs = raw_input('\033[1;32mip(s)>: \033[0m')
|
||||
# if inputs in ['q', 'Q']:
|
||||
# break
|
||||
# get_hosts = login_user.get_asset_info().keys()
|
||||
#
|
||||
# if ',' in inputs:
|
||||
# ips_input = inputs.split(',')
|
||||
# for host in ips_input:
|
||||
# if host in get_hosts:
|
||||
# hosts.append(host)
|
||||
# else:
|
||||
# for host in get_hosts:
|
||||
# if fnmatch.fnmatch(host, inputs):
|
||||
# hosts.append(host.strip())
|
||||
#
|
||||
# if len(hosts) == 0:
|
||||
# color_print("Check again, Not matched any ip!", 'red')
|
||||
# continue
|
||||
# else:
|
||||
# print "You matched ip: %s" % hosts
|
||||
# color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green')
|
||||
# while True:
|
||||
# cmd = raw_input('\033[1;32mCmd(s): \033[0m')
|
||||
# if cmd in ['q', 'Q']:
|
||||
# break
|
||||
# exec_log_dir = os.path.join(log_dir, 'exec_cmds')
|
||||
# if not os.path.isdir(exec_log_dir):
|
||||
# os.mkdir(exec_log_dir)
|
||||
# os.chmod(exec_log_dir, 0777)
|
||||
# filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d'))
|
||||
# f = open(filename, 'a')
|
||||
# f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" %
|
||||
# (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd))
|
||||
# multi_remote_exec_cmd(hosts, username, cmd)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
he he
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# coding: utf-8
|
||||
|
||||
|
||||
from argparse import ArgumentParser, FileType
|
||||
from contextlib import closing
|
||||
from codecs import open as copen
|
||||
from json import dumps
|
||||
from math import ceil
|
||||
from os.path import basename, dirname, exists, join
|
||||
from struct import unpack
|
||||
from subprocess import Popen
|
||||
from sys import platform, prefix, stderr
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from jinja2 import FileSystemLoader, Template
|
||||
from jinja2.environment import Environment
|
||||
|
||||
from jumpserver.api import BASE_DIR
|
||||
|
||||
|
||||
DEFAULT_TEMPLATE = join(BASE_DIR, 'templates', 'jlog', 'static.jinja2')
|
||||
|
||||
|
||||
def escapeString(string):
|
||||
string = string.encode('unicode_escape').decode('utf-8')
|
||||
string = string.replace("'", "\\'")
|
||||
string = '\'' + string + '\''
|
||||
return string
|
||||
|
||||
|
||||
def getTiming(timef):
|
||||
timing = None
|
||||
with closing(timef):
|
||||
timing = [l.strip().split(' ') for l in timef]
|
||||
timing = [(int(ceil(float(r[0]) * 1000)), int(r[1])) for r in timing]
|
||||
return timing
|
||||
|
||||
|
||||
def scriptToJSON(scriptf, timing=None):
|
||||
ret = []
|
||||
|
||||
with closing(scriptf):
|
||||
scriptf.readline() # ignore first header line from script file
|
||||
offset = 0
|
||||
for t in timing:
|
||||
data = escapeString(scriptf.read(t[1]))
|
||||
offset += t[0]
|
||||
ret.append((data, offset))
|
||||
return dumps(ret)
|
||||
|
||||
|
||||
def renderTemplate(script_path, time_file_path, dimensions=(24, 60), templatename=DEFAULT_TEMPLATE):
|
||||
with copen(script_path, encoding='utf-8', errors='replace') as scriptf:
|
||||
with open(time_file_path) as timef:
|
||||
timing = getTiming(timef)
|
||||
json = scriptToJSON(scriptf, timing)
|
||||
|
||||
fsl = FileSystemLoader(dirname(templatename), 'utf-8')
|
||||
e = Environment()
|
||||
e.loader = fsl
|
||||
|
||||
templatename = basename(templatename)
|
||||
rendered = e.get_template(templatename).render(json=json,
|
||||
dimensions=dimensions)
|
||||
|
||||
return rendered
|
||||
|
||||
|
|
@ -5,12 +5,10 @@ class Log(models.Model):
|
|||
user = models.CharField(max_length=20, null=True)
|
||||
host = models.CharField(max_length=20, null=True)
|
||||
remote_ip = models.CharField(max_length=100)
|
||||
dept_name = models.CharField(max_length=20)
|
||||
log_path = models.CharField(max_length=100)
|
||||
start_time = models.DateTimeField(null=True)
|
||||
pid = models.IntegerField(max_length=10)
|
||||
is_finished = models.BooleanField(default=False)
|
||||
handle_finished = models.BooleanField(default=False)
|
||||
end_time = models.DateTimeField(null=True)
|
||||
|
||||
def __unicode__(self):
|
||||
|
|
|
@ -5,7 +5,7 @@ from jlog.views import *
|
|||
urlpatterns = patterns('',
|
||||
url(r'^$', log_list),
|
||||
url(r'^log_list/(\w+)/$', log_list),
|
||||
url(r'^log_kill/', log_kill),
|
||||
url(r'^history/$', log_history),
|
||||
url(r'^search/$', log_search),
|
||||
# url(r'^log_kill/', log_kill),
|
||||
# url(r'^history/$', log_history),
|
||||
# url(r'^search/$', log_search),
|
||||
)
|
184
jlog/views.py
184
jlog/views.py
|
@ -4,117 +4,103 @@ from django.template import RequestContext
|
|||
from django.shortcuts import render_to_response
|
||||
|
||||
from jumpserver.api import *
|
||||
from jasset.views import httperror
|
||||
from django.http import HttpResponseNotFound
|
||||
|
||||
CONF = ConfigParser()
|
||||
CONF.read('%s/jumpserver.conf' % BASE_DIR)
|
||||
from jlog.models import Log
|
||||
|
||||
# def get_user_info(request, offset):
|
||||
# """ 获取用户信息及环境 """
|
||||
# env_dic = {'online': 0, 'offline': 1}
|
||||
# env = env_dic[offset]
|
||||
# keyword = request.GET.get('keyword', '')
|
||||
# user_info = get_session_user_info(request)
|
||||
# user_id, username = user_info[0:2]
|
||||
# dept_id, dept_name = user_info[3:5]
|
||||
# ret = [request, keyword, env, username, dept_name]
|
||||
#
|
||||
# return ret
|
||||
#
|
||||
#
|
||||
# def get_user_log(ret_list):
|
||||
# """ 获取不同类型用户日志记录 """
|
||||
# request, keyword, env, username, dept_name = ret_list
|
||||
# post_all = Log.objects.filter(is_finished=env).order_by('-start_time')
|
||||
# post_keyword_all = Log.objects.filter(Q(user__contains=keyword) |
|
||||
# Q(host__contains=keyword)) \
|
||||
# .filter(is_finished=env).order_by('-start_time')
|
||||
#
|
||||
# if keyword:
|
||||
# posts = post_keyword_all
|
||||
# else:
|
||||
# posts = post_all
|
||||
#
|
||||
# return posts
|
||||
|
||||
|
||||
def get_user_info(request, offset):
|
||||
""" 获取用户信息及环境 """
|
||||
env_dic = {'online': 0, 'offline': 1}
|
||||
env = env_dic[offset]
|
||||
keyword = request.GET.get('keyword', '')
|
||||
user_info = get_session_user_info(request)
|
||||
user_id, username = user_info[0:2]
|
||||
dept_id, dept_name = user_info[3:5]
|
||||
ret = [request, keyword, env, username, dept_name]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_user_log(ret_list):
|
||||
""" 获取不同类型用户日志记录 """
|
||||
request, keyword, env, username, dept_name = ret_list
|
||||
post_all = Log.objects.filter(is_finished=env).order_by('-start_time')
|
||||
post_keyword_all = Log.objects.filter(Q(user__contains=keyword) |
|
||||
Q(host__contains=keyword)) \
|
||||
.filter(is_finished=env).order_by('-start_time')
|
||||
|
||||
if is_super_user(request):
|
||||
if keyword:
|
||||
posts = post_keyword_all
|
||||
else:
|
||||
posts = post_all
|
||||
|
||||
elif is_group_admin(request):
|
||||
if keyword:
|
||||
posts = post_keyword_all.filter(dept_name=dept_name)
|
||||
else:
|
||||
posts = post_all.filter(dept_name=dept_name)
|
||||
|
||||
elif is_common_user(request):
|
||||
if keyword:
|
||||
posts = post_keyword_all.filter(user=username)
|
||||
else:
|
||||
posts = post_all.filter(user=username)
|
||||
|
||||
return posts
|
||||
|
||||
|
||||
@require_login
|
||||
def log_list(request, offset):
|
||||
""" 显示日志 """
|
||||
header_title, path1, path2 = u'查看日志', u'查看日志', u'在线用户'
|
||||
keyword = request.GET.get('keyword', '')
|
||||
web_socket_host = CONF.get('websocket', 'web_socket_host')
|
||||
posts = get_user_log(get_user_info(request, offset))
|
||||
# posts = get_user_log(get_user_info(request, offset))
|
||||
if offset == 'online':
|
||||
posts = Log.objects.filter(is_finished=False).order_by('-start_time')
|
||||
else:
|
||||
posts = Log.objects.filter(is_finished=True).order_by('-start_time')
|
||||
contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
|
||||
|
||||
return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@require_admin
|
||||
def log_kill(request):
|
||||
""" 杀掉connect进程 """
|
||||
pid = request.GET.get('id', '')
|
||||
log = Log.objects.filter(pid=pid)
|
||||
if log:
|
||||
log = log[0]
|
||||
dept_name = log.dept_name
|
||||
deptname = get_session_user_info(request)[4]
|
||||
if is_group_admin(request) and dept_name != deptname:
|
||||
return httperror(request, u'Kill失败, 您无权操作!')
|
||||
try:
|
||||
os.kill(int(pid), 9)
|
||||
except OSError:
|
||||
pass
|
||||
Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now())
|
||||
return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request))
|
||||
else:
|
||||
return HttpResponseNotFound(u'没有此进程!')
|
||||
|
||||
|
||||
@require_login
|
||||
def log_history(request):
|
||||
""" 命令历史记录 """
|
||||
log_id = request.GET.get('id', 0)
|
||||
log = Log.objects.filter(id=int(log_id))
|
||||
if log:
|
||||
log = log[0]
|
||||
dept_name = log.dept_name
|
||||
deptname = get_session_user_info(request)[4]
|
||||
if is_group_admin(request) and dept_name != deptname:
|
||||
return httperror(request, '查看失败, 您无权查看!')
|
||||
|
||||
elif is_common_user(request):
|
||||
return httperror(request, '查看失败, 您无权查看!')
|
||||
|
||||
log_his = "%s.his" % log.log_path
|
||||
if os.path.isfile(log_his):
|
||||
f = open(log_his)
|
||||
content = f.read()
|
||||
return HttpResponse(content)
|
||||
else:
|
||||
return httperror(request, '无日志记录, 请查看日志处理脚本是否开启!')
|
||||
|
||||
|
||||
@require_login
|
||||
def log_search(request):
|
||||
""" 日志搜索 """
|
||||
offset = request.GET.get('env', '')
|
||||
keyword = request.GET.get('keyword', '')
|
||||
posts = get_user_log(get_user_info(request, offset))
|
||||
contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
|
||||
return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request))
|
||||
#
|
||||
# def log_kill(request):
|
||||
# """ 杀掉connect进程 """
|
||||
# pid = request.GET.get('id', '')
|
||||
# log = Log.objects.filter(pid=pid)
|
||||
# if log:
|
||||
# log = log[0]
|
||||
# dept_name = log.dept_name
|
||||
# deptname = get_session_user_info(request)[4]
|
||||
# if is_group_admin(request) and dept_name != deptname:
|
||||
# return httperror(request, u'Kill失败, 您无权操作!')
|
||||
# try:
|
||||
# os.kill(int(pid), 9)
|
||||
# except OSError:
|
||||
# pass
|
||||
# Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now())
|
||||
# return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request))
|
||||
# else:
|
||||
# return HttpResponseNotFound(u'没有此进程!')
|
||||
#
|
||||
#
|
||||
# def log_history(request):
|
||||
# """ 命令历史记录 """
|
||||
# log_id = request.GET.get('id', 0)
|
||||
# log = Log.objects.filter(id=int(log_id))
|
||||
# if log:
|
||||
# log = log[0]
|
||||
# dept_name = log.dept_name
|
||||
# deptname = get_session_user_info(request)[4]
|
||||
# if is_group_admin(request) and dept_name != deptname:
|
||||
# return httperror(request, '查看失败, 您无权查看!')
|
||||
#
|
||||
# elif is_common_user(request):
|
||||
# return httperror(request, '查看失败, 您无权查看!')
|
||||
#
|
||||
# log_his = "%s.his" % log.log_path
|
||||
# if os.path.isfile(log_his):
|
||||
# f = open(log_his)
|
||||
# content = f.read()
|
||||
# return HttpResponse(content)
|
||||
# else:
|
||||
# return httperror(request, '无日志记录, 请查看日志处理脚本是否开启!')
|
||||
#
|
||||
#
|
||||
# def log_search(request):
|
||||
# """ 日志搜索 """
|
||||
# offset = request.GET.get('env', '')
|
||||
# keyword = request.GET.get('keyword', '')
|
||||
# posts = get_user_log(get_user_info(request, offset))
|
||||
# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
|
||||
# return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request))
|
||||
|
|
|
@ -12,6 +12,7 @@ import random
|
|||
import subprocess
|
||||
import paramiko
|
||||
import struct, fcntl, signal,socket, select, fnmatch
|
||||
import re
|
||||
|
||||
from django.core.paginator import Paginator, EmptyPage, InvalidPage
|
||||
from django.http import HttpResponse, Http404
|
||||
|
@ -69,75 +70,6 @@ def set_log(level):
|
|||
return logger_f
|
||||
|
||||
|
||||
# class LDAPMgmt():
|
||||
# """
|
||||
# LDAP class for add, select, del, update
|
||||
# LDAP 管理类,增删改查
|
||||
# """
|
||||
# def __init__(self,
|
||||
# host_url,
|
||||
# base_dn,
|
||||
# root_cn,
|
||||
# root_pw):
|
||||
# self.ldap_host = host_url
|
||||
# self.ldap_base_dn = base_dn
|
||||
# self.conn = ldap.initialize(host_url)
|
||||
# self.conn.set_option(ldap.OPT_REFERRALS, 0)
|
||||
# self.conn.protocol_version = ldap.VERSION3
|
||||
# self.conn.simple_bind_s(root_cn, root_pw)
|
||||
#
|
||||
# def list(self, filter, scope=ldap.SCOPE_SUBTREE, attr=None):
|
||||
# """
|
||||
# query
|
||||
# 查询
|
||||
# """
|
||||
# result = {}
|
||||
# try:
|
||||
# ldap_result = self.conn.search_s(self.ldap_base_dn, scope, filter, attr)
|
||||
# for entry in ldap_result:
|
||||
# name, data = entry
|
||||
# for k, v in data.items():
|
||||
# print '%s: %s' % (k, v)
|
||||
# result[k] = v
|
||||
# return result
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
#
|
||||
# def add(self, dn, attrs):
|
||||
# """
|
||||
# add
|
||||
# 添加
|
||||
# """
|
||||
# try:
|
||||
# ldif = modlist.addModlist(attrs)
|
||||
# self.conn.add_s(dn, ldif)
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
#
|
||||
# def modify(self, dn, attrs):
|
||||
# """
|
||||
# modify
|
||||
# 更改
|
||||
# """
|
||||
# try:
|
||||
# attr_s = []
|
||||
# for k, v in attrs.items():
|
||||
# attr_s.append((2, k, v))
|
||||
# self.conn.modify_s(dn, attr_s)
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
#
|
||||
# def delete(self, dn):
|
||||
# """
|
||||
# delete
|
||||
# 删除
|
||||
# """
|
||||
# try:
|
||||
# self.conn.delete_s(dn)
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
|
||||
|
||||
def page_list_return(total, current=1):
|
||||
"""
|
||||
page
|
||||
|
@ -181,17 +113,46 @@ def pages(post_objects, request):
|
|||
return post_objects, paginator, page_objects, page_range, current_page, show_first, show_end
|
||||
|
||||
|
||||
def remove_control_char(str_r):
|
||||
"""
|
||||
处理日志特殊字符
|
||||
"""
|
||||
control_char = re.compile(r"""
|
||||
\x1b[ #%()*+\-.\/]. |
|
||||
\r | #匹配 回车符(CR)
|
||||
(?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd
|
||||
(?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL)
|
||||
(?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST)
|
||||
\x1b. #匹配 转义过后的字符
|
||||
[\x80-\x9f] #匹配 所有控制字符
|
||||
""", re.X)
|
||||
backspace = re.compile(r"[^\b][\b]")
|
||||
line_filtered = control_char.sub('', str_r.rstrip())
|
||||
while backspace.search(line_filtered):
|
||||
line_filtered = backspace.sub('', line_filtered)
|
||||
|
||||
return line_filtered
|
||||
|
||||
|
||||
def newline_code_in(strings):
|
||||
for i in ['\r', '\r\n', '\n']:
|
||||
if i in strings:
|
||||
#print "new line"
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Jtty(object):
|
||||
"""
|
||||
A virtual tty class
|
||||
一个虚拟终端类,实现连接ssh和记录日志
|
||||
"""
|
||||
def __init__(self, user, asset):
|
||||
def __init__(self, username, ip):
|
||||
self.chan = None
|
||||
self.username = user.username
|
||||
self.ip = asset.ip
|
||||
self.user = user
|
||||
self.asset = asset
|
||||
self.username = username
|
||||
self.ip = ip
|
||||
# self.user = user
|
||||
# self.asset = asset
|
||||
|
||||
@staticmethod
|
||||
def get_win_size():
|
||||
|
@ -227,11 +188,8 @@ class Jtty(object):
|
|||
timestamp_start = int(time.time())
|
||||
date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start))
|
||||
time_start = time.strftime('%H%M%S', time.localtime(timestamp_start))
|
||||
log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start)
|
||||
today_connect_log_dir = os.path.join(tty_log_dir, date_start)
|
||||
log_file_path = os.path.join(today_connect_log_dir, log_filename)
|
||||
dept_name = self.user.dept.name
|
||||
|
||||
log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.ip, time_start))
|
||||
pid = os.getpid()
|
||||
pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip()
|
||||
ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n')
|
||||
|
@ -242,23 +200,29 @@ class Jtty(object):
|
|||
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir))
|
||||
|
||||
try:
|
||||
log_file = open(log_file_path, 'a')
|
||||
log_file_f = open(log_file_path + '.log', 'a')
|
||||
log_time_f = open(log_file_path + '.time', 'a')
|
||||
log_res_f = open(log_file_path + '.res', 'a')
|
||||
except IOError:
|
||||
raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir)
|
||||
|
||||
log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name,
|
||||
log = Log(user=self.username, host=self.ip, remote_ip=ip_list,
|
||||
log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid)
|
||||
log_file.write('Start time is %s\n' % datetime.datetime.now())
|
||||
log_file_f.write('Start time is %s\n' % datetime.datetime.now())
|
||||
log.save()
|
||||
return log_file, log
|
||||
return log_file_f, log_time_f, log_res_f, log
|
||||
|
||||
def posix_shell(self):
|
||||
"""
|
||||
Use paramiko channel connect server interactive.
|
||||
使用paramiko模块的channel,连接后端,进入交互式
|
||||
"""
|
||||
log_file, log = self.log_record()
|
||||
log_file_f, log_time_f, log_res_f, log = self.log_record()
|
||||
old_tty = termios.tcgetattr(sys.stdin)
|
||||
pre_timestamp = time.time()
|
||||
input_r = ''
|
||||
input_mode = False
|
||||
|
||||
try:
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
tty.setcbreak(sys.stdin.fileno())
|
||||
|
@ -277,23 +241,40 @@ class Jtty(object):
|
|||
break
|
||||
sys.stdout.write(x)
|
||||
sys.stdout.flush()
|
||||
log_file.write(x)
|
||||
log_file.flush()
|
||||
log_file_f.write(x)
|
||||
now_timestamp = time.time()
|
||||
log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x)))
|
||||
pre_timestamp = now_timestamp
|
||||
log_file_f.flush()
|
||||
log_time_f.flush()
|
||||
|
||||
if input_mode and not newline_code_in(x):
|
||||
input_r += x
|
||||
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
if sys.stdin in r:
|
||||
x = os.read(sys.stdin.fileno(), 1)
|
||||
if not input_mode:
|
||||
input_mode = True
|
||||
|
||||
if str(x) in ['\r', '\n', '\r\n']:
|
||||
input_r = remove_control_char(input_r)
|
||||
log_res_f.write('%s: %s\n' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), input_r))
|
||||
log_res_f.flush()
|
||||
input_r = ''
|
||||
input_mode = False
|
||||
|
||||
if len(x) == 0:
|
||||
break
|
||||
self.chan.send(x)
|
||||
|
||||
finally:
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
|
||||
log_file.write('End time is %s' % datetime.datetime.now())
|
||||
log_file.close()
|
||||
log_file_f.write('End time is %s' % datetime.datetime.now())
|
||||
log_file_f.close()
|
||||
log.is_finished = True
|
||||
log.handle_finished = False
|
||||
log.end_time = datetime.datetime.now()
|
||||
log.save()
|
||||
|
||||
|
@ -302,27 +283,15 @@ class Jtty(object):
|
|||
get args for connect: ip, port, username, passwd
|
||||
获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码
|
||||
"""
|
||||
if not self.asset.is_active:
|
||||
raise ServerError('该主机被禁用 Host %s is not active.' % self.ip)
|
||||
# if not self.asset.is_active:
|
||||
# raise ServerError('该主机被禁用 Host %s is not active.' % self.ip)
|
||||
#
|
||||
# if not self.user.is_active:
|
||||
# raise ServerError('该用户被禁用 User %s is not active.' % self.username)
|
||||
|
||||
if not self.user.is_active:
|
||||
raise ServerError('该用户被禁用 User %s is not active.' % self.username)
|
||||
|
||||
login_type_dict = {
|
||||
'L': self.user.ldap_pwd,
|
||||
}
|
||||
|
||||
if self.asset.login_type in login_type_dict:
|
||||
password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type])
|
||||
return self.username, password, self.ip, int(self.asset.port)
|
||||
|
||||
elif self.asset.login_type == 'M':
|
||||
username = self.asset.username
|
||||
password = CRYPTOR.decrypt(self.asset.password)
|
||||
return username, password, self.ip, int(self.asset.port)
|
||||
|
||||
else:
|
||||
raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]')
|
||||
# password = CRYPTOR.decrypt(self.])
|
||||
# return self.username, password, self.ip, int(self.asset.port)
|
||||
return 'root', 'redhat', '127.0.0.1', 22
|
||||
|
||||
def get_connection(self):
|
||||
"""
|
||||
|
@ -337,7 +306,7 @@ class Jtty(object):
|
|||
ssh.load_system_host_keys()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
ssh.connect(ip, port=port, username=username, password=password, compress=True)
|
||||
ssh.connect(ip, port=port, username=username, password=password)
|
||||
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
|
||||
raise ServerError('认证错误 Authentication Error.')
|
||||
except socket.error:
|
||||
|
@ -351,7 +320,7 @@ class Jtty(object):
|
|||
连接服务器
|
||||
"""
|
||||
ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip
|
||||
login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.asset.ip
|
||||
login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip
|
||||
|
||||
# 发起ssh连接请求 Make a ssh connection
|
||||
ssh = self.get_connection()
|
||||
|
@ -706,14 +675,5 @@ def my_render(template, data, request):
|
|||
|
||||
CRYPTOR = PyCrypt(KEY)
|
||||
|
||||
# if LDAP_ENABLE:
|
||||
# LDAP_HOST_URL = CONF.get('ldap', 'host_url')
|
||||
# LDAP_BASE_DN = CONF.get('ldap', 'base_dn')
|
||||
# LDAP_ROOT_DN = CONF.get('ldap', 'root_dn')
|
||||
# LDAP_ROOT_PW = CONF.get('ldap', 'root_pw')
|
||||
# ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW)
|
||||
# else:
|
||||
# ldap_conn = None
|
||||
|
||||
log_level = CONF.get('base', 'log')
|
||||
logger = set_log(log_level)
|
|
@ -62,7 +62,7 @@ INSTALLED_APPS = (
|
|||
'juser',
|
||||
'jasset',
|
||||
'jperm',
|
||||
# 'jlog',
|
||||
'jlog',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
|
|
|
@ -16,7 +16,7 @@ urlpatterns = patterns('',
|
|||
(r'^error/$', 'jumpserver.views.httperror'),
|
||||
(r'^juser/', include('juser.urls')),
|
||||
(r'^jasset/', include('jasset.urls')),
|
||||
# (r'^jlog/', include('jlog.urls')),
|
||||
(r'^jlog/', include('jlog.urls')),
|
||||
(r'^jperm/', include('jperm.urls')),
|
||||
(r'^node_auth/', 'jumpserver.views.node_auth'),
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<html>
|
||||
<head>{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<input type="button" value="Play/Pause" onclick="pause(false);" />
|
||||
<input type="button" value="Restart" onclick="restart(1);" />
|
||||
<span id="beforeScrubberText"></span>
|
||||
<input id="scrubber" type="range" value="0" min=0 max=100
|
||||
onmousedown="pause(true);" onmouseup="scrub();" />
|
||||
<span id="afterScrubberText"></span>
|
||||
-5x <input id="speed" type="range" value="0" min=-5 max=5
|
||||
onmouseup="setSpeed();" /> +5x
|
||||
<script>
|
||||
var data = {{ json }};
|
||||
var toggle = true;
|
||||
var totalTime = 0;
|
||||
var TICK = 33;
|
||||
var TIMESTEP = 33;
|
||||
var time = 33;
|
||||
var pos = 0;
|
||||
var timer;
|
||||
|
||||
// Thanks http://stackoverflow.com/a/2998822
|
||||
function zeroPad(num, size) {
|
||||
var s = "0" + num;
|
||||
return s.substr(s.length-size);
|
||||
}
|
||||
|
||||
function scrub() {
|
||||
setPercent = document.getElementById('scrubber').value;
|
||||
time = (setPercent / 100) * totalTime;
|
||||
restart(time);
|
||||
}
|
||||
|
||||
function buildTimeString(millis) {
|
||||
hours = zeroPad(Math.floor(millis / (1000 * 60 * 60)), 2);
|
||||
millis -= hours * (1000 * 60 * 60)
|
||||
minutes = zeroPad(Math.floor(millis / (1000 * 60)), 2);
|
||||
millis -= minutes * (1000 * 60);
|
||||
seconds = zeroPad(Math.floor(millis / 1000), 2);
|
||||
return hours + ':' + minutes + ':' + seconds;
|
||||
}
|
||||
|
||||
function advance() {
|
||||
document.getElementById('scrubber').value =
|
||||
Math.ceil((time / totalTime) * 100);
|
||||
timestr = buildTimeString(time);
|
||||
document.getElementById("beforeScrubberText").innerHTML =
|
||||
timestr;
|
||||
for (; pos < data.length; pos++) {
|
||||
if (data[pos][1] <= time) {
|
||||
term.write(eval(data[pos][0]));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos >= data.length) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
|
||||
time += TIMESTEP;
|
||||
}
|
||||
|
||||
function pause(test) {
|
||||
if (!toggle && test) {
|
||||
return;
|
||||
}
|
||||
if (toggle) {
|
||||
clearInterval(timer);
|
||||
toggle = !toggle;
|
||||
} else {
|
||||
timer = setInterval(advance, TICK);
|
||||
toggle = !toggle;
|
||||
}
|
||||
}
|
||||
|
||||
function setSpeed() {
|
||||
speed = document.getElementById('speed').value;
|
||||
if (speed == 0) {
|
||||
TIMESTEP = TICK;
|
||||
} else if (speed < 0) {
|
||||
TIMESTEP = TICK / -speed;
|
||||
} else {
|
||||
TIMESTEP = TICK * speed;
|
||||
}
|
||||
}
|
||||
|
||||
function restart(millis) {
|
||||
clearInterval(timer);
|
||||
term.reset();
|
||||
time = millis;
|
||||
pos = 0;
|
||||
toggle = true;
|
||||
timer = setInterval(advance, TICK);
|
||||
}
|
||||
|
||||
var term = new Terminal({
|
||||
cols: {{ dimensions[1] }},
|
||||
rows: {{ dimensions[0] }},
|
||||
screenKeys: true
|
||||
});
|
||||
totalTime = data[data.length - 1][1];
|
||||
timestr = buildTimeString(totalTime);
|
||||
document.getElementById("afterScrubberText").innerHTML = timestr;
|
||||
term.open(document.body);
|
||||
timer = setInterval(advance, TICK);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
{% extends "base.jinja2" %}
|
||||
{% block head %}
|
||||
<script src='term.js'></script>
|
||||
<link href='http://fonts.googleapis.com/css?family=Ubuntu+Mono' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Ubuntu Mono', Courier, monospace;
|
||||
font-size: 14pt;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
|
@ -39,12 +39,6 @@
|
|||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
<li><a href="#">未启用 1</a>
|
||||
</li>
|
||||
<li><a href="#">未启用 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
|
@ -77,12 +71,12 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"> 用户名 </th>
|
||||
<th class="text-center"> 所属部门 </th>
|
||||
<th class="text-center"> 登录主机 </th>
|
||||
<th class="text-center"> 来源IP </th>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<th class="text-center"> 命令统计 </th>
|
||||
{% endifnotequal %}
|
||||
<th class="text-center"> 回放录像 </th>
|
||||
<th class="text-center"> 登录时间 </th>
|
||||
<th class="text-center"> 结束时间 </th>
|
||||
|
||||
|
@ -92,12 +86,12 @@
|
|||
{% for post in contacts.object_list %}
|
||||
<tr class="gradeX">
|
||||
<td class="text-center" id="username"> {{ post.user }} </td>
|
||||
<td class="text-center" id="dept"> {{ post.dept_name }} </td>
|
||||
<td class="text-center" id="ip"> {{ post.host }} </td>
|
||||
<td class="text-center" id="remote_ip"> {{ post.remote_ip }} </td>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<td class="text-center"><a href="/jlog/history/?id={{ post.id }}" class="log_command"> 命令统计 </td>
|
||||
{% endifnotequal %}
|
||||
<td class="text-center"><a href="/jlog/record/?id={{ post.id }}" class="log_command"> 回放 </td>
|
||||
<td class="text-center" id="start_time"> {{ post.start_time|date:"Y-m-d H:i:s"}} </td>
|
||||
<td class="text-center" id="end_time"> {{ post.end_time|date:"Y-m-d H:i:s" }} </td>
|
||||
</tr>
|
||||
|
|
|
@ -39,12 +39,6 @@
|
|||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
<li><a href="#">未启用 1</a>
|
||||
</li>
|
||||
<li><a href="#">未启用 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
|
@ -77,7 +71,6 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"> 用户名 </th>
|
||||
<th class="text-center"> 所属部门 </th>
|
||||
<th class="text-center"> 登录主机 </th>
|
||||
<th class="text-center"> 来源IP </th>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
|
@ -92,7 +85,6 @@
|
|||
{% for post in contacts.object_list %}
|
||||
<tr class="gradeX">
|
||||
<td id="username" class="text-center"> {{ post.user }} </td>
|
||||
<td id="deptname" class="text-center"> {{ post.dept_name }} </td>
|
||||
<td id="ip" class="text-center"> {{ post.host }} </td>
|
||||
<td id="remote_ip" class="text-center"> {{ post.remote_ip }} </td>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue