pull/26/head
广宏伟 2015-10-26 22:17:16 +08:00
parent 72a18d6abf
commit 9366003f7b
13 changed files with 379 additions and 312 deletions

View File

@ -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

68
jlog/log_api.py Normal file
View File

@ -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

View File

@ -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):

View File

@ -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),
)

View File

@ -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))

View File

@ -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)

View File

@ -62,7 +62,7 @@ INSTALLED_APPS = (
'juser',
'jasset',
'jperm',
# 'jlog',
'jlog',
)
MIDDLEWARE_CLASSES = (

View File

@ -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'),

111
templates/jlog/base.jinja2 Normal file
View File

@ -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>

View File

@ -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 %}

View File

@ -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>

View File

@ -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