# Conflicts:
#	jperm/ansible_api.py
pull/26/head
yumaojun 2015-12-10 00:17:01 +08:00
commit a068498561
94 changed files with 2863 additions and 2198 deletions

View File

@ -14,21 +14,25 @@ import getpass
import readline
import django
import paramiko
import errno
import struct, fcntl, signal, socket, select
from io import open as copen
import uuid
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
if django.get_version() != '1.6':
django.setup()
from django.contrib.sessions.models import Session
from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info, get_role
from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR
from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm
from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info
from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR, bash, get_tmp_dir
from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm, PermRole
from jumpserver.settings import LOG_DIR
from jperm.ansible_api import Command
from jlog.log_api import escapeString
from jperm.ansible_api import MyRunner
# from jlog.log_api import escapeString
from jlog.models import ExecLog, FileLog
login_user = get_object(User, username=getpass.getuser())
remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n')
try:
import termios
@ -46,8 +50,11 @@ def color_print(msg, color='red', exits=False):
"""
color_msg = {'blue': '\033[1;36m%s\033[0m',
'green': '\033[1;32m%s\033[0m',
'red': '\033[1;31m%s\033[0m'}
msg = color_msg.get(color, 'blue') % msg
'yellow': '\033[1;33m%s\033[0m',
'red': '\033[1;31m%s\033[0m',
'title': '\033[30;42m%s\033[0m',
'info': '\033[32m%s\033[0m'}
msg = color_msg.get(color, 'red') % msg
print msg
if exits:
time.sleep(2)
@ -66,19 +73,18 @@ class Tty(object):
A virtual tty class
一个虚拟终端类实现连接ssh和记录日志基类
"""
def __init__(self, user, asset, role):
def __init__(self, user, asset, role, login_type='ssh'):
self.username = user.username
self.asset_name = asset.hostname
self.ip = None
self.port = 22
self.ssh = None
self.channel = None
self.asset = asset
self.user = user
self.role = role
self.ssh = None
self.remote_ip = ''
self.connect_info = None
self.login_type = 'ssh'
self.login_type = login_type
self.vim_flag = False
self.ps1_pattern = re.compile('\[.*@.*\][\$#]')
self.vim_data = ''
@ -101,6 +107,41 @@ class Tty(object):
cmd_str = patch_char.sub('', cmd_str.rstrip())
return cmd_str
@staticmethod
def deal_backspace(match_str, result_command, pattern_str, backspace_num):
'''
处理删除确认键
'''
if backspace_num > 0:
if backspace_num > len(result_command):
result_command += pattern_str
result_command = result_command[0:-backspace_num]
else:
result_command = result_command[0:-backspace_num]
result_command += pattern_str
del_len = len(match_str)-3
if del_len > 0:
result_command = result_command[0:-del_len]
return result_command, len(match_str)
@staticmethod
def deal_replace_char(match_str,result_command,backspace_num):
'''
处理替换命令
'''
str_lists = re.findall(r'(?<=\x1b\[1@)\w',match_str)
tmp_str =''.join(str_lists)
result_command_list = list(result_command)
if len(tmp_str) > 1:
result_command_list[-backspace_num:-(backspace_num-len(tmp_str))] = tmp_str
elif len(tmp_str) > 0:
if result_command_list[-backspace_num] == ' ':
result_command_list.insert(-backspace_num, tmp_str)
else:
result_command_list[-backspace_num] = tmp_str
result_command = ''.join(result_command_list)
return result_command, len(match_str)
def remove_control_char(self, result_command):
"""
处理日志特殊字符
@ -127,10 +168,7 @@ class Tty(object):
"""
处理命令中特殊字符
"""
str_r = re.sub('\x07', '', str_r) # 删除响铃
patch_char = re.compile('\x08\x1b\[C') # 删除方向左右一起的按键
while patch_char.search(str_r):
str_r = patch_char.sub('', str_r.rstrip())
str_r = self.remove_obstruct_char(str_r)
result_command = '' # 最后的结果
backspace_num = 0 # 光标移动的个数
@ -139,31 +177,21 @@ class Tty(object):
while str_r:
tmp = re.match(r'\s*\w+\s*', str_r)
if tmp:
str_r = str_r[len(str(tmp.group(0))):]
if reach_backspace_flag:
pattern_str += str(tmp.group(0))
str_r = str_r[len(str(tmp.group(0))):]
continue
else:
result_command += str(tmp.group(0))
str_r = str_r[len(str(tmp.group(0))):]
continue
tmp = re.match(r'\x1b\[K[\x08]*', str_r)
if tmp:
if backspace_num > 0:
if backspace_num > len(result_command):
result_command += pattern_str
result_command = result_command[0:-backspace_num]
else:
result_command = result_command[0:-backspace_num]
result_command += pattern_str
del_len = len(str(tmp.group(0)))-3
if del_len > 0:
result_command = result_command[0:-del_len]
result_command, del_len = self.deal_backspace(str(tmp.group(0)), result_command, pattern_str, backspace_num)
reach_backspace_flag = False
backspace_num = 0
pattern_str = ''
str_r = str_r[len(str(tmp.group(0))):]
str_r = str_r[del_len:]
continue
tmp = re.match(r'\x08+', str_r)
@ -179,7 +207,14 @@ class Tty(object):
continue
else:
break
tmp = re.match(r'(\x1b\[1@\w)+', str_r) #处理替换的命令
if tmp:
result_command,del_len = self.deal_replace_char(str(tmp.group(0)), result_command, backspace_num)
str_r = str_r[del_len:]
backspace_num = 0
continue
if reach_backspace_flag:
pattern_str += str_r[0]
else:
@ -189,22 +224,8 @@ class Tty(object):
if backspace_num > 0:
result_command = result_command[0:-backspace_num] + pattern_str
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] | (?:\x1b\]0.*) | \[.*@.*\][\$#] | (.*mysql>.*) #匹配 所有控制字符
""", re.X)
result_command = control_char.sub('', result_command.strip())
if not self.vim_flag:
if result_command.startswith('vi') or result_command.startswith('fg'):
self.vim_flag = True
return result_command.decode('utf8', "ignore")
else:
return ''
result_command = self.remove_control_char(result_command)
return result_command
def get_log(self):
"""
@ -223,29 +244,26 @@ class Tty(object):
mkdir(today_connect_log_dir, mode=0777)
except OSError:
logger.debug('创建目录 %s 失败,请修改%s目录权限' % (today_connect_log_dir, tty_log_dir))
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir))
raise ServerError('创建目录 %s 失败,请修改%s目录权限' % (today_connect_log_dir, tty_log_dir))
try:
# log_file_f = copen(log_file_path + '.log', mode='at', encoding='utf-8', errors='replace')
# log_time_f = copen(log_file_path + '.time', mode='at', encoding='utf-8', errors='replace')
log_file_f = open(log_file_path + '.log', 'a')
log_time_f = open(log_file_path + '.time', 'a')
except IOError:
logger.debug('创建tty日志文件失败, 请修改目录%s权限' % today_connect_log_dir)
raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir)
raise ServerError('创建tty日志文件失败, 请修改目录%s权限' % today_connect_log_dir)
if self.login_type == 'ssh': # 如果是ssh连接过来记录connect.py的pidweb terminal记录为日志的id
pid = os.getpid()
self.remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n') # 获取远端IP
self.remote_ip = remote_ip # 获取远端IP
else:
pid = 0
log = Log(user=self.username, host=self.asset_name, remote_ip=self.remote_ip, login_type=self.login_type,
log_path=log_file_path, start_time=date_today, pid=pid)
log.save()
if self.login_type == 'web':
log.pid = log.id
log.pid = log.id # 设置log id为websocket的id, 然后kill时干掉websocket
log.save()
log_file_f.write('Start at %s\r\n' % datetime.datetime.now())
@ -256,17 +274,13 @@ class Tty(object):
获取需要登陆的主机的信息和映射用户的账号密码
"""
asset_info = get_asset_info(self.asset)
role_key = get_role_key(self.user, self.role)
role_key = get_role_key(self.user, self.role) # 获取角色的key因为ansible需要权限是600所以统一生成用户_角色key
role_pass = CRYPTOR.decrypt(self.role.password)
self.connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'),
'port': int(asset_info.get('port')), 'role_name': self.role.name,
'role_pass': role_pass, 'role_key': role_key}
logger.debug("Connect: Host: %s Port: %s User: %s Pass: %s Key: %s" % (asset_info.get('ip'),
asset_info.get('port'),
self.role.name,
role_pass,
role_key))
return self.connect_info
connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'),
'port': int(asset_info.get('port')), 'role_name': self.role.name,
'role_pass': role_pass, 'role_key': role_key}
logger.debug(connect_info)
return connect_info
def get_connection(self):
"""
@ -285,18 +299,19 @@ class Tty(object):
ssh.connect(connect_info.get('ip'),
port=connect_info.get('port'),
username=connect_info.get('role_name'),
password=connect_info.get('role_pass'),
key_filename=role_key,
look_for_keys=False)
self.ssh = ssh
return ssh
except (paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException):
logger.warning('Use ssh key %s Failed.' % role_key)
logger.warning(u'使用ssh key %s 失败, 尝试只使用密码' % role_key)
pass
ssh.connect(connect_info.get('ip'),
port=connect_info.get('port'),
username=connect_info.get('role_name'),
password=connect_info.get('role_pass'),
allow_agent=False,
look_for_keys=False)
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
@ -357,18 +372,30 @@ class SshTty(Tty):
while True:
try:
r, w, e = select.select([self.channel, sys.stdin], [], [])
flag = fcntl.fcntl(sys.stdin, fcntl.F_GETFL, 0)
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, flag|os.O_NONBLOCK)
except Exception:
pass
if self.channel in r:
try:
x = self.channel.recv(1024)
x = self.channel.recv(10240)
if len(x) == 0:
break
if self.vim_flag:
self.vim_data += x
sys.stdout.write(x)
sys.stdout.flush()
index = 0
len_x = len(x)
while index < len_x:
try:
n = os.write(sys.stdout.fileno(), x[index:])
sys.stdout.flush()
index += n
except OSError as msg:
if msg.errno == errno.EAGAIN:
continue
#sys.stdout.write(x)
#sys.stdout.flush()
now_timestamp = time.time()
log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x)))
log_time_f.flush()
@ -384,7 +411,7 @@ class SshTty(Tty):
pass
if sys.stdin in r:
x = os.read(sys.stdin.fileno(), 1)
x = os.read(sys.stdin.fileno(), 4096)
input_mode = True
if str(x) in ['\r', '\n', '\r\n']:
if self.vim_flag:
@ -420,34 +447,25 @@ class SshTty(Tty):
Connect server.
连接服务器
"""
ps1 = "PS1='[\u@%s \W]\$ '\n" % self.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()
transport = ssh.get_transport()
transport.set_keepalive(30)
transport.use_compression(True)
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
global channel
win_size = self.get_win_size()
self.channel = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1], term='xterm')
#self.channel = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1], term='xterm')
self.channel = channel = transport.open_session()
channel.get_pty(term='xterm', height=win_size[0], width=win_size[1])
channel.invoke_shell()
try:
signal.signal(signal.SIGWINCH, self.set_win_size)
except:
pass
# 设置PS1并提示 Set PS1 and msg it
#channel.send(ps1)
#channel.send(login_msg)
# channel.send('echo ${SSH_TTY}\n')
# global SSH_TTY
# while not channel.recv_ready():
# time.sleep(1)
# tmp = channel.recv(1024)
#print 'ok'+tmp+'ok'
# SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip()
# SSH_TTY = ''
# channel.send('clear\n')
# Make ssh interactive tunnel
self.posix_shell()
# Shutdown channel socket
@ -456,6 +474,9 @@ class SshTty(Tty):
class Nav(object):
"""
导航提示类
"""
def __init__(self, user):
self.user = user
self.search_result = {}
@ -467,24 +488,17 @@ class Nav(object):
Print prompt
打印提示导航
"""
msg = """\n\033[1;32m### Welcome To Use JumpServer, A Open Source System . ### \033[0m
1) Type \033[32mID\033[0m To Login.
2) Type \033[32m/\033[0m + \033[32mIP, Host Name, Host Alias or Comments \033[0mTo Search.
3) Type \033[32mP/p\033[0m To Print The Servers You Available.
4) Type \033[32mG/g\033[0m To Print The Server Groups You Available.
5) Type \033[32mG/g\033[0m\033[0m + \033[32mGroup ID\033[0m To Print The Server Group You Available.
6) Type \033[32mE/e\033[0m To Execute Command On Several Servers.
7) Type \033[32mQ/q\033[0m To Quit.
"""
msg = """\n\033[1;32m### 欢迎使用Jumpserver开源跳板机系统 ### \033[0m
msg = """\n\033[1;32m### 欢迎使用Jumpserver开源跳板机 ### \033[0m
1) 输入 \033[32mID\033[0m 直接登录.
2) 输入 \033[32m/\033[0m + \033[32mIP, 主机名, 主机别名 or 备注 \033[0m搜索.
2) 输入 \033[32m/\033[0m + \033[32mIP, 主机名 or 备注 \033[0m搜索.
3) 输入 \033[32mP/p\033[0m 显示您有权限的主机.
4) 输入 \033[32mG/g\033[0m 显示您有权限的主机组.
5) 输入 \033[32mG/g\033[0m\033[0m + \033[32m组ID\033[0m 显示该组下主机.
6) 输入 \033[32mE/e\033[0m 批量执行命令.
7) 输入 \033[32mQ/q\033[0m 退出.
7) 输入 \033[32mU/u\033[0m 批量上传文件.
8) 输入 \033[32mD/d\033[0m 批量下载文件.
9) 输入 \033[32mQ/q\033[0m 退出.
"""
print textwrap.dedent(msg)
@ -512,17 +526,14 @@ class Nav(object):
user_asset_search = user_asset_all
self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search))
print '\033[32m[%-3s] %-15s %-15s %-5s %-10s %s \033[0m' % ('ID', 'AssetName', 'IP', 'Port', 'Role', 'Comment')
color_print('[%-3s] %-12s %-15s %-5s %-10s %s' % ('ID', u'主机名', 'IP', u'端口', u'角色', u'备注'), 'title')
for index, asset in self.search_result.items():
# 获取该资产信息
asset_info = get_asset_info(asset)
# 获取该资产包含的角色
role = [str(role.name) for role in self.user_perm.get('asset').get(asset).get('role')]
if asset.comment:
print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'),
role, asset.comment)
else:
print '[%-3s] %-15s %-15s %-5s %-10s' % (index, asset.hostname, asset.ip, asset_info.get('port'), role)
print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'),
role, asset.comment)
print
def print_asset_group(self):
@ -530,44 +541,11 @@ class Nav(object):
打印用户授权的资产组
"""
user_asset_group_all = get_group_user_perm(self.user).get('asset_group', [])
print '\033[32m[%-3s] %-15s %s \033[0m' % ('ID', 'GroupName', 'Comment')
color_print('[%-3s] %-20s %s' % ('ID', '组名', '备注'), 'title')
for asset_group in user_asset_group_all:
if asset_group.comment:
print '[%-3s] %-15s %s' % (asset_group.id, asset_group.name, asset_group.comment)
else:
print '[%-3s] %-15s' % (asset_group.id, asset_group.name)
print '[%-3s] %-15s %s' % (asset_group.id, asset_group.name, asset_group.comment)
print
def get_exec_log(self, assets_name_str):
exec_log_dir = os.path.join(LOG_DIR, 'exec')
date_today = datetime.datetime.now()
date_start = date_today.strftime('%Y%m%d')
time_start = date_today.strftime('%H%M%S')
today_connect_log_dir = os.path.join(exec_log_dir, date_start)
log_file_path = os.path.join(today_connect_log_dir, '%s_%s' % (self.user.username, time_start))
try:
mkdir(os.path.dirname(today_connect_log_dir), mode=0777)
mkdir(today_connect_log_dir, mode=0777)
except OSError:
logger.debug('创建目录 %s 失败,请修改%s目录权限' % (today_connect_log_dir, exec_log_dir))
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, exec_log_dir))
try:
log_file_f = open(log_file_path + '.log', 'a')
log_file_f.write('Start at %s\r\n' % datetime.datetime.now())
log_time_f = open(log_file_path + '.time', 'a')
except IOError:
logger.debug('创建tty日志文件失败, 请修改目录%s权限' % today_connect_log_dir)
raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir)
remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n')
log = Log(user=self.user.username, host=assets_name_str, remote_ip=remote_ip, login_type='exec',
log_path=log_file_path, start_time=datetime.datetime.now(), pid=os.getpid())
log.save()
return log_file_f, log_time_f, log
def exec_cmd(self):
"""
批量执行命令
@ -575,98 +553,173 @@ class Nav(object):
while True:
if not self.user_perm:
self.user_perm = get_group_user_perm(self.user)
print '\033[32m[%-2s] %-15s \033[0m' % ('ID', '角色')
roles = self.user_perm.get('role').keys()
role_check = dict(zip(range(len(roles)), roles))
if len(roles) > 1: # 授权角色数大于1
color_print('[%-2s] %-15s' % ('ID', '角色'), 'info')
role_check = dict(zip(range(len(roles)), roles))
for i, r in role_check.items():
print '[%-2s] %-15s' % (i, r.name)
print
print "请输入运行命令角色的ID, q退出"
for i, r in role_check.items():
print '[%-2s] %-15s' % (i, r.name)
print
print "请输入运行命令角色的ID, q退出"
try:
role_id = raw_input("\033[1;32mRole>:\033[0m ").strip()
if role_id == 'q':
break
try:
role_id = raw_input("\033[1;32mRole>:\033[0m ").strip()
if role_id == 'q':
break
except (IndexError, ValueError):
color_print('错误输入')
else:
role = role_check[int(role_id)]
assets = list(self.user_perm.get('role', {}).get(role).get('asset'))
print "该角色有权限的所有主机"
for asset in assets:
print asset.hostname
print
print "请输入主机名、IP或ansile支持的pattern, q退出"
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
if pattern == 'q':
break
else:
res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm)
cmd = Command(res)
logger.debug("批量执行res: %s" % res)
asset_name_str = ''
for inv in cmd.inventory.get_hosts(pattern=pattern):
print inv.name
asset_name_str += inv.name
print
log_file_f, log_time_f, log = self.get_exec_log(asset_name_str)
pre_timestamp = time.time()
while True:
print "请输入执行的命令, 按q退出"
data = 'ansible> '
write_log(log_file_f, data)
now_timestamp = time.time()
write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
pre_timestamp = now_timestamp
command = raw_input("\033[1;32mCmds>:\033[0m ").strip()
data = '%s\r\n' % command
write_log(log_file_f, data)
now_timestamp = time.time()
write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
pre_timestamp = now_timestamp
TtyLog(log=log, cmd=command, datetime=datetime.datetime.now()).save()
if command == 'q':
log.is_finished = True
log.end_time = datetime.datetime.now()
log.save()
break
result = cmd.run(module_name='shell', command=command, pattern=pattern)
for k, v in result.items():
if k == 'ok':
for host, output in v.items():
header = color_print("%s => %s" % (host, 'Ok'), 'green')
print output
output = re.sub(r'[\r\n]', '\r\n', output)
data = '%s\r\n%s\r\n' % (header, output)
now_timestamp = time.time()
write_log(log_file_f, data)
write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
pre_timestamp = now_timestamp
print
else:
for host, output in v.items():
header = color_print("%s => %s" % (host, k), 'red')
output = color_print(output, 'red')
output = re.sub(r'[\r\n]', '\r\n', output)
data = '%s\r\n%s\r\n' % (header, output)
now_timestamp = time.time()
write_log(log_file_f, data)
write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
pre_timestamp = now_timestamp
print
print "=" * 20
print
except (IndexError, KeyError):
color_print('ID输入错误')
continue
except EOFError:
print
elif len(roles) == 1: # 授权角色数为1
role = roles[0]
assets = list(self.user_perm.get('role', {}).get(role).get('asset')) # 获取该用户,角色授权主机
print "该角色有权限的所有主机"
for asset in assets:
print ' %s' % asset.hostname
print
print "请输入主机名、IP或ansile支持的pattern, 多个主机:分隔, q退出"
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
if pattern == 'q':
break
finally:
log.is_finished = True
log.end_time = datetime.datetime.now()
else:
res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm)
runner = MyRunner(res)
asset_name_str = ''
print "匹配主机:"
for inv in runner.inventory.get_hosts(pattern=pattern):
print ' %s' % inv.name
asset_name_str += '%s ' % inv.name
print
while True:
print "请输入执行的命令, 按q退出"
command = raw_input("\033[1;32mCmds>:\033[0m ").strip()
if command == 'q':
break
runner.run('shell', command, pattern=pattern)
ExecLog(host=asset_name_str, user=self.user.username, cmd=command, remote_ip=remote_ip,
result=runner.results).save()
for k, v in runner.results.items():
if k == 'ok':
for host, output in v.items():
color_print("%s => %s" % (host, 'Ok'), 'green')
print output
print
else:
for host, output in v.items():
color_print("%s => %s" % (host, k), 'red')
color_print(output, 'red')
print
print "~o~ Task finished ~o~"
print
def upload(self):
while True:
if not self.user_perm:
self.user_perm = get_group_user_perm(self.user)
try:
print "进入批量上传模式"
print "请输入主机名、IP或ansile支持的pattern, 多个主机:分隔 q退出"
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
if pattern == 'q':
break
else:
assets = self.user_perm.get('asset').keys()
res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm)
runner = MyRunner(res)
asset_name_str = ''
print "匹配主机:"
for inv in runner.inventory.get_hosts(pattern=pattern):
print inv.name
asset_name_str += '%s ' % inv.name
if not asset_name_str:
color_print('没有匹配主机')
continue
tmp_dir = get_tmp_dir()
logger.debug('Upload tmp dir: %s' % tmp_dir)
os.chdir(tmp_dir)
bash('rz')
filename_str = ' '.join(os.listdir(tmp_dir))
if not filename_str:
color_print("上传文件为空")
continue
logger.debug('上传文件: %s' % filename_str)
runner = MyRunner(res)
runner.run('copy', module_args='src=%s dest=%s directory_mode'
% (tmp_dir, tmp_dir), pattern=pattern)
ret = runner.results
FileLog(user=self.user.name, host=asset_name_str, filename=filename_str,
remote_ip=remote_ip, type='upload', result=ret).save()
logger.debug('Upload file: %s' % ret)
if ret.get('failed'):
error = '上传目录: %s \n上传失败: [ %s ] \n上传成功 [ %s ]' % (tmp_dir,
', '.join(ret.get('failed').keys()),
', '.join(ret.get('ok').keys()))
color_print(error)
else:
msg = '上传目录: %s \n传送成功 [ %s ]' % (tmp_dir, ', '.join(ret.get('ok').keys()))
color_print(msg, 'green')
print
except IndexError:
pass
def download(self):
while True:
if not self.user_perm:
self.user_perm = get_group_user_perm(self.user)
try:
print "进入批量下载模式"
print "请输入主机名、IP或ansile支持的pattern, 多个主机:分隔,q退出"
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
if pattern == 'q':
break
else:
assets = self.user_perm.get('asset').keys()
res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm)
runner = MyRunner(res)
asset_name_str = ''
print "匹配用户:\n"
for inv in runner.inventory.get_hosts(pattern=pattern):
asset_name_str += '%s ' % inv.name
print ' %s' % inv.name
if not asset_name_str:
color_print('没有匹配主机')
continue
print
while True:
tmp_dir = get_tmp_dir()
logger.debug('Download tmp dir: %s' % tmp_dir)
print "请输入文件路径(不支持目录)"
file_path = raw_input("\033[1;32mPath>:\033[0m ").strip()
if file_path == 'q':
break
runner.run('fetch', module_args='src=%s dest=%s' % (file_path, tmp_dir), pattern=pattern)
ret = runner.results
FileLog(user=self.user.name, host=asset_name_str, filename=file_path, type='download',
remote_ip=remote_ip, result=ret).save()
logger.debug('Download file result: %s' % ret)
os.chdir('/tmp')
tmp_dir_name = os.path.basename(tmp_dir)
if not os.listdir(tmp_dir):
color_print('下载全部失败')
continue
bash('tar czf %s.tar.gz %s && sz %s.tar.gz' % (tmp_dir, tmp_dir_name, tmp_dir))
if ret.get('failed'):
error = '文件名称: %s \n下载失败: [ %s ] \n下载成功 [ %s ]' % \
('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('failed').keys()), ', '.join(ret.get('ok').keys()))
color_print(error)
else:
msg = '文件名称: %s \n下载成功 [ %s ]' % ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('ok').keys()))
color_print(msg, 'green')
print
except IndexError:
pass
def main():
@ -701,12 +754,16 @@ def main():
elif option in ['E', 'e']:
nav.exec_cmd()
continue
elif option in ['U', 'u']:
nav.upload()
elif option in ['D', 'd']:
nav.download()
elif option in ['Q', 'q', 'exit']:
sys.exit()
else:
try:
asset = nav.search_result[int(option)]
roles = get_role(login_user, asset)
roles = nav.user_perm.get('asset').get(asset).get('role')
if len(roles) > 1:
role_check = dict(zip(range(len(roles)), roles))
print "\033[32m[ID] 角色\033[0m"
@ -724,7 +781,7 @@ def main():
color_print('请输入正确ID', 'red')
continue
elif len(roles) == 1:
role = roles[0]
role = list(roles)[0]
else:
color_print('没有映射用户', 'red')
continue

View File

@ -1,141 +0,0 @@
#coding:utf-8
import django
import os
import sys
import random
import datetime
sys.path.append('../')
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
#django.setup()
from juser.views import db_add_user, md5_crypt, CRYPTOR, db_add_group
from jasset.models import Asset, IDC, BisGroup
from juser.models import UserGroup, DEPT, User
from jperm.models import CmdGroup
from jlog.models import Log
def install():
IDC.objects.create(name='ALL', comment='ALL')
IDC.objects.create(name='默认', comment='默认')
DEPT.objects.create(name="默认", comment="默认部门")
DEPT.objects.create(name="超管部", comment="超级管理员部门")
dept = DEPT.objects.get(name='超管部')
dept2 = DEPT.objects.get(name='默认')
UserGroup.objects.create(name='ALL', dept=dept, comment='ALL')
UserGroup.objects.create(name='默认', dept=dept, comment='默认')
BisGroup.objects.create(name='ALL', dept=dept, comment='ALL')
BisGroup.objects.create(name='默认', dept=dept, comment='默认')
User(id=5000, username="admin", password=md5_crypt('admin'),
name='admin', email='admin@jumpserver.org', role='SU', is_active=True, dept=dept).save()
User(id=5001, username="group_admin", password=md5_crypt('group_admin'),
name='group_admin', email='group_admin@jumpserver.org', role='DA', is_active=True, dept=dept2).save()
def test_add_idc():
for i in range(1, 20):
name = 'IDC' + str(i)
IDC.objects.create(name=name, comment='')
print 'Add: %s' % name
def test_add_dept():
for i in range(1, 100):
name = 'DEPT' + str(i)
print "Add: %s" % name
DEPT.objects.create(name=name, comment=name)
def test_add_group():
dept_all = DEPT.objects.all()
for i in range(1, 100):
name = 'UserGroup' + str(i)
UserGroup.objects.create(name=name, dept=random.choice(dept_all), comment=name)
print 'Add: %s' % name
def test_add_cmd_group():
for i in range(1, 20):
name = 'CMD' + str(i)
cmd = '/sbin/ping%s, /sbin/ifconfig/' % str(i)
CmdGroup.objects.create(name=name, cmd=cmd, comment=name)
print 'Add: %s' % name
def test_add_user():
for i in range(1, 500):
username = "test" + str(i)
dept_all = DEPT.objects.all()
group_all = UserGroup.objects.all()
group_all_id = [group.id for group in group_all]
db_add_user(username=username,
password=md5_crypt(username),
dept=random.choice(dept_all),
name=username, email='%s@jumpserver.org' % username,
groups=[random.choice(group_all_id) for i in range(1, 4)], role='CU',
ssh_key_pwd=CRYPTOR.encrypt(username),
ldap_pwd=CRYPTOR.encrypt(username),
is_active=True,
date_joined=datetime.datetime.now())
print "Add: %s" % username
def test_add_asset_group():
dept = DEPT.objects.get(name='默认')
for i in range(1, 20):
name = 'AssetGroup' + str(i)
group = BisGroup(name=name, dept=dept, comment=name)
group.save()
print 'Add: %s' % name
def test_add_asset():
idc_all = IDC.objects.all()
test_idc = random.choice(idc_all)
bis_group_all = BisGroup.objects.all()
dept_all = DEPT.objects.all()
for i in range(1, 500):
ip = '192.168.5.' + str(i)
asset = Asset(ip=ip, port=22, login_type='L', idc=test_idc, is_active=True, comment='test')
asset.save()
asset.bis_group = [random.choice(bis_group_all) for i in range(2)]
asset.dept = [random.choice(dept_all) for i in range(2)]
print "Add: %s" % ip
def test_add_log():
li_date = []
today = datetime.date.today()
oneday = datetime.timedelta(days=1)
for i in range(0, 7):
today = today-oneday
li_date.append(today)
user_list = ['马云', '马化腾', '丁磊', '周鸿祎', '雷军', '柳传志', '陈天桥', '李彦宏', '李开复', '罗永浩']
for i in range(1, 1000):
user = random.choice(user_list)
ip = random.randint(1, 20)
start_time = random.choice(li_date)
end_time = datetime.datetime.now()
log_path = '/var/log/jumpserver/test.log'
host = '192.168.1.' + str(ip)
Log.objects.create(user=user, host=host, remote_ip='8.8.8.8', dept_name='运维部', log_path=log_path, pid=168, start_time=start_time,
is_finished=1, log_finished=1, end_time=end_time)
if __name__ == '__main__':
# install()
# test_add_dept()
# test_add_group()
# test_add_user()
# test_add_idc()
# test_add_asset_group()
test_add_asset()
# test_add_log()

View File

@ -7,22 +7,3 @@
email: admin@jumpserver.org
role: SU
is_active: 1
- model: juser.user
pk: 5001
fields:
username: group_admin
name: group_admin
password: pbkdf2_sha256$20000$ttObUWd15q10$NJoyZf2OZz9oiw2g4j2TkTh9zGgyVDRFdUkhn8X0nB0=
email: group_admin@jumpserver.org
role: GA
is_active: 1
- model: juser.usergroup
pk: 1
fields:
name: ALL
comment: ALL
- model: juser.usergroup
pk: 2
fields:
name: 默认
comment: 默认

View File

@ -1,135 +0,0 @@
#coding:utf-8
import django
import os
import sys
import random
import datetime
sys.path.append('../')
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
#django.setup()
from juser.views import db_add_user, md5_crypt, CRYPTOR, db_add_group
from jasset.models import Asset, IDC, BisGroup
from juser.models import UserGroup, DEPT, User
from jasset.views import jasset_group_add
from jperm.models import CmdGroup
from jlog.models import Log
def install():
IDC.objects.create(name='ALL', comment='ALL')
IDC.objects.create(name='默认', comment='默认')
DEPT.objects.create(name="默认", comment="默认部门")
DEPT.objects.create(name="超管部", comment="超级管理员部门")
dept = DEPT.objects.get(name='超管部')
dept2 = DEPT.objects.get(name='默认')
UserGroup.objects.create(name='ALL', dept=dept, comment='ALL')
UserGroup.objects.create(name='默认', dept=dept, comment='默认')
BisGroup.objects.create(name='ALL', dept=dept, comment='ALL')
BisGroup.objects.create(name='默认', dept=dept, comment='默认')
User(id=5000, username="admin", password=md5_crypt('admin'),
name='admin', email='admin@jumpserver.org', role='SU', is_active=True, dept=dept).save()
User(id=5001, username="group_admin", password=md5_crypt('group_admin'),
name='group_admin', email='group_admin@jumpserver.org', role='DA', is_active=True, dept=dept2).save()
def test_add_idc():
for i in range(1, 20):
name = 'IDC' + str(i)
IDC.objects.create(name=name, comment='')
print 'Add: %s' % name
def test_add_dept():
for i in range(1, 100):
name = 'DEPT' + str(i)
print "Add: %s" % name
DEPT.objects.create(name=name, comment=name)
def test_add_group():
dept_all = DEPT.objects.all()
for i in range(1, 100):
name = 'UserGroup' + str(i)
UserGroup.objects.create(name=name, dept=random.choice(dept_all), comment=name)
print 'Add: %s' % name
def test_add_cmd_group():
for i in range(1, 20):
name = 'CMD' + str(i)
cmd = '/sbin/ping%s, /sbin/ifconfig/' % str(i)
CmdGroup.objects.create(name=name, cmd=cmd, comment=name)
print 'Add: %s' % name
def test_add_user():
for i in range(1, 500):
username = "test" + str(i)
dept_all = DEPT.objects.all()
group_all = UserGroup.objects.all()
group_all_id = [group.id for group in group_all]
db_add_user(username=username,
password=md5_crypt(username),
dept=random.choice(dept_all),
name=username, email='%s@jumpserver.org' % username,
groups=[random.choice(group_all_id) for i in range(1, 4)], role='CU',
ssh_key_pwd=CRYPTOR.encrypt(username),
ldap_pwd=CRYPTOR.encrypt(username),
is_active=True,
date_joined=datetime.datetime.now())
print "Add: %s" % username
def test_add_asset_group():
dept = DEPT.objects.get(name='默认')
for i in range(1, 20):
name = 'AssetGroup' + str(i)
group = BisGroup(name=name, dept=dept, comment=name)
group.save()
print 'Add: %s' % name
def test_add_asset():
idc_all = IDC.objects.all()
test_idc = random.choice(idc_all)
bis_group_all = BisGroup.objects.all()
dept_all = DEPT.objects.all()
for i in range(1, 500):
ip = '192.168.1.' + str(i)
asset = Asset(ip=ip, port=22, login_type='L', idc=test_idc, is_active=True, comment='test')
asset.save()
asset.bis_group = [random.choice(bis_group_all) for i in range(2)]
asset.dept = [random.choice(dept_all) for i in range(2)]
print "Add: %s" % ip
def test_add_log():
li_date = []
today = datetime.date.today()
oneday = datetime.timedelta(days=1)
for i in range(0, 7):
today = today-oneday
li_date.append(today)
user_list = ['马云', '马化腾', '丁磊', '周鸿祎', '雷军', '柳传志', '陈天桥', '李彦宏', '李开复', '罗永浩']
for i in range(1, 1000):
user = random.choice(user_list)
ip = random.randint(1, 20)
start_time = random.choice(li_date)
end_time = datetime.datetime.now()
log_path = '/var/log/jumpserver/test.log'
host = '192.168.1.' + str(ip)
Log.objects.create(user=user, host=host, log_path=log_path, pid=168, start_time=start_time,
is_finished=1, log_finished=1, end_time=end_time)
if __name__ == '__main__':
install()

View File

@ -1,16 +1,17 @@
sphinx-me==0.3
#sphinx-me==0.3
django==1.6
pycrypto==2.6.1
paramiko==1.15.2
paramiko==1.16.0
ecdsa==0.13
MySQL-python==1.2.5
django-uuidfield==0.5.0
psutil==2.2.1
#django-uuidfield==0.5.0
psutil==3.3.0
xlsxwriter==0.7.7
xlrd==0.9.4
django-bootstrap-form
tornado
ansible
pyinotify
passlib
argparse
django-bootstrap-form==3.2
tornado==4.3
ansible==1.9.4
pyinotify==0.9.6
passlib==1.6.5
argparse==1.4.0
django_crontab==0.6.0

View File

@ -1,4 +1,5 @@
# coding: utf-8
from __future__ import division
import xlrd
import xlsxwriter
from django.db.models import AutoField
@ -6,6 +7,7 @@ from jumpserver.api import *
from jasset.models import ASSET_STATUS, ASSET_TYPE, ASSET_ENV, IDC, AssetRecord
from jperm.ansible_api import MyRunner
from jperm.perm_api import gen_resource
from jumpserver.templatetags.mytags import get_disk_info
def group_add_asset(group, asset_id=None, asset_ip=None):
@ -75,83 +77,6 @@ def db_asset_update(**kwargs):
Asset.objects.filter(id=asset_id).update(**kwargs)
#
#
# def batch_host_edit(host_alter_dic, j_user='', j_password=''):
# """ 批量修改主机函数 """
# j_id, j_ip, j_idc, j_port, j_type, j_group, j_dept, j_active, j_comment = host_alter_dic
# groups, depts = [], []
# is_active = {u'是': '1', u'否': '2'}
# login_types = {'LDAP': 'L', 'MAP': 'M'}
# a = Asset.objects.get(id=j_id)
# if '...' in j_group[0].split():
# groups = a.bis_group.all()
# else:
# for group in j_group[0].split():
# c = BisGroup.objects.get(name=group.strip())
# groups.append(c)
#
# if '...' in j_dept[0].split():
# depts = a.dept.all()
# else:
# for d in j_dept[0].split():
# p = DEPT.objects.get(name=d.strip())
# depts.append(p)
#
# j_type = login_types[j_type]
# j_idc = IDC.objects.get(name=j_idc)
# if j_type == 'M':
# if a.password != j_password:
# j_password = cryptor.decrypt(j_password)
# a.ip = j_ip
# a.port = j_port
# a.login_type = j_type
# a.idc = j_idc
# a.is_active = j_active
# a.comment = j_comment
# a.username = j_user
# a.password = j_password
# else:
# a.ip = j_ip
# a.port = j_port
# a.idc = j_idc
# a.login_type = j_type
# a.is_active = is_active[j_active]
# a.comment = j_comment
# a.save()
# a.bis_group = groups
# a.dept = depts
# a.save()
#
#
# def db_host_delete(request, host_id):
# """ 删除主机操作 """
# if is_group_admin(request) and not validate(request, asset=[host_id]):
# return httperror(request, '删除失败, 您无权删除!')
#
# asset = Asset.objects.filter(id=host_id)
# if asset:
# asset.delete()
# else:
# return httperror(request, '删除失败, 没有此主机!')
#
#
# def db_idc_delete(request, idc_id):
# """ 删除IDC操作 """
# if idc_id == 1:
# return httperror(request, '删除失败, 默认IDC不能删除!')
#
# default_idc = IDC.objects.get(id=1)
#
# idc = IDC.objects.filter(id=idc_id)
# if idc:
# idc_class = idc[0]
# idc_class.asset_set.update(idc=default_idc)
# idc.delete()
# else:
# return httperror(request, '删除失败, 没有这个IDC!')
def sort_ip_list(ip_list):
""" ip地址排序 """
ip_list.sort(key=lambda s: map(int, s.split('.')))
@ -232,7 +157,7 @@ def db_asset_alert(asset, username, alert_dic):
for group_id in value[1]:
group_name = AssetGroup.objects.get(id=int(group_id)).name
new.append(group_name)
if old == new:
if sorted(old) == sorted(new):
continue
else:
alert_info = [field_name, ','.join(old), ','.join(new)]
@ -274,28 +199,32 @@ def write_excel(asset_all):
workbook = xlsxwriter.Workbook('static/files/excels/%s' % file_name)
worksheet = workbook.add_worksheet(u'CMDB数据')
worksheet.set_first_sheet()
worksheet.set_column('A:Z', 14)
title = [u'主机名', u'IP', u'IDC', u'MAC', u'远控IP', u'CPU', u'内存', u'硬盘', u'操作系统', u'机柜位置',
u'所属主机组', u'机器状态', u'备注']
worksheet.set_column('A:E', 15)
worksheet.set_column('F:F', 40)
worksheet.set_column('G:Z', 15)
title = [u'主机名', u'IP', u'IDC', u'所属主机组', u'操作系统', u'CPU', u'内存(G)', u'硬盘(G)',
u'机柜位置', u'MAC', u'远控IP', u'机器状态', u'备注']
for asset in asset_all:
group_list = []
for p in asset.group.all():
group_list.append(p.name)
disk = get_disk_info(asset.disk)
group_all = '/'.join(group_list)
status = asset.get_status_display()
idc_name = asset.idc.name if asset.idc else u''
system_type = asset.system_type if asset.idc else u''
system_version = asset.system_version if asset.idc else u''
system_type = asset.system_type if asset.system_type else u''
system_version = asset.system_version if asset.system_version else u''
system_os = unicode(system_type) + unicode(system_version)
alter_dic = [asset.hostname, asset.ip, idc_name, asset.mac, asset.remote_ip, asset.cpu, asset.memory,
asset.disk, system_os, asset.cabinet, group_all, status,
asset.comment]
alter_dic = [asset.hostname, asset.ip, idc_name, group_all, system_os, asset.cpu, asset.memory,
disk, asset.cabinet, asset.mac, asset.remote_ip, status, asset.comment]
data.append(alter_dic)
format = workbook.add_format()
format.set_border(1)
format.set_align('center')
format.set_align('vcenter')
format.set_text_wrap()
format_title = workbook.add_format()
format_title.set_border(1)
@ -384,12 +313,21 @@ def excel_to_db(excel_file):
def get_ansible_asset_info(asset_ip, setup_info):
disk_all = setup_info.get("ansible_devices")
print asset_ip
disk_need = {}
for disk_name, disk_info in disk_all.iteritems():
if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'):
disk_need[disk_name] = disk_info.get("size")
disk_all = setup_info.get("ansible_devices")
if disk_all:
for disk_name, disk_info in disk_all.iteritems():
print disk_name, disk_info
if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'):
disk_size = disk_info.get("size", '')
if 'M' in disk_size:
disk_format = round(float(disk_size[:-2]) / 1000, 0)
elif 'T' in disk_size:
disk_format = round(float(disk_size[:-2]) * 1000, 0)
else:
disk_format = float(disk_size[:-2])
disk_need[disk_name] = disk_format
all_ip = setup_info.get("ansible_all_ipv4_addresses")
other_ip_list = all_ip.remove(asset_ip) if asset_ip in all_ip else []
other_ip = ','.join(other_ip_list) if other_ip_list else ''
@ -401,13 +339,17 @@ def get_ansible_asset_info(asset_ip, setup_info):
cpu_cores = setup_info.get("ansible_processor_count")
cpu = cpu_type + ' * ' + unicode(cpu_cores)
memory = setup_info.get("ansible_memtotal_mb")
try:
memory_format = int(round((int(memory) / 1000), 0))
except Exception:
memory_format = memory
disk = disk_need
system_type = setup_info.get("ansible_distribution")
system_version = setup_info.get("ansible_distribution_version")
system_arch = setup_info.get("ansible_architecture")
# asset_type = setup_info.get("ansible_system")
sn = setup_info.get("ansible_product_serial")
asset_info = [other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand]
asset_info = [other_ip, mac, cpu, memory_format, disk, sn, system_type, system_version, brand, system_arch]
return asset_info
@ -415,6 +357,7 @@ def asset_ansible_update(obj_list, name=''):
resource = gen_resource(obj_list)
ansible_instance = MyRunner(resource)
ansible_asset_info = ansible_instance.run(module_name='setup', pattern='*')
logger.debug('获取硬件信息: %s' % ansible_asset_info)
for asset in obj_list:
try:
setup_info = ansible_asset_info['contacted'][asset.hostname]['ansible_facts']
@ -422,7 +365,7 @@ def asset_ansible_update(obj_list, name=''):
continue
else:
asset_info = get_ansible_asset_info(asset.ip, setup_info)
other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand = asset_info
other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand, system_arch = asset_info
asset_dic = {"other_ip": other_ip,
"mac": mac,
"cpu": cpu,
@ -431,6 +374,7 @@ def asset_ansible_update(obj_list, name=''):
"sn": sn,
"system_type": system_type,
"system_version": system_version,
"system_arch": system_arch,
"brand": brand
}

View File

@ -12,7 +12,8 @@ class AssetForm(forms.ModelForm):
fields = [
"ip", "other_ip", "hostname", "port", "group", "username", "password", "use_default_auth",
"idc", "mac", "remote_ip", "brand", "cpu", "memory", "disk", "system_type", "system_version",
"cabinet", "position", "number", "status", "asset_type", "env", "sn", "is_active", "comment"
"cabinet", "position", "number", "status", "asset_type", "env", "sn", "is_active", "comment",
"system_arch"
]

View File

@ -78,6 +78,7 @@ class Asset(models.Model):
disk = models.CharField(max_length=128, blank=True, null=True, verbose_name=u'硬盘')
system_type = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"系统类型")
system_version = models.CharField(max_length=8, blank=True, null=True, verbose_name=u"系统版本号")
system_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=u"系统平台")
cabinet = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'机柜号')
position = models.IntegerField(blank=True, null=True, verbose_name=u'机器位置')
number = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'资产编号')

View File

@ -6,8 +6,7 @@ from jumpserver.api import *
from jumpserver.models import Setting
from jasset.forms import AssetForm, IdcForm
from jasset.models import Asset, IDC, AssetGroup, ASSET_TYPE, ASSET_STATUS
from jperm.ansible_api import Tasks, MyRunner
from jperm.perm_api import gen_resource
from jperm.perm_api import get_group_asset_perm, get_group_user_perm
@require_role('admin')
@ -96,7 +95,9 @@ def group_list(request):
header_title, path1, path2 = u'查看资产组', u'资产管理', u'查看资产组'
keyword = request.GET.get('keyword', '')
asset_group_list = AssetGroup.objects.all()
group_id = request.GET.get('id')
if group_id:
asset_group_list = asset_group_list.filter(id=group_id)
if keyword:
asset_group_list = asset_group_list.filter(Q(name__contains=keyword) | Q(comment__contains=keyword))
@ -256,11 +257,13 @@ def asset_list(request):
asset list view
"""
header_title, path1, path2 = u'查看资产', u'资产管理', u'查看资产'
username = request.user.username
user_perm = request.session['role_id']
idc_all = IDC.objects.filter()
asset_group_all = AssetGroup.objects.all()
asset_types = ASSET_TYPE
asset_status = ASSET_STATUS
asset_id = request.GET.get('id')
idc_name = request.GET.get('idc', '')
group_name = request.GET.get('group', '')
asset_type = request.GET.get('asset_type', '')
@ -279,12 +282,19 @@ def asset_list(request):
if idc:
asset_find = Asset.objects.filter(idc=idc)
else:
asset_find = Asset.objects.all()
if user_perm != 0:
asset_find = Asset.objects.all()
else:
user = get_object(User, username=username)
asset_perm = get_group_user_perm(user) if user else {'asset': ''}
asset_find = asset_perm['asset'].keys()
asset_group_all = list(asset_perm['asset_group'])
if idc_name:
asset_find = asset_find.filter(idc__name__contains=idc_name)
if group_name:
print asset_find, type(asset_find)
asset_find = asset_find.filter(group__name__contains=group_name)
if asset_type:
@ -293,6 +303,9 @@ def asset_list(request):
if status:
asset_find = asset_find.filter(status__contains=status)
if asset_id:
asset_find = asset_find.filter(id=asset_id)
if keyword:
asset_find = asset_find.filter(
Q(hostname__contains=keyword) |
@ -318,7 +331,10 @@ def asset_list(request):
smg = u'excel文件已生成请点击下载!'
return my_render('jasset/asset_excel_download.html', locals(), request)
assets_list, p, assets, page_range, current_page, show_first, show_end = pages(asset_find, request)
return my_render('jasset/asset_list.html', locals(), request)
if user_perm != 0:
return my_render('jasset/asset_list.html', locals(), request)
else:
return my_render('jasset/asset_cu_list.html', locals(), request)
@require_role('admin')
@ -410,6 +426,18 @@ def asset_detail(request):
header_title, path1, path2 = u'主机详细信息', u'资产管理', u'主机详情'
asset_id = request.GET.get('id', '')
asset = get_object(Asset, id=asset_id)
perm_info = get_group_asset_perm(asset)
log = Log.objects.filter(host=asset.hostname)
if perm_info:
user_perm = []
for perm, value in perm_info.items():
if perm == 'user':
for user, role_dic in value.items():
user_perm.append([user, role_dic.get('role', '')])
elif perm == 'user_group' or perm == 'rule':
user_group_perm = value
print perm_info
asset_record = AssetRecord.objects.filter(asset=asset).order_by('-alert_time')
return my_render('jasset/asset_detail.html', locals(), request)

View File

@ -3,7 +3,7 @@ from django.db import models
class Log(models.Model):
user = models.CharField(max_length=20, null=True)
host = models.CharField(max_length=20, null=True)
host = models.CharField(max_length=200, null=True)
remote_ip = models.CharField(max_length=100)
login_type = models.CharField(max_length=100)
log_path = models.CharField(max_length=100)
@ -24,5 +24,26 @@ class Alert(models.Model):
class TtyLog(models.Model):
log = models.ForeignKey(Log)
datetime = models.DateTimeField()
datetime = models.DateTimeField(auto_now=True)
cmd = models.CharField(max_length=200)
class ExecLog(models.Model):
user = models.CharField(max_length=100)
host = models.TextField()
cmd = models.TextField()
remote_ip = models.CharField(max_length=100)
result = models.TextField(default='')
datetime = models.DateTimeField(auto_now=True)
class FileLog(models.Model):
user = models.CharField(max_length=100)
host = models.TextField()
filename = models.TextField()
type = models.CharField(max_length=20)
remote_ip = models.CharField(max_length=100)
result = models.TextField(default='')
datetime = models.DateTimeField(auto_now=True)

View File

@ -3,11 +3,11 @@ from django.conf.urls import patterns, include, url
from jlog.views import *
urlpatterns = patterns('',
url(r'^$', log_list),
url(r'^log_list/(\w+)/$', log_list),
url(r'^history/$', log_history),
url(r'^log_kill/', log_kill),
url(r'^record/$', log_record),
url(r'^web_terminal/$', web_terminal),
url(r'^get_role_name/$', get_role_name),
(r'^$', log_list),
(r'^log_list/(\w+)/$', log_list),
(r'^log_detail/(\w+)/$', log_detail),
(r'^history/$', log_history),
(r'^log_kill/', log_kill),
(r'^record/$', log_record),
(r'^web_terminal/$', web_terminal),
)

View File

@ -8,7 +8,7 @@ from jperm.perm_api import user_have_perm
from django.http import HttpResponseNotFound
from jlog.log_api import renderTemplate
from models import Log
from jlog.models import Log, ExecLog, FileLog
from jumpserver.settings import WEB_SOCKET_HOST
@ -21,9 +21,24 @@ def log_list(request, offset):
username_list = request.GET.getlist('username', [])
host_list = request.GET.getlist('host', [])
cmd = request.GET.get('cmd', '')
print date_seven_day, date_now_str
if offset == 'online':
keyword = request.GET.get('keyword', '')
posts = Log.objects.filter(is_finished=False).order_by('-start_time')
if keyword:
posts = posts.filter(Q(user__icontains=keyword) | Q(host__icontains=keyword) |
Q(login_type_icontains=keyword))
elif offset == 'exec':
posts = ExecLog.objects.all().order_by('-id')
keyword = request.GET.get('keyword', '')
if keyword:
posts = posts.filter(Q(user__icontains=keyword)|Q(host__icontains=keyword)|Q(cmd__icontains=keyword))
elif offset == 'file':
posts = FileLog.objects.all().order_by('-id')
keyword = request.GET.get('keyword', '')
if keyword:
posts = posts.filter(Q(user__icontains=keyword)|Q(host__icontains=keyword)|Q(filename__icontains=keyword))
else:
posts = Log.objects.filter(is_finished=True).order_by('-start_time')
username_all = set([log.user for log in Log.objects.all()])
@ -57,6 +72,11 @@ def log_list(request, offset):
return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request))
@require_role('admin')
def log_detail(request):
return my_render('jlog/exec_detail.html', locals(), request)
@require_role('admin')
def log_kill(request):
""" 杀掉connect进程 """
@ -107,16 +127,6 @@ def log_record(request):
return HttpResponse('无日志记录!')
@require_role('user')
def get_role_name(request):
asset_id = request.GET.get('id', 9999)
asset = get_object(Asset, id=asset_id)
if asset:
role = user_have_perm(request.user, asset=asset)
return HttpResponse(','.join([i.name for i in role]))
return HttpResponse('error')
@require_role('user')
def web_terminal(request):
asset_id = request.GET.get('id')
@ -124,3 +134,21 @@ def web_terminal(request):
web_terminal_uri = 'ws://%s/terminal?id=%s&role=%s' % (WEB_SOCKET_HOST, asset_id, role_name)
return render_to_response('jlog/web_terminal.html', locals())
@require_role('admin')
def log_detail(request, offset):
log_id = request.GET.get('id')
if offset == 'exec':
log = get_object(ExecLog, id=log_id)
assets_hostname = log.host.split(' ')
result = eval(str(log.result))
return my_render('jlog/exec_detail.html', locals(), request)
elif offset == 'file':
log = get_object(FileLog, id=log_id)
assets_hostname = log.host.split(' ')
file_list = log.filename.split(' ')
try:
result = eval(str(log.result))
except (SyntaxError, NameError):
result = {}
return my_render('jlog/file_detail.html', locals(), request)

View File

@ -12,7 +12,11 @@ from ansible import utils
from passlib.hash import sha512_crypt
from utils import get_rand_pass
from jumpserver.api import logger
from tempfile import NamedTemporaryFile
from django.template.loader import get_template
from django.template import Context
import os.path
@ -117,10 +121,10 @@ class MyRunner(MyInventory):
"""
def __init__(self, *args, **kwargs):
super(MyRunner, self).__init__(*args, **kwargs)
self.results = {}
self.results_raw = {}
def run(self, module_name, module_args='', timeout=10, forks=10, pattern='',
sudo=False, sudo_user='root', sudo_pass=''):
def run(self, module_name='shell', module_args='', timeout=10, forks=10, pattern='*',
become=False, become_method='sudo', become_user='root', become_pass=''):
"""
run module from andible ad-hoc.
module_name: ansible module_name
@ -132,13 +136,40 @@ class MyRunner(MyInventory):
inventory=self.inventory,
pattern=pattern,
forks=forks,
become=sudo,
become_method='sudo',
become_user=sudo_user,
become_pass=sudo_pass
become=become,
become_method=become_method,
become_user=become_user,
become_pass=become_pass
)
self.results = hoc.run()
return self.results
self.results_raw = hoc.run()
logger.debug(self.results_raw)
return self.results_raw
@property
def results(self):
"""
{'failed': {'localhost': ''}, 'ok': {'jumpserver': ''}}
"""
result = {'failed': {}, 'ok': {}}
dark = self.results_raw.get('dark')
contacted = self.results_raw.get('contacted')
if dark:
for host, info in dark.items():
result['failed'][host] = info.get('msg')
if contacted:
for host, info in contacted.items():
if info.get('invocation').get('module_name') in ['raw', 'shell', 'command', 'script']:
if info.get('rc') == 0:
result['ok'][host] = info.get('stdout') + info.get('stderr')
else:
result['failed'][host] = info.get('stdout') + info.get('stderr')
else:
if info.get('failed'):
result['failed'][host] = info.get('msg')
else:
result['ok'][host] = info.get('changed')
return result
class Command(MyInventory):
@ -147,9 +178,9 @@ class Command(MyInventory):
"""
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
self.results = {}
self.results_raw = {}
def run(self, command, module_name="command", timeout=10, forks=10, pattern='*'):
def run(self, command, module_name="command", timeout=10, forks=10, pattern=''):
"""
run command from andible ad-hoc.
command : 必须是一个需要执行的命令字符串 比如
@ -167,25 +198,34 @@ class Command(MyInventory):
pattern=pattern,
forks=forks,
)
self.results = hoc.run()
ret = {}
if self.stdout:
data['ok'] = self.stdout
if self.stderr:
data['err'] = self.stderr
if self.dark:
data['dark'] = self.dark
return data
self.results_raw = hoc.run()
@property
def raw_results(self):
"""
get the ansible raw results.
"""
return self.results
def result(self):
result = {}
for k, v in self.results_raw.items():
if k == 'dark':
for host, info in v.items():
result[host] = {'dark': info.get('msg')}
elif k == 'contacted':
for host, info in v.items():
result[host] = {}
if info.get('stdout'):
result[host]['stdout'] = info.get('stdout')
elif info.get('stderr'):
result[host]['stderr'] = info.get('stderr')
return result
@property
def state(self):
result = {}
if self.stdout:
result['ok'] = self.stdout
if self.stderr:
result['err'] = self.stderr
if self.dark:
result['dark'] = self.dark
return result
@property
def exec_time(self):
@ -193,7 +233,7 @@ class Command(MyInventory):
get the command execute time.
"""
result = {}
all = self.results.get("contacted")
all = self.results_raw.get("contacted")
for key, value in all.iteritems():
result[key] = {
"start": value.get("start"),
@ -207,7 +247,7 @@ class Command(MyInventory):
get the comamnd standard output.
"""
result = {}
all = self.results.get("contacted")
all = self.results_raw.get("contacted")
for key, value in all.iteritems():
result[key] = value.get("stdout")
return result
@ -218,7 +258,7 @@ class Command(MyInventory):
get the command standard error.
"""
result = {}
all = self.results.get("contacted")
all = self.results_raw.get("contacted")
for key, value in all.iteritems():
if value.get("stderr") or value.get("warnings"):
result[key] = {
@ -231,64 +271,24 @@ class Command(MyInventory):
"""
get the dark results.
"""
return self.results.get("dark")
return self.results_raw.get("dark")
class Tasks(Command):
class MyTask(MyRunner):
"""
this is a tasks object for include the common command.
"""
def __init__(self, *args, **kwargs):
super(Tasks, self).__init__(*args, **kwargs)
def __run(self,
module_args,
module_name="command",
timeout=5,
forks=10,
group='default_group',
pattern='*',
become=False,
):
"""
run command from andible ad-hoc.
command : 必须是一个需要执行的命令字符串 比如
'uname -a'
"""
hoc = Runner(module_name=module_name,
module_args=module_args,
timeout=timeout,
inventory=self.inventory,
subset=group,
pattern=pattern,
forks=forks,
become=become,
)
self.results = hoc.run()
return {"msg": self.msg, "result": self.results}
@property
def msg(self):
"""
get the contacted and dark msg
"""
msg = {}
for result in ["contacted", "dark"]:
all = self.results.get(result)
for key, value in all.iteritems():
if value.get("msg"):
msg[key] = value.get("msg")
return msg
super(MyTask, self).__init__(*args, **kwargs)
def push_key(self, user, key_path):
"""
push the ssh authorized key to target.
"""
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state=present' % (user, key_path)
self.__run(module_args, "authorized_key", become=True)
self.run("authorized_key", module_args, become=True)
return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok"}
return self.results
def push_multi_key(self, **user_info):
"""
@ -315,9 +315,9 @@ class Tasks(Command):
push the ssh authorized key to target.
"""
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path)
self.__run(module_args, "authorized_key")
self.run("authorized_key", module_args, become=True)
return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok"}
return self.results
def add_user(self, username, password=''):
"""
@ -329,9 +329,10 @@ class Tasks(Command):
module_args = 'name=%s shell=/bin/bash password=%s' % (username, encrypt_pass)
else:
module_args = 'name=%s shell=/bin/bash' % username
self.__run(module_args, "user", become=True)
return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok"}
self.run("user", module_args, become=True)
return self.results
def add_multi_user(self, **user_info):
"""
@ -358,94 +359,38 @@ class Tasks(Command):
"""
delete a host user.
"""
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % (username)
self.__run(module_args,
"user",)
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % username
self.run("user", module_args, become=True)
return self.results
return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"}
def add_init_users(self):
"""
add initail users: SA, DBA, DEV
"""
results = {}
action = results["action_info"] = {}
users = {"SA": get_rand_pass(), "DBA": get_rand_pass(), "DEV": get_rand_pass()}
for user, password in users.iteritems():
ret = self.add_user(user, password)
action[user] = ret
results["user_info"] = users
@staticmethod
def gen_sudo_script(role_list, sudo_list):
# receive role_list = [role1, role2] sudo_list = [sudo1, sudo2]
# return sudo_alias={'NETWORK': '/sbin/ifconfig, /ls'} sudo_user={'user1': ['NETWORK', 'SYSTEM']}
sudo_alias = {}
sudo_user = {}
for sudo in sudo_list:
sudo_alias[sudo.name] = sudo.commands
return results
for role in role_list:
sudo_user[role.name] = ','.join(sudo_alias.keys())
def del_init_users(self):
"""
delete initail users: SA, DBA, DEV
"""
results = {}
action = results["action_info"] = {}
for user in ["SA", "DBA", "DEV"]:
ret = self.del_user(user)
action[user] = ret
return results
sudo_j2 = get_template('jperm/role_sudo.j2')
sudo_content = sudo_j2.render(Context({"sudo_alias": sudo_alias, "sudo_user": sudo_user}))
sudo_file = NamedTemporaryFile(delete=False)
sudo_file.write(sudo_content)
sudo_file.close()
return sudo_file.name
def get_host_info(self):
"""
use the setup module get host informations
:return:
all_ip is list
processor_count is int
system_dist_version is string
system_type is string
disk is dict (device_name: device_size}
system_dist is string
processor_type is string
default_ip is string
hostname is string
product_sn is string
memory_total is int (MB)
default_mac is string
product_name is string
"""
self.__run('', 'setup', become=True)
result = {}
all = self.results.get("contacted")
for key, value in all.iteritems():
setup =value.get("ansible_facts")
# get disk informations
disk_all = setup.get("ansible_devices")
disk_need = {}
for disk_name, disk_info in disk_all.iteritems():
if disk_name.startswith('sd') or disk_name.startswith('hd'):
disk_need[disk_name] = disk_info.get("size")
result[key] = {
"all_ip": setup.get("ansible_all_ipv4_addresses"),
"hostname" : setup.get("ansible_hostname"),
"default_ip": setup.get("ansible_default_ipv4").get("address"),
"default_mac": setup.get("ansible_default_ipv4").get("macaddress"),
"product_name": setup.get("ansible_product_name"),
"processor_type": ' '.join(setup.get("ansible_processor")),
"processor_count": setup.get("ansible_processor_count"),
"memory_total": setup.get("ansible_memtotal_mb"),
"disk": disk_need,
"system_type": setup.get("ansible_system"),
"system_dist": setup.get("ansible_distribution"),
"system_dist_verion": setup.get("ansible_distribution_major_version"),
"product_sn": setup.get("ansible_product_serial")
}
return {"failed": self.msg, "ok": result}
def push_sudo_file(self, file_path):
def push_sudo_file(self, role_list, sudo_list):
"""
use template to render pushed sudoers file
:return:
"""
module_args1 = file_path
ret = self.__run(module_args1, "script")
return ret
module_args1 = self.gen_sudo_script(role_list, sudo_list)
self.run("script", module_args1, become=True)
return self.results
class CustomAggregateStats(callbacks.AggregateStats):

View File

@ -55,6 +55,6 @@ class PermPush(models.Model):
is_public_key = models.BooleanField(default=False)
is_password = models.BooleanField(default=False)
success = models.BooleanField(default=False)
result = models.TextField()
result = models.TextField(default='')
date_added = models.DateTimeField(auto_now=True)

View File

@ -43,13 +43,16 @@ def get_group_user_perm(ob):
asset_groups = rule.asset_group.all()
assets = rule.asset.all()
perm_roles = rule.role.all()
group_assets = []
for asset_group in asset_groups:
group_assets.extend(asset_group.asset_set.all())
# 获取一个规则授权的角色和对应主机
for role in perm_roles:
if perm_role.get('role'):
perm_role[role]['asset'] = perm_role[role].get('asset', set()).union(set(assets))
if perm_role.get(role):
perm_role[role]['asset'] = perm_role[role].get('asset', set()).union(set(assets).union(set(group_assets)))
perm_role[role]['asset_group'] = perm_role[role].get('asset_group', set()).union(set(asset_groups))
else:
perm_role[role] = {'asset': set(assets), 'asset_group': set(asset_groups)}
perm_role[role] = {'asset': set(assets).union(set(group_assets)), 'asset_group': set(asset_groups)}
# 获取一个规则用户授权的资产
for asset in assets:
@ -161,23 +164,42 @@ def gen_resource(ob, perm=None):
user = ob.get('user')
if not perm:
perm = get_group_user_perm(user)
roles = perm.get('role', {}).keys()
if role not in roles:
return {}
role_assets_all = perm.get('role').get(role).get('asset')
assets = set(role_assets_all) & set(asset_r)
if role:
roles = perm.get('role', {}).keys() # 获取用户所有授权角色
if role not in roles:
return {}
for asset in assets:
asset_info = get_asset_info(asset)
info = {'hostname': asset.hostname,
'ip': asset.ip,
'port': asset_info.get('port', 22),
'username': role.name,
'password': CRYPTOR.decrypt(role.password),
'ssh_key': get_role_key(user, role)
}
res.append(info)
role_assets_all = perm.get('role').get(role).get('asset') # 获取用户该角色所有授权主机
assets = set(role_assets_all) & set(asset_r) # 获取用户提交中合法的主机
for asset in assets:
asset_info = get_asset_info(asset)
info = {'hostname': asset.hostname,
'ip': asset.ip,
'port': asset_info.get('port', 22),
'username': role.name,
'password': CRYPTOR.decrypt(role.password),
'ssh_key': get_role_key(user, role)
}
res.append(info)
else:
for asset, asset_info in perm.get('asset').items():
if asset not in asset_r:
continue
asset_info = get_asset_info(asset)
try:
role = sorted(list(perm.get('asset').get(asset).get('role')))[0]
except IndexError:
continue
info = {'hostname': asset.hostname,
'ip': asset.ip,
'port': asset_info.get('port', 22),
'username': role.name,
'password': CRYPTOR.decrypt(role.password),
'ssh_key': get_role_key(user, role)
}
res.append(info)
elif isinstance(ob, User):
if not perm:
@ -198,6 +220,7 @@ def gen_resource(ob, perm=None):
for asset in ob:
info = get_asset_info(asset)
res.append(info)
logger.debug('生成res: %s' % res)
return res
@ -281,6 +304,7 @@ def get_role_push_host(role):
asset_no_push = set(asset_all) - set(asset_pushed.keys())
return asset_pushed, asset_no_push
if __name__ == "__main__":
print get_role_info(1)

View File

@ -14,6 +14,7 @@ urlpatterns = patterns('jperm.views',
(r'^role/perm_role_edit/$', perm_role_edit),
(r'^role/push/$', perm_role_push),
(r'^role/recycle/$', perm_role_recycle),
(r'^role/get/$', perm_role_get),
(r'^sudo/$', perm_sudo_list),
(r'^sudo/perm_sudo_add/$', perm_sudo_add),
(r'^sudo/perm_sudo_delete/$', perm_sudo_delete),

View File

@ -10,11 +10,8 @@ from uuid import uuid4
from jumpserver.api import CRYPTOR
from os import makedirs
from django.template.loader import get_template
from django.template import Context
from tempfile import NamedTemporaryFile
from jumpserver.settings import KEY_DIR
@ -72,45 +69,6 @@ def gen_keys(key="", key_path_dir=""):
return key_path_dir
def gen_sudo(role_custom, role_name, role_chosen):
"""
生成sudo file, 仅测试了cenos7
role_custom: 自定义支持的sudo 命令 格式: 'CMD1, CMD2, CMD3, ...'
role_name: role name
role_chosen: 选择那些sudo的命令别名
    NETWORKING, SOFTWARE, SERVICES, STORAGE,
    DELEGATING, PROCESSES, LOCATE, DRIVERS
:return:
"""
sudo_file_basename = os.path.join(os.path.dirname(KEY_DIR), 'role_sudo_file')
makedirs(sudo_file_basename)
sudo_file_path = os.path.join(sudo_file_basename, role_name)
t = get_template('role_sudo.j2')
content = t.render(Context({"role_custom": role_custom,
"role_name": role_name,
"role_chosen": role_chosen,
}))
with open(sudo_file_path, 'w') as f:
f.write(content)
return sudo_file_path
def get_add_sudo_script(role_chosen_aliase, sudo_alias):
"""
get the sudo file
:param kwargs:
:return:
"""
sudo_j2 = get_template('jperm/role_sudo.j2')
sudo_content = sudo_j2.render(Context({"role_chosen_aliase": role_chosen_aliase,
"sudo_alias": sudo_alias}))
sudo_file = NamedTemporaryFile(delete=False)
sudo_file.write(sudo_content)
sudo_file.close()
print(sudo_file.name)
return sudo_file.name
if __name__ == "__main__":
print gen_keys()

View File

@ -10,10 +10,9 @@ from jasset.models import Asset, AssetGroup
from jperm.models import PermRole, PermRule, PermSudo, PermPush
from jumpserver.models import Setting
from jperm.utils import updates_dict, gen_keys, get_rand_pass, get_add_sudo_script
from jperm.ansible_api import Tasks
from jperm.utils import updates_dict, gen_keys, get_rand_pass
from jperm.ansible_api import MyTask
from jperm.perm_api import get_role_info, get_role_push_host
from jumpserver.api import my_render, get_object, CRYPTOR
@ -24,12 +23,14 @@ def perm_rule_list(request):
"""
# 渲染数据
header_title, path1, path2 = "授权规则", "规则管理", "查看规则"
# 获取所有规则
rules_list = PermRule.objects.all()
rule_id = request.GET.get('id')
# TODO: 搜索和分页
keyword = request.GET.get('search', '')
if rule_id:
rules_list = rules_list.filter(id=rule_id)
if keyword:
rules_list = rules_list.filter(Q(name=keyword))
@ -78,24 +79,29 @@ def perm_rule_add(request):
if request.method == 'POST':
# 获取用户选择的 用户,用户组,资产,资产组,用户角色
users_select = request.POST.getlist('user', [])
user_groups_select = request.POST.getlist('usergroup', [])
assets_select = request.POST.getlist('asset', [])
asset_groups_select = request.POST.getlist('assetgroup', [])
roles_select = request.POST.getlist('role', [])
rule_name = request.POST.get('rulename')
rule_comment = request.POST.get('rule_comment')
users_select = request.POST.getlist('user', []) # 需要授权用户
user_groups_select = request.POST.getlist('user_group', []) # 需要授权用户组
assets_select = request.POST.getlist('asset', []) # 需要授权资产
asset_groups_select = request.POST.getlist('asset_group', []) # 需要授权资产组
roles_select = request.POST.getlist('role', []) # 需要授权角色
rule_name = request.POST.get('name')
rule_comment = request.POST.get('comment')
try:
rule = get_object(PermRule, name=rule_name)
if rule:
raise ServerError(u'授权规则 %s 已存在' % rule_name)
if not rule_name or not roles_select:
raise ServerError(u'角色名称和授权角色不能为空')
# 获取需要授权的主机列表
assets_obj = [Asset.objects.get(id=asset_id) for asset_id in assets_select]
asset_groups_obj = [AssetGroup.objects.get(id=group_id) for group_id in asset_groups_select]
group_assets_obj = [asset for asset in [group.asset_set.all() for group in asset_groups_obj]]
calc_assets = set(group_assets_obj) | set(assets_obj)
group_assets_obj = []
for asset_group in asset_groups_obj:
group_assets_obj.extend(list(asset_group.asset_set.all()))
calc_assets = set(group_assets_obj) | set(assets_obj) # 授权资产和资产组包含的资产
# 获取需要授权的用户列表
users_obj = [User.objects.get(id=user_id) for user_id in users_select]
@ -106,8 +112,9 @@ def perm_rule_add(request):
# 获取授予的角色列表
roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select]
need_push_asset = set()
for role in roles_obj:
asset_no_push = get_role_push_host(role=role)[1]
asset_no_push = get_role_push_host(role=role)[0] # 获取某角色已经推送的资产
need_push_asset.update(set(calc_assets) - set(asset_no_push))
if need_push_asset:
raise ServerError(u'没有推送角色 %s 的主机 %s'
@ -140,61 +147,68 @@ def perm_rule_edit(request):
# 根据rule_id 取得rule对象
rule_id = request.GET.get("id")
rule = PermRule.objects.get(id=rule_id)
rule = get_object(PermRule, id=rule_id)
if request.method == 'GET' and rule_id:
# 渲染数据, 获取所选的rule对象
rule_comment = rule.comment
users_select = rule.user.all()
user_groups_select = rule.user_group.all()
assets_select = rule.asset.all()
asset_groups_select = rule.asset_group.all()
roles_select = rule.role.all()
# 渲染数据, 获取所选的rule对象
users = User.objects.all()
user_groups = UserGroup.objects.all()
assets = Asset.objects.all()
asset_groups = AssetGroup.objects.all()
roles = PermRole.objects.all()
return my_render('jperm/perm_rule_edit.html', locals(), request)
users = User.objects.all()
user_groups = UserGroup.objects.all()
assets = Asset.objects.all()
asset_groups = AssetGroup.objects.all()
roles = PermRole.objects.all()
elif request.method == 'POST' and rule_id:
if request.method == 'POST' and rule_id:
# 获取用户选择的 用户,用户组,资产,资产组,用户角色
rule_name = request.POST.get('rule_name')
rule_comment = request.POST.get("rule_comment")
rule_name = request.POST.get('name')
rule_comment = request.POST.get("comment")
users_select = request.POST.getlist('user', [])
user_groups_select = request.POST.getlist('usergroup', [])
user_groups_select = request.POST.getlist('user_group', [])
assets_select = request.POST.getlist('asset', [])
asset_groups_select = request.POST.getlist('assetgroup', [])
asset_groups_select = request.POST.getlist('asset_group', [])
roles_select = request.POST.getlist('role', [])
print rule_name, roles_select
try:
if not rule_name or not roles_select:
raise ServerError(u'角色名称和授权角色不能为空')
assets_obj = [Asset.objects.get(id=asset_id) for asset_id in assets_select]
asset_groups_obj = [AssetGroup.objects.get(id=group_id) for group_id in asset_groups_select]
# group_assets_obj = [asset for asset in [group.asset_set.all() for group in asset_groups_obj]]
# calc_assets = set(group_assets_obj) | set(assets_obj)
assets_obj = [Asset.objects.get(id=asset_id) for asset_id in assets_select]
asset_groups_obj = [AssetGroup.objects.get(id=group_id) for group_id in asset_groups_select]
group_assets_obj = []
for asset_group in asset_groups_obj:
group_assets_obj.extend(list(asset_group.asset_set.all()))
calc_assets = set(group_assets_obj) | set(assets_obj) # 授权资产和资产组包含的资产
# 获取需要授权的用户列表
users_obj = [User.objects.get(id=user_id) for user_id in users_select]
user_groups_obj = [UserGroup.objects.get(id=group_id) for group_id in user_groups_select]
# group_users_obj = [user for user in [group.user_set.all() for group in user_groups_obj]]
# calc_users = set(group_users_obj) | set(users_obj)
# 获取需要授权的用户列表
users_obj = [User.objects.get(id=user_id) for user_id in users_select]
user_groups_obj = [UserGroup.objects.get(id=group_id) for group_id in user_groups_select]
# group_users_obj = [user for user in [group.user_set.all() for group in user_groups_obj]]
# calc_users = set(group_users_obj) | set(users_obj)
# 获取授予的角色列表
roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select]
# 获取授予的角色列表
roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select]
need_push_asset = set()
for role in roles_obj:
asset_no_push = get_role_push_host(role=role)[0] # 获取某角色已经推送的资产
need_push_asset.update(set(calc_assets) - set(asset_no_push))
if need_push_asset:
raise ServerError(u'没有推送角色 %s 的主机 %s'
% (role.name, ','.join([asset.hostname for asset in need_push_asset])))
# 仅授权成功的,写回数据库(授权规则,用户,用户组,资产,资产组,用户角色)
rule.user = users_obj
rule.user_group = user_groups_obj
rule.asset = assets_obj
rule.asset_group = asset_groups_obj
rule.role = roles_obj
rule.name = rule_name
rule.comment = rule.comment
rule.save()
# 仅授权成功的,写回数据库(授权规则,用户,用户组,资产,资产组,用户角色)
rule.user = users_obj
rule.user_group = user_groups_obj
rule.asset = assets_obj
rule.asset_group = asset_groups_obj
rule.role = roles_obj
rule.name = rule_name
rule.comment = rule.comment
rule.save()
msg = u"更新授权规则:%s成功" % rule.name
msg = u"更新授权规则:%s" % rule.name
except ServerError, e:
error = e
return HttpResponseRedirect('/jperm/rule/')
return my_render('jperm/perm_rule_edit.html', locals(), request)
@require_role('admin')
@ -208,8 +222,6 @@ def perm_rule_delete(request):
# 根据rule_id 取得rule对象
rule_id = request.POST.get("id")
rule_obj = PermRule.objects.get(id=rule_id)
print rule_id, rule_obj
print rule_obj.name
rule_obj.delete()
return HttpResponse(u"删除授权规则:%s" % rule_obj.name)
else:
@ -226,12 +238,15 @@ def perm_role_list(request):
# 获取所有系统角色
roles_list = PermRole.objects.all()
role_id = request.GET.get('id')
# TODO: 搜索和分页
keyword = request.GET.get('search', '')
if keyword:
roles_list = roles_list.filter(Q(name=keyword))
if role_id:
roles_list = roles_list.filter(id=role_id)
roles_list, p, roles, page_range, current_page, show_first, show_end = pages(roles_list, request)
return my_render('jperm/perm_role_list.html', locals(), request)
@ -396,9 +411,12 @@ def perm_role_push(request):
# 渲染数据
header_title, path1, path2 = "系统角色", "角色管理", "角色推送"
role_id = request.GET.get('id')
asset_ids = request.GET.get('asset_id')
role = get_object(PermRole, id=role_id)
assets = Asset.objects.all()
asset_groups = AssetGroup.objects.all()
if asset_ids:
need_push_asset = [get_object(Asset, id=asset_id) for asset_id in asset_ids.split(',')]
if request.method == "POST":
# 获取推荐角色的名称列表
@ -417,43 +435,48 @@ def perm_role_push(request):
# 调用Ansible API 进行推送
password_push = True if request.POST.get("use_password") else False
key_push = True if request.POST.get("use_publicKey") else False
task = Tasks(push_resource)
task = MyTask(push_resource)
ret = {}
ret_failed = {}
# 因为要先建立用户所以password 是必选项而push key是在 password也完成的情况下的 可选项
# 1. 以password 方式推送角色
if password_push:
ret["password_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password))
if ret["password_push"].get("status") != "success":
ret_failed = ret["password_push"].get('msg')
# 2. 以秘钥 方式推送角色
# 1. 以秘钥 方式推送角色
if key_push:
ret["password_push"] = task.add_user(role.name)
if ret["password_push"].get("status") != "ok":
ret_failed = ret["password_push"].get('msg')
ret["pass_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password))
ret["key_push"] = task.push_key(role.name, os.path.join(role.key_path, 'id_rsa.pub'))
if ret["key_push"].get("status") != "ok":
ret_failed = ret["key_push"].get('msg')
# 2. 推送账号密码
elif password_push:
ret["pass_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password))
# 3. 推送sudo配置文件
if password_push or key_push:
role_chosen_aliase = {} # {'dev': 'NETWORKING, SHUTDOWN'}
sudo_alias = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
role_chosen_aliase[role.name] = ','.join(sudo.name for sudo in sudo_alias)
add_sudo_script = get_add_sudo_script(role_chosen_aliase, sudo_alias)
ret['sudo'] = task.push_sudo_file(add_sudo_script)
if ret['sudo'].get('msg'):
ret_failed = ret['sudo'].get('msg')
# os.remove(add_sudo_script)
sudo_list = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
ret['sudo'] = task.push_sudo_file([role], sudo_list)
logger.debug('推送role结果: %s' % ret)
logger.debug('推送role错误: %s' % ret_failed)
success_asset = {}
failed_asset = {}
logger.debug(ret)
for push_type, result in ret.items():
if result.get('failed'):
for hostname, info in result.get('failed').items():
if hostname in failed_asset.keys():
if info in failed_asset.get(hostname):
failed_asset[hostname] += info
else:
failed_asset[hostname] = info
for push_type, result in ret.items():
if result.get('ok'):
for hostname, info in result.get('ok').items():
if hostname in failed_asset.keys():
continue
elif hostname in success_asset.keys():
if str(info) in success_asset.get(hostname, ''):
success_asset[hostname] += str(info)
else:
success_asset[hostname] = str(info)
success_asset = []
failed_asset = []
# 推送成功 回写push表
for asset in calc_assets:
push_check = PermPush.objects.filter(role=role, asset=asset)
@ -463,20 +486,18 @@ def perm_role_push(request):
def func(**kwargs):
PermPush(**kwargs).save()
if ret_failed.get(asset.hostname):
failed_asset.append(asset)
if failed_asset.get(asset.hostname):
func(is_password=password_push, is_public_key=key_push, role=role, asset=asset, success=False,
result=ret_failed.get(asset.hostname))
result=failed_asset.get(asset.hostname))
else:
success_asset.append(asset)
func(is_password=password_push, is_public_key=key_push, role=role, asset=asset, success=True)
if not failed_asset:
msg = u'角色 %s 推送成功[ %s ]' % (role.name, ','.join([asset.hostname for asset in success_asset]))
msg = u'角色 %s 推送成功[ %s ]' % (role.name, ','.join(success_asset.keys()))
else:
error = u'角色 %s 推送失败 [ %s ], 推送成功 [ %s ]' % (role.name,
','.join([asset.hostname for asset in failed_asset]),
','.join([asset.hostname for asset in success_asset]))
','.join(failed_asset.keys()),
','.join(success_asset.keys()))
return my_render('jperm/perm_role_push.html', locals(), request)
@ -515,14 +536,18 @@ def perm_sudo_add(request):
if request.method == "POST":
# 获取参数: name, comment
name = request.POST.get("sudo_name").strip()
name = request.POST.get("sudo_name").strip().upper()
comment = request.POST.get("sudo_comment").strip()
commands = request.POST.get("sudo_commands").strip()
pattern = re.compile(r'[ \n,\r]')
commands = ', '.join(list_drop_str(pattern.split(commands), u''))
logger.debug(u'添加sudo %s: %s' % (name, commands))
if get_object(PermSudo, name=name):
error = 'Sudo别名 %s已经存在' % name
else:
sudo = PermSudo(name=name.strip(), comment=comment, commands=commands.strip())
sudo = PermSudo(name=name.strip(), comment=comment, commands=commands)
sudo.save()
msg = u"添加Sudo命令别名: %s" % name
# 渲染数据
@ -544,11 +569,16 @@ def perm_sudo_edit(request):
sudo = PermSudo.objects.get(id=sudo_id)
if request.method == "POST":
name = request.POST.get("sudo_name")
name = request.POST.get("sudo_name").upper()
commands = request.POST.get("sudo_commands")
comment = request.POST.get("sudo_comment")
pattern = re.compile(r'[ \n,\r]')
commands = ', '.join(list_drop_str(pattern.split(commands), u'')).strip()
logger.debug(u'添加sudo %s: %s' % (name, commands))
sudo.name = name.strip()
sudo.commands = commands.strip()
sudo.commands = commands
sudo.comment = comment
sudo.save()
@ -579,11 +609,31 @@ def perm_sudo_delete(request):
def perm_role_recycle(request):
role_id = request.GET.get('role_id')
asset_ids = request.GET.get('asset_id').split(',')
assets = []
for asset_id in asset_ids:
asset = get_object(Asset, id=asset_id)
assets.append(asset)
role = get_object(PermRole, id=role_id)
PermPush.objects.filter(asset=asset, role=role).delete()
res = gen_resource(assets)
task = MyTask(res)
return HttpResponse('删除成功')
@require_role('user')
def perm_role_get(request):
asset_id = request.GET.get('id', 0)
if asset_id:
asset = get_object(Asset, id=asset_id)
if asset:
role = user_have_perm(request.user, asset=asset)
logger.debug('#' + ','.join([i.name for i in role]) + '#')
return HttpResponse(','.join([i.name for i in role]))
else:
roles = get_group_user_perm(request.user).get('role').keys()
return HttpResponse(','.join(i.name for i in roles))
return HttpResponse('error')

View File

@ -9,6 +9,7 @@ import hashlib
import datetime
import random
import subprocess
import uuid
import json
import logging
@ -47,9 +48,16 @@ def set_log(level):
return logger_f
def list_drop_str(a_list, a_str):
for i in a_list:
if i == a_str:
a_list.remove(a_str)
return a_list
def get_asset_info(asset):
"""
获取资产的相关账号端口信息
获取资产的相关管理账号端口信息
"""
default = get_object(Setting, name='default')
info = {'hostname': asset.hostname, 'ip': asset.ip}
@ -70,17 +78,6 @@ def get_asset_info(asset):
return info
def get_role(user, asset):
"""
获取用户在这个资产上的授权角色列表
"""
roles = []
rules = PermRule.objects.filter(user=user, asset=asset)
for rule in rules:
roles.extend(list(rule.role.all()))
return roles
def get_role_key(user, role):
"""
由于role的key的权限是所有人可以读的 ansible执行命令等要求为600所以拷贝一份到特殊目录
@ -95,7 +92,7 @@ def get_role_key(user, role):
with open(os.path.join(role.key_path, 'id_rsa')) as fk:
with open(user_role_key_path, 'w') as fu:
fu.write(fk.read())
logger.debug("创建新的用户角色key %s" % user_role_key_path)
logger.debug(u"创建新的用户角色key %s, Owner: %s" % (user_role_key_path, user.username))
chown(user_role_key_path, user.username)
os.chmod(user_role_key_path, 0600)
return user_role_key_path
@ -482,5 +479,10 @@ def my_render(template, data, request):
return render_to_response(template, data, context_instance=RequestContext(request))
def get_tmp_dir():
dir_name = os.path.join('/tmp', uuid.uuid4().hex)
mkdir(dir_name, mode=0777)
return dir_name
CRYPTOR = PyCrypt(KEY)
logger = set_log(LOG_LEVEL)

View File

@ -7,7 +7,7 @@ import time
from django import template
from jperm.models import PermPush
from jumpserver.api import *
from jasset.models import AssetAlias
from jperm.perm_api import get_group_user_perm
register = template.Library()
@ -237,7 +237,7 @@ def key_exist(username):
"""
ssh key is exist or not
"""
if os.path.isfile(os.path.join(KEY_DIR, 'user', username)):
if os.path.isfile(os.path.join(KEY_DIR, 'user', username+'.pem')):
return True
else:
return False
@ -272,3 +272,35 @@ def get_push_info(push_id, arg):
return [role.name for role in push.role.all()]
else:
return []
@register.filter(name='get_cpu_core')
def get_cpu_core(cpu_info):
cpu_core = cpu_info.split('* ')[1] if cpu_info and '*' in cpu_info else cpu_info
return cpu_core
@register.filter(name='get_disk_info')
def get_disk_info(disk_info):
try:
disk_size = 0
if disk_info:
disk_dic = ast.literal_eval(disk_info)
for disk, size in disk_dic.items():
disk_size += size
disk_size = int(disk_size)
else:
disk_size = ''
except Exception:
disk_size = ''
return disk_size
@register.filter(name='user_perm_asset_num')
def user_perm_asset_num(user_id):
user = get_object(User, id=user_id)
if user:
user_perm_info = get_group_user_perm(user)
return len(user_perm_info.get('asset').keys())
else:
return 0

View File

@ -6,19 +6,14 @@ urlpatterns = patterns('',
(r'^$', 'jumpserver.views.index'),
(r'^api/user/$', 'jumpserver.api.api_user'),
(r'^skin_config/$', 'jumpserver.views.skin_config'),
(r'^install/$', 'jumpserver.views.install'),
(r'^base/$', 'jumpserver.views.base'),
(r'^login/$', 'jumpserver.views.Login'),
(r'^logout/$', 'jumpserver.views.Logout'),
(r'^exec_cmd/$', 'jumpserver.views.exec_cmd'),
(r'^file/upload/$', 'jumpserver.views.upload'),
(r'^file/download/$', 'jumpserver.views.download'),
(r'^setting', 'jumpserver.views.setting'),
(r'^error/$', 'jumpserver.views.httperror'),
(r'^juser/', include('juser.urls')),
(r'^jasset/', include('jasset.urls')),
(r'^jlog/', include('jlog.urls')),
(r'^jperm/', include('jperm.urls')),
(r'^node_auth/', 'jumpserver.views.node_auth'),
(r'download/(\d{4}/\d\d/\d\d/.*)', 'jumpserver.views.download_file'),
(r'test2', 'jumpserver.views.test2'),
)

View File

@ -15,8 +15,11 @@ from jumpserver.api import *
from jumpserver.models import Setting
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from jlog.models import Log
from jperm.perm_api import get_group_user_perm
from jlog.models import Log, FileLog
from jperm.perm_api import get_group_user_perm, gen_resource
from jasset.models import Asset, IDC
from jperm.ansible_api import MyRunner
def getDaysByNum(num):
"""
@ -72,42 +75,11 @@ def get_count_by_date(date_li, item):
return len(set(data_count_tmp))
from jasset.models import Asset, IDC
@require_role(role='user')
def index_cu(request):
# user_id = request.user.id
# user = get_object(User, id=user_id)
login_types = {'L': 'LDAP', 'M': 'MAP'}
username = request.user.username
# TODO: need fix,liuzheng need Asset help
GUP = get_group_user_perm(request.user)
print GUP
assets = GUP.get('asset')
idcs = []
for i in assets:
if i.idc_id:
idcs.append(i.idc_id)
idc_all = IDC.objects.filter(id__in=idcs)
for i in idc_all:
print i.name
# idc_all = []
# for i in assets:
# idc_all.append(i.idc)
# print i.idc.name
asset_group_all = GUP.get('asset_group')
# posts = Asset.object.all()
# host_count = len(posts)
#
# new_posts = []
# post_five = []
# for post in posts:
# if len(post_five) < 5:
# post_five.append(post)
# else:
# new_posts.append(post_five)
# post_five = []
# new_posts.append(post_five)
return render_to_response('index_cu.html', locals(), context_instance=RequestContext(request))
return HttpResponseRedirect('/juser/user_detail/')
@require_role(role='user')
@ -181,34 +153,6 @@ def skin_config(request):
return render_to_response('skin_config.html')
# def pages(posts, r):
# """分页公用函数"""
# contact_list = posts
# p = paginator = Paginator(contact_list, 10)
# try:
# current_page = int(r.GET.get('page', '1'))
# except ValueError:
# current_page = 1
#
# page_range = page_list_return(len(p.page_range), current_page)
#
# try:
# contacts = paginator.page(current_page)
# except (EmptyPage, InvalidPage):
# contacts = paginator.page(paginator.num_pages)
#
# if current_page >= 5:
# show_first = 1
# else:
# show_first = 0
# if current_page <= (len(p.page_range) - 3):
# show_end = 1
# else:
# show_end = 0
#
# return contact_list, p, contacts, page_range, current_page, show_first, show_end
def is_latest():
node = uuid.getnode()
jsn = uuid.UUID(int=node).hex[-12:]
@ -308,166 +252,91 @@ def setting(request):
return my_render('setting.html', locals(), request)
def test2(request):
return my_render('test2.html', locals(), request)
#
# def filter_ajax_api(request):
# attr = request.GET.get('attr', 'user')
# value = request.GET.get('value', '')
# if attr == 'user':
# contact_list = User.objects.filter(name__icontains=value)
# elif attr == "user_group":
# contact_list = UserGroup.objects.filter(name__icontains=value)
# elif attr == "asset":
# contact_list = Asset.objects.filter(ip__icontains=value)
# elif attr == "asset":
# contact_list = BisGroup.objects.filter(name__icontains=value)
#
# return render_to_response('filter_ajax_api.html', locals())
#
#
# def install(request):
# from juser.models import DEPT, User
# if User.objects.filter(id=5000):
# return http_error(request, 'Jumpserver已初始化不能重复安装')
#
# dept = DEPT(id=1, name="超管部", comment="超级管理部门")
# dept.save()
# dept2 = DEPT(id=2, name="默认", comment="默认部门")
# dept2.save()
# IDC(id=1, name="默认", comment="默认IDC").save()
# BisGroup(id=1, name="ALL", dept=dept, comment="所有主机组").save()
#
# User(id=5000, username="admin", password=PyCrypt.md5_crypt('admin'),
# name='admin', email='admin@jumpserver.org', role='SU', is_active=True, dept=dept).save()
# return http_success(request, u'Jumpserver初始化成功')
#
#
# def download(request):
# return render_to_response('download.html', locals(), context_instance=RequestContext(request))
#
#
# def transfer(sftp, filenames):
# # pool = Pool(processes=5)
# for filename, file_path in filenames.items():
# print filename, file_path
# sftp.put(file_path, '/tmp/%s' % filename)
# # pool.apply_async(transfer, (sftp, file_path, '/tmp/%s' % filename))
# sftp.close()
# # pool.close()
# # pool.join()
#
#
# def upload(request):
# pass
# # user, dept = get_session_user_dept(request)
# # if request.method == 'POST':
# # hosts = request.POST.get('hosts')
# # upload_files = request.FILES.getlist('file[]', None)
# # upload_dir = "/tmp/%s" % user.username
# # is_dir(upload_dir)
# # date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
# # hosts_list = hosts.split(',')
# # user_hosts = [asset.ip for asset in user.get_asset()]
# # unperm_hosts = []
# # filenames = {}
# # for ip in hosts_list:
# # if ip not in user_hosts:
# # unperm_hosts.append(ip)
# #
# # if not hosts:
# # return HttpResponseNotFound(u'地址不能为空')
# #
# # if unperm_hosts:
# # print hosts_list
# # return HttpResponseNotFound(u'%s 没有权限.' % ', '.join(unperm_hosts))
# #
# # for upload_file in upload_files:
# # file_path = '%s/%s.%s' % (upload_dir, upload_file.name, date_now)
# # filenames[upload_file.name] = file_path
# # f = open(file_path, 'w')
# # for chunk in upload_file.chunks():
# # f.write(chunk)
# # f.close()
# #
# # sftps = []
# # for host in hosts_list:
# # username, password, host, port = get_connect_item(user.username, host)
# # try:
# # t = paramiko.Transport((host, port))
# # t.connect(username=username, password=password)
# # sftp = paramiko.SFTPClient.from_transport(t)
# # sftps.append(sftp)
# # except paramiko.AuthenticationException:
# # return HttpResponseNotFound(u'%s 连接失败.' % host)
# #
# # # pool = Pool(processes=5)
# # for sftp in sftps:
# # transfer(sftp, filenames)
# # # pool.close()
# # # pool.join()
# # return HttpResponse('传送成功')
# #
# # return render_to_response('upload.html', locals(), context_instance=RequestContext(request))
#
#
# def node_auth(request):
# username = request.POST.get('username', ' ')
# seed = request.POST.get('seed', ' ')
# filename = request.POST.get('filename', ' ')
# user = User.objects.filter(username=username, password=seed)
# auth = 1
# if not user:
# auth = 0
# if not filename.startswith('/opt/jumpserver/logs/connect/'):
# auth = 0
# if auth:
# result = {'auth': {'username': username, 'result': 'success'}}
# else:
# result = {'auth': {'username': username, 'result': 'failed'}}
#
# return HttpResponse(json.dumps(result, sort_keys=True, indent=2), content_type='application/json')
####################### liuzheng's test(start) ########################
from django.contrib.auth.decorators import login_required
from juser.models import Document
@login_required(login_url='/login')
def upload(request):
if request.method == 'GET':
machines = [{'name':'aaa'}]
return render_to_response('upload.html', locals(), context_instance=RequestContext(request))
elif request.method == 'POST':
user = request.user
assets = get_group_user_perm(user).get('asset').keys()
asset_select = []
if request.method == 'POST':
remote_ip = request.META.get('REMOTE_ADDR')
asset_ids = request.POST.getlist('asset_ids', '')
upload_files = request.FILES.getlist('file[]', None)
for file in upload_files:
print file
newdoc = Document(docfile=file, user_id=request.user.id)
newdoc.save()
return HttpResponse("success")
else:
return HttpResponse("ERROR")
date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
upload_dir = get_tmp_dir()
# file_dict = {}
for asset_id in asset_ids:
asset_select.append(get_object(Asset, id=asset_id))
if not set(asset_select).issubset(set(assets)):
illegal_asset = set(asset_select).issubset(set(assets))
return HttpResponse('没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset]))
for upload_file in upload_files:
file_path = '%s/%s' % (upload_dir, upload_file.name)
with open(file_path, 'w') as f:
for chunk in upload_file.chunks():
f.write(chunk)
res = gen_resource({'user': user, 'asset': asset_select})
runner = MyRunner(res)
runner.run('copy', module_args='src=%s dest=%s directory_mode'
% (upload_dir, upload_dir), pattern='*')
ret = runner.results
logger.debug(ret)
FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]),
filename=' '.join([f.name for f in upload_files]), type='upload', remote_ip=remote_ip,
result=ret).save()
if ret.get('failed'):
error = u'上传目录: %s <br> 上传失败: [ %s ] <br>上传成功 [ %s ]' % (upload_dir,
', '.join(ret.get('failed').keys()),
', '.join(ret.get('ok').keys()))
return HttpResponse(error, status=500)
msg = u'上传目录: %s <br> 传送成功 [ %s ]' % (upload_dir, ', '.join(ret.get('ok').keys()))
return HttpResponse(msg)
return my_render('upload.html', locals(), request)
@login_required(login_url='/login')
def download(request):
documents = []
for doc in Document.objects.filter(user_id=request.user.id).all():
documents.append('/'.join(str(doc.docfile).split('/')[2:]))
user = request.user
assets = get_group_user_perm(user).get('asset').keys()
asset_select = []
if request.method == 'POST':
remote_ip = request.META.get('REMOTE_ADDR')
asset_ids = request.POST.getlist('asset_ids', '')
file_path = request.POST.get('file_path')
date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
upload_dir = get_tmp_dir()
for asset_id in asset_ids:
asset_select.append(get_object(Asset, id=asset_id))
if not set(asset_select).issubset(set(assets)):
illegal_asset = set(asset_select).issubset(set(assets))
return HttpResponse(u'没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset]))
res = gen_resource({'user': user, 'asset': asset_select})
runner = MyRunner(res)
runner.run('fetch', module_args='src=%s dest=%s' % (file_path, upload_dir), pattern='*')
FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]),
filename=file_path, type='download', remote_ip=remote_ip, result=runner.results).save()
logger.debug(runner.results)
os.chdir('/tmp')
tmp_dir_name = os.path.basename(upload_dir)
tar_file = '%s.tar.gz' % upload_dir
bash('tar czf %s %s' % (tar_file, tmp_dir_name))
f = open(tar_file)
data = f.read()
f.close()
response = HttpResponse(data, content_type='application/octet-stream')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(tar_file)
return response
return render_to_response('download.html', locals(), context_instance=RequestContext(request))
def download_file(request, path):
# TODO: get downlode file and make sure it is exist!
# by liuzheng
filepath = 'upload/' + str(request.user.id)+'/'+path
return HttpResponse(filepath)
def node_auth(request):
return HttpResponse('nothing')
def httperror(request):
return HttpResponse('nothing')
def base(request):
return HttpResponse('nothing')
def install(request):
return HttpResponse('nothing')
####################### liuzheng's test(end) ########################
@login_required(login_url='/login')
def exec_cmd(request):
role = request.GET.get('role')
check_assets = request.GET.get('check_assets', '')
web_terminal_uri = 'ws://%s/exec?role=%s' % (WEB_SOCKET_HOST, role)
return my_render('exec_cmd.html', locals(), request)

View File

@ -25,9 +25,6 @@ urlpatterns = patterns('juser.views',
(r'^forget_password/$', forget_password),
(r'^change_info/$', 'change_info'),
(r'^change_role/$', 'chg_role'),
(r'^regen_ssh_key/$', 'regen_ssh_key'),
(r'^down_key/$', 'down_key'),
(r'runcommand/$', 'RunCommand'),
)

View File

@ -129,8 +129,8 @@ def gen_ssh_key(username, password='',
生成一个用户ssh密钥对
"""
logger.debug('生成ssh key 并设置authorized_keys')
private_key_file = os.path.join(key_dir, username)
mkdir(key_dir, mode=777)
private_key_file = os.path.join(key_dir, username+'.pem')
mkdir(key_dir, mode=0700)
if os.path.isfile(private_key_file):
os.unlink(private_key_file)
ret = bash('echo -e "y\n"|ssh-keygen -t rsa -f %s -b %s -P "%s"' % (private_key_file, length, password))
@ -166,7 +166,7 @@ def user_add_mail(user, kwargs):
mail_msg = u"""
Hi, %s
您的用户名 %s
您的角色 %s
您的权限 %s
您的web登录密码 %s
您的ssh密钥文件密码 %s
密钥下载地址 %s/juser/down_key/?uuid=%s
@ -195,9 +195,9 @@ def get_display_msg(user, password, ssh_key_pwd, ssh_key_login_need, send_mail_n
用户名%s
密码%s
密钥密码%s
密钥下载url: %s/juser/down_key/?id=%s
密钥下载url: %s/juser/down_key/?uuid=%s
该账号密码可以登陆web和跳板机
""" % (URL, user.username, password, ssh_key_pwd, URL, user.id)
""" % (URL, user.username, password, ssh_key_pwd, URL, user.uuid)
else:
msg = u"""
跳板机地址 %s \n

View File

@ -4,23 +4,15 @@
# import random
# from Crypto.PublicKey import RSA
import uuid as uuid_r
import uuid
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from juser.user_api import *
from jperm.perm_api import get_group_user_perm
MAIL_FROM = EMAIL_HOST_USER
@login_required(login_url='/login')
def chg_role(request):
role = {'SU': 2, 'GA': 1, 'CU': 0}
if request.session['role_id'] > 0:
request.session['role_id'] = 0
elif request.session['role_id'] == 0:
request.session['role_id'] = role.get(request.user.role, 0)
return HttpResponseRedirect('/')
@require_role(role='super')
def group_add(request):
@ -96,8 +88,8 @@ def group_edit(request):
if request.method == 'GET':
group_id = request.GET.get('id', '')
# user_group = get_object(UserGroup, id=group_id)
user_group = UserGroup.objects.get(id=group_id)
user_group = get_object(UserGroup, id=group_id)
# user_group = UserGroup.objects.get(id=group_id)
users_selected = User.objects.filter(group=user_group)
users_remain = User.objects.filter(~Q(group=user_group))
users_all = User.objects.all()
@ -126,8 +118,9 @@ def group_edit(request):
if g == user_group:
continue
user.group.add(g)
user_group.name = group_name
user_group.comment = comment
user_group.save()
except ServerError, e:
error = e
if not error:
@ -140,7 +133,6 @@ def group_edit(request):
return my_render('juser/group_edit.html', locals(), request)
@login_required(login_url='/login')
@require_role(role='super')
def user_add(request):
error = ''
@ -157,11 +149,11 @@ def user_add(request):
groups = request.POST.getlist('groups', [])
admin_groups = request.POST.getlist('admin_groups', [])
role = request.POST.get('role', 'CU')
uuid = uuid_r.uuid1()
uuid_r = uuid.uuid4().get_hex()
ssh_key_pwd = PyCrypt.gen_rand_pass(16)
extra = request.POST.getlist('extra', [])
is_active = False if '0' in extra else True
ssh_key_login_need = True if '1' in extra else False
ssh_key_login_need = True
send_mail_need = True if '2' in extra else False
try:
@ -179,7 +171,7 @@ def user_add(request):
try:
user = db_add_user(username=username, name=name,
password=password,
email=email, role=role, uuid=uuid,
email=email, role=role, uuid=uuid_r,
groups=groups, admin_groups=admin_groups,
ssh_key_pwd=ssh_key_pwd,
is_active=is_active,
@ -190,7 +182,7 @@ def user_add(request):
user_groups = []
for user_group_id in groups:
user_groups.extend(UserGroup.objects.filter(id=user_group_id))
print user_groups
except IndexError, e:
error = u'添加用户 %s 失败 %s ' % (username, e)
try:
@ -230,24 +222,20 @@ def user_list(request):
@require_role(role='user')
def user_detail(request):
header_title, path1, path2 = '用户详情', '用户管理', '用户详情'
# if request.session.get('role_id') == 0:
# user_id = request.user.id
# else:
# user_id = request.GET.get('id', '')
# if request.session.get('role_id') == 1:
# user, dept = get_session_user_dept(request)
# if not validate(request, user=[user_id]):
# return HttpResponseRedirect('/')
user_id = request.GET.get('id', '')
if not user_id:
if request.session.get('role_id') == 0:
user_id = request.user.id
else:
user_id = request.GET.get('id', '')
user = get_object(User, id=user_id)
if not user:
return HttpResponseRedirect('/juser/user_list/')
user = User.objects.get(id=user_id)
# if user:
# pass
# asset_group_permed = user.get_asset_group()
# logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10]
# logs_all = Log.objects.filter(user=user.name).order_by('-start_time')
# logs_num = len(logs_all)
user_perm_info = get_group_user_perm(user)
role_assets = user_perm_info.get('role')
user_log_ten = Log.objects.filter(user=user.username).order_by('id')[0:10]
user_log_last = Log.objects.filter(user=user.username).order_by('id')[0:50]
user_log_last_num = len(user_log_last)
return my_render('juser/user_detail.html', locals(), request)
@ -262,21 +250,20 @@ def user_del(request):
user_id_list = user_ids.split(',')
else:
return HttpResponse('错误请求')
for user_id in user_id_list:
user = get_object(User, id=user_id)
if user:
# TODO: annotation by liuzheng, because useless for me
# assets = user_permed(user)
# result = _public_perm_api({'type': 'del_user', 'user': user, 'asset': assets})
# print result
if user and user.username != 'admin':
logger.debug(u"删除用户 %s " % user.username)
bash('userdel -r %s' % user.username)
user.delete()
return HttpResponse('删除成功')
@require_role('admin')
def send_mail_retry(request):
user_uuid = request.GET.get('uuid', '1')
user = get_object(User, uuid=user_uuid)
uuid_r = request.GET.get('uuid', '1')
user = get_object(User, uuid=uuid_r)
msg = u"""
跳板机地址 %s
用户名%s
@ -305,36 +292,38 @@ def forget_password(request):
""" % (user.name, URL, user.uuid, timestamp, hash_encode)
send_mail('忘记跳板机密码', msg, MAIL_FROM, [email], fail_silently=False)
msg = u'请登陆邮箱,点击邮件重设密码'
return HttpResponse(msg)
return http_success(request, msg)
else:
error = u'用户不存在或邮件地址错误'
return render_to_response('juser/forget_password.html', locals())
@require_role('user')
def reset_password(request):
uuid = request.GET.get('uuid', '')
uuid_r = request.GET.get('uuid', '')
timestamp = request.GET.get('timestamp', '')
hash_encode = request.GET.get('hash', '')
action = '/juser/reset_password/?uuid=%s&timestamp=%s&hash=%s' % (uuid, timestamp, hash_encode)
action = '/juser/reset_password/?uuid=%s&timestamp=%s&hash=%s' % (uuid_r, timestamp, hash_encode)
if request.method == 'POST':
password = request.POST.get('password')
password_confirm = request.POST.get('password_confirm')
print password, password_confirm
if password != password_confirm:
return HttpResponse('密码不匹配')
else:
user = get_object(User, uuid=uuid)
user = get_object(User, uuid=uuid_r)
if user:
user.password = PyCrypt.md5_crypt(password)
user.save()
return HttpResponse('密码重设成功')
return http_success(request, u'密码重设成功')
else:
return HttpResponse('用户不存在')
if hash_encode == PyCrypt.md5_crypt(uuid + timestamp + KEY):
if hash_encode == PyCrypt.md5_crypt(uuid_r + timestamp + KEY):
if int(time.time()) - int(timestamp) > 600:
return HttpResponse('链接已超时')
return http_error(request, u'链接已超时')
else:
return render_to_response('juser/reset_password.html', locals())
@ -401,24 +390,15 @@ def user_edit(request):
send_mail('您的信息已修改', msg, MAIL_FROM, [email], fail_silently=False)
return HttpResponseRedirect('/juser/user_list/')
return my_render('juser/user_edit.html', locals(), request)
# @require_role(role='admin')
def user_edit_adm(request):
pass
def profile(request):
a = request.user.id
a = request.user.groups
user_id = request.user.id
if not user_id:
return HttpResponseRedirect('/')
user = User.objects.get(id=user_id)
return render_to_response('juser/profile.html', locals(), context_instance=RequestContext(request))
return my_render('juser/profile.html', locals(), request)
def change_info(request):
@ -436,26 +416,24 @@ def change_info(request):
if '' in [name, email]:
error = '不能为空'
if len(password) > 0 and len(password) < 6:
if len(password) < 6:
error = '密码须大于6位'
if not error:
# if password != user.password:
# password = CRYPTOR.md5_crypt(password)
User.objects.filter(id=user_id).update(name=name, email=email)
if len(password) > 0:
user.set_password(password)
user.save()
msg = '修改成功'
return render_to_response('juser/change_info.html', locals(), context_instance=RequestContext(request))
return my_render('juser/change_info.html', locals(), request)
@require_role(role='user')
def regen_ssh_key(request):
uuid = request.GET.get('uuid', '')
user = get_object(User, uuid=uuid)
uuid_r = request.GET.get('uuid', '')
user = get_object(User, uuid=uuid_r)
if not user:
return HttpResponse('没有该用户')
@ -467,18 +445,17 @@ def regen_ssh_key(request):
@require_role(role='user')
def down_key(request):
user_id = ''
if is_role_request(request, 'super'):
user_id = request.GET.get('id')
uuid_r = request.GET.get('uuid', '')
else:
uuid_r = request.user.uuid
if is_role_request(request, 'user'):
user_id = request.user.id
if user_id:
user = get_object(User, id=user_id)
if uuid_r:
user = get_object(User, uuid=uuid_r)
if user:
username = user.username
private_key_file = os.path.join(KEY_DIR, 'user', username)
private_key_file = os.path.join(KEY_DIR, 'user', username+'.pem')
print private_key_file
if os.path.isfile(private_key_file):
f = open(private_key_file)
data = f.read()
@ -486,13 +463,5 @@ def down_key(request):
response = HttpResponse(data, content_type='application/octet-stream')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(private_key_file)
return response
return HttpResponse('No Key File. Contact Admin.')
from jperm.perm_api import get_group_user_perm
@require_role(role='user')
def RunCommand(request):
if request.method == 'GET':
GUP = get_group_user_perm(request.user)
print GUP
assets = GUP.get('asset')
return render_to_response('juser/run_command.html', locals(), context_instance=RequestContext(request))

View File

@ -8,7 +8,7 @@ import sys
import os.path
import threading
import datetime
import urllib
import re
import tornado.ioloop
import tornado.options
@ -23,8 +23,8 @@ from tornado.options import define, options
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier
import select
from connect import Tty, User, Asset, PermRole, logger, get_object
from connect import TtyLog, Log, Session, user_have_perm
from connect import Tty, User, Asset, PermRole, logger, get_object, PermRole, gen_resource
from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, MyRunner, ExecLog
try:
import simplejson as json
@ -67,22 +67,6 @@ def require_auth(role='user'):
except AttributeError:
pass
logger.warning('Websocket: Request auth failed.')
# asset_id = int(request.get_argument('id', 9999))
# print asset_id
# asset = Asset.objects.filter(id=asset_id)
# if asset:
# asset = asset[0]
# request.asset = asset
# else:
# request.close()
#
# if user:
# user = user[0]
# request.user = user
#
# else:
# print("No session user.")
# request.close()
return _deco2
return _deco
@ -138,6 +122,7 @@ class Application(tornado.web.Application):
(r'/monitor', MonitorHandler),
(r'/terminal', WebTerminalHandler),
(r'/kill', WebTerminalKillHandler),
(r'/exec', ExecHandler),
]
setting = {
@ -206,7 +191,6 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
class WebTty(Tty):
def __init__(self, *args, **kwargs):
super(WebTty, self).__init__(*args, **kwargs)
self.login_type = 'web'
self.ws = None
self.data = ''
self.input_mode = False
@ -225,6 +209,82 @@ class WebTerminalKillHandler(tornado.web.RequestHandler):
logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients))
class ExecHandler(tornado.websocket.WebSocketHandler):
clients = []
tasks = []
def __init__(self, *args, **kwargs):
self.id = 0
self.user = None
self.role = None
self.runner = None
self.assets = []
self.perm = {}
self.remote_ip = ''
super(ExecHandler, self).__init__(*args, **kwargs)
def check_origin(self, origin):
return True
@require_auth('user')
def open(self):
logger.debug('Websocket: Open exec request')
role_name = self.get_argument('role', 'sb')
self.remote_ip = self.request.remote_ip
logger.debug('Web执行命令: 请求角色 %s' % role_name)
self.role = get_object(PermRole, name=role_name)
self.perm = get_group_user_perm(self.user)
roles = self.perm.get('role').keys()
if self.role not in roles:
self.write_message('No perm that role %s' % role_name)
self.close()
self.assets = self.perm.get('role').get(self.role).get('asset')
res = gen_resource({'user': self.user, 'asset': self.assets, 'role': self.role})
self.runner = MyRunner(res)
message = '有权限的主机: ' + ', '.join([asset.hostname for asset in self.assets])
self.__class__.clients.append(self)
self.write_message(message)
def on_message(self, message):
data = json.loads(message)
pattern = data.get('pattern', '')
command = data.get('command', '')
asset_name_str = ''
if pattern and command:
for inv in self.runner.inventory.get_hosts(pattern=pattern):
asset_name_str += '%s ' % inv.name
self.write_message('匹配主机: ' + asset_name_str)
self.write_message('<span style="color: yellow">Ansible> %s</span>\n\n' % command)
self.__class__.tasks.append(MyThread(target=self.run_cmd, args=(command, pattern)))
ExecLog(host=asset_name_str, cmd=command, user=self.user.username, remote_ip=self.remote_ip).save()
for t in self.__class__.tasks:
if t.is_alive():
continue
try:
t.setDaemon(True)
t.start()
except RuntimeError:
pass
def run_cmd(self, command, pattern):
self.runner.run('shell', command, pattern=pattern)
for k, v in self.runner.results.items():
for host, output in v.items():
if k == 'ok':
header = "<span style='color: green'>[ %s => %s]</span>\n" % (host, 'Ok')
else:
header = "<span style='color: red'>[ %s => %s]</span>\n" % (host, 'failed')
self.write_message(header)
self.write_message(output)
self.write_message('\n~o~ Task finished ~o~\n')
def on_close(self):
logger.debug('关闭web_exec请求')
class WebTerminalHandler(tornado.websocket.WebSocketHandler):
clients = []
tasks = []
@ -236,6 +296,8 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
self.log = None
self.id = 0
self.user = None
self.ssh = None
self.channel = None
super(WebTerminalHandler, self).__init__(*args, **kwargs)
def check_origin(self, origin):
@ -250,7 +312,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
if asset:
roles = user_have_perm(self.user, asset)
logger.debug(roles)
logger.debug('rolename: %s' % role_name)
logger.debug('角色: %s' % role_name)
login_role = ''
for role in roles:
if role.name == role_name:
@ -267,10 +329,10 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
return
logger.debug('Websocket: request web terminal Host: %s User: %s Role: %s' % (asset.hostname, self.user.username,
login_role.name))
self.term = WebTty(self.user, asset, login_role)
self.term = WebTty(self.user, asset, login_role, login_type='web')
self.term.remote_ip = self.request.remote_ip
self.term.get_connection()
self.term.channel = self.term.ssh.invoke_shell(term='xterm')
self.ssh = self.term.get_connection()
self.channel = self.ssh.invoke_shell(term='xterm')
WebTerminalHandler.tasks.append(MyThread(target=self.forward_outbound))
WebTerminalHandler.clients.append(self)
@ -303,7 +365,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
self.term.vim_data = ''
self.term.data = ''
self.term.input_mode = False
self.term.channel.send(data['data'])
self.channel.send(data['data'])
def on_close(self):
logger.debug('Websocket: Close request')
@ -326,9 +388,9 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
data = ''
pre_timestamp = time.time()
while True:
r, w, e = select.select([self.term.channel, sys.stdin], [], [])
if self.term.channel in r:
recv = self.term.channel.recv(1024)
r, w, e = select.select([self.channel, sys.stdin], [], [])
if self.channel in r:
recv = self.channel.recv(1024)
if not len(recv):
return
data += recv
@ -347,8 +409,8 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
data = ''
except UnicodeDecodeError:
pass
finally:
self.close()
except IndexError:
pass
if __name__ == '__main__':
tornado.options.parse_command_line()

View File

@ -2822,7 +2822,9 @@ body.body-small .footer.fixed {
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td {
border-top: 1px solid #e7eaec;
/*border-top: 1px solid #e7eaec;*/
border-bottom: 1px solid #e7eaec;
border-top: none;
line-height: 1.42857;
padding: 8px;
vertical-align: top;

Binary file not shown.

View File

@ -16,9 +16,9 @@ function check_all(form) {
}
}
function checkAll(){
var checklist = document.getElementsByName ("checked");
if(document.getElementById("check_all").checked)
function checkAll(id, name){
var checklist = document.getElementsByName(name);
if(document.getElementById(id).checked)
{
for(var i=0;i<checklist.length;i++)
{

View File

@ -2,7 +2,6 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -12,8 +11,6 @@
{% include 'link_css.html' %}
{% include 'head_script.html' %}
{% block self_head_css_js %} {% endblock %}
</head>
<body>

View File

@ -1,11 +1,16 @@
{% extends 'base.html' %}
{% load mytags %}
{% block self_head_css_js %}
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
{% endblock %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeIn">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>下载文件</h5>
@ -16,30 +21,62 @@
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#">Config option 1</a>
</li>
<li><a href="#">Config option 2</a>
</li>
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<h2>下载文件可联系管理员在服务器安装lrzsz使用sz命令下载。 </h2>
<div>
{% for document in documents %}
<a href="/download/{{ document }}">{{ document }}</a>
{% endfor %}
</div>
<form id="downForm" class="form-horizontal" method="post">
{% if error %}
<div class="alert alert-warning text-center">{{ error }}</div>
{% endif %}
{% if msg %}
<div class="alert alert-success text-center">{{ msg }}</div>
{% endif %}
<div class="form-group">
<label for="file_path" class="col-sm-2 control-label">文件路径<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<input id="file_path" name="file_path" placeholder="File Path" type="text" class="form-control">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="user" class="col-sm-2 control-label">选择主机<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<select name="asset_ids" id="asset_ids" data-placeholder="请输入" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset in assets %}
<option value="{{ asset.id }}">{{ asset.hostname }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">取消</button>
<button id="submit_button" class="btn btn-primary" type="submit">下载</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block self_footer_js %}
<script>
var config = {
'.chosen-select' : {},
'.chosen-select-deselect' : {allow_single_deselect:true},
'.chosen-select-no-single' : {disable_search_threshold:10},
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
'.chosen-select-width' : {width:"95%"}
};
for (var selector in config) {
$(selector).chosen(config[selector]);
}
</script>
{% endblock %}

198
templates/exec_cmd.html Normal file
View File

@ -0,0 +1,198 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=320, initial-scale=1">
<title>Jumpserver Exec Terminal</title>
<style type="text/css"></style>
</head>
<body>
<div id="wrapper">
<div id="chat_box" class="content">
<p class="system"></p>
</div>
<div id="footer">
<div class="content">
<input type="text" id="pattern" value="{{ check_assets }}" placeholder="Ansible Pattern">
<input type="text" id="command" placeholder="Command to execute">
<input type="button" id="send_btn" value="Send" onclick="sendMessage()">
</div>
</div>
</div>
<script type="text/javascript">
var wsUri = "{{ web_terminal_uri }}"; //请求的websocket url
var ws = new WebSocket(wsUri);
function createSystemMessage(message) {
var message = document.createTextNode(message);
var messageBox = document.createElement('p');
messageBox.className = 'system';
messageBox.appendChild(message);
var chat = document.getElementById('chat_box');
chat.appendChild(messageBox);
}
function createUserMessage(message) {
message = message.replace('/\n/g', '<br>');
var messageOb = document.createElement('div');
messageOb.innerHTML = message;
var messageBox = document.createElement('p');
messageBox.appendChild(messageOb);
var chat = document.getElementById('chat_box');
chat.appendChild(messageBox);
}
ws.onopen = function(ev) {
createSystemMessage('[Connected]');
};
ws.onclose = function(ev) {
createSystemMessage('[Disconnected]');
};
ws.onmessage = function(ev) {
createUserMessage(ev.data);
var chat = document.getElementById('chat_box');
chat.scrollTop = chat.scrollHeight;
};
function sendMessage() {
var pattern = document.getElementById('pattern');
var command = document.getElementById('command');
var data = {
pattern: pattern.value,
command: command.value,
{# ts: (new Date()).getTime()#}
};
ws.send(JSON.stringify(data));
command.value = ''
}
</script>
<style type="text/css">
* {
font-family: "Monaco", "DejaVu Sans Mono", "Liberation Mono", monospace;
font-size: 11px;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
background-color: #fff;
}
#wrapper {
{# background-color: #ecf0f1;#}
{# border: #000 solid 5px;#}
background: #000;
width: 800px;
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
color: #fff;
}
#chat_box {
box-sizing: border-box;
height: 100%;
overflow: auto;
padding-bottom: 50px;
}
#footer {
box-sizing: border-box;
position: fixed;
bottom: 0;
height: 50px;
width: 800px;
{# border: #000 solid -10px;#}
background-color: #2980b9;
}
#footer .content {
padding-top: 4px;
position: relative;
}
#pattern { width: 25%; }
#command { width: 60%; }
#send_btn {
width: 10%;
{# position: absolute;#}
margin-left: 5px;
right: 0;
bottom: 0;
}
.content {
width: 800px;
margin-left: 5px;
}
input[type="text"],
input[type="button"] {
border: 0;
color: #fff;
}
input[type="text"] {
background-color: #146EA8;
padding: 3px 10px;
}
input[type="button"] {
background-color: #f39c12;
border-right: 2px solid #e67e22;
border-bottom: 2px solid #e67e22;
min-width: 70px;
display: inline-block;
}
input[type="button"]:hover {
background-color: #e67e22;
border-right: 2px solid #f39c12;
border-bottom: 2px solid #f39c12;
cursor: pointer;
}
.system,
.username {
color: #aaa;
font-style: italic;
font-family: monospace;
font-size: 16px;
}
@media(max-width: 1000px) {
.content { width: 90%; }
}
@media(max-width: 780px) {
#footer { height: 91px; }
#chat_box { padding-bottom: 91px; }
#user { width: 100%; }
#message { width: 80%; }
}
@media(max-width: 400px) {
#footer { height: 135px; }
#chat_box { padding-bottom: 135px; }
#message { width: 100%; }
#send_btn {
position: relative;
margin-top: 3px;
width: 100%;
}
}
</style>
</body>
<div></div>
<div></div>
</html>

View File

@ -1,6 +1,6 @@
<div class="footer fixed">
<div class="pull-right">
Version <strong>0.2.0</strong> GPL.
Version <strong>0.3.0</strong> GPL.
</div>
<div>
<strong>Copyright</strong> Jumpserver.org Team &copy; 2014-2015

View File

@ -6,7 +6,7 @@
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-3">
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-success pull-right">Users</span>
@ -18,7 +18,7 @@
</div>
</div>
</div>
<div class="col-lg-3">
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-info pull-right">Hosts</span>
@ -31,7 +31,7 @@
</div>
</div>
<div class="col-lg-3">
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary pull-right">Online</span>
@ -45,7 +45,7 @@
</div>
</div>
<div class="col-lg-3">
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-danger pull-right">Connected</span>
@ -74,7 +74,7 @@
</ul>
</div>
<div class="col-sm-7" id="top10" style="margin-left: -15px;height: 346px;padding: 15px 0 15px 0;"></div>
<div class="col-lg-3 white-bg" id="top1" style="margin-left: -15px;height: 346px">
<div class="col-sm-3 white-bg" id="top1" style="margin-left: -15px;height: 346px">
<div class="statistic-box">
<h4>
活跃用户资产占比
@ -83,13 +83,13 @@
以下图形分别描述一个月活跃用户和资产占所有用户主机的百分比
</p>
<div class="row text-center">
<div class="col-lg-6">
<div class="col-sm-6">
<div id="activeUser" style="width: 140px; height: 140px;">
</div>
<h5>用户</h5>
</div>
<div class="col-lg-6">
<div class="col-sm-6">
<div id="activeAsset" style="width: 140px; height: 140px;"></div>
<h5>主机</h5>
</div>
@ -103,7 +103,7 @@
<br/>
<div class="row">
<div class="col-lg-4">
<div class="col-sm-4">
{# <div class="ibox float-e-margins">#}
{# <div class="ibox-title">#}
{# <h5>权限申请</h5>#}
@ -192,7 +192,7 @@
</div>
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>最近十次登录</h5>
@ -258,7 +258,7 @@
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>一周Top10用户</h5>

View File

@ -5,200 +5,87 @@
<div class="wrapper wrapper-content" xmlns="http://www.w3.org/1999/html">
<div class="row">
<div class="col-lg-8">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 使用说明 </h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
<div class="col-sm-8">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 使用说明 </h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
</div>
<div class="ibox-content" style="line-height: 26px">
<span style="font-size: large"></span>迎使用<span class="text-navy"><b>Jumpserver</b></span>跳板机系统,
首先需要 <b><a href="/juser/down_key/?id={{ user.id }}">下载</a></b> 登录跳板机的SSH密钥文件然后导入到工具或者ssh命令指定密钥文件(确保密钥文件权限600),输入收到的密钥密码,登录跳板机。
登录后根据提示进行操作。跳板机web界面支持修改密码、个人信息和上传下载文件等功能可以向管理员申请权限。
<div class="ibox-content" style="line-height: 26px">
<span style="font-size: large"></span>迎使用<span class="text-navy"><b>Jumpserver</b></span>跳板机系统,
首先需要 <b><a href="/juser/down_key/?id={{ user.id }}">下载</a></b> 登录跳板机的SSH密钥文件然后导入到工具或者ssh命令指定密钥文件(确保密钥文件权限600),输入收到的密钥密码,登录跳板机。
登录后根据提示进行操作。跳板机web界面支持修改密码、个人信息和上传下载文件等功能可以向管理员申请权限。
</div>
</div>
</div>
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 已授权主机 </h5>
<div class="ibox-tools">
<div class="label label-primary float-left"><b>总共:{{ host_count }}</b></div>
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<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>
</div>
</div>
<div class="ibox-content">
<form id="asset_form">
<div class="col-sm-2" style="padding-left: 0px">
<label>
<select name="idc" class="form-control m-b" onchange="change_info()">
<option value="">IDC机房</option>
{% for idc in idc_all %}
{% ifequal idc.name idc_name %}
<option value="{{ idc.name }}" selected> {{ idc.name }}</option>
{% else %}
<option value="{{ idc.name }}"> {{ idc.name }}</option>
{% endifequal %}
{% endfor %}
</select>
</label>
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>登录记录</h5>
<div class="ibox-tools">
<span class="label label-warning-light">最近登录</span>
</div>
</div>
<div class="col-sm-2">
<label>
<select name="group" class="form-control m-b" onchange="change_info()">
<option value="">主机组</option>
{% for asset_group in asset_group_all %}
{% ifequal asset_group.name group_name %}
<option value="{{ asset_group.name }}"
selected> {{ asset_group.name }} </option>
{% else %}
<option value="{{ asset_group.name }}"> {{ asset_group.name }} </option>
{% endifequal %}
<div class="ibox-content">
<div id="last">
<div class="feed-activity-list" >
{% for log in logs_last %}
<div class="feed-element">
<a href="profile.html" class="pull-left">
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
</a>
<div class="media-body ">
{# <small class="pull-right">{{ log.start_time|time_delta }}</small>#}
<small class="pull-right">{{ log.start_time }}</small>
<strong>{{ log.user }}</strong> 登录了 <span class="text-navy">{{ log.host }}. </span><br>
<small class="text-muted">{{ log.start_time|date:"Y-m-d H:i:s" }}</small>
</div>
</div>
{% endfor %}
</select>
</label>
</div>
{% if not logs_last %}
(暂无)
{% endif %}
</div>
{% if logs_num > 10 %}
<button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> Show All</button>
{% endif %}
</div>
<div class="col-sm-2">
<label>
<select name="asset_type" class="form-control m-b" onchange="change_info()">
<option value="">所有类型</option>
{% for type in asset_types %}
{% ifequal type.0|int2str asset_type %}
<option value="{{ type.0 }}" selected> {{ type.1 }}</option>
{% else %}
<option value="{{ type.0 }}"> {{ type.1 }}</option>
{% endifequal %}
<div id="all" style="display: none">
<div class="feed-activity-list" >
{% for log in logs_all %}
<div class="feed-element">
<a href="profile.html" class="pull-left">
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
</a>
<div class="media-body ">
<small class="pull-right">{{ log.start_time }}</small>
{# <small class="pull-right">{{ log.start_time|time_delta }}</small>#}
<strong>{{ log.user }}</strong> 登录了 <span class="text-navy">{{ log.host }}. </span><br>
<small class="text-muted">{{ log.start_time|date:"Y-m-d H:i:s" }}</small>
</div>
</div>
{% endfor %}
</select>
</label>
</div>
<div class="col-sm-2">
<label>
<select name="status" class="form-control m-b" onchange="change_info()">
<option value="">所有状态</option>
{% for s in asset_status %}
{% ifequal s.0|int2str status %}
<option value="{{ s.0 }}" selected> {{ s.1 }}</option>
{% else %}
<option value="{{ s.0 }}"> {{ s.1 }}</option>
{% endifequal %}
{% endfor %}
</select>
</label>
</div>
<div class="col-sm-4">
<div class="input-group">
<input type="text" class="form-control m-b" id="search_input" name="keyword"
value="{{ keyword }}" placeholder="Search">
<input type="text" style="display: none">
<div class="input-group-btn">
<button id='search_btn' href="/jasset/asset_list/?search=true" type="button"
class="btn btn-xm btn-primary search-btn" onclick="change_info()">
- 搜索 -
</button>
<button type="button" href="/jasset/asset_list/?export=true" name="export"
class="btn btn-xm btn-success search-btn-excel" onclick="return false">
- 导出 -
</button>
</div>
</div>
</div>
<div id="export"></div>
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
<thead>
<tr>
<th class="text-center">
<input id="checkall" type="checkbox" class="i-checks" name="checkall"
value="checkall" data-editable='false' onclick="check_all('asset_form')">
</th>
<th class="text-center"> 主机名</th>
<th class="text-center" name="ip"> IP地址</th>
<th class="text-center"> IDC</th>
<th class="text-center"> 所属主机组</th>
{# <th class="text-center"> 配置信息 </th>#}
<th class="text-center"> 操作系统</th>
<th class="text-center"> 使用默认管理</th>
<th class="text-center"> 操作</th>
</tr>
</thead>
<tbody>
{% for asset in assets %}
<tr class="gradeX">
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
</td>
<td class="text-center"> {{ asset.hostname|default_if_none:"" }} </td>
<td class="text-center"> {{ asset.ip|default_if_none:"" }} </td>
<td class="text-center"> {{ asset.idc.name|default_if_none:"" }} </td>
<td class="text-center">{{ asset.group.all|group_str2 }}</td>
{# <td class="text-center">{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}</td>#}
<td class="text-center">
{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }}</td>
<td class="text-center"> {{ asset.use_default_auth|bool2str }} </td>
<td class="text-center" data-editable='false'>
<a href="/jasset/asset_detail/?id={{ asset.id }}"
class="btn btn-xs btn-primary">详情</a>
{% ifnotequal session_role_id 0 %}
{% if user.role == 'admin' %}
<a href="/jasset/asset_edit/?id={{ asset.id }}"
class="btn btn-xs btn-info">编辑</a>
<a href="/jasset/asset_update/?id={{ asset.id }}"
class="btn btn-xs btn-info">更新</a>
<a value="/jasset/asset_del/?id={{ asset.id }}"
class="btn btn-xs btn-danger asset_del">删除</a>
{% endif %}
{% endifnotequal %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-sm-6">
{% if user.role == 'admin' %}
<input type="button" id="asset_del" class="btn btn-danger btn-sm" name="del_button"
value="删除"/>
<a value="/jasset/asset_edit_batch/" type="button"
class="btn btn-sm btn-warning iframe">修改</a>
{% endif %}
</div>
{% include 'paginator.html' %}
</div>
</form>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>{{ user.username }}</b></span>

View File

@ -5,7 +5,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 填写资产基本信息 </h5>
@ -76,9 +76,9 @@
<div class="form-group" id="id_port">
<div class="hr-line-dashed"></div>
<label class="col-sm-2 control-label"> 端口<span class="red-fonts">*</span> </label>
<div class="col-sm-8">
<input type="text" placeholder="Port" name="port" class="form-control" value="{{ default_port }}">
</div>
<div class="col-sm-8">
<input type="text" placeholder="Port" name="port" class="form-control" value="{{ default_port }}">
</div>
</div>
<div class="hr-line-dashed"></div>
@ -118,14 +118,23 @@
{% block self_footer_js %}
<script>
$('document').ready(function(){
$('#id_use_default_auth').click(function(){
if ($(this).is(':checked')){
$('#admin_account').css('display', 'none');
}
else {
$('#admin_account').css('display', 'block');
}
})
var check_default = "{{ default_setting.name }}";
console.log(check_default);
if (check_default != 'default'){
$('#id_use_default_auth').attr('disabled', true);
$('#id_use_default_auth').attr('checked', false);
$('#admin_account').css('display', 'block');
} else {
$('#id_use_default_auth').click(function(){
if ($(this).is(':checked')){
$('#admin_account').css('display', 'none');
}
else {
$('#admin_account').css('display', 'block');
}
})
}
});
var required_fields = ["id_hostname", "id_port"];

View File

@ -8,7 +8,7 @@
</style>
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 填写主机基本信息 </h5>

View File

@ -0,0 +1,388 @@
{% extends 'base.html' %}
{% load mytags %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins" id="all">
<div class="ibox-title">
<h5> 主机详细信息列表</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form id="asset_form">
<div class="col-sm-7" style="padding-left: 0px">
<label>
<select name="idc" class="form-control m-b input-sm" onchange="change_info()">
<option value="">机房</option>
{% for idc in idc_all %}
{% ifequal idc.name idc_name %}
<option value="{{idc.name}}" selected> {{ idc.name }}</option>
{% else %}
<option value="{{idc.name}}"> {{ idc.name }}</option>
{% endifequal %}
{% endfor %}
</select>
</label>
<label>
<select name="group" class="form-control m-b input-sm" onchange="change_info()">
<option value="">主机组</option>
{% for asset_group in asset_group_all %}
{% ifequal asset_group.name group_name %}
<option value="{{ asset_group.name }}" selected> {{ asset_group.name }} </option>
{% else %}
<option value="{{ asset_group.name }}"> {{ asset_group.name }} </option>
{% endifequal %}
{% endfor %}
</select>
</label>
<label>
<select name="asset_type" class="form-control m-b input-sm" onchange="change_info()">
<option value="">资产类型</option>
{% for type in asset_types %}
{% ifequal type.0|int2str asset_type %}
<option value="{{ type.0 }}" selected> {{ type.1 }}</option>
{% else %}
<option value="{{ type.0 }}"> {{ type.1 }}</option>
{% endifequal %}
{% endfor %}
</select>
</label>
<label>
<select name="status" class="form-control m-b input-sm" onchange="change_info()">
<option value="">资产状态</option>
{% for s in asset_status %}
{% ifequal s.0|int2str status %}
<option value="{{ s.0 }}" selected> {{ s.1 }}</option>
{% else %}
<option value="{{ s.0 }}"> {{ s.1 }}</option>
{% endifequal %}
{% endfor %}
</select>
</label>
</div>
<div class="col-sm-4" style="padding-right: 0">
<div class="input-group inline-group">
<input type="text" class="form-control m-b input-sm" id="search_input" name="keyword" value="{{ keyword }}" placeholder="Search">
<input type="text" style="display: none">
<div class="input-group-btn">
<button id='search_btn' href="/jasset/asset_list/?search=true" type="button" class="btn btn-sm btn-primary search-btn" onclick="change_info()">
- 搜索 -
</button>
<button type="button" href="/jasset/asset_list/?export=true" name="export" class="btn btn-sm btn-success search-btn-excel" onclick="return false">
- 导出 -
</button>
</div>
</div>
</div>
<div id="export"></div>
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
<thead>
<tr>
<th class="text-center">
<input id="checkall" type="checkbox" class="i-checks" name="checkall" value="checkall" data-editable='false' onclick="check_all('asset_form')">
</th>
<th class="text-center"> 主机名 </th>
<th class="text-center" name="ip"> IP地址 </th>
<th class="text-center"> IDC </th>
<th class="text-center"> 所属主机组 </th>
{# <th class="text-center"> 配置信息 </th>#}
<th class="text-center"> 操作系统 </th>
<th class="text-center"> cpu核数 </th>
<th class="text-center"> 内存 </th>
<th class="text-center"> 硬盘 </th>
<th class="text-center"> 操作 </th>
</tr>
</thead>
<tbody>
{% for asset in assets.object_list %}
<tr class="gradeX">
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
</td>
<td class="text-center hostname"> <a href="/jasset/asset_detail/?id={{ asset.id }}">{{ asset.hostname|default_if_none:"" }}</a></td>
<td class="text-center"> {{ asset.ip|default_if_none:"" }} </td>
<td class="text-center"> {{ asset.idc.name|default_if_none:"" }} </td>
<td class="text-center">{{ asset.group.all|group_str2 }}</td>
{# <td class="text-center">{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}</td>#}
<td class="text-center">{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }}</td>
<td class="text-center"> {{ asset.cpu|get_cpu_core|default_if_none:"" }} </td>
<td class="text-center"> {{ asset.memory }}{% if asset.memory %}G{% endif %}</td>
<td class="text-center"> {{ asset.disk|get_disk_info }}{% if asset.memory %}G{% endif %}</td>
<td class="text-center" data-editable='false'>
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-sm-6">
<input type="button" id="exec_cmd" class="btn btn-sm btn-primary" name="exec_cmd" value="执行命令"/>
</div>
{% include 'paginator.html' %}
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block self_footer_js %}
<script>
$(document).ready(function(){
$('.asset_del').click(function(){
var row = $(this).closest('tr');
if (confirm("确定删除?")) {
$.get(
$(this).attr('value'),
{},
function (data) {
row.remove()
}
)
}
});
$('#exec_cmd').click(function(){
var url = '/jperm/role/get/';
var new_url = '/exec_cmd/?role=';
var check_array = [];
$(".gradeX input:checked").closest('tr').find('.hostname a').each(function() {
check_array.push($(this).text())
});
check_assets = check_array.join(':');
$.ajax({
type: 'GET',
url: url,
data: {},
success: function(data){
var dataArray = data.split(',');
if (dataArray.length == 1 && data != 'error'){
var title = 'Jumpserver Exec Terminal';
layer.open({
type: 2,
title: title,
maxmin: true,
shade: false,
area: ['800px', '600px'],
content: new_url+data+'&check_assets='+check_assets
});
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
} else if (dataArray.length == '1' && data == 'error'){
layer.alert('没有授权角色')
} else {
aUrl = '';
$.each(dataArray, function(index, value){
aUrl += '<a onclick="windowOpenExec(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + '&check_assets=' + check_assets + '>' + value + '</a> '
});
layer.alert(aUrl, {
skin: 'layui-layer-molv',
title: '多个角色,请选择一个连接',
shade: false,
closeBtn: 0
})
}
}
});
return false
});
$('.conn').click(function(){
var url='/jperm/role/get/?id=' + $(this).attr('value');
var href = $(this).attr('href');
var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role=';
var hostname = $(this).closest('tr').find('.hostname a')[0].innerHTML;
$.ajax({
type: 'GET',
url: url,
data: {},
success: function(data){
var dataArray = data.split(',');
if (dataArray.length == 1 && data != 'error'){
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
layer.open({
type: 2,
title: title,
maxmin: true,
shade: false,
area: ['628px', '452px'],
content: new_url+data
});
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
} else if (dataArray.length == '1' && data == 'error'){
layer.alert('没有授权角色')
} else {
aUrl = '';
$.each(dataArray, function(index, value){
aUrl += '<a onclick="windowOpen(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + ' value=' + hostname + '>' + value + '</a> '
});
console.log(aUrl);
layer.alert(aUrl, {
skin: 'layui-layer-molv',
title: '多个角色,请选择一个连接',
shade: false,
closeBtn: 0
})
}
}
});
return false
});
});
function windowOpen(a){
var new_url = $(a).attr('href');
var hostname = $(a).attr('value');
var title = 'Jumpserver Web Terminal - ' + '<span class="text-info"> '+ hostname +'</span>';
layer.open({
type: 2,
title: title,
maxmin: true,
area: ['628px', '452px'],
shade: false,
content: new_url
});
return false
}
function windowOpenExec(a){
var new_url = $(a).attr('href');
var title = 'Jumpserver Exec Terminal';
layer.open({
type: 2,
title: title,
maxmin: true,
area: ['800px', '600px'],
shade: false,
content: new_url
});
console.log(new_url);
return false
}
$(".iframe").on('click', function(){
var asset_id_all = getIDall();
if (asset_id_all == ''){
alert("请至少选择一行!");
return false;
}
var url= $(this).attr("value") + '?asset_id_all=' + asset_id_all;
layer.open({
type: 2,
title: 'JumpServer - 批量修改主机',
maxmin: true,
shift: 'top',
border: [2, 0.3, '#1AB394'],
shade: [0.5, '#000000'],
area: ['800px', '600px'],
shadeClose: true,
content: url,
cancel: function(){
location.replace(location.href);
}
});
});
$('.search-btn-excel').unbind('click').bind('click',function(){
var url= $(this).attr("href");
$.ajax({
type: "GET",
url: url,
data: $("#asset_form").serialize(),
success: function (data) {
$("#export").html(data);
}
});
});
$('#asset_del').click(function () {
var asset_id_all = getIDall();
if (asset_id_all == ''){
alert("请至少选择一行!");
return false;
}
if (confirm("确定删除?")) {
$.ajax({
type: "post",
data: {asset_id_all: asset_id_all},
url: "/jasset/asset_del/?arg=batch",
success: function () {
parent.location.reload();
}
});
}
});
$('#asset_update').click(function () {
var asset_id_all = getIDall();
if (asset_id_all == ''){
if (confirm("更新全部资产信息?")) {
layer.msg('玩命更新中...', {time: 200000});
$.ajax({
type: "post",
url: "/jasset/asset_update_batch/?arg=all",
success: function () {
parent.location.reload();
}
});
}
}
else {
layer.msg('玩命更新中...', {time: 200000});
$.ajax({
type: "post",
data: {asset_id_all: asset_id_all},
url: "/jasset/asset_update_batch/",
success: function () {
parent.location.reload();
}
});
}
});
{# $('#asset_update_all').click(function () {#}
{# layer.msg('玩命更新中...', {time: 200000});#}
{# $.ajax({#}
{# type: "post",#}
{# url: "/jasset/asset_update_batch/?arg=all",#}
{# success: function () {#}
{# parent.location.reload();#}
{# }#}
{# });#}
{# });#}
function change_info(){
var args = $("#asset_form").serialize();
window.location = "/jasset/asset_list/?" + args
}
$("#search_input").keydown(function(e){
if(e.keyCode==13){
change_info()
}
});
</script>
{% endblock %}

View File

@ -6,7 +6,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="text text-primary"><b>{{ asset.ip }}</b></span>
@ -97,7 +97,7 @@
</tr>
<tr>
<td class="text-navy">内存</td>
<td>{{ asset.memory|default_if_none:"" }}{% if asset.memory %}M{% endif %}</td>
<td>{{ asset.memory|default_if_none:"" }}{% if asset.memory %}G{% endif %}</td>
</tr>
<tr>
<td class="text-navy">硬盘</td>
@ -129,6 +129,10 @@
<td class="text-navy">系统版本</td>
<td>{{ asset.system_type|default_if_none:"" }} {{ asset.system_version|default_if_none:"" }}</td>
</tr>
<tr>
<td class="text-navy">系统平台</td>
<td>{{ asset.system_arch|default_if_none:"" }}</td>
</tr>
<tr>
<td class="text-navy">运行环境</td>
<td>{{ asset.get_env_display|default_if_none:"" }}</td>
@ -163,7 +167,7 @@
</div>
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>拥有权限的用户</h5>
@ -182,26 +186,67 @@
</div>
</div>
<div class="ibox-content ibox-heading">
<h3>主机所有授权的用户</h3>
<small><i class="fa fa-map-marker"></i> 包含了此主机所有授权的用户.</small>
<h3>主机所有授权的信息</h3>
<small><i class="fa fa-map-marker"></i> 包含了此主机所有授权的信息.</small>
</div>
<div class="ibox-content">
<div>
{# <div class="text-left">#}
{# <table class="table">#}
{# {% if user_permed_list %}#}
{# {% for user in user_permed_list %}#}
{# <tr>#}
{# <td class="text-navy">{{ user.name }}</td>#}
{# <td>{{ user.dept.name }}</td>#}
{# <td><a href="/juser/user_detail/?id={{ user.id }}">详情</a></td>#}
{# </tr>#}
{# {% endfor %}#}
{# {% else %}#}
{# <p class="text-center">(暂无)</p>#}
{# {% endif %}#}
{# </table>#}
{# </div>#}
<div class="text-left">
{% if perm_info %}
{% if user_perm %}
<table class="table">
<p>授权用户信息</p>
<td class="text-navy">授权用户</td>
<td class="text-navy">系统角色</td>
{% for perm in user_perm %}
<tr>
<td class="text-navy"><a href="/juser/user_detail/?id={{ perm.0.id }}">{{ perm.0 }}</a></td>
<td>
<table class="table">
{% if perm.1 %}
{% for role in perm.1 %}
<tr>
<td class="text-navy"><a href="/jperm/role/perm_role_detail/?id={{ role.id }}">{{ role }}</a></td>
</tr>
{% endfor %}
{% endif %}
</table>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% if user_group_perm %}
<table class="table">
<p>授权用户组信息</p>
<td class="text-navy">授权用户组</td>
<td class="text-navy">组详情</td>
{% for user_group in user_group_perm %}
<tr>
<td class="text-navy">{{ user_group }}</td>
<td class="text-navy"><a href="/juser/user_list/?gid={{ user_group.id }}">详情</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
{% if user_rule_perm %}
<table class="table">
<p>授权规则信息</p>
<td class="text-navy">授权规则</td>
<td class="text-navy">详情</td>
{% for rule in user_rule_perm %}
<tr>
<td class="text-navy">{{ rule }}</td>
<td class="text-navy"><a href="/jperm/perm_rule_detail/?id={{ rule.id }}">详情</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
{% else %}
<p class="text-center">(暂无)</p>
{% endif %}
</div>
</div>
</div>
<div class="ibox-title">
@ -248,7 +293,7 @@
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>最近一周登录记录</h5>
@ -271,49 +316,47 @@
<small><i class="fa fa-map-marker"></i> 此主机最近一周用户登录信息.</small>
</div>
<div class="ibox-content inspinia-timeline">
{# {% if log %}#}
{# {% for l in log %}#}
{# <div class="timeline-item">#}
{# <div class="row">#}
{# <div class="col-xs-5 date">#}
{# <i class="fa fa-info-circle"></i>#}
{# <small class="text-navy">{{ l.user }}</small>#}
{# <br/>#}
{# <strong>{{l.dept_name}}</strong>#}
{# </div>#}
{# <div class="col-xs-7 content no-top-border">#}
{# <p class="m-b-xs"><strong>详细信息</strong></p>#}
{# <p>来源IP: {{ l.remote_ip }}</p>#}
{# <p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>#}
{# <p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>#}
{# </div>#}
{# </div>#}
{# </div>#}
{# {% endfor %}#}
{# <button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> 所有 </button>#}
{# <div id='more' style="display: none">#}
{# <br/>#}
{# {% for l in log_more %}#}
{# <div class="timeline-item">#}
{# <div class="row">#}
{# <div class="col-xs-5 date">#}
{# <i class="fa fa-info-circle"></i>#}
{# <small class="text-navy">{{ l.user }}</small>#}
{# <br/>#}
{# <strong>{{l.dept_name}}</strong>#}
{# </div>#}
{# <div class="col-xs-7 content no-top-border">#}
{# <p class="m-b-xs"><strong>详细信息</strong></p>#}
{# <p>来源IP: {{ l.remote_ip }}</p>#}
{# <p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>#}
{# <p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>#}
{# </div>#}
{# </div>#}
{# </div>#}
{# {% endfor %}#}
{# {% else %}#}
{# <p class="text-center">(暂无)</p>#}
{# {% endif %}#}
{% if log %}
{% for l in log %}
<div class="timeline-item">
<div class="row">
<div class="col-xs-5 date">
<i class="fa fa-info-circle"></i>
<small class="text-navy">{{ l.user }}</small>
<br/>
</div>
<div class="col-xs-7 content no-top-border">
<p class="m-b-xs"><strong>详细信息</strong></p>
<p>来源IP: {{ l.remote_ip }}</p>
<p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>
<p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>
</div>
</div>
</div>
{% endfor %}
<button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> 所有 </button>
<div id='more' style="display: none">
<br/>
{% for l in log_more %}
<div class="timeline-item">
<div class="row">
<div class="col-xs-5 date">
<i class="fa fa-info-circle"></i>
<small class="text-navy">{{ l.user }}</small>
<br/>
</div>
<div class="col-xs-7 content no-top-border">
<p class="m-b-xs"><strong>详细信息</strong></p>
<p>来源IP: {{ l.remote_ip }}</p>
<p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>
<p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>
</div>
</div>
</div>
{% endfor %}
{% else %}
<p class="text-center">(暂无)</p>
{% endif %}
</div>
</div>

View File

@ -5,7 +5,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 修改资产基本信息 </h5>
@ -46,6 +46,9 @@
<div class="hr-line-dashed"></div>
{{ af.remote_ip|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{{ af.mac|bootstrap_horizontal }}
{# <div class="hr-line-dashed"></div>#}
{# {{ af.port|bootstrap_horizontal }}#}
@ -105,6 +108,9 @@
<div class="hr-line-dashed"></div>
{{ af.system_version|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{{ af.system_arch|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{{ af.number|bootstrap_horizontal }}

View File

@ -18,7 +18,7 @@
<body>
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
{# <div class="ibox-title">#}
{# <h5 class="text-center"> 填写修改主机信息. </h5>#}

View File

@ -30,7 +30,7 @@
<div class="col-sm-7" style="padding-left: 0px">
<label>
<select name="idc" class="form-control m-b" onchange="change_info()">
<select name="idc" class="form-control m-b input-sm" onchange="change_info()">
<option value="">机房</option>
{% for idc in idc_all %}
{% ifequal idc.name idc_name %}
@ -43,7 +43,7 @@
</label>
<label>
<select name="group" class="form-control m-b" onchange="change_info()">
<select name="group" class="form-control m-b input-sm" onchange="change_info()">
<option value="">主机组</option>
{% for asset_group in asset_group_all %}
{% ifequal asset_group.name group_name %}
@ -56,7 +56,7 @@
</label>
<label>
<select name="asset_type" class="form-control m-b" onchange="change_info()">
<select name="asset_type" class="form-control m-b input-sm" onchange="change_info()">
<option value="">资产类型</option>
{% for type in asset_types %}
{% ifequal type.0|int2str asset_type %}
@ -69,7 +69,7 @@
</label>
<label>
<select name="status" class="form-control m-b" onchange="change_info()">
<select name="status" class="form-control m-b input-sm" onchange="change_info()">
<option value="">资产状态</option>
{% for s in asset_status %}
{% ifequal s.0|int2str status %}
@ -84,43 +84,18 @@
<div class="col-sm-4" style="padding-right: 0">
<div class="input-group inline-group">
<input type="text" class="form-control m-b" id="search_input" name="keyword" value="{{ keyword }}" placeholder="Search">
<input type="text" class="form-control m-b input-sm" id="search_input" name="keyword" value="{{ keyword }}" placeholder="Search">
<input type="text" style="display: none">
<div class="input-group-btn">
<button id='search_btn' href="/jasset/asset_list/?search=true" type="button" class="btn btn-xm btn-primary search-btn" onclick="change_info()">
<button id='search_btn' href="/jasset/asset_list/?search=true" type="button" class="btn btn-sm btn-primary search-btn" onclick="change_info()">
- 搜索 -
</button>
<button type="button" href="/jasset/asset_list/?export=true" name="export" class="btn btn-xm btn-success search-btn-excel" onclick="return false">
<button type="button" href="/jasset/asset_list/?export=true" name="export" class="btn btn-sm btn-success search-btn-excel" onclick="return false">
- 导出 -
</button>
</div>
</div>
</div>
{# <div class="col-sm-2">#}
{# </div>#}
{##}
{# <div class="col-sm-2">#}
{# </div>#}
{# <div class="col-sm-2">#}
{# </div>#}
{# <div class="col-sm-4">#}
{# <div class="input-group">#}
{# <input type="text" class="form-control m-b" id="search_input" name="keyword" value="{{ keyword }}" placeholder="Search">#}
{# <input type="text" style="display: none">#}
{# <div class="input-group-btn">#}
{# <button id='search_btn' href="/jasset/asset_list/?search=true" type="button" class="btn btn-xm btn-primary search-btn" onclick="change_info()">#}
{# - 搜索 -#}
{# </button>#}
{# <button type="button" href="/jasset/asset_list/?export=true" name="export" class="btn btn-xm btn-success search-btn-excel" onclick="return false">#}
{# - 导出 -#}
{# </button>#}
{# </div>#}
{# </div>#}
{# </div>#}
<div id="export"></div>
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
<thead>
@ -134,7 +109,9 @@
<th class="text-center"> 所属主机组 </th>
{# <th class="text-center"> 配置信息 </th>#}
<th class="text-center"> 操作系统 </th>
<th class="text-center"> 使用默认管理 </th>
<th class="text-center"> cpu核数 </th>
<th class="text-center"> 内存 </th>
<th class="text-center"> 硬盘 </th>
<th class="text-center"> 操作 </th>
</tr>
</thead>
@ -144,20 +121,19 @@
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
</td>
<td class="text-center hostname"> {{ asset.hostname|default_if_none:"" }} </td>
<td class="text-center hostname"> <a href="/jasset/asset_detail/?id={{ asset.id }}">{{ asset.hostname|default_if_none:"" }}</a></td>
<td class="text-center"> {{ asset.ip|default_if_none:"" }} </td>
<td class="text-center"> {{ asset.idc.name|default_if_none:"" }} </td>
<td class="text-center">{{ asset.group.all|group_str2 }}</td>
{# <td class="text-center">{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}</td>#}
<td class="text-center">{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }}</td>
<td class="text-center"> {{ asset.use_default_auth|bool2str }} </td>
<td class="text-center"> {{ asset.cpu|get_cpu_core|default_if_none:"" }} </td>
<td class="text-center"> {{ asset.memory }}{% if asset.memory %}G{% endif %}</td>
<td class="text-center"> {{ asset.disk|get_disk_info }}{% if asset.memory %}G{% endif %}</td>
<td class="text-center" data-editable='false'>
<a href="/jasset/asset_detail/?id={{ asset.id }}" class="btn btn-xs btn-primary">详情</a>
{% ifnotequal session_role_id 0 %}
<a href="/jasset/asset_edit/?id={{ asset.id }}" class="btn btn-xs btn-info">编辑</a>
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
<a value="/jasset/asset_del/?id={{ asset.id }}" class="btn btn-xs btn-danger asset_del">删除</a>
{% endifnotequal %}
<a href="/jasset/asset_edit/?id={{ asset.id }}" class="btn btn-xs btn-info">编辑</a>
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
<a value="/jasset/asset_del/?id={{ asset.id }}" class="btn btn-xs btn-danger asset_del">删除</a>
</td>
</tr>
{% endfor %}
@ -168,7 +144,8 @@
<input type="button" id="asset_del" class="btn btn-danger btn-sm" name="del_button" value="删除"/>
<a value="/jasset/asset_edit_batch/" type="button" class="btn btn-sm btn-warning iframe">修改</a>
<input type="button" id="asset_update" class="btn btn-info btn-sm" name="update_button" value="更新"/>
<input type="button" id="asset_update_all" class="btn btn-primary btn-sm" name="update_button" value="更新全部"/>
{# <input type="button" id="asset_update_all" class="btn btn-primary btn-sm" name="update_button" value="更新全部"/>#}
<input type="button" id="exec_cmd" class="btn btn-sm btn-primary" name="exec_cmd" value="执行命令"/>
</div>
{% include 'paginator.html' %}
</div>
@ -196,19 +173,67 @@
}
});
$('.conn').click(function(){
var url='/jlog/get_role_name/?id=' + $(this).attr('value');
var href = $(this).attr('href');
var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role=';
var hostname = $(this).closest('tr').find('.hostname')[0].innerHTML;
$('#exec_cmd').click(function(){
var url = '/jperm/role/get/';
var new_url = '/exec_cmd/?role=';
var check_array = [];
$(".gradeX input:checked").closest('tr').find('.hostname a').each(function() {
check_array.push($(this).text())
});
check_assets = check_array.join(':');
$.ajax({
type: 'GET',
url: url,
data: {},
success: function(data){
var dataArray = data.split(',');
if (dataArray.length == 1 && data != 'error'){
var title = 'Jumpserver Exec Terminal';
layer.open({
type: 2,
title: title,
maxmin: true,
shade: false,
area: ['800px', '600px'],
content: new_url+data+'&check_assets='+check_assets
});
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
} else if (dataArray.length == '1' && data == 'error'){
layer.alert('没有授权角色')
} else {
aUrl = '';
$.each(dataArray, function(index, value){
aUrl += '<a onclick="windowOpenExec(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + '&check_assets=' + check_assets + '>' + value + '</a> '
});
layer.alert(aUrl, {
skin: 'layui-layer-molv',
title: '多个角色,请选择一个连接',
shade: false,
closeBtn: 0
})
}
}
});
return false
});
$('.conn').click(function(){
var url='/jperm/role/get/?id=' + $(this).attr('value'); // 获取用户有权限的角色
var href = $(this).attr('href');
var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role='; // webterminal socket url
var hostname = $(this).closest('tr').find('.hostname a')[0].innerHTML;
$.ajax({
type: 'GET',
url: url,
data: {},
success: function(data){
var dataArray = data.split(',');
console.log(data+'a');
console.log(dataArray);
if (dataArray.length == 1 && data != 'error'){
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
console.log(new_url+data);
layer.open({
type: 2,
title: title,
@ -218,13 +243,14 @@
content: new_url+data
});
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
} else if (dataArray.length == '1' && data == 'error'){
} else if (data == 'error' || data == '' || data == null || data == undefined){
layer.alert('没有授权角色')
} else {
aUrl = '';
$.each(dataArray, function(index, value){
aUrl += '<a onclick="windowOpen(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + ' value=' + hostname + '>' + value + '</a> '
});
console.log(aUrl);
layer.alert(aUrl, {
skin: 'layui-layer-molv',
title: '多个角色,请选择一个连接',
@ -250,7 +276,21 @@
shade: false,
content: new_url
});
//window.open(new_url, '', 'height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');
return false
}
function windowOpenExec(a){
var new_url = $(a).attr('href');
var title = 'Jumpserver Exec Terminal';
layer.open({
type: 2,
title: title,
maxmin: true,
area: ['800px', '600px'],
shade: false,
content: new_url
});
console.log(new_url);
return false
}
@ -311,37 +351,40 @@
$('#asset_update').click(function () {
var asset_id_all = getIDall();
if (asset_id_all == ''){
alert("请至少选择一行!");
return false;
if (confirm("更新全部资产信息?")) {
layer.msg('玩命更新中...', {time: 200000});
$.ajax({
type: "post",
url: "/jasset/asset_update_batch/?arg=all",
success: function () {
parent.location.reload();
}
});
}
}
else {
layer.msg('玩命更新中...', {time: 200000});
$.ajax({
type: "post",
data: {asset_id_all: asset_id_all},
url: "/jasset/asset_update_batch/",
success: function () {
parent.location.reload();
}
});
}
layer.msg('玩命更新中...', {time: 200000});
$.ajax({
type: "post",
data: {asset_id_all: asset_id_all},
url: "/jasset/asset_update_batch/",
success: function () {
parent.location.reload();
}
});
});
{# function update_tips(){#}
{# layer.tips('我是另外一个tips只不过我长得跟之前那位稍有些不一样。', '吸附元素选择器', {#}
{# tips: [1, '#3595CC'],#}
{# time: 4000#}
{# $('#asset_update_all').click(function () {#}
{# layer.msg('玩命更新中...', {time: 200000});#}
{# $.ajax({#}
{# type: "post",#}
{# url: "/jasset/asset_update_batch/?arg=all",#}
{# success: function () {#}
{# parent.location.reload();#}
{# }#}
{# });#}
{# }#}
$('#asset_update_all').click(function () {
layer.msg('玩命更新中...', {time: 200000});
$.ajax({
type: "post",
url: "/jasset/asset_update_batch/?arg=all",
success: function () {
parent.location.reload();
}
});
});
{# });#}
function change_info(){
var args = $("#asset_form").serialize();

View File

@ -19,7 +19,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 填写主机组基本信息 </h5>

View File

@ -19,7 +19,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 填写主机组基本信息 </h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 主机组详细信息列表</h5>

View File

@ -4,7 +4,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 填写IDC基本信息 </h5>

View File

@ -4,7 +4,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 填写IDC基本信息 </h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> IDC详细信息列表</h5>

View File

@ -0,0 +1,126 @@
{% extends 'base.html' %}
{% load mytags %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>{{ log.id }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div>
<div class="text-left">
<table class="table">
<tr>
<td class="text-navy">ID</td>
<td>{{ log.id }}</td>
</tr>
<tr>
<td class="text-navy">用户名</td>
<td>{{ log.user }}</td>
</tr>
<tr>
<td class="text-navy">来源IP</td>
<td>{{ log.remote_ip }}</td>
</tr>
<tr>
<td class="text-navy">日期</td>
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
</tr>
<tr>
<td class="text-navy">主机</td>
<td>
<table class="table">
{% for asset_name in assets_hostname %}
{% if asset_name %}
<tr>
<td>{{ asset_name }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>结果</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content inspinia-timeline">
<div>
<div class="text-left">
<table class="table">
<tr>
<td class="text-navy">命令</td>
<td>{{ log.cmd }}</td>
</tr>
{% for result, info in result.items %}
{% for host, msg in info.items %}
{% ifequal result 'failed' %}
<tr>
<td class="text-navy" style="color: #ed5565">{{ host }}</td>
<td>{{ msg }}</td>
</tr>
{% else %}
<tr>
<td class="text-navy">{{ host }}</td>
<td>{{ msg }}</td>
</tr>
{% endifequal %}
{% endfor %}
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
$('#show').click(function(){
$('#last').css('display', 'none');
$('#all').css('display', 'block');
})
})
</script>
{% endblock %}

View File

@ -0,0 +1,140 @@
{% extends 'base.html' %}
{% load mytags %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>{{ log.id }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div>
<div class="text-left">
<table class="table">
<tr>
<td class="text-navy">ID</td>
<td>{{ log.id }}</td>
</tr>
<tr>
<td class="text-navy">用户名</td>
<td>{{ log.user }}</td>
</tr>
<tr>
<td class="text-navy">来源IP</td>
<td>{{ log.remote_ip }}</td>
</tr>
<tr>
<td class="text-navy">类型</td>
<td>{{ log.type }}</td>
</tr>
<tr>
<td class="text-navy">日期</td>
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
</tr>
<tr>
<td class="text-navy">文件</td>
<td>
<table class="table">
{% for file_name in file_list %}
{% if file_name %}
<tr>
<td>{{ file_name }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
</td>
</tr>
<tr>
<td class="text-navy">主机</td>
<td>
<table class="table">
{% for asset_name in assets_hostname %}
{% if asset_name %}
<tr>
<td>{{ asset_name }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>结果</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content inspinia-timeline">
<div>
<div class="text-left">
<table class="table">
{% for result, info in result.items %}
{% for host, msg in info.items %}
{% ifequal result 'failed' %}
<tr>
<td class="text-navy" style="color: #ed5565">{{ host }}</td>
<td>{{ msg }}</td>
</tr>
{% else %}
<tr>
<td class="text-navy">{{ host }}</td>
<td>{{ msg }}</td>
</tr>
{% endifequal %}
{% endfor %}
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
$('#show').click(function(){
$('#last').css('display', 'none');
$('#all').css('display', 'block');
})
})
</script>
{% endblock %}

View File

@ -0,0 +1,106 @@
{% extends 'base.html' %}
{% block self_head_css_js %}
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
{% endblock %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 批量命令日志 </h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="panel-options">
<ul class="nav nav-tabs">
<li><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
<li><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
<li class="active"><a href="/jlog/log_list/exec/" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
<li><a href="/jlog/log_list/file/" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
<div class="" style="float: right">
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" id="keyword" name="keyword" value="{{ keyword }}" placeholder="Search">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
-搜索-
</button>
</div>
</div>
</form>
</div>
</ul>
</div>
<div class="tab-content">
<table class="table table-striped table-bordered table-hover ">
<thead>
<tr>
<th class="text-center"> ID </th>
<th class="text-center"> 用户名 </th>
<th class="text-center"> 主机 </th>
<th class="text-center"> 命令 </th>
<th class="text-center"> 来源IP </th>
<th class="text-center"> 时间 </th>
<th class="text-center"> 详情 </th>
</tr>
</thead>
<tbody>
{% for post in contacts.object_list %}
<tr class="gradeX">
<td class="text-center"> {{ post.id }} </td>
<td class="text-center username"> {{ post.user }} </td>
<td class="text-center ip"> {{ post.host | truncatechars:30 }} </td>
<td class="text-center ip"> {{ post.cmd | truncatechars:30 }} </td>
<td class="text-center remote_ip"> {{ post.remote_ip }} </td>
<td class="text-center start_time"> {{ post.datetime|date:"Y-m-d H:i:s"}} </td>
<td class="text-center">
<a href="/jlog/log_detail/exec/?id={{ post.id }}" class="btn btn-xs btn-primary">详情</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-sm-6">
</div>
{% include 'paginator.html' %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function log_search(){
$.ajax({
type: "GET",
url: "/jlog/search/?env=offline",
data: $("#search_form").serialize(),
success: function (data) {
$(".tab-content").html(data);
}
});
}
</script>
{% endblock %}

View File

@ -0,0 +1,108 @@
{% extends 'base.html' %}
{% block self_head_css_js %}
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
{% endblock %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 上传下载日志 </h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="panel-options">
<ul class="nav nav-tabs">
<li><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
<li><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
<li><a href="/jlog/log_list/exec/" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
<li class="active"><a href="/jlog/log_list/file/" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
<div class="" style="float: right">
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" id="keyword" name="keyword" value="{{ keyword }}" placeholder="Search">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
-搜索-
</button>
</div>
</div>
</form>
</div>
</ul>
</div>
<div class="tab-content">
<table class="table table-striped table-bordered table-hover ">
<thead>
<tr>
<th class="text-center"> ID </th>
<th class="text-center"> 用户名 </th>
<th class="text-center"> 主机 </th>
<th class="text-center"> 文件 </th>
<th class="text-center"> 类型 </th>
<th class="text-center"> 来源IP </th>
<th class="text-center"> 时间 </th>
<th class="text-center"> 详情 </th>
</tr>
</thead>
<tbody>
{% for post in contacts.object_list %}
<tr class="gradeX">
<td class="text-center"> {{ post.id }} </td>
<td class="text-center"> {{ post.user }} </td>
<td class="text-center"> {{ post.host | truncatechars:30 }} </td>
<td class="text-center"> {{ post.filename | truncatechars:20 }} </td>
<td class="text-center"> {{ post.type }} </td>
<td class="text-center"> {{ post.remote_ip }} </td>
<td class="text-center"> {{ post.datetime|date:"Y-m-d H:i:s"}} </td>
<td class="text-center">
<a href="/jlog/log_detail/file/?id={{ post.id }}" class="btn btn-xs btn-primary">详情</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-sm-6">
</div>
{% include 'paginator.html' %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function log_search(){
$.ajax({
type: "GET",
url: "/jlog/search/?env=offline",
data: $("#search_form").serialize(),
success: function (data) {
$(".tab-content").html(data);
}
});
}
</script>
{% endblock %}

View File

@ -9,7 +9,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 用户日志详细信息列表 </h5>

View File

@ -1,56 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jumpserver | 开源跳板机系统</title>
<link rel="shortcut icon" href="/static/img/facio.ico" type="image/x-icon">
{% include 'link_css.html' %}
{% include 'head_script.html' %}
</head>
<body>
<div id="wrapper">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 实时监控 </h5>
</div>
<div class="ibox-content blank-panel" id="content" style="background-color: #0b0b0b; color: #006621; height: 500px; padding: 20px;">
你好<br>
</div>
</div>
</div>
</div>
</body>
{% block self_footer_js %}
<script>
function monitor(){
var wsUri = 'ws://j:8080/send';
var ws = new WebSocket(wsUri);
ws.onopen = function(evt){
$('#content').append('Connect websocket success' + '<br />');
ws.send('Start')
};
ws.onmessage = function(evt){
console.log(evt.data);
$('#content').append(evt.data.replace(/\n|\r|(\r\n)|(\u0085)|(\u2028)|(\u2029)/g, '<br>'));
};
ws.onclose = function(evt){
$('#content').append('Disconnect with websocket')
}
}
monitor();
</script>
{% endblock %}
</html>

View File

@ -32,7 +32,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 用户日志详细信息列表 </h5>
@ -54,9 +54,10 @@
<ul class="nav nav-tabs">
<li><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
<li class="active"><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
<li><a href="/jlog/log_list/exec/" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
<li><a href="/jlog/log_list/file/" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
</ul>
</div>
<br/>
<form class="form-inline" action="" method="get">
<div class="form-group" id="date_5">
<div class="input-daterange input-group" id="datepicker">
@ -87,7 +88,7 @@
<input id="cmd" name="cmd" placeholder="命令" type="text" class="form-control" value="{{ cmd }}" style="width: 200px;">
</div>
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
Search
- 搜索 -
</button>
</form>
<div class="tab-content">

View File

@ -47,7 +47,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 用户日志详细信息列表 </h5>
@ -69,9 +69,22 @@
<ul class="nav nav-tabs">
<li class="active"><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
<li><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
<li><a href="/jlog/log_list/exec/" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
<li><a href="/jlog/log_list/file/" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
<div class="" style="float: right">
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" id="keyword" name="keyword" value="{{ keyword }}" placeholder="Search">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
-搜索-
</button>
</div>
</div>
</form>
</div>
</ul>
</div>
<br/>
<div class="tab-content">
<table class="table table-striped table-bordered table-hover ">
<thead>

View File

@ -28,7 +28,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> 用户{{ username }}日志详细信息列表 </h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> {{ user_group.name }}授权修改</h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 查看小组</h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 查看小组</h5>

View File

@ -9,7 +9,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>

View File

@ -8,7 +8,7 @@
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>授权规则</b></span>
<span class="label label-primary"><b>{{ role.name }} - 授权规则</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
@ -54,7 +54,7 @@
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>授权用户/用户组</b></span>
<span class="label label-primary"><b>{{ role.name }} - 授权用户/用户组</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
@ -100,7 +100,7 @@
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>授权主机/主机组</b></span>
<span class="label label-primary"><b>{{ role.name }} - 授权主机/主机组</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
@ -148,7 +148,7 @@
<div class="col-sm-6">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>推送主机</b></span>
<span class="label label-primary"><b>{{ role.name }} - 推送主机</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
@ -175,7 +175,7 @@
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll()">
<input type="checkbox" id="check_push" onclick="checkAll('check_push', 'asset_id')">
</th>
<th class="text-center">主机</th>
<th class="text-center">密钥</th>
@ -188,7 +188,7 @@
{% for asset, info in pushed_asset.items %}
<tr class="gradeX">
<th class="text-center">
<input type="checkbox" name="checked" value="{{ asset.id }}">
<input type="checkbox" name="asset_id" value="{{ asset.id }}">
</th>
<td class="text-center"> {{ asset.hostname }} </td>
<td class="text-center"> {{ info.key | yesno:"是,否,未知" }} </td>
@ -212,7 +212,7 @@
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-danger"><b>未推送主机</b></span>
<span class="label label-danger"><b>{{ role.name }} - 未推送主机</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
@ -220,7 +220,6 @@
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
@ -229,9 +228,13 @@
<div class="ibox-content">
<div>
<div class="text-left">
<table class="table table-striped" id="ugedit" >
<table class="table table-striped" >
<a class="btn btn-xs btn-primary push_muti"> 推送 </a>
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_no_push" onclick="checkAll('check_no_push', 'asset_no_push_id')">
</th>
<th class="text-center">主机</th>
<th class="text-center">IP</th>
</tr>
@ -239,6 +242,9 @@
<tbody>
{% for asset in need_push_asset %}
<tr class="gradeX">
<th class="text-center">
<input type="checkbox" name="asset_no_push_id" value="{{ asset.id }}">
</th>
<td class="text-center"> {{ asset.hostname }} </td>
<td class="text-center"> {{ asset.ip }} </td>
</tr>
@ -276,10 +282,11 @@
$('.del_muti').click(function(){
var check_array = [];
if (confirm("确定删除")) {
$(".gradeX input:checked").each(function() {
$(".gradeX input[name='asset_id']:checked").each(function() {
check_array.push($(this).attr("value"))
});
var url = '/jperm/role/recycle/?role_id={{ role.id }}&asset_id=' + check_array.join(',');
console.log(check_array);
$.get(url,
{},
function(data){
@ -290,6 +297,15 @@
return false;
});
$('.push_muti').click(function(){
var check_array = [];
$(".gradeX input[name='asset_no_push_id']:checked").each(function() {
check_array.push($(this).attr("value"))
});
var url = '/jperm/role/push/?id={{ role.id }}&asset_id=' + check_array.join(',');
$(this).attr('href', url)
})
})
</script>
{% endblock %}

View File

@ -9,7 +9,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div>
{% if error %}
@ -50,19 +50,16 @@
<th class="text-center">名称 </th>
<th class="text-center">sudo别名</th>
<th class="text-center">创建时间</th>
<th class="text-center">备注</th>
<th class="text-center">操作</th>
</tr>
</thead>
<tbody id="edittbody">
{% for role in roles %}
<tr class="gradeX" id={{ role.id }}>
<td class="text-center"> {{ role.name }} </td>
<td class="text-center"><a href="/jperm/role/perm_role_detail/?id={{ role.id }}">{{ role.name }} </a></td>
<td class="text-center"> {{ role | role_contain_which_sudos }} </td>
<td class="text-center"> {{ role.date_added | date:"Y-m-d H:i:s"}} </td>
<td class="text-center"> {{ role.comment }} </td>
<td class="text-center">
<a href="/jperm/role/perm_role_detail/?id={{ role.id }}" class="btn btn-xs btn-primary">详情</a>
<a href="/jperm/role/perm_role_edit/?id={{ role.id }}" class="btn btn-xs btn-info">编辑</a>
<a href="/jperm/role/push/?id={{ role.id }}" class="btn btn-xs btn-warning">推送</a>
<button onclick="remove_role({{ role.id }})" class="btn btn-xs btn-danger">删除</button>

View File

@ -9,7 +9,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>
@ -45,7 +45,7 @@
<div class="col-sm-8">
<select name="assets" id="assets" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset in assets %}
<option value="{{ asset.id }}" {% if asset in pushed_assets %}selected{% endif %}>{{ asset.hostname }}</option>
<option value="{{ asset.id }}" {% if asset in need_push_asset %}selected{% endif %}>{{ asset.hostname }}</option>
{% endfor %}
</select>
</div>

View File

@ -9,7 +9,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>
@ -34,9 +34,9 @@
<div class="alert alert-success text-center">{{ msg }}</div>
{% endif %}
<div class="form-group">
<label for="rulename" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label>
<label for="name" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<input id="rulename" name="rulename" placeholder="Rule Name" type="text" class="form-control">
<input id="name" name="name" placeholder="Rule Name" type="text" class="form-control">
</div>
</div>
<div class="hr-line-dashed"></div>
@ -52,10 +52,9 @@
</div>
</div>
<div class="form-group">
<label for="usergroup" class="col-sm-2 control-label">用户组</label>
<label for="user_group" class="col-sm-2 control-label">用户组</label>
<div class="col-sm-8">
<select name="usergroup" id="usergroup" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2">
<select name="user_group" id="user_group" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2">
{% for user_group in user_groups %}
<option value="{{ user_group.id }}">{{ user_group.name }}</option>
{% endfor %}
@ -75,9 +74,9 @@
</div>
</div>
<div class="form-group">
<label for="assetgroup" id="assetgroup" class="col-sm-2 control-label">资产组</label>
<label for="asset_group" id="asset_group" class="col-sm-2 control-label">资产组</label>
<div class="col-sm-8">
<select name="assetgroup" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
<select name="asset_group" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset_group in asset_groups %}
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
{% endfor %}
@ -100,7 +99,7 @@
<div class="form-group">
<label for="comment" class="col-sm-2 control-label">备注</label>
<div class="col-sm-8">
<input id="rule_comment" name="rule_comment" placeholder="Rule Comment" type="text" class="form-control" {% if error %}value="{{ username }}" {% endif %}>
<input id="comment" name="comment" placeholder="Rule Comment" type="text" class="form-control" {% if error %}value="{{ username }}" {% endif %}>
</div>
</div>
<div class="hr-line-dashed"></div>
@ -133,17 +132,17 @@ $('#ruleForm').validator({
},
fields: {
"rulename": {
"name": {
rule: "required;check_name",
tip: "输入规则名称",
msg: {required: "规则名称必填"}
},
"usergroup": {
"user_group": {
rule: "required(check_user)",
tip: "请选择用户组",
msg: {required: "用户和用户组必选一个!"}
},
"assetgroup": {
"asset_group": {
rule: "required(check_asset)",
tip: "输入资产组",
msg: {required: "资产和资产组必选一个!"}

View File

@ -7,7 +7,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>{{ rule.name }}</b></span>
@ -60,7 +60,7 @@
</div>
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>授权用户/用户组</b></span>
@ -106,7 +106,7 @@
</div>
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>授权主机/主机组</b></span>

View File

@ -9,7 +9,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>
@ -26,7 +26,7 @@
</div>
</div>
<div class="ibox-content">
<form method="post" id="userForm" class="form-horizontal" action="">
<form method="post" id="ruleForm" class="form-horizontal" action="">
{% if error %}
<div class="alert alert-warning text-center">{{ error }}</div>
{% endif %}
@ -34,51 +34,51 @@
<div class="alert alert-success text-center">{{ msg }}</div>
{% endif %}
<div class="form-group">
<label for="username_lab" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label>
<label for="name" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<input id="rule_name" name="rule_name" placeholder="RuleName" type="text" class="form-control" value="{{ rule.name }}">
<input id="name" name="name" placeholder="Rule Name" type="text" class="form-control" value={{ rule.name }}>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="user" class="col-sm-2 control-label">用户<span class="red-fonts">*</span></label>
<label for="user" class="col-sm-2 control-label">用户</label>
<div class="col-sm-8">
<select name="user" data-placeholder="用户名" class="chosen-select form-control m-b" multiple tabindex="2">
<select name="user" id="user" data-placeholder="用户名" class="chosen-select form-control m-b" multiple tabindex="2">
{% for user in users %}
<option value="{{ user.id }}" {% if user in users_select %} selected {% endif %}>{{ user.name }}</option>
<option value="{{ user.id }}" {% if user in rule.user.all %} selected {% endif %}>{{ user.name }}</option>
{% endfor %}
</select>
<span class="help-block m-b-none">用户和用户组必选一个</span>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="usergroup" class="col-sm-2 control-label">用户组<span class="red-fonts">*</span></label>
<label for="user_group" class="col-sm-2 control-label">用户组</label>
<div class="col-sm-8">
<select name="usergroup" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2">
<select name="user_group" id="user_group" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2">
{% for user_group in user_groups %}
<option value="{{ user_group.id }}"{% if user_group in user_groups_select %} selected {% endif %}>{{ user_group.name }}</option>
<option value="{{ user_group.id }}"{% if user_group in rule.user_group.all %} selected {% endif %}>{{ user_group.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="asset" class="col-sm-2 control-label">资产<span class="red-fonts">*</span></label>
<label for="asset" class="col-sm-2 control-label">资产</label>
<div class="col-sm-8">
<select name="asset" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
<select name="asset" id="asset" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset in assets %}
<option value="{{ asset.id }}"{% if asset in assets_select %} selected {% endif %}>{{ asset.ip }}</option>
<option value="{{ asset.id }}"{% if asset in rule.asset.all %} selected {% endif %}>{{ asset.ip }}</option>
{% endfor %}
</select>
<span class="help-block m-b-none">资产和资产组必选一个</span>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="assetgroup" class="col-sm-2 control-label">资产组<span class="red-fonts">*</span></label>
<label for="asset_group" id="asset_group" class="col-sm-2 control-label">资产组</label>
<div class="col-sm-8">
<select name="assetgroup" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
<select name="asset_group" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset_group in asset_groups %}
<option value="{{ asset_group.id }}"{% if asset_group in asset_groups_select %} selected {% endif %}>{{ asset_group.name }}</option>
<option value="{{ asset_group.id }}"{% if asset_group in rule.asset_group.all %} selected {% endif %}>{{ asset_group.name }}</option>
{% endfor %}
</select>
</div>
@ -88,8 +88,8 @@
<label for="role" class="col-sm-2 control-label">角色<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<select name="role" data-placeholder="请选择角色" class="chosen-select form-control m-b" multiple tabindex="2">
{% for role in roles %}
<option value="{{ role.id }}"{% if role in roles_select %} selected {% endif %}>{{ role.name }}</option>
{% for role in roles %}
<option value="{{ role.id }}"{% if role in rule.role.all %} selected {% endif %}>{{ role.name }}</option>
{% endfor %}
</select>
</div>
@ -99,7 +99,7 @@
<div class="form-group">
<label for="comment" class="col-sm-2 control-label">备注</label>
<div class="col-sm-8">
<input id="role_comment" name="role_comment" placeholder="Rule Comment" type="text" class="form-control" value="{{ rule_comment }}">
<input id="comment" name="rule_comment" placeholder="Rule Comment" type="text" class="form-control" value="{{ rule.comment }}">
</div>
</div>
<div class="hr-line-dashed"></div>
@ -119,6 +119,47 @@
{% block self_footer_js %}
<script>
$('#ruleForm').validator({
timely: 2,
theme: "yellow_right_effect",
rules: {
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位'],
check_user: function(){
return $('#user').val() == null
},
check_asset: function(){
return $('#asset').val() == null
}
},
fields: {
"name": {
rule: "required;check_name",
tip: "输入规则名称",
msg: {required: "规则名称必填"}
},
"user_group": {
rule: "required(check_user)",
tip: "请选择用户组",
msg: {required: "用户和用户组必选一个!"}
},
"asset_group": {
rule: "required(check_asset)",
tip: "输入资产组",
msg: {required: "资产和资产组必选一个!"}
},
"role": {
rule: "required",
tip: "请选择角色",
msg: {required: "必须选择角色"}
}
},
valid: function(form) {
form.submit();
}
});
var config = {
'.chosen-select' : {},
'.chosen-select-deselect' : {allow_single_deselect:true},

View File

@ -7,7 +7,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div>
{% if error %}
@ -35,7 +35,6 @@
<div class="ibox-content">
<div class="">
<a href="/jperm/perm_rule_add/" class="btn btn-sm btn-primary "> 添加规则 </a>
<a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" id="search_input" name="search" placeholder="Search">

View File

@ -4,7 +4,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>
@ -69,7 +69,7 @@ $('#sudoForm').validator({
timely: 2,
theme: "yellow_right_effect",
rules: {
check_name: [/^\w{2,20}$/, '大写字母数字和下划线,2-20位']
check_name: [/^\w{2,20}$/, '大写字母,2-20位']
},
fields: {

View File

@ -9,7 +9,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div>
{% if error %}

View File

@ -10,11 +10,11 @@ cp ${sudo_file} ${sudo_file_bak}
# Add Command Aliases
add_cmd_alias() {
sudo_file=$1
{% for sudo in sudo_alias %}
if $(grep '^Cmnd_Alias {{ sudo.name }}' ${sudo_file} &> /dev/null); then
sed -i 's@^Cmnd_Alias.*{{ sudo.name }}.*@Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }}@g' ${sudo_file}
{% for sudo_name, sudo_cmd in sudo_alias.items %}
if $(grep '^Cmnd_Alias {{ sudo_name }}' ${sudo_file} &> /dev/null); then
sed -i 's@^Cmnd_Alias.*{{ sudo_name }}.*@Cmnd_Alias {{ sudo_name }} = {{ sudo_cmd }}@g' ${sudo_file}
else
echo "Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }}" >> ${sudo_file}
echo "Cmnd_Alias {{ sudo_name }} = {{ sudo_cmd }}" >> ${sudo_file}
fi
{% endfor %}
}
@ -23,11 +23,11 @@ add_cmd_alias() {
# Add Command Aliases to role
add_role_chosen() {
sudo_file=$1
{% for role, alias in role_chosen_aliase.items %}
if $(grep '^{{ role }}.*' ${sudo_file} &> /dev/null); then
sed -i 's@^{{ role }}.*@{{ role }} ALL = NOPASSWD: {{ alias }}@g' ${sudo_file}
{% for user, alias in sudo_user.items %}
if $(grep '^{{ user }}.*' ${sudo_file} &> /dev/null); then
sed -i 's@^{{ user }}.*@{{ user }} ALL = (root) NOPASSWD: {{ alias }}@g' ${sudo_file}
else
echo "{{ role }} ALL = NOPASSWD: {{ alias }}" >> ${sudo_file}
echo "{{ user }} ALL = (root) NOPASSWD: {{ alias }}" >> ${sudo_file}
fi
{% endfor %}
}

View File

@ -4,7 +4,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="panel blank-panel">
<div class="panel-heading">

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel blank-panel">
<div class="panel-heading">

View File

@ -6,7 +6,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>编辑用户信息</h5>
@ -45,7 +45,7 @@
<div class="col-sm-8">
<input id="password" name="password" placeholder="Password" type="password" class="form-control">
<span class="help-block m-b-none">
登陆web的密码
登陆web的密码, 不修改请留空
</span>
</div>
</div>
@ -88,10 +88,13 @@
$('#userForm').validator({
timely: 2,
theme: "yellow_right_effect",
rules: {
check_pass: [/^\w+$/, '数字和字符']
},
fields: {
"password": {
rule: "length[6~50]",
rule: "length[6~50];check_pass",
tip: "输入密码",
ok: "",
msg: {required: "必须填写!"}

View File

@ -15,9 +15,9 @@
<body class="gray-bg">
<div class="lock-word animated fadeInDown">
<span class="first-word">Jumperver</span>
</div>
<div class="lock-word animated fadeInDown">
<span class="first-word">Jumperver</span>
</div>
<div class="middle-box text-center lockscreen animated fadeInDown">
<div>
<div class="m-b-md">
@ -47,7 +47,5 @@
<script src="/static/js/jquery-2.1.1.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
</body></html>
</body>
</html>

View File

@ -4,7 +4,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">

View File

@ -26,21 +26,16 @@
<td class="text-center">角色</td>
<td class="text-center">Email</td>
<td class="text-center">激活</td>
{# <td class="text-center">添加时间</td>#}
{# <td class="text-center">最后登录</td>#}
</tr>
</thead>
<tbody>
<tr class="gradeX">
<td class="text-center">{{ user.id }}</td>
<td class="text-center">{{ user.username }}</td>
<td class="text-center">{{ user.name }}</td>
<td class="text-center">{{ user.id | get_role }}</td>
<td class="text-center">{{ user.email }}</td>
<td class="text-center">{{ user.is_active|bool2str }}</td>
{# <td class="text-center">{{ user.date_joined }}</td>#}
{# <td class="text-center">{{ user.last_login }}</td>#}
</tr>
<tr>
<td class="text-center">添加日期: </td>
@ -56,23 +51,6 @@
{% endfor %}
</td>
</tr>
{# <tr>#}
{# <td colspan="1" class="text-center">授权主机组:</td>#}
{# <td colspan="6" class="text-center">#}
{# {% for group in user|get_user_asset_group %}#}
{# <b>{{ group.name }}</b>#}
{# {% endfor %}#}
{# </td>#}
{# </tr>#}
{# #}
{# <tr class="gradeX">#}
{# <td class="text-center">授权主机组</td>#}
{# <td class="text-center">#}
{# {% for asset_group in asset_group_permed %}#}
{# {{ asset_group.name }}#}
{# {% endfor %}#}
{# </td>#}
{# </tr>#}
</table>
</div>
</div>

View File

@ -1,4 +1,5 @@
<html><head>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -7,21 +8,20 @@
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/font-awesome/css/font-awesome.css" rel="stylesheet">
<link href="/static/css/animate.css" rel="stylesheet">
<link href="/static/css/style.css" rel="stylesheet">
<style type="text/css"></style></head>
<style type="text/css"></style>
</head>
<body class="gray-bg">
<div class="lock-word animated fadeInDown">
<span class="first-word">Jump</span><span>Server</span>
</div>
<div class="lock-word animated fadeInDown">
<span class="first-word">Jump</span><span>Server</span>
</div>
<div class="middle-box text-center lockscreen animated fadeInDown">
<div>
<div class="m-b-md">
{# <img alt="image" class="img-circle circle-border" src="https://s3.amazonaws.com/uifaces/faces/twitter/ok/128.jpg">#}
{% if error %}
<div class="alert alert-warning text-center">{{ error }}</div>
{% endif %}
@ -30,21 +30,44 @@
{% endif %}
</div>
<h3>请输入新密码</h3>
<form class="m-t" role="form" action="{{ action }}" method="post">
<form class="m-t" role="form" id="restForm" action="{{ action }}" method="post">
<div class="form-group">
<input type="password" name='password' class="form-control" placeholder="New Password" required="">
<input type="password" name='password_confirm' class="form-control" placeholder="Password Confirm" required="">
<input type="password" name='password' class="form-control" placeholder="New Password">
<input type="password" name='password_confirm' class="form-control" placeholder="Password Confirm">
</div>
<button type="submit" class="btn btn-primary block full-width">确定</button>
</form>
</div>
</div>
<!-- Mainly scripts -->
<script src="/static/js/jquery-2.1.1.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/validator/jquery.validator.js"></script>
</body></html>
<script>
$('#restForm').validator({
timely: 2,
theme: "yellow_right_effect",
rules: {
check_pass: [/^\w{8,50}$/, '数字和字符,8-50位']
},
fields: {
"password": {
rule: "required;check_pass",
tip: "输入密码",
ok: "",
msg: {required: "必须填写!"}
},
'password_confirm': {
rule: "required;check_pass;",
tip: '确认密码',
msg: {required: "必须填写!"}
}
},
valid: function(form) {
form.submit();
}
});
</script>
</body>
</html>

View File

@ -6,7 +6,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 命令批量执行 </h5>

View File

@ -6,7 +6,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>
@ -99,11 +99,11 @@
<label><input type="checkbox" value="0" name="extra" >禁用 </label>
</div>
</div>
<div class="col-sm-2">
<div class="checkbox i-checks">
<label><input type="checkbox" value="1" name="extra">ssh key登录 </label>
</div>
</div>
{# <div class="col-sm-2">#}
{# <div class="checkbox i-checks">#}
{# <label><input type="checkbox" value="1" name="extra">ssh key登录 </label>#}
{# </div>#}
{# </div>#}
<div class="col-sm-2">
<div class="checkbox i-checks">
<label><input type="checkbox" value="2" name="extra">发送邮件 </label>
@ -167,16 +167,16 @@ $('#userForm').validator({
}
});
$("document").ready(function(){
$("input.role").click(function(){
if($("input.role[value=GA]").is( ":checked" )){
$("#admin_groups").css("display", 'block');
}
else {
$("#admin_groups").css("display", 'none');
}
})
})
{#$("document").ready(function(){#}
{# $("input.role").click(function(){#}
{# if($("input.role[value=GA]").is( ":checked" )){#}
{# $("#admin_groups").css("display", 'block');#}
{# }#}
{# else {#}
{# $("#admin_groups").css("display", 'none');#}
{# }#}
{# })#}
{#})#}
</script>
{% endblock %}

View File

@ -6,7 +6,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>{{ user.name }}</b></span>
@ -45,9 +45,17 @@
<td>{{ user.name }}</td>
</tr>
<tr>
<td class="text-navy">角色</td>
<td class="text-navy">权限</td>
<td>{{ user.id | get_role }}</td>
</tr>
<tr>
<td class="text-navy">key</td>
{% if user.username|key_exist %}
<td><a href="/juser/down_key/?id={{ user.id }}" >下载</a></td>
{% else %}
<td><span style="color: #586b7d">下载</span></td>
{% endif %}
</tr>
<tr>
<td class="text-navy">Email</td>
<td>{{ user.email }}</td>
@ -76,13 +84,43 @@
</table>
</td>
</tr>
<tr>
<td class="text-navy">授权主机数量</td>
<td>
{{ user_perm_info.asset.keys | length }}
</td>
</tr>
<tr>
<td class="text-navy">授权主机组</td>
<td>
<table class="table">
{% for group in user_perm_info.asset_group.keys%}
<tr>
<td><a href="/jasset/group_list/?id={{ group.id }}">{{ group.name }}</a></td>
</tr>
{% endfor %}
</table>
</td>
</tr>
<tr>
<td class="text-navy">授权规则</td>
<td>
<table class="table">
{% for rule in user_perm_info.rule%}
<tr>
<td><a href="/jperm/role/?id={{ rule.id }}">{{ rule.name }}</a></td>
</tr>
{% endfor %}
</table>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>授权主机/组</h5>
@ -94,10 +132,6 @@
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#">Config option 1</a>
</li>
<li><a href="#">Config option 2</a>
</li>
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
@ -106,38 +140,40 @@
</div>
<div class="ibox-content ibox-heading">
<h3>用户的所有授权主机</h3>
<small><i class="fa fa-map-marker"></i> 这里包含了用户所有的主机组和组下的主机.</small>
<small><i class="fa fa-map-marker"></i> 这里包含了用户授权角色和角色下的主机.</small>
</div>
<div class="ibox-content inspinia-timeline">
{# {% for group in user|get_user_asset_group %}#}
{# <div class="timeline-item">#}
{# <div class="row">#}
{# <div class="col-xs-3 date">#}
{# <i class="fa fa-repeat"></i>#}
{# <b><a href="/jperm/perm_list/?uid={{ user.id }}&agid={{ group.id }}">{{ group.name }}</a></b>#}
{# <br>#}
{# <small class="text-navy">共: {{ group | group_asset_list_count }}台</small>#}
{# </div>#}
{# <div class="col-xs-7 content no-top-border">#}
{# <p class="m-b-xs"><strong>{{ group.comment }}</strong></p>#}
{# <p>#}
{# {% for asset in group|group_asset_list %}#}
{# {{ asset.ip }}<br>#}
{# {% endfor %}#}
{# </p>#}
{# <p></p>#}
{# </div>#}
{# </div>#}
{# </div>#}
{# {% endfor %}#}
{# {% if not user|get_user_asset_group %}#}
{# (无)#}
{# {% endif %}#}
{% for role, assets in role_assets.items %}
<div class="timeline-item">
<div class="row">
<div class="col-xs-3 date">
<i class="fa fa-info"></i>
<b>
<a href="/jperm/role/&id={{ role.id }}">{{ role.name }}</a></b>
<br>
<small class="text-navy">共: {{ assets.asset | length }}台</small>
</div>
<div class="col-xs-7 content no-top-border">
<p class="m-b-xs">
<strong>{{ role.comment }}</strong></p>
<p>
{% for asset in assets.asset %}
<a href="/jasset/asset_list/?id={{ asset.id }}">{{ asset.hostname }}</a><br>
{% endfor %}
</p>
<p></p>
</div>
</div>
</div>
{% endfor %}
{% ifequal '{{ asset.asset | length }}' 0 %}
(无)
{% endifequal %}
</div>
</div>
</div>
<div class="col-lg-4">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>登录记录</h5>
@ -147,8 +183,9 @@
</div>
<div class="ibox-content">
<div id="last">
<div class="feed-activity-list" >
{% for log in logs_last %}
{% for log in user_log_ten %}
<div class="feed-element">
<a href="profile.html" class="pull-left">
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
@ -161,18 +198,18 @@
</div>
</div>
{% endfor %}
{% if not logs_last %}
{% if not user_log_ten %}
(暂无)
{% endif %}
</div>
{% if logs_num > 10 %}
<button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> Show All</button>
{% if user_log_last_num > 10 %}
<button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> Show More</button>
{% endif %}
</div>
<div id="all" style="display: none">
<div class="feed-activity-list" >
{% for log in logs_all %}
{% for log in user_log_last %}
<div class="feed-element">
<a href="profile.html" class="pull-left">
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">

View File

@ -6,7 +6,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>编辑用户信息</h5>
@ -55,7 +55,7 @@
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="groups" class="col-lg-2 control-label">小组</label>
<label for="groups" class="col-sm-2 control-label">小组</label>
<div class="col-sm-8">
<select id="groups" name="groups" class="form-control m-b" multiple size="12">
{% for group in group_all %}
@ -70,7 +70,7 @@
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="role" class="col-lg-2 control-label">角色<span class="red-fonts">*</span></label>
<label for="role" class="col-sm-2 control-label">角色<span class="red-fonts">*</span></label>
<div class="col-sm-8">
{% for r, role_name in user_role.items %}
<div class="col-sm-3">
@ -140,13 +140,9 @@
$('#userForm').validator({
timely: 2,
theme: "yellow_right_effect",
{# rules: {#}
{# check_ip: [/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/, 'ip地址不正确'],#}
{# check_port: [/^\d{1,5}$/, '端口号不正确'],#}
{# type_m: function (element) {#}
{# return $("#M").is(":checked");#}
{# }#}
{# },#}
rules: {
check_pass: [/^\w+$/, '数字和字符']
},
fields: {
"username": {
rule: "required",
@ -155,7 +151,7 @@ $('#userForm').validator({
msg: {required: "必须填写!"}
},
"password": {
rule: "length[6~50]",
rule: "length[6~50];check_pass",
tip: "输入密码",
ok: "",
msg: {required: "必须填写!"}

View File

@ -5,7 +5,7 @@
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 查看用户 </h5>
@ -24,30 +24,31 @@
<div class="ibox-content">
<div class="">
<a target="_blank" href="/juser/user_add/" class="btn btn-sm btn-primary "> 添加用户 </a>
<a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" id="search_input" name="keyword" placeholder="Search">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
-搜索-
</button>
<a target="_blank" href="/juser/user_add/" class="btn btn-sm btn-primary "> 添加用户 </a>
<a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" id="search_input" name="keyword" placeholder="Search">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
-搜索-
</button>
</div>
</div>
</div>
</form>
</form>
</div>
<table class="table table-striped table-bordered table-hover " id="editable" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll()">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th>
<th class="text-center">用户名</th>
<th class="text-center">姓名</th>
<th class="text-center">小组</th>
<th class="text-center">角色</th>
<th class="text-center">权限</th>
<th class="text-center">主机数量</th>
<th class="text-center">激活</th>
<th class="text-center">下载密钥</th>
<th class="text-center">操作</th>
@ -59,20 +60,20 @@
<td class="text-center">
<input type="checkbox" name="checked" value="{{ user.id }}">
</td>
<td class="text-center"> {{ user.username }} </td>
<td class="text-center"><a href="../user_detail/?id={{ user.id }}">{{ user.username }}</a></td>
<td class="text-center"> {{ user.name }} </td>
<td class="text-center" title="{% for user_group in user.group.all %} {{ user_group.name }} {% endfor %}"> {{ user.group.all | groups2str }} </td>
<td class="text-center"> {{ user.id | get_role }}</td>
<th class="text-center">{{ user.id | user_perm_asset_num }}</th>
<td class="text-center">{{ user.is_active | bool2str }}</td>
<td class="text-center">
{% if user.username|key_exist %}
<a href="/juser/down_key/?id={{ user.id }}" >下载</a>
<a href="/juser/down_key/?uuid={{ user.uuid }}" >下载</a>
{% else %}
<span style="color: #586b7d">下载</span>
{% endif %}
</td>
<td class="text-center">
<a href="../user_detail/?id={{ user.id }}" class="btn btn-xs btn-primary">详情</a>
<a href="../user_edit/?id={{ user.id }}" class="btn btn-xs btn-info">编辑</a>
<a value="{{ user.uuid }}" class="btn btn-xs btn-warning email">Email</a>
<a value="../user_del/?id={{ user.id }}" class="btn btn-xs btn-danger del {% if user.username == 'admin' %} disabled {% endif %}">删除</a>
@ -122,6 +123,7 @@
$(".gradeX input:checked").each(function() {
check_array.push($(this).attr("value"))
});
console.log(check_array.join(","));
$.post("/juser/user_del/",
{id: check_array.join(",")},
function(data){

View File

@ -99,7 +99,7 @@
{# <div id="message"></div>#}
{# </div>#}
{# </div>#}
<div class="col-lg-12">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title" style="border: solid">
<h5> 实时监控 </h5>

View File

@ -9,37 +9,42 @@
<li id="juser">
<a href="#"><i class="fa fa-group"></i> <span class="nav-label">用户管理</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li class="group_list group_edit"><a href="/juser/group_list/">查看用户组</a></li>
<li class="user_list user_edit user_detail"><a href="/juser/user_list/">查看用户<span class="label {% ifequal user_active_num user_total_num %}label-primary {% else %}label-warning {% endifequal %}pull-right">{{ user_active_num }}/{{ user_total_num }}</span></a></li>
<li class="group_list group_edit group_add"><a href="/juser/group_list/">查看用户组</a></li>
<li class="user_list user_edit user_detail user_add"><a href="/juser/user_list/">查看用户<span class="label {% ifequal user_active_num user_total_num %}label-primary {% else %}label-warning {% endifequal %}pull-right">{{ user_active_num }}/{{ user_total_num }}</span></a></li>
</ul>
</li>
<li id="jasset">
<a><i class="fa fa-inbox"></i> <span class="nav-label">资产管理</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li class="group_list group_detail group_edit"><a href="/jasset/group_list/">查看资产组</a></li>
<li class="asset_list asset_detail asset_edit"><a href="/jasset/asset_list/">查看资产<span class="label label-info pull-right">{{ host_active_num }}/{{ host_total_num}}</span></a></li>
<li class="asset_list asset_detail asset_edit asset_add"><a href="/jasset/asset_list/">查看资产<span class="label label-info pull-right">{{ host_active_num }}/{{ host_total_num}}</span></a></li>
<li class="idc_list idc_detail idc_edit"><a href="/jasset/idc_list/">查看机房</a></li>
</ul>
</li>
<li id="jperm">
<a href="#"><i class="fa fa-edit"></i> <span class="nav-label">授权管理</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li class="rule perm_rule_add">
<li class="rule perm_rule_add perm_rule_detail perm_rule_edit">
<a href="/jperm/rule/">授权规则</a>
</li>
<li class="role">
<a href="/jperm/role/">系统角色</a>
</li>
<li class="sudo">
<a href="/jperm/sudo/">Sudo命令</a>
</li>
</ul>
</li>
<li id="jlog">
<a href="/jlog/log_list/online/"><i class="fa fa-files-o"></i> <span class="nav-label">日志审计</span><span class="label label-info pull-right"></span></a>
</li>
<li id="file">
<a href="#"><i class="fa fa-download"></i> <span class="nav-label">上传下载</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li class="upload"><a href="/file/upload/">文件上传</a></li>
<li class="download"><a href="/file/download/">文件下载</a></li>
</ul>
</li>
<li id="setting">
<a href="/setting/"><i class="fa fa-gears"></i> <span class="nav-label">设置</span><span class="label label-info pull-right"></span></a>
</li>
@ -51,87 +56,18 @@
</div>
</nav>
{% endif %}
{% if request.session.role_id == 1 %}
<nav class="navbar-default navbar-static-side" role="navigation">
<div class="sidebar-collapse">
<ul class="nav" id="side-menu">
{% include 'nav_li_profile.html' %}
<li id="index">
<a href="/"><i class="fa fa-th-large"></i> <span class="nav-label">仪表盘</span><span class="label label-info pull-right"></span></a>
</li>
<li id="juser">
<a href="#"><i class="fa fa-rebel"></i> <span class="nav-label">用户管理</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li class="dept_list dept_edit"><a href="/juser/dept_list/">查看部门</a></li>
<li class="group_list group_edit"><a href="/juser/group_list/">查看小组</a></li>
<li class="group_add"><a href="/juser/group_add/">添加小组</a></li>
<li class="user_list user_detail"><a href="/juser/user_list/">查看用户<span class="label {% ifequal user_active_num user_total_num %}label-primary {% else %}label-warning {% endifequal %}pull-right">{{ user_active_num }}/{{ user_total_num }}</span></a></li>
<li class="user_add"><a href="/juser/user_add/">添加用户</a></li>
</ul>
</li>
<li id="jasset">
<a><i class="fa fa-cube"></i> <span class="nav-label">资产管理</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
{# <li class="host_add host_add_multi"><a href="/jasset/host_add/">添加资产</a></li>#}
<li class="asset_list asset_detail asset_edit"><a href="/jasset/asset_list/">查看资产<span class="label label-info pull-right">{{ host_active_num }}/{{ host_total_num}}</span></a></li>
<li class="idc_list idc_detail idc_edit"><a href="/jasset/idc_list/">查看IDC</a></li>
<li class="group_add"><a href="/jasset/group_add/">添加主机组</a></li>
<li class="group_list group_detail group_edit"><a href="/jasset/group_list/">查看主机组</a></li>
</ul>
</li>
<li id="jperm">
<a href="#"><i class="fa fa-edit"></i> <span class="nav-label">授权管理</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li class="perm_list perm_edit perm_detail">
<a href="/jperm/perm_list/">小组授权</a>
</li>
<li class="sudo_list sudo_edit sudo_add cmd_list cmd_edit cmd_add sudo_detail">
<a href="/jperm/sudo_list/">Sudo授权</a>
</li>
<li class="apply_show online"><a href="/jperm/apply_show/online/">权限审批</a></li>
</ul>
</li>
<li id="jlog">
<a href="/jlog/log_list/online/"><i class="fa fa-files-o"></i> <span class="nav-label">日志审计</span><span class="label label-info pull-right"></span></a>
</li>
<li class="special_link">
<a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i> <span class="nav-label">访问官网</span></a>
</li>
</ul>
</div>
</nav>
{% endif %}
{% if request.session.role_id == 0 %}
<nav class="navbar-default navbar-static-side" role="navigation">
<div class="sidebar-collapse">
<ul class="nav" id="side-menu">
{% include 'nav_li_profile.html' %}
<li id="index">
<a href="/"><i class="fa fa-th-large"></i> <span class="nav-label">仪表盘</span><span class="label label-info pull-right"></span></a>
</li>
<li class="user_detail">
<a href="/juser/user_detail/?id={{ session_user_id }}"><i class="fa fa-rebel"></i> <span class="nav-label">个人信息</span><span class="label label-info pull-right"></span></a>
<li id="juser">
<a href="/juser/user_detail/"><i class="fa fa-dashboard"></i> <span class="nav-label">仪表盘</span><span class="label label-info pull-right"></span></a>
</li>
<li id="jasset">
<a href="/jasset/asset_list/"><i class="fa fa-cube"></i> <span class="nav-label">查看主机</span><span class="label label-info pull-right"></span></a>
<a href="/jasset/asset_list/"><i class="fa fa-inbox"></i> <span class="nav-label">查看主机</span><span class="label label-info pull-right"></span></a>
</li>
<li id="jperm">
<a><i class="fa fa-cube"></i> <span class="nav-label">权限申请</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li class="apply"><a href="/jperm/apply/">申请主机</a></li>
<li class="apply_show online"><a href="/jperm/apply_show/online/">申请记录</a></li>
</ul>
</li>
<li id="command">
<a href="/juser/runcommand/"><span>批量执行命令</span></a>
</li>
<li id="jlog">
<a href="/jlog/log_list/online/"><i class="fa fa-files-o"></i> <span class="nav-label">登录历史</span><span class="label label-info pull-right"></span></a>
</li>
<li id="file">
<a href="#"><i class="fa fa-download"></i> <span class="nav-label">上传下载</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">

View File

@ -1,5 +1,5 @@
<div class="row wrapper border-bottom white-bg page-heading">
<div class="col-lg-10">
<div class="col-sm-10">
<h2>{{ header_title }}</h2>
<ol class="breadcrumb">
<li>
@ -17,6 +17,6 @@
{% endif %}
</ol>
</div>
<div class="col-lg-2">
<div class="col-sm-2">
</div>
</div>

View File

@ -17,13 +17,13 @@
<ul class="dropdown-menu animated fadeInRight m-t-xs">
<li><a value="/juser/profile/?id={{ session_user_id }}" class="iframe_user">个人信息</a></li>
<li><a href="/juser/change_info/">修改信息</a></li>
{% if not user.role == 'CU' %}
{% if request.session.role_id == 0 %}
<li><a href="/juser/change_role/">系统后台</a></li>
{% else %}
<li><a href="/juser/change_role/">主机控制台</a></li>
{% endif %}
{% endif %}
{# {% if not user.role == 'CU' %}#}
{# {% if request.session.role_id == 0 %}#}
{# <li><a href="/juser/change_role/">系统后台</a></li>#}
{# {% else %}#}
{# <li><a href="/juser/change_role/">主机控制台</a></li>#}
{# {% endif %}#}
{# {% endif %}#}
<li class="divider"></li>
<li><a href="/logout/">注销</a></li>
</ul>

View File

@ -6,7 +6,7 @@
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 项目设置 </h5>

View File

View File

@ -1,85 +0,0 @@
<html>
<body>
<div id="main" style="height:400px;"></div>
...
<script src="/static/js/echarts/echarts.js"></script>
<script type="text/javascript">
require.config({
paths: {
'echarts': '/static/js/echarts/chart',
'echarts/chart/line': '/static/js/echarts/chart/line'
}
});
require(
[
'echarts',
'echarts/chart/line'
],
function (ec) {
var myChart = ec.init(document.getElementById('main'));
var option = {
title : {
text: '某楼盘销售情况',
subtext: '纯属虚构'
},
tooltip : {
trigger: 'axis'
},
legend: {
data:['意向','预购','成交']
},
toolbox: {
show : true,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {show: true, type: ['line', 'bar', 'stack', 'tiled']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis : [
{
type : 'category',
boundaryGap : false,
data : ['周一','周二','周三','周四','周五','周六','周日']
}
],
yAxis : [
{
type : 'value'
}
],
series : [
{
name:'成交',
type:'line',
smooth:true,
itemStyle: {normal: {areaStyle: {type: 'default'}}},
data:[10, 12, 21, 54, 260, 830, 710]
},
{
name:'预购',
type:'line',
smooth:true,
itemStyle: {normal: {areaStyle: {type: 'default'}}},
data:[30, 182, 434, 791, 390, 30, 10]
},
{
name:'意向',
type:'line',
smooth:true,
itemStyle: {normal: {areaStyle: {type: 'default'}}},
data:[1320, 1132, 601, 234, 120, 90, 20]
}
]
};
myChart.setOption(option);
}
);
</script>
</body>
</html>

View File

@ -1,5 +1,10 @@
{% extends 'base.html' %}
{% load mytags %}
{% block self_head_css_js %}
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
{% endblock %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<style>
@ -45,7 +50,7 @@
</style>
<div class="wrapper wrapper-content animated fadeIn">
<div class="row">
<div class="col-lg-12">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>上传文件</h5>
@ -53,15 +58,6 @@
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#">Config option 1</a>
</li>
<li><a href="#">Config option 2</a>
</li>
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
@ -69,36 +65,19 @@
</div>
<div class="ibox-content">
<div>
<form id="my-awesome-dropzone" class="dropzone" action="#">
<form id="my-awesome-dropzone" class="dropzone" action="">
<div class="alert alert-warning text-center" id="error" style="display: none"></div>
<div class="alert alert-success text-center" id="msg" style="display: none"></div>
<div class="dropzone-previews">
<input id="hosts" name="hosts" type="text" class="form-control" required="不能为空"
placeholder="输入主机地址,逗号隔开,确保你有输入主机地址的权限" size="80%">
<select name="assetgroup" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset_group in asset_groups %}
<option value="{{ asset_group.name }}">{{ asset_group.name }}</option>
<div class="form-group">
<div class="col-sm-10">
<select name="asset_ids" id="assets" data-placeholder="请选择上传的主机" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset in assets %}
<option value="{{ asset.id }}">{{ asset.hostname }}</option>
{% endfor %}
</select>
{# <div id="hosts_list" style="position:absolute;display: none;z-index:999;">#}
{# TODO: by liuzheng#}
{# <table class="table hovered border ">#}
{# <tbody style="background-color: white">#}
{# <tr>#}
{# <td tabindex="2"><a>aaa</a></td>#}
{# </tr>#}
{# <tr>#}
{# <td tabindex="2"><a>aaa</a></td>#}
{# </tr>#}
{# <tr>#}
{# <td tabindex="2"><a>aaa</a></td>#}
{# </tr>#}
{# <tr>#}
{# <td tabindex="2"><a>aaa</a></td>#}
{# </tr>#}
{##}
{# </tbody>#}
{# </table>#}
{# </div>#}
</div>
</div>
</div>
<button type="submit" class="btn btn-primary pull-right">全部上传</button>
</form>
@ -111,13 +90,12 @@
</div>
</div>
</div>
{% endblock %}
{% block self_footer_js %}
<script>
$(document).ready(function(){
Dropzone.options.myAwesomeDropzone = {
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
@ -127,7 +105,6 @@
// Dropzone settings
init: function() {
var myDropzone = this;
this.element.querySelector("button[type=submit]").addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
@ -136,29 +113,38 @@
this.on("sendingmultiple", function() {
});
this.on("successmultiple", function(files, response) {
alert(response)
$('#msg').css('display', 'block');
$('#msg').html(response)
});
this.on("errormultiple", function(files, response) {
$('#error').css('display', 'block');
$('#error').html(response)
});
}
}
});
{# $("#hosts")[0].onfocus = function () {#}
{# TODO: by liuzheng#}
{# $("#hosts_list").show()#}
{# };#}
{# $("#hosts")[0].focusout = function () {#}
{# $("#hosts_list").hide()#}
{# };#}
var config = {
'.chosen-select' : {},
'.chosen-select-deselect' : {allow_single_deselect:true},
'.chosen-select-no-single' : {disable_search_threshold:10},
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
'.chosen-select-width' : {width:"95%"}
};
for (var selector in config) {
$(selector).chosen(config[selector]);
}
$('#my-awesome-dropzone').validator({
timely: 2,
theme: "yellow_right_effect",
fields: {
"hosts": {
rule: "required",
tip: "输入上传的Host",
tip: "输入上传的主机",
ok: "",
msg: {required: "必须填写!"}
}
@ -168,6 +154,7 @@ $('#my-awesome-dropzone').validator({
}
});
</script>
<script src="/static/js/cropper/cropper.min.js"></script>
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
{% endblock %}