mirror of https://github.com/jumpserver/jumpserver
Merge branch 'dev' of https://git.coding.net/jumpserver/jumpserver into dev
# Conflicts: # jperm/ansible_api.pypull/26/head
commit
a068498561
511
connect.py
511
connect.py
|
@ -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的pid,web 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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
|
@ -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: 默认
|
||||
|
|
135
docs/install.py
135
docs/install.py
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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'资产编号')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
14
jlog/urls.py
14
jlog/urls.py
|
@ -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),
|
||||
)
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
244
jperm/views.py
244
jperm/views.py
|
@ -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')
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'),
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'),
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
137
juser/views.py
137
juser/views.py
|
@ -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×tamp=%s&hash=%s' % (uuid, timestamp, hash_encode)
|
||||
action = '/juser/reset_password/?uuid=%s×tamp=%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))
|
||||
|
||||
|
|
122
run_websocket.py
122
run_websocket.py
|
@ -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()
|
||||
|
|
|
@ -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.
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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>
|
|
@ -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 © 2014-2015
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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>
|
||||
|
|
|
@ -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 }}
|
||||
|
||||
|
|
|
@ -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>#}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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: "资产和资产组必选一个!"}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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: "必须填写!"}
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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">
|
||||
|
|
|
@ -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: "必须填写!"}
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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 %}
|
Loading…
Reference in New Issue