mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-07-05 11:06:34 +00:00
Merge branch 'dev' of https://git.coding.net/jumpserver/jumpserver into dev
# Conflicts: # jperm/ansible_api.py
This commit is contained in:
commit
a068498561
511
connect.py
511
connect.py
@ -14,21 +14,25 @@ import getpass
|
|||||||
import readline
|
import readline
|
||||||
import django
|
import django
|
||||||
import paramiko
|
import paramiko
|
||||||
|
import errno
|
||||||
import struct, fcntl, signal, socket, select
|
import struct, fcntl, signal, socket, select
|
||||||
from io import open as copen
|
from io import open as copen
|
||||||
|
import uuid
|
||||||
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
|
||||||
if django.get_version() != '1.6':
|
if django.get_version() != '1.6':
|
||||||
django.setup()
|
django.setup()
|
||||||
from django.contrib.sessions.models import Session
|
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 ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info
|
||||||
from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR
|
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
|
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 jumpserver.settings import LOG_DIR
|
||||||
from jperm.ansible_api import Command
|
from jperm.ansible_api import MyRunner
|
||||||
from jlog.log_api import escapeString
|
# from jlog.log_api import escapeString
|
||||||
|
from jlog.models import ExecLog, FileLog
|
||||||
|
|
||||||
login_user = get_object(User, username=getpass.getuser())
|
login_user = get_object(User, username=getpass.getuser())
|
||||||
|
remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import termios
|
import termios
|
||||||
@ -46,8 +50,11 @@ def color_print(msg, color='red', exits=False):
|
|||||||
"""
|
"""
|
||||||
color_msg = {'blue': '\033[1;36m%s\033[0m',
|
color_msg = {'blue': '\033[1;36m%s\033[0m',
|
||||||
'green': '\033[1;32m%s\033[0m',
|
'green': '\033[1;32m%s\033[0m',
|
||||||
'red': '\033[1;31m%s\033[0m'}
|
'yellow': '\033[1;33m%s\033[0m',
|
||||||
msg = color_msg.get(color, 'blue') % msg
|
'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
|
print msg
|
||||||
if exits:
|
if exits:
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
@ -66,19 +73,18 @@ class Tty(object):
|
|||||||
A virtual tty class
|
A virtual tty class
|
||||||
一个虚拟终端类,实现连接ssh和记录日志,基类
|
一个虚拟终端类,实现连接ssh和记录日志,基类
|
||||||
"""
|
"""
|
||||||
def __init__(self, user, asset, role):
|
def __init__(self, user, asset, role, login_type='ssh'):
|
||||||
self.username = user.username
|
self.username = user.username
|
||||||
self.asset_name = asset.hostname
|
self.asset_name = asset.hostname
|
||||||
self.ip = None
|
self.ip = None
|
||||||
self.port = 22
|
self.port = 22
|
||||||
|
self.ssh = None
|
||||||
self.channel = None
|
self.channel = None
|
||||||
self.asset = asset
|
self.asset = asset
|
||||||
self.user = user
|
self.user = user
|
||||||
self.role = role
|
self.role = role
|
||||||
self.ssh = None
|
|
||||||
self.remote_ip = ''
|
self.remote_ip = ''
|
||||||
self.connect_info = None
|
self.login_type = login_type
|
||||||
self.login_type = 'ssh'
|
|
||||||
self.vim_flag = False
|
self.vim_flag = False
|
||||||
self.ps1_pattern = re.compile('\[.*@.*\][\$#]')
|
self.ps1_pattern = re.compile('\[.*@.*\][\$#]')
|
||||||
self.vim_data = ''
|
self.vim_data = ''
|
||||||
@ -101,6 +107,41 @@ class Tty(object):
|
|||||||
cmd_str = patch_char.sub('', cmd_str.rstrip())
|
cmd_str = patch_char.sub('', cmd_str.rstrip())
|
||||||
return cmd_str
|
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):
|
def remove_control_char(self, result_command):
|
||||||
"""
|
"""
|
||||||
处理日志特殊字符
|
处理日志特殊字符
|
||||||
@ -127,10 +168,7 @@ class Tty(object):
|
|||||||
"""
|
"""
|
||||||
处理命令中特殊字符
|
处理命令中特殊字符
|
||||||
"""
|
"""
|
||||||
str_r = re.sub('\x07', '', str_r) # 删除响铃
|
str_r = self.remove_obstruct_char(str_r)
|
||||||
patch_char = re.compile('\x08\x1b\[C') # 删除方向左右一起的按键
|
|
||||||
while patch_char.search(str_r):
|
|
||||||
str_r = patch_char.sub('', str_r.rstrip())
|
|
||||||
|
|
||||||
result_command = '' # 最后的结果
|
result_command = '' # 最后的结果
|
||||||
backspace_num = 0 # 光标移动的个数
|
backspace_num = 0 # 光标移动的个数
|
||||||
@ -139,31 +177,21 @@ class Tty(object):
|
|||||||
while str_r:
|
while str_r:
|
||||||
tmp = re.match(r'\s*\w+\s*', str_r)
|
tmp = re.match(r'\s*\w+\s*', str_r)
|
||||||
if tmp:
|
if tmp:
|
||||||
|
str_r = str_r[len(str(tmp.group(0))):]
|
||||||
if reach_backspace_flag:
|
if reach_backspace_flag:
|
||||||
pattern_str += str(tmp.group(0))
|
pattern_str += str(tmp.group(0))
|
||||||
str_r = str_r[len(str(tmp.group(0))):]
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
result_command += str(tmp.group(0))
|
result_command += str(tmp.group(0))
|
||||||
str_r = str_r[len(str(tmp.group(0))):]
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tmp = re.match(r'\x1b\[K[\x08]*', str_r)
|
tmp = re.match(r'\x1b\[K[\x08]*', str_r)
|
||||||
if tmp:
|
if tmp:
|
||||||
if backspace_num > 0:
|
result_command, del_len = self.deal_backspace(str(tmp.group(0)), result_command, pattern_str, backspace_num)
|
||||||
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]
|
|
||||||
reach_backspace_flag = False
|
reach_backspace_flag = False
|
||||||
backspace_num = 0
|
backspace_num = 0
|
||||||
pattern_str = ''
|
pattern_str = ''
|
||||||
str_r = str_r[len(str(tmp.group(0))):]
|
str_r = str_r[del_len:]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tmp = re.match(r'\x08+', str_r)
|
tmp = re.match(r'\x08+', str_r)
|
||||||
@ -179,7 +207,14 @@ class Tty(object):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
break
|
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:
|
if reach_backspace_flag:
|
||||||
pattern_str += str_r[0]
|
pattern_str += str_r[0]
|
||||||
else:
|
else:
|
||||||
@ -189,22 +224,8 @@ class Tty(object):
|
|||||||
if backspace_num > 0:
|
if backspace_num > 0:
|
||||||
result_command = result_command[0:-backspace_num] + pattern_str
|
result_command = result_command[0:-backspace_num] + pattern_str
|
||||||
|
|
||||||
control_char = re.compile(r"""
|
result_command = self.remove_control_char(result_command)
|
||||||
\x1b[ #%()*+\-.\/]. |
|
return result_command
|
||||||
\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 ''
|
|
||||||
|
|
||||||
def get_log(self):
|
def get_log(self):
|
||||||
"""
|
"""
|
||||||
@ -223,29 +244,26 @@ class Tty(object):
|
|||||||
mkdir(today_connect_log_dir, mode=0777)
|
mkdir(today_connect_log_dir, mode=0777)
|
||||||
except OSError:
|
except OSError:
|
||||||
logger.debug('创建目录 %s 失败,请修改%s目录权限' % (today_connect_log_dir, tty_log_dir))
|
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:
|
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_file_f = open(log_file_path + '.log', 'a')
|
||||||
log_time_f = open(log_file_path + '.time', 'a')
|
log_time_f = open(log_file_path + '.time', 'a')
|
||||||
except IOError:
|
except IOError:
|
||||||
logger.debug('创建tty日志文件失败, 请修改目录%s权限' % today_connect_log_dir)
|
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
|
if self.login_type == 'ssh': # 如果是ssh连接过来,记录connect.py的pid,web terminal记录为日志的id
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
self.remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n') # 获取远端IP
|
self.remote_ip = remote_ip # 获取远端IP
|
||||||
else:
|
else:
|
||||||
pid = 0
|
pid = 0
|
||||||
|
|
||||||
log = Log(user=self.username, host=self.asset_name, remote_ip=self.remote_ip, login_type=self.login_type,
|
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_path=log_file_path, start_time=date_today, pid=pid)
|
||||||
|
|
||||||
log.save()
|
log.save()
|
||||||
if self.login_type == 'web':
|
if self.login_type == 'web':
|
||||||
log.pid = log.id
|
log.pid = log.id # 设置log id为websocket的id, 然后kill时干掉websocket
|
||||||
log.save()
|
log.save()
|
||||||
|
|
||||||
log_file_f.write('Start at %s\r\n' % datetime.datetime.now())
|
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)
|
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)
|
role_pass = CRYPTOR.decrypt(self.role.password)
|
||||||
self.connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'),
|
connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'),
|
||||||
'port': int(asset_info.get('port')), 'role_name': self.role.name,
|
'port': int(asset_info.get('port')), 'role_name': self.role.name,
|
||||||
'role_pass': role_pass, 'role_key': role_key}
|
'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'),
|
logger.debug(connect_info)
|
||||||
asset_info.get('port'),
|
return connect_info
|
||||||
self.role.name,
|
|
||||||
role_pass,
|
|
||||||
role_key))
|
|
||||||
return self.connect_info
|
|
||||||
|
|
||||||
def get_connection(self):
|
def get_connection(self):
|
||||||
"""
|
"""
|
||||||
@ -285,18 +299,19 @@ class Tty(object):
|
|||||||
ssh.connect(connect_info.get('ip'),
|
ssh.connect(connect_info.get('ip'),
|
||||||
port=connect_info.get('port'),
|
port=connect_info.get('port'),
|
||||||
username=connect_info.get('role_name'),
|
username=connect_info.get('role_name'),
|
||||||
|
password=connect_info.get('role_pass'),
|
||||||
key_filename=role_key,
|
key_filename=role_key,
|
||||||
look_for_keys=False)
|
look_for_keys=False)
|
||||||
self.ssh = ssh
|
|
||||||
return ssh
|
return ssh
|
||||||
except (paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException):
|
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
|
pass
|
||||||
|
|
||||||
ssh.connect(connect_info.get('ip'),
|
ssh.connect(connect_info.get('ip'),
|
||||||
port=connect_info.get('port'),
|
port=connect_info.get('port'),
|
||||||
username=connect_info.get('role_name'),
|
username=connect_info.get('role_name'),
|
||||||
password=connect_info.get('role_pass'),
|
password=connect_info.get('role_pass'),
|
||||||
|
allow_agent=False,
|
||||||
look_for_keys=False)
|
look_for_keys=False)
|
||||||
|
|
||||||
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
|
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
|
||||||
@ -357,18 +372,30 @@ class SshTty(Tty):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
r, w, e = select.select([self.channel, sys.stdin], [], [])
|
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:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if self.channel in r:
|
if self.channel in r:
|
||||||
try:
|
try:
|
||||||
x = self.channel.recv(1024)
|
x = self.channel.recv(10240)
|
||||||
if len(x) == 0:
|
if len(x) == 0:
|
||||||
break
|
break
|
||||||
if self.vim_flag:
|
if self.vim_flag:
|
||||||
self.vim_data += x
|
self.vim_data += x
|
||||||
sys.stdout.write(x)
|
index = 0
|
||||||
sys.stdout.flush()
|
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()
|
now_timestamp = time.time()
|
||||||
log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x)))
|
log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x)))
|
||||||
log_time_f.flush()
|
log_time_f.flush()
|
||||||
@ -384,7 +411,7 @@ class SshTty(Tty):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if sys.stdin in r:
|
if sys.stdin in r:
|
||||||
x = os.read(sys.stdin.fileno(), 1)
|
x = os.read(sys.stdin.fileno(), 4096)
|
||||||
input_mode = True
|
input_mode = True
|
||||||
if str(x) in ['\r', '\n', '\r\n']:
|
if str(x) in ['\r', '\n', '\r\n']:
|
||||||
if self.vim_flag:
|
if self.vim_flag:
|
||||||
@ -420,34 +447,25 @@ class SshTty(Tty):
|
|||||||
Connect server.
|
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连接请求 Make a ssh connection
|
||||||
ssh = self.get_connection()
|
ssh = self.get_connection()
|
||||||
|
|
||||||
|
transport = ssh.get_transport()
|
||||||
|
transport.set_keepalive(30)
|
||||||
|
transport.use_compression(True)
|
||||||
|
|
||||||
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
|
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
|
||||||
global channel
|
global channel
|
||||||
win_size = self.get_win_size()
|
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:
|
try:
|
||||||
signal.signal(signal.SIGWINCH, self.set_win_size)
|
signal.signal(signal.SIGWINCH, self.set_win_size)
|
||||||
except:
|
except:
|
||||||
pass
|
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()
|
self.posix_shell()
|
||||||
|
|
||||||
# Shutdown channel socket
|
# Shutdown channel socket
|
||||||
@ -456,6 +474,9 @@ class SshTty(Tty):
|
|||||||
|
|
||||||
|
|
||||||
class Nav(object):
|
class Nav(object):
|
||||||
|
"""
|
||||||
|
导航提示类
|
||||||
|
"""
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.search_result = {}
|
self.search_result = {}
|
||||||
@ -467,24 +488,17 @@ class Nav(object):
|
|||||||
Print prompt
|
Print prompt
|
||||||
打印提示导航
|
打印提示导航
|
||||||
"""
|
"""
|
||||||
msg = """\n\033[1;32m### Welcome To Use JumpServer, A Open Source System . ### \033[0m
|
msg = """\n\033[1;32m### 欢迎使用Jumpserver开源跳板机系统 ### \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
|
|
||||||
1) 输入 \033[32mID\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 显示您有权限的主机.
|
3) 输入 \033[32mP/p\033[0m 显示您有权限的主机.
|
||||||
4) 输入 \033[32mG/g\033[0m 显示您有权限的主机组.
|
4) 输入 \033[32mG/g\033[0m 显示您有权限的主机组.
|
||||||
5) 输入 \033[32mG/g\033[0m\033[0m + \033[32m组ID\033[0m 显示该组下主机.
|
5) 输入 \033[32mG/g\033[0m\033[0m + \033[32m组ID\033[0m 显示该组下主机.
|
||||||
6) 输入 \033[32mE/e\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)
|
print textwrap.dedent(msg)
|
||||||
|
|
||||||
@ -512,17 +526,14 @@ class Nav(object):
|
|||||||
user_asset_search = user_asset_all
|
user_asset_search = user_asset_all
|
||||||
|
|
||||||
self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search))
|
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():
|
for index, asset in self.search_result.items():
|
||||||
# 获取该资产信息
|
# 获取该资产信息
|
||||||
asset_info = get_asset_info(asset)
|
asset_info = get_asset_info(asset)
|
||||||
# 获取该资产包含的角色
|
# 获取该资产包含的角色
|
||||||
role = [str(role.name) for role in self.user_perm.get('asset').get(asset).get('role')]
|
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'),
|
||||||
print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'),
|
role, asset.comment)
|
||||||
role, asset.comment)
|
|
||||||
else:
|
|
||||||
print '[%-3s] %-15s %-15s %-5s %-10s' % (index, asset.hostname, asset.ip, asset_info.get('port'), role)
|
|
||||||
print
|
print
|
||||||
|
|
||||||
def print_asset_group(self):
|
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', [])
|
user_asset_group_all = get_group_user_perm(self.user).get('asset_group', [])
|
||||||
|
color_print('[%-3s] %-20s %s' % ('ID', '组名', '备注'), 'title')
|
||||||
print '\033[32m[%-3s] %-15s %s \033[0m' % ('ID', 'GroupName', 'Comment')
|
|
||||||
for asset_group in user_asset_group_all:
|
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)
|
||||||
print '[%-3s] %-15s %s' % (asset_group.id, asset_group.name, asset_group.comment)
|
|
||||||
else:
|
|
||||||
print '[%-3s] %-15s' % (asset_group.id, asset_group.name)
|
|
||||||
print
|
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):
|
def exec_cmd(self):
|
||||||
"""
|
"""
|
||||||
批量执行命令
|
批量执行命令
|
||||||
@ -575,98 +553,173 @@ class Nav(object):
|
|||||||
while True:
|
while True:
|
||||||
if not self.user_perm:
|
if not self.user_perm:
|
||||||
self.user_perm = get_group_user_perm(self.user)
|
self.user_perm = get_group_user_perm(self.user)
|
||||||
print '\033[32m[%-2s] %-15s \033[0m' % ('ID', '角色')
|
|
||||||
roles = self.user_perm.get('role').keys()
|
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():
|
for i, r in role_check.items():
|
||||||
print '[%-2s] %-15s' % (i, r.name)
|
print '[%-2s] %-15s' % (i, r.name)
|
||||||
print
|
print
|
||||||
print "请输入运行命令角色的ID, q退出"
|
print "请输入运行命令角色的ID, q退出"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
role_id = raw_input("\033[1;32mRole>:\033[0m ").strip()
|
role_id = raw_input("\033[1;32mRole>:\033[0m ").strip()
|
||||||
if role_id == 'q':
|
if role_id == 'q':
|
||||||
break
|
break
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
color_print('错误输入')
|
||||||
else:
|
else:
|
||||||
role = role_check[int(role_id)]
|
role = role_check[int(role_id)]
|
||||||
assets = list(self.user_perm.get('role', {}).get(role).get('asset'))
|
elif len(roles) == 1: # 授权角色数为1
|
||||||
print "该角色有权限的所有主机"
|
role = roles[0]
|
||||||
for asset in assets:
|
assets = list(self.user_perm.get('role', {}).get(role).get('asset')) # 获取该用户,角色授权主机
|
||||||
print asset.hostname
|
print "该角色有权限的所有主机"
|
||||||
print
|
for asset in assets:
|
||||||
print "请输入主机名、IP或ansile支持的pattern, q退出"
|
print ' %s' % asset.hostname
|
||||||
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
|
print
|
||||||
if pattern == 'q':
|
print "请输入主机名、IP或ansile支持的pattern, 多个主机:分隔, q退出"
|
||||||
break
|
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
|
||||||
else:
|
if pattern == 'q':
|
||||||
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
|
|
||||||
break
|
break
|
||||||
finally:
|
else:
|
||||||
log.is_finished = True
|
res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm)
|
||||||
log.end_time = datetime.datetime.now()
|
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():
|
def main():
|
||||||
@ -701,12 +754,16 @@ def main():
|
|||||||
elif option in ['E', 'e']:
|
elif option in ['E', 'e']:
|
||||||
nav.exec_cmd()
|
nav.exec_cmd()
|
||||||
continue
|
continue
|
||||||
|
elif option in ['U', 'u']:
|
||||||
|
nav.upload()
|
||||||
|
elif option in ['D', 'd']:
|
||||||
|
nav.download()
|
||||||
elif option in ['Q', 'q', 'exit']:
|
elif option in ['Q', 'q', 'exit']:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
asset = nav.search_result[int(option)]
|
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:
|
if len(roles) > 1:
|
||||||
role_check = dict(zip(range(len(roles)), roles))
|
role_check = dict(zip(range(len(roles)), roles))
|
||||||
print "\033[32m[ID] 角色\033[0m"
|
print "\033[32m[ID] 角色\033[0m"
|
||||||
@ -724,7 +781,7 @@ def main():
|
|||||||
color_print('请输入正确ID', 'red')
|
color_print('请输入正确ID', 'red')
|
||||||
continue
|
continue
|
||||||
elif len(roles) == 1:
|
elif len(roles) == 1:
|
||||||
role = roles[0]
|
role = list(roles)[0]
|
||||||
else:
|
else:
|
||||||
color_print('没有映射用户', 'red')
|
color_print('没有映射用户', 'red')
|
||||||
continue
|
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
|
email: admin@jumpserver.org
|
||||||
role: SU
|
role: SU
|
||||||
is_active: 1
|
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
|
django==1.6
|
||||||
pycrypto==2.6.1
|
pycrypto==2.6.1
|
||||||
paramiko==1.15.2
|
paramiko==1.16.0
|
||||||
ecdsa==0.13
|
ecdsa==0.13
|
||||||
MySQL-python==1.2.5
|
MySQL-python==1.2.5
|
||||||
django-uuidfield==0.5.0
|
#django-uuidfield==0.5.0
|
||||||
psutil==2.2.1
|
psutil==3.3.0
|
||||||
xlsxwriter==0.7.7
|
xlsxwriter==0.7.7
|
||||||
xlrd==0.9.4
|
xlrd==0.9.4
|
||||||
django-bootstrap-form
|
django-bootstrap-form==3.2
|
||||||
tornado
|
tornado==4.3
|
||||||
ansible
|
ansible==1.9.4
|
||||||
pyinotify
|
pyinotify==0.9.6
|
||||||
passlib
|
passlib==1.6.5
|
||||||
argparse
|
argparse==1.4.0
|
||||||
|
django_crontab==0.6.0
|
@ -1,4 +1,5 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
from __future__ import division
|
||||||
import xlrd
|
import xlrd
|
||||||
import xlsxwriter
|
import xlsxwriter
|
||||||
from django.db.models import AutoField
|
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 jasset.models import ASSET_STATUS, ASSET_TYPE, ASSET_ENV, IDC, AssetRecord
|
||||||
from jperm.ansible_api import MyRunner
|
from jperm.ansible_api import MyRunner
|
||||||
from jperm.perm_api import gen_resource
|
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):
|
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)
|
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):
|
def sort_ip_list(ip_list):
|
||||||
""" ip地址排序 """
|
""" ip地址排序 """
|
||||||
ip_list.sort(key=lambda s: map(int, s.split('.')))
|
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]:
|
for group_id in value[1]:
|
||||||
group_name = AssetGroup.objects.get(id=int(group_id)).name
|
group_name = AssetGroup.objects.get(id=int(group_id)).name
|
||||||
new.append(group_name)
|
new.append(group_name)
|
||||||
if old == new:
|
if sorted(old) == sorted(new):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
alert_info = [field_name, ','.join(old), ','.join(new)]
|
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)
|
workbook = xlsxwriter.Workbook('static/files/excels/%s' % file_name)
|
||||||
worksheet = workbook.add_worksheet(u'CMDB数据')
|
worksheet = workbook.add_worksheet(u'CMDB数据')
|
||||||
worksheet.set_first_sheet()
|
worksheet.set_first_sheet()
|
||||||
worksheet.set_column('A:Z', 14)
|
worksheet.set_column('A:E', 15)
|
||||||
title = [u'主机名', u'IP', u'IDC', u'MAC', u'远控IP', u'CPU', u'内存', u'硬盘', u'操作系统', u'机柜位置',
|
worksheet.set_column('F:F', 40)
|
||||||
u'所属主机组', u'机器状态', u'备注']
|
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:
|
for asset in asset_all:
|
||||||
group_list = []
|
group_list = []
|
||||||
for p in asset.group.all():
|
for p in asset.group.all():
|
||||||
group_list.append(p.name)
|
group_list.append(p.name)
|
||||||
|
|
||||||
|
disk = get_disk_info(asset.disk)
|
||||||
group_all = '/'.join(group_list)
|
group_all = '/'.join(group_list)
|
||||||
status = asset.get_status_display()
|
status = asset.get_status_display()
|
||||||
idc_name = asset.idc.name if asset.idc else u''
|
idc_name = asset.idc.name if asset.idc else u''
|
||||||
system_type = asset.system_type if asset.idc else u''
|
system_type = asset.system_type if asset.system_type else u''
|
||||||
system_version = asset.system_version if asset.idc else u''
|
system_version = asset.system_version if asset.system_version else u''
|
||||||
system_os = unicode(system_type) + unicode(system_version)
|
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,
|
alter_dic = [asset.hostname, asset.ip, idc_name, group_all, system_os, asset.cpu, asset.memory,
|
||||||
asset.disk, system_os, asset.cabinet, group_all, status,
|
disk, asset.cabinet, asset.mac, asset.remote_ip, status, asset.comment]
|
||||||
asset.comment]
|
|
||||||
data.append(alter_dic)
|
data.append(alter_dic)
|
||||||
format = workbook.add_format()
|
format = workbook.add_format()
|
||||||
format.set_border(1)
|
format.set_border(1)
|
||||||
format.set_align('center')
|
format.set_align('center')
|
||||||
|
format.set_align('vcenter')
|
||||||
|
format.set_text_wrap()
|
||||||
|
|
||||||
format_title = workbook.add_format()
|
format_title = workbook.add_format()
|
||||||
format_title.set_border(1)
|
format_title.set_border(1)
|
||||||
@ -384,12 +313,21 @@ def excel_to_db(excel_file):
|
|||||||
|
|
||||||
|
|
||||||
def get_ansible_asset_info(asset_ip, setup_info):
|
def get_ansible_asset_info(asset_ip, setup_info):
|
||||||
disk_all = setup_info.get("ansible_devices")
|
print asset_ip
|
||||||
disk_need = {}
|
disk_need = {}
|
||||||
for disk_name, disk_info in disk_all.iteritems():
|
disk_all = setup_info.get("ansible_devices")
|
||||||
if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'):
|
if disk_all:
|
||||||
disk_need[disk_name] = disk_info.get("size")
|
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")
|
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_list = all_ip.remove(asset_ip) if asset_ip in all_ip else []
|
||||||
other_ip = ','.join(other_ip_list) if other_ip_list 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_cores = setup_info.get("ansible_processor_count")
|
||||||
cpu = cpu_type + ' * ' + unicode(cpu_cores)
|
cpu = cpu_type + ' * ' + unicode(cpu_cores)
|
||||||
memory = setup_info.get("ansible_memtotal_mb")
|
memory = setup_info.get("ansible_memtotal_mb")
|
||||||
|
try:
|
||||||
|
memory_format = int(round((int(memory) / 1000), 0))
|
||||||
|
except Exception:
|
||||||
|
memory_format = memory
|
||||||
disk = disk_need
|
disk = disk_need
|
||||||
system_type = setup_info.get("ansible_distribution")
|
system_type = setup_info.get("ansible_distribution")
|
||||||
system_version = setup_info.get("ansible_distribution_version")
|
system_version = setup_info.get("ansible_distribution_version")
|
||||||
|
system_arch = setup_info.get("ansible_architecture")
|
||||||
# asset_type = setup_info.get("ansible_system")
|
# asset_type = setup_info.get("ansible_system")
|
||||||
sn = setup_info.get("ansible_product_serial")
|
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
|
return asset_info
|
||||||
|
|
||||||
|
|
||||||
@ -415,6 +357,7 @@ def asset_ansible_update(obj_list, name=''):
|
|||||||
resource = gen_resource(obj_list)
|
resource = gen_resource(obj_list)
|
||||||
ansible_instance = MyRunner(resource)
|
ansible_instance = MyRunner(resource)
|
||||||
ansible_asset_info = ansible_instance.run(module_name='setup', pattern='*')
|
ansible_asset_info = ansible_instance.run(module_name='setup', pattern='*')
|
||||||
|
logger.debug('获取硬件信息: %s' % ansible_asset_info)
|
||||||
for asset in obj_list:
|
for asset in obj_list:
|
||||||
try:
|
try:
|
||||||
setup_info = ansible_asset_info['contacted'][asset.hostname]['ansible_facts']
|
setup_info = ansible_asset_info['contacted'][asset.hostname]['ansible_facts']
|
||||||
@ -422,7 +365,7 @@ def asset_ansible_update(obj_list, name=''):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
asset_info = get_ansible_asset_info(asset.ip, setup_info)
|
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,
|
asset_dic = {"other_ip": other_ip,
|
||||||
"mac": mac,
|
"mac": mac,
|
||||||
"cpu": cpu,
|
"cpu": cpu,
|
||||||
@ -431,6 +374,7 @@ def asset_ansible_update(obj_list, name=''):
|
|||||||
"sn": sn,
|
"sn": sn,
|
||||||
"system_type": system_type,
|
"system_type": system_type,
|
||||||
"system_version": system_version,
|
"system_version": system_version,
|
||||||
|
"system_arch": system_arch,
|
||||||
"brand": brand
|
"brand": brand
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ class AssetForm(forms.ModelForm):
|
|||||||
fields = [
|
fields = [
|
||||||
"ip", "other_ip", "hostname", "port", "group", "username", "password", "use_default_auth",
|
"ip", "other_ip", "hostname", "port", "group", "username", "password", "use_default_auth",
|
||||||
"idc", "mac", "remote_ip", "brand", "cpu", "memory", "disk", "system_type", "system_version",
|
"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'硬盘')
|
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_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_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'机柜号')
|
cabinet = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'机柜号')
|
||||||
position = models.IntegerField(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'资产编号')
|
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 jumpserver.models import Setting
|
||||||
from jasset.forms import AssetForm, IdcForm
|
from jasset.forms import AssetForm, IdcForm
|
||||||
from jasset.models import Asset, IDC, AssetGroup, ASSET_TYPE, ASSET_STATUS
|
from jasset.models import Asset, IDC, AssetGroup, ASSET_TYPE, ASSET_STATUS
|
||||||
from jperm.ansible_api import Tasks, MyRunner
|
from jperm.perm_api import get_group_asset_perm, get_group_user_perm
|
||||||
from jperm.perm_api import gen_resource
|
|
||||||
|
|
||||||
|
|
||||||
@require_role('admin')
|
@require_role('admin')
|
||||||
@ -96,7 +95,9 @@ def group_list(request):
|
|||||||
header_title, path1, path2 = u'查看资产组', u'资产管理', u'查看资产组'
|
header_title, path1, path2 = u'查看资产组', u'资产管理', u'查看资产组'
|
||||||
keyword = request.GET.get('keyword', '')
|
keyword = request.GET.get('keyword', '')
|
||||||
asset_group_list = AssetGroup.objects.all()
|
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:
|
if keyword:
|
||||||
asset_group_list = asset_group_list.filter(Q(name__contains=keyword) | Q(comment__contains=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
|
asset list view
|
||||||
"""
|
"""
|
||||||
header_title, path1, path2 = u'查看资产', u'资产管理', u'查看资产'
|
header_title, path1, path2 = u'查看资产', u'资产管理', u'查看资产'
|
||||||
|
username = request.user.username
|
||||||
|
user_perm = request.session['role_id']
|
||||||
idc_all = IDC.objects.filter()
|
idc_all = IDC.objects.filter()
|
||||||
asset_group_all = AssetGroup.objects.all()
|
asset_group_all = AssetGroup.objects.all()
|
||||||
asset_types = ASSET_TYPE
|
asset_types = ASSET_TYPE
|
||||||
asset_status = ASSET_STATUS
|
asset_status = ASSET_STATUS
|
||||||
|
asset_id = request.GET.get('id')
|
||||||
idc_name = request.GET.get('idc', '')
|
idc_name = request.GET.get('idc', '')
|
||||||
group_name = request.GET.get('group', '')
|
group_name = request.GET.get('group', '')
|
||||||
asset_type = request.GET.get('asset_type', '')
|
asset_type = request.GET.get('asset_type', '')
|
||||||
@ -279,12 +282,19 @@ def asset_list(request):
|
|||||||
if idc:
|
if idc:
|
||||||
asset_find = Asset.objects.filter(idc=idc)
|
asset_find = Asset.objects.filter(idc=idc)
|
||||||
else:
|
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:
|
if idc_name:
|
||||||
asset_find = asset_find.filter(idc__name__contains=idc_name)
|
asset_find = asset_find.filter(idc__name__contains=idc_name)
|
||||||
|
|
||||||
if group_name:
|
if group_name:
|
||||||
|
print asset_find, type(asset_find)
|
||||||
asset_find = asset_find.filter(group__name__contains=group_name)
|
asset_find = asset_find.filter(group__name__contains=group_name)
|
||||||
|
|
||||||
if asset_type:
|
if asset_type:
|
||||||
@ -293,6 +303,9 @@ def asset_list(request):
|
|||||||
if status:
|
if status:
|
||||||
asset_find = asset_find.filter(status__contains=status)
|
asset_find = asset_find.filter(status__contains=status)
|
||||||
|
|
||||||
|
if asset_id:
|
||||||
|
asset_find = asset_find.filter(id=asset_id)
|
||||||
|
|
||||||
if keyword:
|
if keyword:
|
||||||
asset_find = asset_find.filter(
|
asset_find = asset_find.filter(
|
||||||
Q(hostname__contains=keyword) |
|
Q(hostname__contains=keyword) |
|
||||||
@ -318,7 +331,10 @@ def asset_list(request):
|
|||||||
smg = u'excel文件已生成,请点击下载!'
|
smg = u'excel文件已生成,请点击下载!'
|
||||||
return my_render('jasset/asset_excel_download.html', locals(), request)
|
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)
|
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')
|
@require_role('admin')
|
||||||
@ -410,6 +426,18 @@ def asset_detail(request):
|
|||||||
header_title, path1, path2 = u'主机详细信息', u'资产管理', u'主机详情'
|
header_title, path1, path2 = u'主机详细信息', u'资产管理', u'主机详情'
|
||||||
asset_id = request.GET.get('id', '')
|
asset_id = request.GET.get('id', '')
|
||||||
asset = get_object(Asset, id=asset_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')
|
asset_record = AssetRecord.objects.filter(asset=asset).order_by('-alert_time')
|
||||||
|
|
||||||
return my_render('jasset/asset_detail.html', locals(), request)
|
return my_render('jasset/asset_detail.html', locals(), request)
|
||||||
|
@ -3,7 +3,7 @@ from django.db import models
|
|||||||
|
|
||||||
class Log(models.Model):
|
class Log(models.Model):
|
||||||
user = models.CharField(max_length=20, null=True)
|
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)
|
remote_ip = models.CharField(max_length=100)
|
||||||
login_type = models.CharField(max_length=100)
|
login_type = models.CharField(max_length=100)
|
||||||
log_path = models.CharField(max_length=100)
|
log_path = models.CharField(max_length=100)
|
||||||
@ -24,5 +24,26 @@ class Alert(models.Model):
|
|||||||
|
|
||||||
class TtyLog(models.Model):
|
class TtyLog(models.Model):
|
||||||
log = models.ForeignKey(Log)
|
log = models.ForeignKey(Log)
|
||||||
datetime = models.DateTimeField()
|
datetime = models.DateTimeField(auto_now=True)
|
||||||
cmd = models.CharField(max_length=200)
|
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 *
|
from jlog.views import *
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', log_list),
|
(r'^$', log_list),
|
||||||
url(r'^log_list/(\w+)/$', log_list),
|
(r'^log_list/(\w+)/$', log_list),
|
||||||
url(r'^history/$', log_history),
|
(r'^log_detail/(\w+)/$', log_detail),
|
||||||
url(r'^log_kill/', log_kill),
|
(r'^history/$', log_history),
|
||||||
url(r'^record/$', log_record),
|
(r'^log_kill/', log_kill),
|
||||||
url(r'^web_terminal/$', web_terminal),
|
(r'^record/$', log_record),
|
||||||
url(r'^get_role_name/$', get_role_name),
|
(r'^web_terminal/$', web_terminal),
|
||||||
)
|
)
|
@ -8,7 +8,7 @@ from jperm.perm_api import user_have_perm
|
|||||||
from django.http import HttpResponseNotFound
|
from django.http import HttpResponseNotFound
|
||||||
from jlog.log_api import renderTemplate
|
from jlog.log_api import renderTemplate
|
||||||
|
|
||||||
from models import Log
|
from jlog.models import Log, ExecLog, FileLog
|
||||||
from jumpserver.settings import WEB_SOCKET_HOST
|
from jumpserver.settings import WEB_SOCKET_HOST
|
||||||
|
|
||||||
|
|
||||||
@ -21,9 +21,24 @@ def log_list(request, offset):
|
|||||||
username_list = request.GET.getlist('username', [])
|
username_list = request.GET.getlist('username', [])
|
||||||
host_list = request.GET.getlist('host', [])
|
host_list = request.GET.getlist('host', [])
|
||||||
cmd = request.GET.get('cmd', '')
|
cmd = request.GET.get('cmd', '')
|
||||||
print date_seven_day, date_now_str
|
|
||||||
if offset == 'online':
|
if offset == 'online':
|
||||||
|
keyword = request.GET.get('keyword', '')
|
||||||
posts = Log.objects.filter(is_finished=False).order_by('-start_time')
|
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:
|
else:
|
||||||
posts = Log.objects.filter(is_finished=True).order_by('-start_time')
|
posts = Log.objects.filter(is_finished=True).order_by('-start_time')
|
||||||
username_all = set([log.user for log in Log.objects.all()])
|
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))
|
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')
|
@require_role('admin')
|
||||||
def log_kill(request):
|
def log_kill(request):
|
||||||
""" 杀掉connect进程 """
|
""" 杀掉connect进程 """
|
||||||
@ -107,16 +127,6 @@ def log_record(request):
|
|||||||
return HttpResponse('无日志记录!')
|
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')
|
@require_role('user')
|
||||||
def web_terminal(request):
|
def web_terminal(request):
|
||||||
asset_id = request.GET.get('id')
|
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)
|
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())
|
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 passlib.hash import sha512_crypt
|
||||||
|
|
||||||
from utils import get_rand_pass
|
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
|
import os.path
|
||||||
|
|
||||||
@ -117,10 +121,10 @@ class MyRunner(MyInventory):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(MyRunner, self).__init__(*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='',
|
def run(self, module_name='shell', module_args='', timeout=10, forks=10, pattern='*',
|
||||||
sudo=False, sudo_user='root', sudo_pass=''):
|
become=False, become_method='sudo', become_user='root', become_pass=''):
|
||||||
"""
|
"""
|
||||||
run module from andible ad-hoc.
|
run module from andible ad-hoc.
|
||||||
module_name: ansible module_name
|
module_name: ansible module_name
|
||||||
@ -132,13 +136,40 @@ class MyRunner(MyInventory):
|
|||||||
inventory=self.inventory,
|
inventory=self.inventory,
|
||||||
pattern=pattern,
|
pattern=pattern,
|
||||||
forks=forks,
|
forks=forks,
|
||||||
become=sudo,
|
become=become,
|
||||||
become_method='sudo',
|
become_method=become_method,
|
||||||
become_user=sudo_user,
|
become_user=become_user,
|
||||||
become_pass=sudo_pass
|
become_pass=become_pass
|
||||||
)
|
)
|
||||||
self.results = hoc.run()
|
self.results_raw = hoc.run()
|
||||||
return self.results
|
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):
|
class Command(MyInventory):
|
||||||
@ -147,9 +178,9 @@ class Command(MyInventory):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Command, self).__init__(*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.
|
run command from andible ad-hoc.
|
||||||
command : 必须是一个需要执行的命令字符串, 比如
|
command : 必须是一个需要执行的命令字符串, 比如
|
||||||
@ -167,25 +198,34 @@ class Command(MyInventory):
|
|||||||
pattern=pattern,
|
pattern=pattern,
|
||||||
forks=forks,
|
forks=forks,
|
||||||
)
|
)
|
||||||
self.results = hoc.run()
|
self.results_raw = 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
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def raw_results(self):
|
def result(self):
|
||||||
"""
|
result = {}
|
||||||
get the ansible raw results.
|
for k, v in self.results_raw.items():
|
||||||
"""
|
if k == 'dark':
|
||||||
return self.results
|
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
|
@property
|
||||||
def exec_time(self):
|
def exec_time(self):
|
||||||
@ -193,7 +233,7 @@ class Command(MyInventory):
|
|||||||
get the command execute time.
|
get the command execute time.
|
||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
all = self.results.get("contacted")
|
all = self.results_raw.get("contacted")
|
||||||
for key, value in all.iteritems():
|
for key, value in all.iteritems():
|
||||||
result[key] = {
|
result[key] = {
|
||||||
"start": value.get("start"),
|
"start": value.get("start"),
|
||||||
@ -207,7 +247,7 @@ class Command(MyInventory):
|
|||||||
get the comamnd standard output.
|
get the comamnd standard output.
|
||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
all = self.results.get("contacted")
|
all = self.results_raw.get("contacted")
|
||||||
for key, value in all.iteritems():
|
for key, value in all.iteritems():
|
||||||
result[key] = value.get("stdout")
|
result[key] = value.get("stdout")
|
||||||
return result
|
return result
|
||||||
@ -218,7 +258,7 @@ class Command(MyInventory):
|
|||||||
get the command standard error.
|
get the command standard error.
|
||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
all = self.results.get("contacted")
|
all = self.results_raw.get("contacted")
|
||||||
for key, value in all.iteritems():
|
for key, value in all.iteritems():
|
||||||
if value.get("stderr") or value.get("warnings"):
|
if value.get("stderr") or value.get("warnings"):
|
||||||
result[key] = {
|
result[key] = {
|
||||||
@ -231,64 +271,24 @@ class Command(MyInventory):
|
|||||||
"""
|
"""
|
||||||
get the dark results.
|
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.
|
this is a tasks object for include the common command.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Tasks, self).__init__(*args, **kwargs)
|
super(MyTask, 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
|
|
||||||
|
|
||||||
def push_key(self, user, key_path):
|
def push_key(self, user, key_path):
|
||||||
"""
|
"""
|
||||||
push the ssh authorized key to target.
|
push the ssh authorized key to target.
|
||||||
"""
|
"""
|
||||||
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state=present' % (user, key_path)
|
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):
|
def push_multi_key(self, **user_info):
|
||||||
"""
|
"""
|
||||||
@ -315,9 +315,9 @@ class Tasks(Command):
|
|||||||
push the ssh authorized key to target.
|
push the ssh authorized key to target.
|
||||||
"""
|
"""
|
||||||
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path)
|
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=''):
|
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)
|
module_args = 'name=%s shell=/bin/bash password=%s' % (username, encrypt_pass)
|
||||||
else:
|
else:
|
||||||
module_args = 'name=%s shell=/bin/bash' % username
|
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):
|
def add_multi_user(self, **user_info):
|
||||||
"""
|
"""
|
||||||
@ -358,94 +359,38 @@ class Tasks(Command):
|
|||||||
"""
|
"""
|
||||||
delete a host user.
|
delete a host user.
|
||||||
"""
|
"""
|
||||||
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % (username)
|
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % username
|
||||||
self.__run(module_args,
|
self.run("user", module_args, become=True)
|
||||||
"user",)
|
return self.results
|
||||||
|
|
||||||
return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"}
|
|
||||||
|
|
||||||
def add_init_users(self):
|
@staticmethod
|
||||||
"""
|
def gen_sudo_script(role_list, sudo_list):
|
||||||
add initail users: SA, DBA, DEV
|
# receive role_list = [role1, role2] sudo_list = [sudo1, sudo2]
|
||||||
"""
|
# return sudo_alias={'NETWORK': '/sbin/ifconfig, /ls'} sudo_user={'user1': ['NETWORK', 'SYSTEM']}
|
||||||
results = {}
|
sudo_alias = {}
|
||||||
action = results["action_info"] = {}
|
sudo_user = {}
|
||||||
users = {"SA": get_rand_pass(), "DBA": get_rand_pass(), "DEV": get_rand_pass()}
|
for sudo in sudo_list:
|
||||||
for user, password in users.iteritems():
|
sudo_alias[sudo.name] = sudo.commands
|
||||||
ret = self.add_user(user, password)
|
|
||||||
action[user] = ret
|
|
||||||
results["user_info"] = users
|
|
||||||
|
|
||||||
return results
|
for role in role_list:
|
||||||
|
sudo_user[role.name] = ','.join(sudo_alias.keys())
|
||||||
|
|
||||||
def del_init_users(self):
|
sudo_j2 = get_template('jperm/role_sudo.j2')
|
||||||
"""
|
sudo_content = sudo_j2.render(Context({"sudo_alias": sudo_alias, "sudo_user": sudo_user}))
|
||||||
delete initail users: SA, DBA, DEV
|
sudo_file = NamedTemporaryFile(delete=False)
|
||||||
"""
|
sudo_file.write(sudo_content)
|
||||||
results = {}
|
sudo_file.close()
|
||||||
action = results["action_info"] = {}
|
return sudo_file.name
|
||||||
for user in ["SA", "DBA", "DEV"]:
|
|
||||||
ret = self.del_user(user)
|
|
||||||
action[user] = ret
|
|
||||||
return results
|
|
||||||
|
|
||||||
def get_host_info(self):
|
def push_sudo_file(self, role_list, sudo_list):
|
||||||
"""
|
|
||||||
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):
|
|
||||||
"""
|
"""
|
||||||
use template to render pushed sudoers file
|
use template to render pushed sudoers file
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
module_args1 = file_path
|
module_args1 = self.gen_sudo_script(role_list, sudo_list)
|
||||||
ret = self.__run(module_args1, "script")
|
self.run("script", module_args1, become=True)
|
||||||
return ret
|
return self.results
|
||||||
|
|
||||||
|
|
||||||
class CustomAggregateStats(callbacks.AggregateStats):
|
class CustomAggregateStats(callbacks.AggregateStats):
|
||||||
|
@ -55,6 +55,6 @@ class PermPush(models.Model):
|
|||||||
is_public_key = models.BooleanField(default=False)
|
is_public_key = models.BooleanField(default=False)
|
||||||
is_password = models.BooleanField(default=False)
|
is_password = models.BooleanField(default=False)
|
||||||
success = models.BooleanField(default=False)
|
success = models.BooleanField(default=False)
|
||||||
result = models.TextField()
|
result = models.TextField(default='')
|
||||||
date_added = models.DateTimeField(auto_now=True)
|
date_added = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
@ -43,13 +43,16 @@ def get_group_user_perm(ob):
|
|||||||
asset_groups = rule.asset_group.all()
|
asset_groups = rule.asset_group.all()
|
||||||
assets = rule.asset.all()
|
assets = rule.asset.all()
|
||||||
perm_roles = rule.role.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:
|
for role in perm_roles:
|
||||||
if perm_role.get('role'):
|
if perm_role.get(role):
|
||||||
perm_role[role]['asset'] = perm_role[role].get('asset', set()).union(set(assets))
|
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))
|
perm_role[role]['asset_group'] = perm_role[role].get('asset_group', set()).union(set(asset_groups))
|
||||||
else:
|
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:
|
for asset in assets:
|
||||||
@ -161,23 +164,42 @@ def gen_resource(ob, perm=None):
|
|||||||
user = ob.get('user')
|
user = ob.get('user')
|
||||||
if not perm:
|
if not perm:
|
||||||
perm = get_group_user_perm(user)
|
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')
|
if role:
|
||||||
assets = set(role_assets_all) & set(asset_r)
|
roles = perm.get('role', {}).keys() # 获取用户所有授权角色
|
||||||
|
if role not in roles:
|
||||||
|
return {}
|
||||||
|
|
||||||
for asset in assets:
|
role_assets_all = perm.get('role').get(role).get('asset') # 获取用户该角色所有授权主机
|
||||||
asset_info = get_asset_info(asset)
|
assets = set(role_assets_all) & set(asset_r) # 获取用户提交中合法的主机
|
||||||
info = {'hostname': asset.hostname,
|
|
||||||
'ip': asset.ip,
|
for asset in assets:
|
||||||
'port': asset_info.get('port', 22),
|
asset_info = get_asset_info(asset)
|
||||||
'username': role.name,
|
info = {'hostname': asset.hostname,
|
||||||
'password': CRYPTOR.decrypt(role.password),
|
'ip': asset.ip,
|
||||||
'ssh_key': get_role_key(user, role)
|
'port': asset_info.get('port', 22),
|
||||||
}
|
'username': role.name,
|
||||||
res.append(info)
|
'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):
|
elif isinstance(ob, User):
|
||||||
if not perm:
|
if not perm:
|
||||||
@ -198,6 +220,7 @@ def gen_resource(ob, perm=None):
|
|||||||
for asset in ob:
|
for asset in ob:
|
||||||
info = get_asset_info(asset)
|
info = get_asset_info(asset)
|
||||||
res.append(info)
|
res.append(info)
|
||||||
|
logger.debug('生成res: %s' % res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@ -281,6 +304,7 @@ def get_role_push_host(role):
|
|||||||
asset_no_push = set(asset_all) - set(asset_pushed.keys())
|
asset_no_push = set(asset_all) - set(asset_pushed.keys())
|
||||||
return asset_pushed, asset_no_push
|
return asset_pushed, asset_no_push
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print get_role_info(1)
|
print get_role_info(1)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ urlpatterns = patterns('jperm.views',
|
|||||||
(r'^role/perm_role_edit/$', perm_role_edit),
|
(r'^role/perm_role_edit/$', perm_role_edit),
|
||||||
(r'^role/push/$', perm_role_push),
|
(r'^role/push/$', perm_role_push),
|
||||||
(r'^role/recycle/$', perm_role_recycle),
|
(r'^role/recycle/$', perm_role_recycle),
|
||||||
|
(r'^role/get/$', perm_role_get),
|
||||||
(r'^sudo/$', perm_sudo_list),
|
(r'^sudo/$', perm_sudo_list),
|
||||||
(r'^sudo/perm_sudo_add/$', perm_sudo_add),
|
(r'^sudo/perm_sudo_add/$', perm_sudo_add),
|
||||||
(r'^sudo/perm_sudo_delete/$', perm_sudo_delete),
|
(r'^sudo/perm_sudo_delete/$', perm_sudo_delete),
|
||||||
|
@ -10,11 +10,8 @@ from uuid import uuid4
|
|||||||
from jumpserver.api import CRYPTOR
|
from jumpserver.api import CRYPTOR
|
||||||
from os import makedirs
|
from os import makedirs
|
||||||
|
|
||||||
from django.template.loader import get_template
|
|
||||||
from django.template import Context
|
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
|
||||||
from jumpserver.settings import KEY_DIR
|
from jumpserver.settings import KEY_DIR
|
||||||
|
|
||||||
|
|
||||||
@ -72,45 +69,6 @@ def gen_keys(key="", key_path_dir=""):
|
|||||||
return 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__":
|
if __name__ == "__main__":
|
||||||
print gen_keys()
|
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 jperm.models import PermRole, PermRule, PermSudo, PermPush
|
||||||
from jumpserver.models import Setting
|
from jumpserver.models import Setting
|
||||||
|
|
||||||
from jperm.utils import updates_dict, gen_keys, get_rand_pass, get_add_sudo_script
|
from jperm.utils import updates_dict, gen_keys, get_rand_pass
|
||||||
from jperm.ansible_api import Tasks
|
from jperm.ansible_api import MyTask
|
||||||
from jperm.perm_api import get_role_info, get_role_push_host
|
from jperm.perm_api import get_role_info, get_role_push_host
|
||||||
|
|
||||||
from jumpserver.api import my_render, get_object, CRYPTOR
|
from jumpserver.api import my_render, get_object, CRYPTOR
|
||||||
|
|
||||||
|
|
||||||
@ -24,12 +23,14 @@ def perm_rule_list(request):
|
|||||||
"""
|
"""
|
||||||
# 渲染数据
|
# 渲染数据
|
||||||
header_title, path1, path2 = "授权规则", "规则管理", "查看规则"
|
header_title, path1, path2 = "授权规则", "规则管理", "查看规则"
|
||||||
|
|
||||||
# 获取所有规则
|
# 获取所有规则
|
||||||
rules_list = PermRule.objects.all()
|
rules_list = PermRule.objects.all()
|
||||||
|
rule_id = request.GET.get('id')
|
||||||
# TODO: 搜索和分页
|
# TODO: 搜索和分页
|
||||||
keyword = request.GET.get('search', '')
|
keyword = request.GET.get('search', '')
|
||||||
|
if rule_id:
|
||||||
|
rules_list = rules_list.filter(id=rule_id)
|
||||||
|
|
||||||
if keyword:
|
if keyword:
|
||||||
rules_list = rules_list.filter(Q(name=keyword))
|
rules_list = rules_list.filter(Q(name=keyword))
|
||||||
|
|
||||||
@ -78,24 +79,29 @@ def perm_rule_add(request):
|
|||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
# 获取用户选择的 用户,用户组,资产,资产组,用户角色
|
# 获取用户选择的 用户,用户组,资产,资产组,用户角色
|
||||||
users_select = request.POST.getlist('user', [])
|
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', [])
|
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', [])
|
roles_select = request.POST.getlist('role', []) # 需要授权角色
|
||||||
rule_name = request.POST.get('rulename')
|
rule_name = request.POST.get('name')
|
||||||
rule_comment = request.POST.get('rule_comment')
|
rule_comment = request.POST.get('comment')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rule = get_object(PermRule, name=rule_name)
|
rule = get_object(PermRule, name=rule_name)
|
||||||
if rule:
|
if rule:
|
||||||
raise ServerError(u'授权规则 %s 已存在' % rule_name)
|
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]
|
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]
|
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]]
|
group_assets_obj = []
|
||||||
calc_assets = set(group_assets_obj) | set(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]
|
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]
|
roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select]
|
||||||
need_push_asset = set()
|
need_push_asset = set()
|
||||||
|
|
||||||
for role in roles_obj:
|
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))
|
need_push_asset.update(set(calc_assets) - set(asset_no_push))
|
||||||
if need_push_asset:
|
if need_push_asset:
|
||||||
raise ServerError(u'没有推送角色 %s 的主机 %s'
|
raise ServerError(u'没有推送角色 %s 的主机 %s'
|
||||||
@ -140,61 +147,68 @@ def perm_rule_edit(request):
|
|||||||
|
|
||||||
# 根据rule_id 取得rule对象
|
# 根据rule_id 取得rule对象
|
||||||
rule_id = request.GET.get("id")
|
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对象
|
|
||||||
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()
|
|
||||||
|
|
||||||
users = User.objects.all()
|
users = User.objects.all()
|
||||||
user_groups = UserGroup.objects.all()
|
user_groups = UserGroup.objects.all()
|
||||||
assets = Asset.objects.all()
|
assets = Asset.objects.all()
|
||||||
asset_groups = AssetGroup.objects.all()
|
asset_groups = AssetGroup.objects.all()
|
||||||
roles = PermRole.objects.all()
|
roles = PermRole.objects.all()
|
||||||
return my_render('jperm/perm_rule_edit.html', locals(), request)
|
|
||||||
|
|
||||||
elif request.method == 'POST' and rule_id:
|
if request.method == 'POST' and rule_id:
|
||||||
# 获取用户选择的 用户,用户组,资产,资产组,用户角色
|
# 获取用户选择的 用户,用户组,资产,资产组,用户角色
|
||||||
rule_name = request.POST.get('rule_name')
|
rule_name = request.POST.get('name')
|
||||||
rule_comment = request.POST.get("rule_comment")
|
rule_comment = request.POST.get("comment")
|
||||||
users_select = request.POST.getlist('user', [])
|
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', [])
|
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', [])
|
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]
|
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]
|
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]]
|
group_assets_obj = []
|
||||||
# calc_assets = set(group_assets_obj) | set(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]
|
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]
|
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]]
|
# 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)
|
# 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 = users_obj
|
||||||
rule.user_group = user_groups_obj
|
rule.user_group = user_groups_obj
|
||||||
rule.asset = assets_obj
|
rule.asset = assets_obj
|
||||||
rule.asset_group = asset_groups_obj
|
rule.asset_group = asset_groups_obj
|
||||||
rule.role = roles_obj
|
rule.role = roles_obj
|
||||||
rule.name = rule_name
|
rule.name = rule_name
|
||||||
rule.comment = rule.comment
|
rule.comment = rule.comment
|
||||||
rule.save()
|
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')
|
@require_role('admin')
|
||||||
@ -208,8 +222,6 @@ def perm_rule_delete(request):
|
|||||||
# 根据rule_id 取得rule对象
|
# 根据rule_id 取得rule对象
|
||||||
rule_id = request.POST.get("id")
|
rule_id = request.POST.get("id")
|
||||||
rule_obj = PermRule.objects.get(id=rule_id)
|
rule_obj = PermRule.objects.get(id=rule_id)
|
||||||
print rule_id, rule_obj
|
|
||||||
print rule_obj.name
|
|
||||||
rule_obj.delete()
|
rule_obj.delete()
|
||||||
return HttpResponse(u"删除授权规则:%s" % rule_obj.name)
|
return HttpResponse(u"删除授权规则:%s" % rule_obj.name)
|
||||||
else:
|
else:
|
||||||
@ -226,12 +238,15 @@ def perm_role_list(request):
|
|||||||
|
|
||||||
# 获取所有系统角色
|
# 获取所有系统角色
|
||||||
roles_list = PermRole.objects.all()
|
roles_list = PermRole.objects.all()
|
||||||
|
role_id = request.GET.get('id')
|
||||||
# TODO: 搜索和分页
|
# TODO: 搜索和分页
|
||||||
keyword = request.GET.get('search', '')
|
keyword = request.GET.get('search', '')
|
||||||
if keyword:
|
if keyword:
|
||||||
roles_list = roles_list.filter(Q(name=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)
|
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)
|
return my_render('jperm/perm_role_list.html', locals(), request)
|
||||||
@ -396,9 +411,12 @@ def perm_role_push(request):
|
|||||||
# 渲染数据
|
# 渲染数据
|
||||||
header_title, path1, path2 = "系统角色", "角色管理", "角色推送"
|
header_title, path1, path2 = "系统角色", "角色管理", "角色推送"
|
||||||
role_id = request.GET.get('id')
|
role_id = request.GET.get('id')
|
||||||
|
asset_ids = request.GET.get('asset_id')
|
||||||
role = get_object(PermRole, id=role_id)
|
role = get_object(PermRole, id=role_id)
|
||||||
assets = Asset.objects.all()
|
assets = Asset.objects.all()
|
||||||
asset_groups = AssetGroup.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":
|
if request.method == "POST":
|
||||||
# 获取推荐角色的名称列表
|
# 获取推荐角色的名称列表
|
||||||
@ -417,43 +435,48 @@ def perm_role_push(request):
|
|||||||
# 调用Ansible API 进行推送
|
# 调用Ansible API 进行推送
|
||||||
password_push = True if request.POST.get("use_password") else False
|
password_push = True if request.POST.get("use_password") else False
|
||||||
key_push = True if request.POST.get("use_publicKey") else False
|
key_push = True if request.POST.get("use_publicKey") else False
|
||||||
task = Tasks(push_resource)
|
task = MyTask(push_resource)
|
||||||
ret = {}
|
ret = {}
|
||||||
ret_failed = {}
|
|
||||||
|
|
||||||
# 因为要先建立用户,所以password 是必选项,而push key是在 password也完成的情况下的 可选项
|
# 因为要先建立用户,所以password 是必选项,而push key是在 password也完成的情况下的 可选项
|
||||||
# 1. 以password 方式推送角色
|
# 1. 以秘钥 方式推送角色
|
||||||
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. 以秘钥 方式推送角色
|
|
||||||
if key_push:
|
if key_push:
|
||||||
ret["password_push"] = task.add_user(role.name)
|
ret["pass_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password))
|
||||||
if ret["password_push"].get("status") != "ok":
|
|
||||||
ret_failed = ret["password_push"].get('msg')
|
|
||||||
ret["key_push"] = task.push_key(role.name, os.path.join(role.key_path, 'id_rsa.pub'))
|
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配置文件
|
# 3. 推送sudo配置文件
|
||||||
if password_push or key_push:
|
if password_push or key_push:
|
||||||
role_chosen_aliase = {} # {'dev': 'NETWORKING, SHUTDOWN'}
|
sudo_list = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
|
||||||
sudo_alias = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
|
ret['sudo'] = task.push_sudo_file([role], sudo_list)
|
||||||
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)
|
|
||||||
|
|
||||||
logger.debug('推送role结果: %s' % ret)
|
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表
|
# 推送成功 回写push表
|
||||||
for asset in calc_assets:
|
for asset in calc_assets:
|
||||||
push_check = PermPush.objects.filter(role=role, asset=asset)
|
push_check = PermPush.objects.filter(role=role, asset=asset)
|
||||||
@ -463,20 +486,18 @@ def perm_role_push(request):
|
|||||||
def func(**kwargs):
|
def func(**kwargs):
|
||||||
PermPush(**kwargs).save()
|
PermPush(**kwargs).save()
|
||||||
|
|
||||||
if ret_failed.get(asset.hostname):
|
if failed_asset.get(asset.hostname):
|
||||||
failed_asset.append(asset)
|
|
||||||
func(is_password=password_push, is_public_key=key_push, role=role, asset=asset, success=False,
|
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:
|
else:
|
||||||
success_asset.append(asset)
|
|
||||||
func(is_password=password_push, is_public_key=key_push, role=role, asset=asset, success=True)
|
func(is_password=password_push, is_public_key=key_push, role=role, asset=asset, success=True)
|
||||||
|
|
||||||
if not failed_asset:
|
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:
|
else:
|
||||||
error = u'角色 %s 推送失败 [ %s ], 推送成功 [ %s ]' % (role.name,
|
error = u'角色 %s 推送失败 [ %s ], 推送成功 [ %s ]' % (role.name,
|
||||||
','.join([asset.hostname for asset in failed_asset]),
|
','.join(failed_asset.keys()),
|
||||||
','.join([asset.hostname for asset in success_asset]))
|
','.join(success_asset.keys()))
|
||||||
return my_render('jperm/perm_role_push.html', locals(), request)
|
return my_render('jperm/perm_role_push.html', locals(), request)
|
||||||
|
|
||||||
|
|
||||||
@ -515,14 +536,18 @@ def perm_sudo_add(request):
|
|||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
# 获取参数: name, comment
|
# 获取参数: name, comment
|
||||||
name = request.POST.get("sudo_name").strip()
|
name = request.POST.get("sudo_name").strip().upper()
|
||||||
comment = request.POST.get("sudo_comment").strip()
|
comment = request.POST.get("sudo_comment").strip()
|
||||||
commands = request.POST.get("sudo_commands").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):
|
if get_object(PermSudo, name=name):
|
||||||
error = 'Sudo别名 %s已经存在' % name
|
error = 'Sudo别名 %s已经存在' % name
|
||||||
else:
|
else:
|
||||||
sudo = PermSudo(name=name.strip(), comment=comment, commands=commands.strip())
|
sudo = PermSudo(name=name.strip(), comment=comment, commands=commands)
|
||||||
sudo.save()
|
sudo.save()
|
||||||
msg = u"添加Sudo命令别名: %s" % name
|
msg = u"添加Sudo命令别名: %s" % name
|
||||||
# 渲染数据
|
# 渲染数据
|
||||||
@ -544,11 +569,16 @@ def perm_sudo_edit(request):
|
|||||||
sudo = PermSudo.objects.get(id=sudo_id)
|
sudo = PermSudo.objects.get(id=sudo_id)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
name = request.POST.get("sudo_name")
|
name = request.POST.get("sudo_name").upper()
|
||||||
commands = request.POST.get("sudo_commands")
|
commands = request.POST.get("sudo_commands")
|
||||||
comment = request.POST.get("sudo_comment")
|
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.name = name.strip()
|
||||||
sudo.commands = commands.strip()
|
sudo.commands = commands
|
||||||
sudo.comment = comment
|
sudo.comment = comment
|
||||||
sudo.save()
|
sudo.save()
|
||||||
|
|
||||||
@ -579,11 +609,31 @@ def perm_sudo_delete(request):
|
|||||||
def perm_role_recycle(request):
|
def perm_role_recycle(request):
|
||||||
role_id = request.GET.get('role_id')
|
role_id = request.GET.get('role_id')
|
||||||
asset_ids = request.GET.get('asset_id').split(',')
|
asset_ids = request.GET.get('asset_id').split(',')
|
||||||
|
assets = []
|
||||||
for asset_id in asset_ids:
|
for asset_id in asset_ids:
|
||||||
asset = get_object(Asset, id=asset_id)
|
asset = get_object(Asset, id=asset_id)
|
||||||
|
assets.append(asset)
|
||||||
role = get_object(PermRole, id=role_id)
|
role = get_object(PermRole, id=role_id)
|
||||||
PermPush.objects.filter(asset=asset, role=role).delete()
|
PermPush.objects.filter(asset=asset, role=role).delete()
|
||||||
|
|
||||||
|
res = gen_resource(assets)
|
||||||
|
task = MyTask(res)
|
||||||
|
|
||||||
return HttpResponse('删除成功')
|
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 datetime
|
||||||
import random
|
import random
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import uuid
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -47,9 +48,16 @@ def set_log(level):
|
|||||||
return logger_f
|
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):
|
def get_asset_info(asset):
|
||||||
"""
|
"""
|
||||||
获取资产的相关账号端口信息
|
获取资产的相关管理账号端口等信息
|
||||||
"""
|
"""
|
||||||
default = get_object(Setting, name='default')
|
default = get_object(Setting, name='default')
|
||||||
info = {'hostname': asset.hostname, 'ip': asset.ip}
|
info = {'hostname': asset.hostname, 'ip': asset.ip}
|
||||||
@ -70,17 +78,6 @@ def get_asset_info(asset):
|
|||||||
return info
|
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):
|
def get_role_key(user, role):
|
||||||
"""
|
"""
|
||||||
由于role的key的权限是所有人可以读的, ansible执行命令等要求为600,所以拷贝一份到特殊目录
|
由于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(os.path.join(role.key_path, 'id_rsa')) as fk:
|
||||||
with open(user_role_key_path, 'w') as fu:
|
with open(user_role_key_path, 'w') as fu:
|
||||||
fu.write(fk.read())
|
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)
|
chown(user_role_key_path, user.username)
|
||||||
os.chmod(user_role_key_path, 0600)
|
os.chmod(user_role_key_path, 0600)
|
||||||
return user_role_key_path
|
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))
|
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)
|
CRYPTOR = PyCrypt(KEY)
|
||||||
logger = set_log(LOG_LEVEL)
|
logger = set_log(LOG_LEVEL)
|
||||||
|
@ -7,7 +7,7 @@ import time
|
|||||||
from django import template
|
from django import template
|
||||||
from jperm.models import PermPush
|
from jperm.models import PermPush
|
||||||
from jumpserver.api import *
|
from jumpserver.api import *
|
||||||
from jasset.models import AssetAlias
|
from jperm.perm_api import get_group_user_perm
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ def key_exist(username):
|
|||||||
"""
|
"""
|
||||||
ssh key is exist or not
|
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
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@ -272,3 +272,35 @@ def get_push_info(push_id, arg):
|
|||||||
return [role.name for role in push.role.all()]
|
return [role.name for role in push.role.all()]
|
||||||
else:
|
else:
|
||||||
return []
|
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'^$', 'jumpserver.views.index'),
|
||||||
(r'^api/user/$', 'jumpserver.api.api_user'),
|
(r'^api/user/$', 'jumpserver.api.api_user'),
|
||||||
(r'^skin_config/$', 'jumpserver.views.skin_config'),
|
(r'^skin_config/$', 'jumpserver.views.skin_config'),
|
||||||
(r'^install/$', 'jumpserver.views.install'),
|
|
||||||
(r'^base/$', 'jumpserver.views.base'),
|
|
||||||
(r'^login/$', 'jumpserver.views.Login'),
|
(r'^login/$', 'jumpserver.views.Login'),
|
||||||
(r'^logout/$', 'jumpserver.views.Logout'),
|
(r'^logout/$', 'jumpserver.views.Logout'),
|
||||||
|
(r'^exec_cmd/$', 'jumpserver.views.exec_cmd'),
|
||||||
(r'^file/upload/$', 'jumpserver.views.upload'),
|
(r'^file/upload/$', 'jumpserver.views.upload'),
|
||||||
(r'^file/download/$', 'jumpserver.views.download'),
|
(r'^file/download/$', 'jumpserver.views.download'),
|
||||||
(r'^setting', 'jumpserver.views.setting'),
|
(r'^setting', 'jumpserver.views.setting'),
|
||||||
(r'^error/$', 'jumpserver.views.httperror'),
|
|
||||||
(r'^juser/', include('juser.urls')),
|
(r'^juser/', include('juser.urls')),
|
||||||
(r'^jasset/', include('jasset.urls')),
|
(r'^jasset/', include('jasset.urls')),
|
||||||
(r'^jlog/', include('jlog.urls')),
|
(r'^jlog/', include('jlog.urls')),
|
||||||
(r'^jperm/', include('jperm.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 jumpserver.models import Setting
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from jlog.models import Log
|
from jlog.models import Log, FileLog
|
||||||
from jperm.perm_api import get_group_user_perm
|
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):
|
def getDaysByNum(num):
|
||||||
"""
|
"""
|
||||||
@ -72,42 +75,11 @@ def get_count_by_date(date_li, item):
|
|||||||
|
|
||||||
return len(set(data_count_tmp))
|
return len(set(data_count_tmp))
|
||||||
|
|
||||||
from jasset.models import Asset, IDC
|
|
||||||
@require_role(role='user')
|
@require_role(role='user')
|
||||||
def index_cu(request):
|
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
|
username = request.user.username
|
||||||
# TODO: need fix,liuzheng need Asset help
|
return HttpResponseRedirect('/juser/user_detail/')
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
@require_role(role='user')
|
@require_role(role='user')
|
||||||
@ -181,34 +153,6 @@ def skin_config(request):
|
|||||||
return render_to_response('skin_config.html')
|
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():
|
def is_latest():
|
||||||
node = uuid.getnode()
|
node = uuid.getnode()
|
||||||
jsn = uuid.UUID(int=node).hex[-12:]
|
jsn = uuid.UUID(int=node).hex[-12:]
|
||||||
@ -308,166 +252,91 @@ def setting(request):
|
|||||||
return my_render('setting.html', locals(), 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')
|
@login_required(login_url='/login')
|
||||||
def upload(request):
|
def upload(request):
|
||||||
if request.method == 'GET':
|
user = request.user
|
||||||
machines = [{'name':'aaa'}]
|
assets = get_group_user_perm(user).get('asset').keys()
|
||||||
return render_to_response('upload.html', locals(), context_instance=RequestContext(request))
|
asset_select = []
|
||||||
elif request.method == 'POST':
|
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)
|
upload_files = request.FILES.getlist('file[]', None)
|
||||||
for file in upload_files:
|
date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
print file
|
upload_dir = get_tmp_dir()
|
||||||
newdoc = Document(docfile=file, user_id=request.user.id)
|
# file_dict = {}
|
||||||
newdoc.save()
|
for asset_id in asset_ids:
|
||||||
return HttpResponse("success")
|
asset_select.append(get_object(Asset, id=asset_id))
|
||||||
else:
|
|
||||||
return HttpResponse("ERROR")
|
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')
|
@login_required(login_url='/login')
|
||||||
def download(request):
|
def download(request):
|
||||||
documents = []
|
user = request.user
|
||||||
for doc in Document.objects.filter(user_id=request.user.id).all():
|
assets = get_group_user_perm(user).get('asset').keys()
|
||||||
documents.append('/'.join(str(doc.docfile).split('/')[2:]))
|
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))
|
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):
|
@login_required(login_url='/login')
|
||||||
return HttpResponse('nothing')
|
def exec_cmd(request):
|
||||||
def httperror(request):
|
role = request.GET.get('role')
|
||||||
return HttpResponse('nothing')
|
check_assets = request.GET.get('check_assets', '')
|
||||||
def base(request):
|
web_terminal_uri = 'ws://%s/exec?role=%s' % (WEB_SOCKET_HOST, role)
|
||||||
return HttpResponse('nothing')
|
return my_render('exec_cmd.html', locals(), request)
|
||||||
def install(request):
|
|
||||||
return HttpResponse('nothing')
|
|
||||||
|
|
||||||
####################### liuzheng's test(end) ########################
|
|
||||||
|
@ -25,9 +25,6 @@ urlpatterns = patterns('juser.views',
|
|||||||
(r'^forget_password/$', forget_password),
|
(r'^forget_password/$', forget_password),
|
||||||
|
|
||||||
(r'^change_info/$', 'change_info'),
|
(r'^change_info/$', 'change_info'),
|
||||||
(r'^change_role/$', 'chg_role'),
|
|
||||||
(r'^regen_ssh_key/$', 'regen_ssh_key'),
|
(r'^regen_ssh_key/$', 'regen_ssh_key'),
|
||||||
(r'^down_key/$', 'down_key'),
|
(r'^down_key/$', 'down_key'),
|
||||||
|
|
||||||
(r'runcommand/$', 'RunCommand'),
|
|
||||||
)
|
)
|
||||||
|
@ -129,8 +129,8 @@ def gen_ssh_key(username, password='',
|
|||||||
生成一个用户ssh密钥对
|
生成一个用户ssh密钥对
|
||||||
"""
|
"""
|
||||||
logger.debug('生成ssh key, 并设置authorized_keys')
|
logger.debug('生成ssh key, 并设置authorized_keys')
|
||||||
private_key_file = os.path.join(key_dir, username)
|
private_key_file = os.path.join(key_dir, username+'.pem')
|
||||||
mkdir(key_dir, mode=777)
|
mkdir(key_dir, mode=0700)
|
||||||
if os.path.isfile(private_key_file):
|
if os.path.isfile(private_key_file):
|
||||||
os.unlink(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))
|
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"""
|
mail_msg = u"""
|
||||||
Hi, %s
|
Hi, %s
|
||||||
您的用户名: %s
|
您的用户名: %s
|
||||||
您的角色: %s
|
您的权限: %s
|
||||||
您的web登录密码: %s
|
您的web登录密码: %s
|
||||||
您的ssh密钥文件密码: %s
|
您的ssh密钥文件密码: %s
|
||||||
密钥下载地址: %s/juser/down_key/?uuid=%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
|
密码:%s
|
||||||
密钥密码:%s
|
密钥密码:%s
|
||||||
密钥下载url: %s/juser/down_key/?id=%s
|
密钥下载url: %s/juser/down_key/?uuid=%s
|
||||||
该账号密码可以登陆web和跳板机。
|
该账号密码可以登陆web和跳板机。
|
||||||
""" % (URL, user.username, password, ssh_key_pwd, URL, user.id)
|
""" % (URL, user.username, password, ssh_key_pwd, URL, user.uuid)
|
||||||
else:
|
else:
|
||||||
msg = u"""
|
msg = u"""
|
||||||
跳板机地址: %s \n
|
跳板机地址: %s \n
|
||||||
|
137
juser/views.py
137
juser/views.py
@ -4,23 +4,15 @@
|
|||||||
|
|
||||||
# import random
|
# import random
|
||||||
# from Crypto.PublicKey import RSA
|
# from Crypto.PublicKey import RSA
|
||||||
import uuid as uuid_r
|
import uuid
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from juser.user_api import *
|
from juser.user_api import *
|
||||||
|
from jperm.perm_api import get_group_user_perm
|
||||||
|
|
||||||
MAIL_FROM = EMAIL_HOST_USER
|
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')
|
@require_role(role='super')
|
||||||
def group_add(request):
|
def group_add(request):
|
||||||
@ -96,8 +88,8 @@ def group_edit(request):
|
|||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
group_id = request.GET.get('id', '')
|
group_id = request.GET.get('id', '')
|
||||||
# user_group = get_object(UserGroup, id=group_id)
|
user_group = get_object(UserGroup, id=group_id)
|
||||||
user_group = UserGroup.objects.get(id=group_id)
|
# user_group = UserGroup.objects.get(id=group_id)
|
||||||
users_selected = User.objects.filter(group=user_group)
|
users_selected = User.objects.filter(group=user_group)
|
||||||
users_remain = User.objects.filter(~Q(group=user_group))
|
users_remain = User.objects.filter(~Q(group=user_group))
|
||||||
users_all = User.objects.all()
|
users_all = User.objects.all()
|
||||||
@ -126,8 +118,9 @@ def group_edit(request):
|
|||||||
if g == user_group:
|
if g == user_group:
|
||||||
continue
|
continue
|
||||||
user.group.add(g)
|
user.group.add(g)
|
||||||
|
user_group.name = group_name
|
||||||
|
user_group.comment = comment
|
||||||
|
user_group.save()
|
||||||
except ServerError, e:
|
except ServerError, e:
|
||||||
error = e
|
error = e
|
||||||
if not error:
|
if not error:
|
||||||
@ -140,7 +133,6 @@ def group_edit(request):
|
|||||||
return my_render('juser/group_edit.html', locals(), request)
|
return my_render('juser/group_edit.html', locals(), request)
|
||||||
|
|
||||||
|
|
||||||
@login_required(login_url='/login')
|
|
||||||
@require_role(role='super')
|
@require_role(role='super')
|
||||||
def user_add(request):
|
def user_add(request):
|
||||||
error = ''
|
error = ''
|
||||||
@ -157,11 +149,11 @@ def user_add(request):
|
|||||||
groups = request.POST.getlist('groups', [])
|
groups = request.POST.getlist('groups', [])
|
||||||
admin_groups = request.POST.getlist('admin_groups', [])
|
admin_groups = request.POST.getlist('admin_groups', [])
|
||||||
role = request.POST.get('role', 'CU')
|
role = request.POST.get('role', 'CU')
|
||||||
uuid = uuid_r.uuid1()
|
uuid_r = uuid.uuid4().get_hex()
|
||||||
ssh_key_pwd = PyCrypt.gen_rand_pass(16)
|
ssh_key_pwd = PyCrypt.gen_rand_pass(16)
|
||||||
extra = request.POST.getlist('extra', [])
|
extra = request.POST.getlist('extra', [])
|
||||||
is_active = False if '0' in extra else True
|
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
|
send_mail_need = True if '2' in extra else False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -179,7 +171,7 @@ def user_add(request):
|
|||||||
try:
|
try:
|
||||||
user = db_add_user(username=username, name=name,
|
user = db_add_user(username=username, name=name,
|
||||||
password=password,
|
password=password,
|
||||||
email=email, role=role, uuid=uuid,
|
email=email, role=role, uuid=uuid_r,
|
||||||
groups=groups, admin_groups=admin_groups,
|
groups=groups, admin_groups=admin_groups,
|
||||||
ssh_key_pwd=ssh_key_pwd,
|
ssh_key_pwd=ssh_key_pwd,
|
||||||
is_active=is_active,
|
is_active=is_active,
|
||||||
@ -190,7 +182,7 @@ def user_add(request):
|
|||||||
user_groups = []
|
user_groups = []
|
||||||
for user_group_id in groups:
|
for user_group_id in groups:
|
||||||
user_groups.extend(UserGroup.objects.filter(id=user_group_id))
|
user_groups.extend(UserGroup.objects.filter(id=user_group_id))
|
||||||
print user_groups
|
|
||||||
except IndexError, e:
|
except IndexError, e:
|
||||||
error = u'添加用户 %s 失败 %s ' % (username, e)
|
error = u'添加用户 %s 失败 %s ' % (username, e)
|
||||||
try:
|
try:
|
||||||
@ -230,24 +222,20 @@ def user_list(request):
|
|||||||
@require_role(role='user')
|
@require_role(role='user')
|
||||||
def user_detail(request):
|
def user_detail(request):
|
||||||
header_title, path1, path2 = '用户详情', '用户管理', '用户详情'
|
header_title, path1, path2 = '用户详情', '用户管理', '用户详情'
|
||||||
# if request.session.get('role_id') == 0:
|
if request.session.get('role_id') == 0:
|
||||||
# user_id = request.user.id
|
user_id = request.user.id
|
||||||
# else:
|
else:
|
||||||
# user_id = request.GET.get('id', '')
|
user_id = request.GET.get('id', '')
|
||||||
# if request.session.get('role_id') == 1:
|
|
||||||
# user, dept = get_session_user_dept(request)
|
user = get_object(User, id=user_id)
|
||||||
# if not validate(request, user=[user_id]):
|
if not user:
|
||||||
# return HttpResponseRedirect('/')
|
|
||||||
user_id = request.GET.get('id', '')
|
|
||||||
if not user_id:
|
|
||||||
return HttpResponseRedirect('/juser/user_list/')
|
return HttpResponseRedirect('/juser/user_list/')
|
||||||
user = User.objects.get(id=user_id)
|
|
||||||
# if user:
|
user_perm_info = get_group_user_perm(user)
|
||||||
# pass
|
role_assets = user_perm_info.get('role')
|
||||||
# asset_group_permed = user.get_asset_group()
|
user_log_ten = Log.objects.filter(user=user.username).order_by('id')[0:10]
|
||||||
# logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10]
|
user_log_last = Log.objects.filter(user=user.username).order_by('id')[0:50]
|
||||||
# logs_all = Log.objects.filter(user=user.name).order_by('-start_time')
|
user_log_last_num = len(user_log_last)
|
||||||
# logs_num = len(logs_all)
|
|
||||||
|
|
||||||
return my_render('juser/user_detail.html', locals(), request)
|
return my_render('juser/user_detail.html', locals(), request)
|
||||||
|
|
||||||
@ -262,21 +250,20 @@ def user_del(request):
|
|||||||
user_id_list = user_ids.split(',')
|
user_id_list = user_ids.split(',')
|
||||||
else:
|
else:
|
||||||
return HttpResponse('错误请求')
|
return HttpResponse('错误请求')
|
||||||
|
|
||||||
for user_id in user_id_list:
|
for user_id in user_id_list:
|
||||||
user = get_object(User, id=user_id)
|
user = get_object(User, id=user_id)
|
||||||
if user:
|
if user and user.username != 'admin':
|
||||||
# TODO: annotation by liuzheng, because useless for me
|
logger.debug(u"删除用户 %s " % user.username)
|
||||||
# assets = user_permed(user)
|
bash('userdel -r %s' % user.username)
|
||||||
# result = _public_perm_api({'type': 'del_user', 'user': user, 'asset': assets})
|
|
||||||
# print result
|
|
||||||
user.delete()
|
user.delete()
|
||||||
return HttpResponse('删除成功')
|
return HttpResponse('删除成功')
|
||||||
|
|
||||||
|
|
||||||
@require_role('admin')
|
@require_role('admin')
|
||||||
def send_mail_retry(request):
|
def send_mail_retry(request):
|
||||||
user_uuid = request.GET.get('uuid', '1')
|
uuid_r = request.GET.get('uuid', '1')
|
||||||
user = get_object(User, uuid=user_uuid)
|
user = get_object(User, uuid=uuid_r)
|
||||||
msg = u"""
|
msg = u"""
|
||||||
跳板机地址: %s
|
跳板机地址: %s
|
||||||
用户名:%s
|
用户名:%s
|
||||||
@ -305,36 +292,38 @@ def forget_password(request):
|
|||||||
""" % (user.name, URL, user.uuid, timestamp, hash_encode)
|
""" % (user.name, URL, user.uuid, timestamp, hash_encode)
|
||||||
send_mail('忘记跳板机密码', msg, MAIL_FROM, [email], fail_silently=False)
|
send_mail('忘记跳板机密码', msg, MAIL_FROM, [email], fail_silently=False)
|
||||||
msg = u'请登陆邮箱,点击邮件重设密码'
|
msg = u'请登陆邮箱,点击邮件重设密码'
|
||||||
return HttpResponse(msg)
|
return http_success(request, msg)
|
||||||
else:
|
else:
|
||||||
error = u'用户不存在或邮件地址错误'
|
error = u'用户不存在或邮件地址错误'
|
||||||
|
|
||||||
return render_to_response('juser/forget_password.html', locals())
|
return render_to_response('juser/forget_password.html', locals())
|
||||||
|
|
||||||
|
|
||||||
|
@require_role('user')
|
||||||
def reset_password(request):
|
def reset_password(request):
|
||||||
uuid = request.GET.get('uuid', '')
|
uuid_r = request.GET.get('uuid', '')
|
||||||
timestamp = request.GET.get('timestamp', '')
|
timestamp = request.GET.get('timestamp', '')
|
||||||
hash_encode = request.GET.get('hash', '')
|
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':
|
if request.method == 'POST':
|
||||||
password = request.POST.get('password')
|
password = request.POST.get('password')
|
||||||
password_confirm = request.POST.get('password_confirm')
|
password_confirm = request.POST.get('password_confirm')
|
||||||
|
print password, password_confirm
|
||||||
if password != password_confirm:
|
if password != password_confirm:
|
||||||
return HttpResponse('密码不匹配')
|
return HttpResponse('密码不匹配')
|
||||||
else:
|
else:
|
||||||
user = get_object(User, uuid=uuid)
|
user = get_object(User, uuid=uuid_r)
|
||||||
if user:
|
if user:
|
||||||
user.password = PyCrypt.md5_crypt(password)
|
user.password = PyCrypt.md5_crypt(password)
|
||||||
user.save()
|
user.save()
|
||||||
return HttpResponse('密码重设成功')
|
return http_success(request, u'密码重设成功')
|
||||||
else:
|
else:
|
||||||
return HttpResponse('用户不存在')
|
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:
|
if int(time.time()) - int(timestamp) > 600:
|
||||||
return HttpResponse('链接已超时')
|
return http_error(request, u'链接已超时')
|
||||||
else:
|
else:
|
||||||
return render_to_response('juser/reset_password.html', locals())
|
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)
|
send_mail('您的信息已修改', msg, MAIL_FROM, [email], fail_silently=False)
|
||||||
|
|
||||||
return HttpResponseRedirect('/juser/user_list/')
|
return HttpResponseRedirect('/juser/user_list/')
|
||||||
|
|
||||||
return my_render('juser/user_edit.html', locals(), request)
|
return my_render('juser/user_edit.html', locals(), request)
|
||||||
|
|
||||||
|
|
||||||
# @require_role(role='admin')
|
|
||||||
def user_edit_adm(request):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def profile(request):
|
def profile(request):
|
||||||
a = request.user.id
|
|
||||||
a = request.user.groups
|
|
||||||
|
|
||||||
user_id = request.user.id
|
user_id = request.user.id
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return HttpResponseRedirect('/')
|
return HttpResponseRedirect('/')
|
||||||
user = User.objects.get(id=user_id)
|
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):
|
def change_info(request):
|
||||||
@ -436,26 +416,24 @@ def change_info(request):
|
|||||||
|
|
||||||
if '' in [name, email]:
|
if '' in [name, email]:
|
||||||
error = '不能为空'
|
error = '不能为空'
|
||||||
if len(password) > 0 and len(password) < 6:
|
|
||||||
|
if len(password) < 6:
|
||||||
error = '密码须大于6位'
|
error = '密码须大于6位'
|
||||||
|
|
||||||
if not error:
|
if not error:
|
||||||
# if password != user.password:
|
|
||||||
# password = CRYPTOR.md5_crypt(password)
|
|
||||||
|
|
||||||
User.objects.filter(id=user_id).update(name=name, email=email)
|
User.objects.filter(id=user_id).update(name=name, email=email)
|
||||||
if len(password) > 0:
|
if len(password) > 0:
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
user.save()
|
user.save()
|
||||||
msg = '修改成功'
|
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')
|
@require_role(role='user')
|
||||||
def regen_ssh_key(request):
|
def regen_ssh_key(request):
|
||||||
uuid = request.GET.get('uuid', '')
|
uuid_r = request.GET.get('uuid', '')
|
||||||
user = get_object(User, uuid=uuid)
|
user = get_object(User, uuid=uuid_r)
|
||||||
if not user:
|
if not user:
|
||||||
return HttpResponse('没有该用户')
|
return HttpResponse('没有该用户')
|
||||||
|
|
||||||
@ -467,18 +445,17 @@ def regen_ssh_key(request):
|
|||||||
|
|
||||||
@require_role(role='user')
|
@require_role(role='user')
|
||||||
def down_key(request):
|
def down_key(request):
|
||||||
user_id = ''
|
|
||||||
if is_role_request(request, 'super'):
|
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'):
|
if uuid_r:
|
||||||
user_id = request.user.id
|
user = get_object(User, uuid=uuid_r)
|
||||||
|
|
||||||
if user_id:
|
|
||||||
user = get_object(User, id=user_id)
|
|
||||||
if user:
|
if user:
|
||||||
username = user.username
|
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):
|
if os.path.isfile(private_key_file):
|
||||||
f = open(private_key_file)
|
f = open(private_key_file)
|
||||||
data = f.read()
|
data = f.read()
|
||||||
@ -486,13 +463,5 @@ def down_key(request):
|
|||||||
response = HttpResponse(data, content_type='application/octet-stream')
|
response = HttpResponse(data, content_type='application/octet-stream')
|
||||||
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(private_key_file)
|
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(private_key_file)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
return HttpResponse('No Key File. Contact Admin.')
|
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 os.path
|
||||||
import threading
|
import threading
|
||||||
import datetime
|
import datetime
|
||||||
import urllib
|
import re
|
||||||
|
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
import tornado.options
|
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
|
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier
|
||||||
import select
|
import select
|
||||||
|
|
||||||
from connect import Tty, User, Asset, PermRole, logger, get_object
|
from connect import Tty, User, Asset, PermRole, logger, get_object, PermRole, gen_resource
|
||||||
from connect import TtyLog, Log, Session, user_have_perm
|
from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, MyRunner, ExecLog
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
@ -67,22 +67,6 @@ def require_auth(role='user'):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
logger.warning('Websocket: Request auth failed.')
|
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 _deco2
|
||||||
return _deco
|
return _deco
|
||||||
|
|
||||||
@ -138,6 +122,7 @@ class Application(tornado.web.Application):
|
|||||||
(r'/monitor', MonitorHandler),
|
(r'/monitor', MonitorHandler),
|
||||||
(r'/terminal', WebTerminalHandler),
|
(r'/terminal', WebTerminalHandler),
|
||||||
(r'/kill', WebTerminalKillHandler),
|
(r'/kill', WebTerminalKillHandler),
|
||||||
|
(r'/exec', ExecHandler),
|
||||||
]
|
]
|
||||||
|
|
||||||
setting = {
|
setting = {
|
||||||
@ -206,7 +191,6 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
|
|||||||
class WebTty(Tty):
|
class WebTty(Tty):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(WebTty, self).__init__(*args, **kwargs)
|
super(WebTty, self).__init__(*args, **kwargs)
|
||||||
self.login_type = 'web'
|
|
||||||
self.ws = None
|
self.ws = None
|
||||||
self.data = ''
|
self.data = ''
|
||||||
self.input_mode = False
|
self.input_mode = False
|
||||||
@ -225,6 +209,82 @@ class WebTerminalKillHandler(tornado.web.RequestHandler):
|
|||||||
logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients))
|
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):
|
class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
||||||
clients = []
|
clients = []
|
||||||
tasks = []
|
tasks = []
|
||||||
@ -236,6 +296,8 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||||||
self.log = None
|
self.log = None
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.user = None
|
self.user = None
|
||||||
|
self.ssh = None
|
||||||
|
self.channel = None
|
||||||
super(WebTerminalHandler, self).__init__(*args, **kwargs)
|
super(WebTerminalHandler, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def check_origin(self, origin):
|
def check_origin(self, origin):
|
||||||
@ -250,7 +312,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||||||
if asset:
|
if asset:
|
||||||
roles = user_have_perm(self.user, asset)
|
roles = user_have_perm(self.user, asset)
|
||||||
logger.debug(roles)
|
logger.debug(roles)
|
||||||
logger.debug('rolename: %s' % role_name)
|
logger.debug('角色: %s' % role_name)
|
||||||
login_role = ''
|
login_role = ''
|
||||||
for role in roles:
|
for role in roles:
|
||||||
if role.name == role_name:
|
if role.name == role_name:
|
||||||
@ -267,10 +329,10 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||||||
return
|
return
|
||||||
logger.debug('Websocket: request web terminal Host: %s User: %s Role: %s' % (asset.hostname, self.user.username,
|
logger.debug('Websocket: request web terminal Host: %s User: %s Role: %s' % (asset.hostname, self.user.username,
|
||||||
login_role.name))
|
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.remote_ip = self.request.remote_ip
|
||||||
self.term.get_connection()
|
self.ssh = self.term.get_connection()
|
||||||
self.term.channel = self.term.ssh.invoke_shell(term='xterm')
|
self.channel = self.ssh.invoke_shell(term='xterm')
|
||||||
WebTerminalHandler.tasks.append(MyThread(target=self.forward_outbound))
|
WebTerminalHandler.tasks.append(MyThread(target=self.forward_outbound))
|
||||||
WebTerminalHandler.clients.append(self)
|
WebTerminalHandler.clients.append(self)
|
||||||
|
|
||||||
@ -303,7 +365,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||||||
self.term.vim_data = ''
|
self.term.vim_data = ''
|
||||||
self.term.data = ''
|
self.term.data = ''
|
||||||
self.term.input_mode = False
|
self.term.input_mode = False
|
||||||
self.term.channel.send(data['data'])
|
self.channel.send(data['data'])
|
||||||
|
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
logger.debug('Websocket: Close request')
|
logger.debug('Websocket: Close request')
|
||||||
@ -326,9 +388,9 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||||||
data = ''
|
data = ''
|
||||||
pre_timestamp = time.time()
|
pre_timestamp = time.time()
|
||||||
while True:
|
while True:
|
||||||
r, w, e = select.select([self.term.channel, sys.stdin], [], [])
|
r, w, e = select.select([self.channel, sys.stdin], [], [])
|
||||||
if self.term.channel in r:
|
if self.channel in r:
|
||||||
recv = self.term.channel.recv(1024)
|
recv = self.channel.recv(1024)
|
||||||
if not len(recv):
|
if not len(recv):
|
||||||
return
|
return
|
||||||
data += recv
|
data += recv
|
||||||
@ -347,8 +409,8 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||||||
data = ''
|
data = ''
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
pass
|
pass
|
||||||
finally:
|
except IndexError:
|
||||||
self.close()
|
pass
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
tornado.options.parse_command_line()
|
tornado.options.parse_command_line()
|
||||||
|
@ -2822,7 +2822,9 @@ body.body-small .footer.fixed {
|
|||||||
.table > thead > tr > td,
|
.table > thead > tr > td,
|
||||||
.table > tbody > tr > td,
|
.table > tbody > tr > td,
|
||||||
.table > tfoot > 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;
|
line-height: 1.42857;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
BIN
static/files/excels/asset.xlsx
Normal file
BIN
static/files/excels/asset.xlsx
Normal file
Binary file not shown.
@ -16,9 +16,9 @@ function check_all(form) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAll(){
|
function checkAll(id, name){
|
||||||
var checklist = document.getElementsByName ("checked");
|
var checklist = document.getElementsByName(name);
|
||||||
if(document.getElementById("check_all").checked)
|
if(document.getElementById(id).checked)
|
||||||
{
|
{
|
||||||
for(var i=0;i<checklist.length;i++)
|
for(var i=0;i<checklist.length;i++)
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
@ -12,8 +11,6 @@
|
|||||||
{% include 'link_css.html' %}
|
{% include 'link_css.html' %}
|
||||||
{% include 'head_script.html' %}
|
{% include 'head_script.html' %}
|
||||||
{% block self_head_css_js %} {% endblock %}
|
{% block self_head_css_js %} {% endblock %}
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load mytags %}
|
{% 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 %}
|
{% block content %}
|
||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeIn">
|
<div class="wrapper wrapper-content animated fadeIn">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>下载文件</h5>
|
<h5>下载文件</h5>
|
||||||
@ -16,30 +21,62 @@
|
|||||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
<i class="fa fa-wrench"></i>
|
<i class="fa fa-wrench"></i>
|
||||||
</a>
|
</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">
|
<a class="close-link">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<h2>下载文件可联系管理员在服务器安装lrzsz,使用sz命令下载。 </h2>
|
<form id="downForm" class="form-horizontal" method="post">
|
||||||
<div>
|
{% if error %}
|
||||||
{% for document in documents %}
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
<a href="/download/{{ document }}">{{ document }}</a>
|
{% endif %}
|
||||||
{% endfor %}
|
{% if msg %}
|
||||||
</div>
|
<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>
|
</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 %}
|
{% endblock %}
|
198
templates/exec_cmd.html
Normal file
198
templates/exec_cmd.html
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<meta name="viewport" content="width=320, initial-scale=1">
|
||||||
|
<title>Jumpserver Exec Terminal</title>
|
||||||
|
<style type="text/css"></style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="chat_box" class="content">
|
||||||
|
<p class="system"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<div class="content">
|
||||||
|
<input type="text" id="pattern" value="{{ check_assets }}" placeholder="Ansible Pattern">
|
||||||
|
<input type="text" id="command" placeholder="Command to execute">
|
||||||
|
<input type="button" id="send_btn" value="Send" onclick="sendMessage()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var wsUri = "{{ web_terminal_uri }}"; //请求的websocket url
|
||||||
|
var ws = new WebSocket(wsUri);
|
||||||
|
|
||||||
|
function createSystemMessage(message) {
|
||||||
|
var message = document.createTextNode(message);
|
||||||
|
var messageBox = document.createElement('p');
|
||||||
|
messageBox.className = 'system';
|
||||||
|
messageBox.appendChild(message);
|
||||||
|
var chat = document.getElementById('chat_box');
|
||||||
|
chat.appendChild(messageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUserMessage(message) {
|
||||||
|
message = message.replace('/\n/g', '<br>');
|
||||||
|
var messageOb = document.createElement('div');
|
||||||
|
messageOb.innerHTML = message;
|
||||||
|
var messageBox = document.createElement('p');
|
||||||
|
messageBox.appendChild(messageOb);
|
||||||
|
var chat = document.getElementById('chat_box');
|
||||||
|
chat.appendChild(messageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onopen = function(ev) {
|
||||||
|
createSystemMessage('[Connected]');
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function(ev) {
|
||||||
|
createSystemMessage('[Disconnected]');
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
createUserMessage(ev.data);
|
||||||
|
var chat = document.getElementById('chat_box');
|
||||||
|
chat.scrollTop = chat.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendMessage() {
|
||||||
|
var pattern = document.getElementById('pattern');
|
||||||
|
var command = document.getElementById('command');
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
pattern: pattern.value,
|
||||||
|
command: command.value,
|
||||||
|
{# ts: (new Date()).getTime()#}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.send(JSON.stringify(data));
|
||||||
|
command.value = ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
* {
|
||||||
|
font-family: "Monaco", "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
{# background-color: #ecf0f1;#}
|
||||||
|
{# border: #000 solid 5px;#}
|
||||||
|
background: #000;
|
||||||
|
width: 800px;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat_box {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
height: 50px;
|
||||||
|
width: 800px;
|
||||||
|
{# border: #000 solid -10px;#}
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer .content {
|
||||||
|
padding-top: 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pattern { width: 25%; }
|
||||||
|
#command { width: 60%; }
|
||||||
|
#send_btn {
|
||||||
|
width: 10%;
|
||||||
|
{# position: absolute;#}
|
||||||
|
margin-left: 5px;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 800px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="button"] {
|
||||||
|
border: 0;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
background-color: #146EA8;
|
||||||
|
padding: 3px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="button"] {
|
||||||
|
background-color: #f39c12;
|
||||||
|
border-right: 2px solid #e67e22;
|
||||||
|
border-bottom: 2px solid #e67e22;
|
||||||
|
min-width: 70px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="button"]:hover {
|
||||||
|
background-color: #e67e22;
|
||||||
|
border-right: 2px solid #f39c12;
|
||||||
|
border-bottom: 2px solid #f39c12;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system,
|
||||||
|
.username {
|
||||||
|
color: #aaa;
|
||||||
|
font-style: italic;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: 1000px) {
|
||||||
|
.content { width: 90%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: 780px) {
|
||||||
|
#footer { height: 91px; }
|
||||||
|
#chat_box { padding-bottom: 91px; }
|
||||||
|
|
||||||
|
#user { width: 100%; }
|
||||||
|
#message { width: 80%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: 400px) {
|
||||||
|
#footer { height: 135px; }
|
||||||
|
#chat_box { padding-bottom: 135px; }
|
||||||
|
|
||||||
|
#message { width: 100%; }
|
||||||
|
#send_btn {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 3px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</body>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
</html>
|
@ -1,6 +1,6 @@
|
|||||||
<div class="footer fixed">
|
<div class="footer fixed">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
Version <strong>0.2.0</strong> GPL.
|
Version <strong>0.3.0</strong> GPL.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Copyright</strong> Jumpserver.org Team © 2014-2015
|
<strong>Copyright</strong> Jumpserver.org Team © 2014-2015
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content">
|
<div class="wrapper wrapper-content">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3">
|
<div class="col-sm-3">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-success pull-right">Users</span>
|
<span class="label label-success pull-right">Users</span>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3">
|
<div class="col-sm-3">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-info pull-right">Hosts</span>
|
<span class="label label-info pull-right">Hosts</span>
|
||||||
@ -31,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-3">
|
<div class="col-sm-3">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-primary pull-right">Online</span>
|
<span class="label label-primary pull-right">Online</span>
|
||||||
@ -45,7 +45,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-3">
|
<div class="col-sm-3">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-danger pull-right">Connected</span>
|
<span class="label label-danger pull-right">Connected</span>
|
||||||
@ -74,7 +74,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-7" id="top10" style="margin-left: -15px;height: 346px;padding: 15px 0 15px 0;"></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">
|
<div class="statistic-box">
|
||||||
<h4>
|
<h4>
|
||||||
活跃用户资产占比
|
活跃用户资产占比
|
||||||
@ -83,13 +83,13 @@
|
|||||||
以下图形分别描述一个月活跃用户和资产占所有用户主机的百分比
|
以下图形分别描述一个月活跃用户和资产占所有用户主机的百分比
|
||||||
</p>
|
</p>
|
||||||
<div class="row text-center">
|
<div class="row text-center">
|
||||||
<div class="col-lg-6">
|
<div class="col-sm-6">
|
||||||
<div id="activeUser" style="width: 140px; height: 140px;">
|
<div id="activeUser" style="width: 140px; height: 140px;">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<h5>用户</h5>
|
<h5>用户</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-sm-6">
|
||||||
<div id="activeAsset" style="width: 140px; height: 140px;"></div>
|
<div id="activeAsset" style="width: 140px; height: 140px;"></div>
|
||||||
<h5>主机</h5>
|
<h5>主机</h5>
|
||||||
</div>
|
</div>
|
||||||
@ -103,7 +103,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
{# <div class="ibox float-e-margins">#}
|
{# <div class="ibox float-e-margins">#}
|
||||||
{# <div class="ibox-title">#}
|
{# <div class="ibox-title">#}
|
||||||
{# <h5>权限申请</h5>#}
|
{# <h5>权限申请</h5>#}
|
||||||
@ -192,7 +192,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>最近十次登录</h5>
|
<h5>最近十次登录</h5>
|
||||||
@ -258,7 +258,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>一周Top10用户</h5>
|
<h5>一周Top10用户</h5>
|
||||||
|
@ -5,200 +5,87 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content" xmlns="http://www.w3.org/1999/html">
|
<div class="wrapper wrapper-content" xmlns="http://www.w3.org/1999/html">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-8">
|
<div class="col-sm-8">
|
||||||
<div class="ibox float-e-margins">
|
<div class="col-sm-12">
|
||||||
<div class="ibox-title">
|
<div class="ibox float-e-margins">
|
||||||
<h5> 使用说明 </h5>
|
<div class="ibox-title">
|
||||||
<div class="ibox-tools">
|
<h5> 使用说明 </h5>
|
||||||
<a class="collapse-link">
|
<div class="ibox-tools">
|
||||||
<i class="fa fa-chevron-up"></i>
|
<a class="collapse-link">
|
||||||
</a>
|
<i class="fa fa-chevron-up"></i>
|
||||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
</a>
|
||||||
<i class="fa fa-wrench"></i>
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
</a>
|
<i class="fa fa-wrench"></i>
|
||||||
<a class="close-link">
|
</a>
|
||||||
<i class="fa fa-times"></i>
|
<a class="close-link">
|
||||||
</a>
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ibox-content" style="line-height: 26px">
|
<div class="ibox-content" style="line-height: 26px">
|
||||||
<span style="font-size: large">欢</span>迎使用<span class="text-navy"><b>Jumpserver</b></span>跳板机系统,
|
<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),输入收到的密钥密码,登录跳板机。
|
首先需要 <b><a href="/juser/down_key/?id={{ user.id }}">下载</a></b> 登录跳板机的SSH密钥文件,然后导入到工具或者ssh命令指定密钥文件(确保密钥文件权限600),输入收到的密钥密码,登录跳板机。
|
||||||
登录后根据提示进行操作。跳板机web界面支持修改密码、个人信息和上传下载文件等功能,可以向管理员申请权限。
|
登录后根据提示进行操作。跳板机web界面支持修改密码、个人信息和上传下载文件等功能,可以向管理员申请权限。
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> 已授权主机 </h5>
|
<h5>登录记录</h5>
|
||||||
<div class="ibox-tools">
|
<div class="ibox-tools">
|
||||||
<div class="label label-primary float-left"><b>总共:{{ host_count }}</b></div>
|
<span class="label label-warning-light">最近登录</span>
|
||||||
<a class="collapse-link">
|
</div>
|
||||||
<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>
|
</div>
|
||||||
<div class="col-sm-2">
|
<div class="ibox-content">
|
||||||
<label>
|
<div id="last">
|
||||||
<select name="group" class="form-control m-b" onchange="change_info()">
|
<div class="feed-activity-list" >
|
||||||
<option value="">主机组</option>
|
{% for log in logs_last %}
|
||||||
{% for asset_group in asset_group_all %}
|
<div class="feed-element">
|
||||||
{% ifequal asset_group.name group_name %}
|
<a href="profile.html" class="pull-left">
|
||||||
<option value="{{ asset_group.name }}"
|
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
|
||||||
selected> {{ asset_group.name }} </option>
|
</a>
|
||||||
{% else %}
|
<div class="media-body ">
|
||||||
<option value="{{ asset_group.name }}"> {{ asset_group.name }} </option>
|
{# <small class="pull-right">{{ log.start_time|time_delta }}</small>#}
|
||||||
{% endifequal %}
|
<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 %}
|
{% endfor %}
|
||||||
</select>
|
{% if not logs_last %}
|
||||||
</label>
|
(暂无)
|
||||||
</div>
|
{% 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">
|
<div id="all" style="display: none">
|
||||||
<label>
|
<div class="feed-activity-list" >
|
||||||
<select name="asset_type" class="form-control m-b" onchange="change_info()">
|
{% for log in logs_all %}
|
||||||
<option value="">所有类型</option>
|
<div class="feed-element">
|
||||||
{% for type in asset_types %}
|
<a href="profile.html" class="pull-left">
|
||||||
{% ifequal type.0|int2str asset_type %}
|
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
|
||||||
<option value="{{ type.0 }}" selected> {{ type.1 }}</option>
|
</a>
|
||||||
{% else %}
|
<div class="media-body ">
|
||||||
<option value="{{ type.0 }}"> {{ type.1 }}</option>
|
<small class="pull-right">{{ log.start_time }}</small>
|
||||||
{% endifequal %}
|
{# <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 %}
|
{% 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>
|
||||||
</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>
|
||||||
|
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-primary"><b>{{ user.username }}</b></span>
|
<span class="label label-primary"><b>{{ user.username }}</b></span>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 填写资产基本信息 </h5>
|
<h5> 填写资产基本信息 </h5>
|
||||||
@ -76,9 +76,9 @@
|
|||||||
<div class="form-group" id="id_port">
|
<div class="form-group" id="id_port">
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<label class="col-sm-2 control-label"> 端口<span class="red-fonts">*</span> </label>
|
<label class="col-sm-2 control-label"> 端口<span class="red-fonts">*</span> </label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<input type="text" placeholder="Port" name="port" class="form-control" value="{{ default_port }}">
|
<input type="text" placeholder="Port" name="port" class="form-control" value="{{ default_port }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
@ -118,14 +118,23 @@
|
|||||||
{% block self_footer_js %}
|
{% block self_footer_js %}
|
||||||
<script>
|
<script>
|
||||||
$('document').ready(function(){
|
$('document').ready(function(){
|
||||||
$('#id_use_default_auth').click(function(){
|
var check_default = "{{ default_setting.name }}";
|
||||||
if ($(this).is(':checked')){
|
console.log(check_default);
|
||||||
$('#admin_account').css('display', 'none');
|
if (check_default != 'default'){
|
||||||
}
|
$('#id_use_default_auth').attr('disabled', true);
|
||||||
else {
|
$('#id_use_default_auth').attr('checked', false);
|
||||||
$('#admin_account').css('display', 'block');
|
$('#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"];
|
var required_fields = ["id_hostname", "id_port"];
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
</style>
|
</style>
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 填写主机基本信息 </h5>
|
<h5> 填写主机基本信息 </h5>
|
||||||
|
388
templates/jasset/asset_cu_list.html
Normal file
388
templates/jasset/asset_cu_list.html
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins" id="all">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 主机详细信息列表</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form id="asset_form">
|
||||||
|
<div class="col-sm-7" style="padding-left: 0px">
|
||||||
|
<label>
|
||||||
|
<select name="idc" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">机房</option>
|
||||||
|
{% for idc in idc_all %}
|
||||||
|
{% ifequal idc.name idc_name %}
|
||||||
|
<option value="{{idc.name}}" selected> {{ idc.name }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{idc.name}}"> {{ idc.name }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="group" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">主机组</option>
|
||||||
|
{% for asset_group in asset_group_all %}
|
||||||
|
{% ifequal asset_group.name group_name %}
|
||||||
|
<option value="{{ asset_group.name }}" selected> {{ asset_group.name }} </option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ asset_group.name }}"> {{ asset_group.name }} </option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="asset_type" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">资产类型</option>
|
||||||
|
{% for type in asset_types %}
|
||||||
|
{% ifequal type.0|int2str asset_type %}
|
||||||
|
<option value="{{ type.0 }}" selected> {{ type.1 }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ type.0 }}"> {{ type.1 }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="status" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">资产状态</option>
|
||||||
|
{% for s in asset_status %}
|
||||||
|
{% ifequal s.0|int2str status %}
|
||||||
|
<option value="{{ s.0 }}" selected> {{ s.1 }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ s.0 }}"> {{ s.1 }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-4" style="padding-right: 0">
|
||||||
|
<div class="input-group inline-group">
|
||||||
|
<input type="text" class="form-control m-b input-sm" id="search_input" name="keyword" value="{{ keyword }}" placeholder="Search">
|
||||||
|
<input type="text" style="display: none">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' href="/jasset/asset_list/?search=true" type="button" class="btn btn-sm btn-primary search-btn" onclick="change_info()">
|
||||||
|
- 搜索 -
|
||||||
|
</button>
|
||||||
|
<button type="button" href="/jasset/asset_list/?export=true" name="export" class="btn btn-sm btn-success search-btn-excel" onclick="return false">
|
||||||
|
- 导出 -
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="export"></div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input id="checkall" type="checkbox" class="i-checks" name="checkall" value="checkall" data-editable='false' onclick="check_all('asset_form')">
|
||||||
|
</th>
|
||||||
|
<th class="text-center"> 主机名 </th>
|
||||||
|
<th class="text-center" name="ip"> IP地址 </th>
|
||||||
|
<th class="text-center"> IDC </th>
|
||||||
|
<th class="text-center"> 所属主机组 </th>
|
||||||
|
{# <th class="text-center"> 配置信息 </th>#}
|
||||||
|
<th class="text-center"> 操作系统 </th>
|
||||||
|
<th class="text-center"> cpu核数 </th>
|
||||||
|
<th class="text-center"> 内存 </th>
|
||||||
|
<th class="text-center"> 硬盘 </th>
|
||||||
|
<th class="text-center"> 操作 </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for asset in assets.object_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
|
||||||
|
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
|
||||||
|
</td>
|
||||||
|
<td class="text-center hostname"> <a href="/jasset/asset_detail/?id={{ asset.id }}">{{ asset.hostname|default_if_none:"" }}</a></td>
|
||||||
|
<td class="text-center"> {{ asset.ip|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center"> {{ asset.idc.name|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center">{{ asset.group.all|group_str2 }}</td>
|
||||||
|
{# <td class="text-center">{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}</td>#}
|
||||||
|
<td class="text-center">{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }}</td>
|
||||||
|
<td class="text-center"> {{ asset.cpu|get_cpu_core|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center"> {{ asset.memory }}{% if asset.memory %}G{% endif %}</td>
|
||||||
|
<td class="text-center"> {{ asset.disk|get_disk_info }}{% if asset.memory %}G{% endif %}</td>
|
||||||
|
<td class="text-center" data-editable='false'>
|
||||||
|
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="button" id="exec_cmd" class="btn btn-sm btn-primary" name="exec_cmd" value="执行命令"/>
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('.asset_del').click(function(){
|
||||||
|
var row = $(this).closest('tr');
|
||||||
|
if (confirm("确定删除?")) {
|
||||||
|
$.get(
|
||||||
|
$(this).attr('value'),
|
||||||
|
{},
|
||||||
|
function (data) {
|
||||||
|
row.remove()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#exec_cmd').click(function(){
|
||||||
|
var url = '/jperm/role/get/';
|
||||||
|
var new_url = '/exec_cmd/?role=';
|
||||||
|
var check_array = [];
|
||||||
|
$(".gradeX input:checked").closest('tr').find('.hostname a').each(function() {
|
||||||
|
check_array.push($(this).text())
|
||||||
|
});
|
||||||
|
check_assets = check_array.join(':');
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: url,
|
||||||
|
data: {},
|
||||||
|
success: function(data){
|
||||||
|
var dataArray = data.split(',');
|
||||||
|
if (dataArray.length == 1 && data != 'error'){
|
||||||
|
var title = 'Jumpserver Exec Terminal';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['800px', '600px'],
|
||||||
|
content: new_url+data+'&check_assets='+check_assets
|
||||||
|
});
|
||||||
|
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
|
||||||
|
} else if (dataArray.length == '1' && data == 'error'){
|
||||||
|
layer.alert('没有授权角色')
|
||||||
|
} else {
|
||||||
|
aUrl = '';
|
||||||
|
$.each(dataArray, function(index, value){
|
||||||
|
aUrl += '<a onclick="windowOpenExec(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + '&check_assets=' + check_assets + '>' + value + '</a> '
|
||||||
|
});
|
||||||
|
layer.alert(aUrl, {
|
||||||
|
skin: 'layui-layer-molv',
|
||||||
|
title: '多个角色,请选择一个连接',
|
||||||
|
shade: false,
|
||||||
|
closeBtn: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.conn').click(function(){
|
||||||
|
var url='/jperm/role/get/?id=' + $(this).attr('value');
|
||||||
|
var href = $(this).attr('href');
|
||||||
|
var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role=';
|
||||||
|
var hostname = $(this).closest('tr').find('.hostname a')[0].innerHTML;
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: url,
|
||||||
|
data: {},
|
||||||
|
success: function(data){
|
||||||
|
var dataArray = data.split(',');
|
||||||
|
if (dataArray.length == 1 && data != 'error'){
|
||||||
|
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['628px', '452px'],
|
||||||
|
content: new_url+data
|
||||||
|
});
|
||||||
|
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
|
||||||
|
} else if (dataArray.length == '1' && data == 'error'){
|
||||||
|
layer.alert('没有授权角色')
|
||||||
|
} else {
|
||||||
|
aUrl = '';
|
||||||
|
$.each(dataArray, function(index, value){
|
||||||
|
aUrl += '<a onclick="windowOpen(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + ' value=' + hostname + '>' + value + '</a> '
|
||||||
|
});
|
||||||
|
console.log(aUrl);
|
||||||
|
layer.alert(aUrl, {
|
||||||
|
skin: 'layui-layer-molv',
|
||||||
|
title: '多个角色,请选择一个连接',
|
||||||
|
shade: false,
|
||||||
|
closeBtn: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function windowOpen(a){
|
||||||
|
var new_url = $(a).attr('href');
|
||||||
|
var hostname = $(a).attr('value');
|
||||||
|
var title = 'Jumpserver Web Terminal - ' + '<span class="text-info"> '+ hostname +'</span>';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['628px', '452px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function windowOpenExec(a){
|
||||||
|
var new_url = $(a).attr('href');
|
||||||
|
var title = 'Jumpserver Exec Terminal';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '600px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
console.log(new_url);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".iframe").on('click', function(){
|
||||||
|
var asset_id_all = getIDall();
|
||||||
|
if (asset_id_all == ''){
|
||||||
|
alert("请至少选择一行!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var url= $(this).attr("value") + '?asset_id_all=' + asset_id_all;
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: 'JumpServer - 批量修改主机',
|
||||||
|
maxmin: true,
|
||||||
|
shift: 'top',
|
||||||
|
border: [2, 0.3, '#1AB394'],
|
||||||
|
shade: [0.5, '#000000'],
|
||||||
|
area: ['800px', '600px'],
|
||||||
|
shadeClose: true,
|
||||||
|
content: url,
|
||||||
|
cancel: function(){
|
||||||
|
location.replace(location.href);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.search-btn-excel').unbind('click').bind('click',function(){
|
||||||
|
var url= $(this).attr("href");
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: url,
|
||||||
|
data: $("#asset_form").serialize(),
|
||||||
|
success: function (data) {
|
||||||
|
$("#export").html(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#asset_del').click(function () {
|
||||||
|
var asset_id_all = getIDall();
|
||||||
|
if (asset_id_all == ''){
|
||||||
|
alert("请至少选择一行!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (confirm("确定删除?")) {
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
data: {asset_id_all: asset_id_all},
|
||||||
|
url: "/jasset/asset_del/?arg=batch",
|
||||||
|
success: function () {
|
||||||
|
parent.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#asset_update').click(function () {
|
||||||
|
var asset_id_all = getIDall();
|
||||||
|
if (asset_id_all == ''){
|
||||||
|
if (confirm("更新全部资产信息?")) {
|
||||||
|
layer.msg('玩命更新中...', {time: 200000});
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
url: "/jasset/asset_update_batch/?arg=all",
|
||||||
|
success: function () {
|
||||||
|
parent.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
layer.msg('玩命更新中...', {time: 200000});
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
data: {asset_id_all: asset_id_all},
|
||||||
|
url: "/jasset/asset_update_batch/",
|
||||||
|
success: function () {
|
||||||
|
parent.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{# $('#asset_update_all').click(function () {#}
|
||||||
|
{# layer.msg('玩命更新中...', {time: 200000});#}
|
||||||
|
{# $.ajax({#}
|
||||||
|
{# type: "post",#}
|
||||||
|
{# url: "/jasset/asset_update_batch/?arg=all",#}
|
||||||
|
{# success: function () {#}
|
||||||
|
{# parent.location.reload();#}
|
||||||
|
{# }#}
|
||||||
|
{# });#}
|
||||||
|
{# });#}
|
||||||
|
|
||||||
|
function change_info(){
|
||||||
|
var args = $("#asset_form").serialize();
|
||||||
|
window.location = "/jasset/asset_list/?" + args
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#search_input").keydown(function(e){
|
||||||
|
if(e.keyCode==13){
|
||||||
|
change_info()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -6,7 +6,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="text text-primary"><b>{{ asset.ip }}</b></span>
|
<span class="text text-primary"><b>{{ asset.ip }}</b></span>
|
||||||
@ -97,7 +97,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-navy">内存</td>
|
<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>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-navy">硬盘</td>
|
<td class="text-navy">硬盘</td>
|
||||||
@ -129,6 +129,10 @@
|
|||||||
<td class="text-navy">系统版本</td>
|
<td class="text-navy">系统版本</td>
|
||||||
<td>{{ asset.system_type|default_if_none:"" }} {{ asset.system_version|default_if_none:"" }}</td>
|
<td>{{ asset.system_type|default_if_none:"" }} {{ asset.system_version|default_if_none:"" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">系统平台</td>
|
||||||
|
<td>{{ asset.system_arch|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-navy">运行环境</td>
|
<td class="text-navy">运行环境</td>
|
||||||
<td>{{ asset.get_env_display|default_if_none:"" }}</td>
|
<td>{{ asset.get_env_display|default_if_none:"" }}</td>
|
||||||
@ -163,7 +167,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>拥有权限的用户</h5>
|
<h5>拥有权限的用户</h5>
|
||||||
@ -182,26 +186,67 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ibox-content ibox-heading">
|
<div class="ibox-content ibox-heading">
|
||||||
<h3>主机所有授权的用户</h3>
|
<h3>主机所有授权的信息</h3>
|
||||||
<small><i class="fa fa-map-marker"></i> 包含了此主机所有授权的用户.</small>
|
<small><i class="fa fa-map-marker"></i> 包含了此主机所有授权的信息.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<div>
|
<div>
|
||||||
{# <div class="text-left">#}
|
<div class="text-left">
|
||||||
{# <table class="table">#}
|
{% if perm_info %}
|
||||||
{# {% if user_permed_list %}#}
|
{% if user_perm %}
|
||||||
{# {% for user in user_permed_list %}#}
|
<table class="table">
|
||||||
{# <tr>#}
|
<p>授权用户信息</p>
|
||||||
{# <td class="text-navy">{{ user.name }}</td>#}
|
<td class="text-navy">授权用户</td>
|
||||||
{# <td>{{ user.dept.name }}</td>#}
|
<td class="text-navy">系统角色</td>
|
||||||
{# <td><a href="/juser/user_detail/?id={{ user.id }}">详情</a></td>#}
|
{% for perm in user_perm %}
|
||||||
{# </tr>#}
|
<tr>
|
||||||
{# {% endfor %}#}
|
<td class="text-navy"><a href="/juser/user_detail/?id={{ perm.0.id }}">{{ perm.0 }}</a></td>
|
||||||
{# {% else %}#}
|
<td>
|
||||||
{# <p class="text-center">(暂无)</p>#}
|
<table class="table">
|
||||||
{# {% endif %}#}
|
{% if perm.1 %}
|
||||||
{# </table>#}
|
{% for role in perm.1 %}
|
||||||
{# </div>#}
|
<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>
|
</div>
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
@ -248,7 +293,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>最近一周登录记录</h5>
|
<h5>最近一周登录记录</h5>
|
||||||
@ -271,49 +316,47 @@
|
|||||||
<small><i class="fa fa-map-marker"></i> 此主机最近一周用户登录信息.</small>
|
<small><i class="fa fa-map-marker"></i> 此主机最近一周用户登录信息.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="ibox-content inspinia-timeline">
|
<div class="ibox-content inspinia-timeline">
|
||||||
{# {% if log %}#}
|
{% if log %}
|
||||||
{# {% for l in log %}#}
|
{% for l in log %}
|
||||||
{# <div class="timeline-item">#}
|
<div class="timeline-item">
|
||||||
{# <div class="row">#}
|
<div class="row">
|
||||||
{# <div class="col-xs-5 date">#}
|
<div class="col-xs-5 date">
|
||||||
{# <i class="fa fa-info-circle"></i>#}
|
<i class="fa fa-info-circle"></i>
|
||||||
{# <small class="text-navy">{{ l.user }}</small>#}
|
<small class="text-navy">{{ l.user }}</small>
|
||||||
{# <br/>#}
|
<br/>
|
||||||
{# <strong>{{l.dept_name}}</strong>#}
|
</div>
|
||||||
{# </div>#}
|
<div class="col-xs-7 content no-top-border">
|
||||||
{# <div class="col-xs-7 content no-top-border">#}
|
<p class="m-b-xs"><strong>详细信息</strong></p>
|
||||||
{# <p class="m-b-xs"><strong>详细信息</strong></p>#}
|
<p>来源IP: {{ l.remote_ip }}</p>
|
||||||
{# <p>来源IP: {{ l.remote_ip }}</p>#}
|
<p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</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>
|
||||||
{# <p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>#}
|
</div>
|
||||||
{# </div>#}
|
</div>
|
||||||
{# </div>#}
|
</div>
|
||||||
{# </div>#}
|
{% endfor %}
|
||||||
{# {% endfor %}#}
|
<button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> 所有 </button>
|
||||||
{# <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">
|
||||||
{# <div id='more' style="display: none">#}
|
<br/>
|
||||||
{# <br/>#}
|
{% for l in log_more %}
|
||||||
{# {% for l in log_more %}#}
|
<div class="timeline-item">
|
||||||
{# <div class="timeline-item">#}
|
<div class="row">
|
||||||
{# <div class="row">#}
|
<div class="col-xs-5 date">
|
||||||
{# <div class="col-xs-5 date">#}
|
<i class="fa fa-info-circle"></i>
|
||||||
{# <i class="fa fa-info-circle"></i>#}
|
<small class="text-navy">{{ l.user }}</small>
|
||||||
{# <small class="text-navy">{{ l.user }}</small>#}
|
<br/>
|
||||||
{# <br/>#}
|
</div>
|
||||||
{# <strong>{{l.dept_name}}</strong>#}
|
<div class="col-xs-7 content no-top-border">
|
||||||
{# </div>#}
|
<p class="m-b-xs"><strong>详细信息</strong></p>
|
||||||
{# <div class="col-xs-7 content no-top-border">#}
|
<p>来源IP: {{ l.remote_ip }}</p>
|
||||||
{# <p class="m-b-xs"><strong>详细信息</strong></p>#}
|
<p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>
|
||||||
{# <p>来源IP: {{ l.remote_ip }}</p>#}
|
<p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>
|
||||||
{# <p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>#}
|
</div>
|
||||||
{# <p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>#}
|
</div>
|
||||||
{# </div>#}
|
</div>
|
||||||
{# </div>#}
|
{% endfor %}
|
||||||
{# </div>#}
|
{% else %}
|
||||||
{# {% endfor %}#}
|
<p class="text-center">(暂无)</p>
|
||||||
{# {% else %}#}
|
{% endif %}
|
||||||
{# <p class="text-center">(暂无)</p>#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 修改资产基本信息 </h5>
|
<h5> 修改资产基本信息 </h5>
|
||||||
@ -46,6 +46,9 @@
|
|||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
{{ af.remote_ip|bootstrap_horizontal }}
|
{{ af.remote_ip|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.mac|bootstrap_horizontal }}
|
||||||
|
|
||||||
{# <div class="hr-line-dashed"></div>#}
|
{# <div class="hr-line-dashed"></div>#}
|
||||||
{# {{ af.port|bootstrap_horizontal }}#}
|
{# {{ af.port|bootstrap_horizontal }}#}
|
||||||
|
|
||||||
@ -105,6 +108,9 @@
|
|||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
{{ af.system_version|bootstrap_horizontal }}
|
{{ af.system_version|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.system_arch|bootstrap_horizontal }}
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
{{ af.number|bootstrap_horizontal }}
|
{{ af.number|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
{# <div class="ibox-title">#}
|
{# <div class="ibox-title">#}
|
||||||
{# <h5 class="text-center"> 填写修改主机信息. </h5>#}
|
{# <h5 class="text-center"> 填写修改主机信息. </h5>#}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<div class="col-sm-7" style="padding-left: 0px">
|
<div class="col-sm-7" style="padding-left: 0px">
|
||||||
<label>
|
<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>
|
<option value="">机房</option>
|
||||||
{% for idc in idc_all %}
|
{% for idc in idc_all %}
|
||||||
{% ifequal idc.name idc_name %}
|
{% ifequal idc.name idc_name %}
|
||||||
@ -43,7 +43,7 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<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>
|
<option value="">主机组</option>
|
||||||
{% for asset_group in asset_group_all %}
|
{% for asset_group in asset_group_all %}
|
||||||
{% ifequal asset_group.name group_name %}
|
{% ifequal asset_group.name group_name %}
|
||||||
@ -56,7 +56,7 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<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>
|
<option value="">资产类型</option>
|
||||||
{% for type in asset_types %}
|
{% for type in asset_types %}
|
||||||
{% ifequal type.0|int2str asset_type %}
|
{% ifequal type.0|int2str asset_type %}
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<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>
|
<option value="">资产状态</option>
|
||||||
{% for s in asset_status %}
|
{% for s in asset_status %}
|
||||||
{% ifequal s.0|int2str status %}
|
{% ifequal s.0|int2str status %}
|
||||||
@ -84,43 +84,18 @@
|
|||||||
|
|
||||||
<div class="col-sm-4" style="padding-right: 0">
|
<div class="col-sm-4" style="padding-right: 0">
|
||||||
<div class="input-group inline-group">
|
<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">
|
<input type="text" style="display: none">
|
||||||
<div class="input-group-btn">
|
<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>
|
||||||
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
<div id="export"></div>
|
||||||
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
|
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
|
||||||
<thead>
|
<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"> 操作系统 </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>
|
<th class="text-center"> 操作 </th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -144,20 +121,19 @@
|
|||||||
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
|
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
|
||||||
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
|
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
|
||||||
</td>
|
</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.ip|default_if_none:"" }} </td>
|
||||||
<td class="text-center"> {{ asset.idc.name|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.group.all|group_str2 }}</td>
|
||||||
{# <td class="text-center">{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}</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.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'>
|
<td class="text-center" data-editable='false'>
|
||||||
<a href="/jasset/asset_detail/?id={{ asset.id }}" class="btn btn-xs btn-primary">详情</a>
|
<a href="/jasset/asset_edit/?id={{ asset.id }}" class="btn btn-xs btn-info">编辑</a>
|
||||||
{% ifnotequal session_role_id 0 %}
|
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
|
||||||
<a href="/jasset/asset_edit/?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>
|
||||||
<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 %}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -168,7 +144,8 @@
|
|||||||
<input type="button" id="asset_del" class="btn btn-danger btn-sm" name="del_button" value="删除"/>
|
<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>
|
<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" 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>
|
</div>
|
||||||
{% include 'paginator.html' %}
|
{% include 'paginator.html' %}
|
||||||
</div>
|
</div>
|
||||||
@ -196,19 +173,67 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.conn').click(function(){
|
$('#exec_cmd').click(function(){
|
||||||
var url='/jlog/get_role_name/?id=' + $(this).attr('value');
|
var url = '/jperm/role/get/';
|
||||||
var href = $(this).attr('href');
|
var new_url = '/exec_cmd/?role=';
|
||||||
var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role=';
|
var check_array = [];
|
||||||
var hostname = $(this).closest('tr').find('.hostname')[0].innerHTML;
|
$(".gradeX input:checked").closest('tr').find('.hostname a').each(function() {
|
||||||
|
check_array.push($(this).text())
|
||||||
|
});
|
||||||
|
check_assets = check_array.join(':');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: url,
|
url: url,
|
||||||
data: {},
|
data: {},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
var dataArray = data.split(',');
|
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'){
|
if (dataArray.length == 1 && data != 'error'){
|
||||||
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
|
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
|
||||||
|
console.log(new_url+data);
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 2,
|
type: 2,
|
||||||
title: title,
|
title: title,
|
||||||
@ -218,13 +243,14 @@
|
|||||||
content: new_url+data
|
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');
|
//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('没有授权角色')
|
layer.alert('没有授权角色')
|
||||||
} else {
|
} else {
|
||||||
aUrl = '';
|
aUrl = '';
|
||||||
$.each(dataArray, function(index, value){
|
$.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> '
|
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, {
|
layer.alert(aUrl, {
|
||||||
skin: 'layui-layer-molv',
|
skin: 'layui-layer-molv',
|
||||||
title: '多个角色,请选择一个连接',
|
title: '多个角色,请选择一个连接',
|
||||||
@ -250,7 +276,21 @@
|
|||||||
shade: false,
|
shade: false,
|
||||||
content: new_url
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,37 +351,40 @@
|
|||||||
$('#asset_update').click(function () {
|
$('#asset_update').click(function () {
|
||||||
var asset_id_all = getIDall();
|
var asset_id_all = getIDall();
|
||||||
if (asset_id_all == ''){
|
if (asset_id_all == ''){
|
||||||
alert("请至少选择一行!");
|
if (confirm("更新全部资产信息?")) {
|
||||||
return false;
|
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(){#}
|
{# $('#asset_update_all').click(function () {#}
|
||||||
{# layer.tips('我是另外一个tips,只不过我长得跟之前那位稍有些不一样。', '吸附元素选择器', {#}
|
{# layer.msg('玩命更新中...', {time: 200000});#}
|
||||||
{# tips: [1, '#3595CC'],#}
|
{# $.ajax({#}
|
||||||
{# time: 4000#}
|
{# 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(){
|
function change_info(){
|
||||||
var args = $("#asset_form").serialize();
|
var args = $("#asset_form").serialize();
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 填写主机组基本信息 </h5>
|
<h5> 填写主机组基本信息 </h5>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 填写主机组基本信息 </h5>
|
<h5> 填写主机组基本信息 </h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> 主机组详细信息列表</h5>
|
<h5> 主机组详细信息列表</h5>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 填写IDC基本信息 </h5>
|
<h5> 填写IDC基本信息 </h5>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 填写IDC基本信息 </h5>
|
<h5> 填写IDC基本信息 </h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> IDC详细信息列表</h5>
|
<h5> IDC详细信息列表</h5>
|
||||||
|
126
templates/jlog/exec_detail.html
Normal file
126
templates/jlog/exec_detail.html
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>{{ log.id }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">ID</td>
|
||||||
|
<td>{{ log.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">用户名</td>
|
||||||
|
<td>{{ log.user }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">来源IP</td>
|
||||||
|
<td>{{ log.remote_ip }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">日期</td>
|
||||||
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">主机</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% for asset_name in assets_hostname %}
|
||||||
|
{% if asset_name %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ asset_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>结果</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content inspinia-timeline">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">命令</td>
|
||||||
|
<td>{{ log.cmd }}</td>
|
||||||
|
</tr>
|
||||||
|
{% for result, info in result.items %}
|
||||||
|
{% for host, msg in info.items %}
|
||||||
|
{% ifequal result 'failed' %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy" style="color: #ed5565">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#show').click(function(){
|
||||||
|
$('#last').css('display', 'none');
|
||||||
|
$('#all').css('display', 'block');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
140
templates/jlog/file_detail.html
Normal file
140
templates/jlog/file_detail.html
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>{{ log.id }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">ID</td>
|
||||||
|
<td>{{ log.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">用户名</td>
|
||||||
|
<td>{{ log.user }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">来源IP</td>
|
||||||
|
<td>{{ log.remote_ip }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">类型</td>
|
||||||
|
<td>{{ log.type }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">日期</td>
|
||||||
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">文件</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% for file_name in file_list %}
|
||||||
|
{% if file_name %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ file_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">主机</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% for asset_name in assets_hostname %}
|
||||||
|
{% if asset_name %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ asset_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>结果</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content inspinia-timeline">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
{% for result, info in result.items %}
|
||||||
|
{% for host, msg in info.items %}
|
||||||
|
{% ifequal result 'failed' %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy" style="color: #ed5565">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#show').click(function(){
|
||||||
|
$('#last').css('display', 'none');
|
||||||
|
$('#all').css('display', 'block');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
106
templates/jlog/log_exec.html
Normal file
106
templates/jlog/log_exec.html
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div id="ibox-content" class="ibox-title">
|
||||||
|
<h5> 批量命令日志 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
|
||||||
|
<li><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
|
||||||
|
<li class="active"><a href="/jlog/log_list/exec/" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
|
||||||
|
<li><a href="/jlog/log_list/file/" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
|
||||||
|
<div class="" style="float: right">
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" id="keyword" name="keyword" value="{{ keyword }}" placeholder="Search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
-搜索-
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<table class="table table-striped table-bordered table-hover ">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center"> ID </th>
|
||||||
|
<th class="text-center"> 用户名 </th>
|
||||||
|
<th class="text-center"> 主机 </th>
|
||||||
|
<th class="text-center"> 命令 </th>
|
||||||
|
<th class="text-center"> 来源IP </th>
|
||||||
|
<th class="text-center"> 时间 </th>
|
||||||
|
<th class="text-center"> 详情 </th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for post in contacts.object_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center"> {{ post.id }} </td>
|
||||||
|
<td class="text-center username"> {{ post.user }} </td>
|
||||||
|
<td class="text-center ip"> {{ post.host | truncatechars:30 }} </td>
|
||||||
|
<td class="text-center ip"> {{ post.cmd | truncatechars:30 }} </td>
|
||||||
|
<td class="text-center remote_ip"> {{ post.remote_ip }} </td>
|
||||||
|
<td class="text-center start_time"> {{ post.datetime|date:"Y-m-d H:i:s"}} </td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="/jlog/log_detail/exec/?id={{ post.id }}" class="btn btn-xs btn-primary">详情</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log_search(){
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: "/jlog/search/?env=offline",
|
||||||
|
data: $("#search_form").serialize(),
|
||||||
|
success: function (data) {
|
||||||
|
$(".tab-content").html(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
108
templates/jlog/log_file.html
Normal file
108
templates/jlog/log_file.html
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div id="ibox-content" class="ibox-title">
|
||||||
|
<h5> 上传下载日志 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
|
||||||
|
<li><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
|
||||||
|
<li><a href="/jlog/log_list/exec/" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
|
||||||
|
<li class="active"><a href="/jlog/log_list/file/" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
|
||||||
|
<div class="" style="float: right">
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" id="keyword" name="keyword" value="{{ keyword }}" placeholder="Search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
-搜索-
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<table class="table table-striped table-bordered table-hover ">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center"> ID </th>
|
||||||
|
<th class="text-center"> 用户名 </th>
|
||||||
|
<th class="text-center"> 主机 </th>
|
||||||
|
<th class="text-center"> 文件 </th>
|
||||||
|
<th class="text-center"> 类型 </th>
|
||||||
|
<th class="text-center"> 来源IP </th>
|
||||||
|
<th class="text-center"> 时间 </th>
|
||||||
|
<th class="text-center"> 详情 </th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for post in contacts.object_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center"> {{ post.id }} </td>
|
||||||
|
<td class="text-center"> {{ post.user }} </td>
|
||||||
|
<td class="text-center"> {{ post.host | truncatechars:30 }} </td>
|
||||||
|
<td class="text-center"> {{ post.filename | truncatechars:20 }} </td>
|
||||||
|
<td class="text-center"> {{ post.type }} </td>
|
||||||
|
<td class="text-center"> {{ post.remote_ip }} </td>
|
||||||
|
<td class="text-center"> {{ post.datetime|date:"Y-m-d H:i:s"}} </td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="/jlog/log_detail/file/?id={{ post.id }}" class="btn btn-xs btn-primary">详情</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log_search(){
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: "/jlog/search/?env=offline",
|
||||||
|
data: $("#search_form").serialize(),
|
||||||
|
success: function (data) {
|
||||||
|
$(".tab-content").html(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 用户日志详细信息列表 </h5>
|
<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="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 用户日志详细信息列表 </h5>
|
<h5> 用户日志详细信息列表 </h5>
|
||||||
@ -54,9 +54,10 @@
|
|||||||
<ul class="nav nav-tabs">
|
<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/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 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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
|
||||||
<form class="form-inline" action="" method="get">
|
<form class="form-inline" action="" method="get">
|
||||||
<div class="form-group" id="date_5">
|
<div class="form-group" id="date_5">
|
||||||
<div class="input-daterange input-group" id="datepicker">
|
<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;">
|
<input id="cmd" name="cmd" placeholder="命令" type="text" class="form-control" value="{{ cmd }}" style="width: 200px;">
|
||||||
</div>
|
</div>
|
||||||
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
Search
|
- 搜索 -
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 用户日志详细信息列表 </h5>
|
<h5> 用户日志详细信息列表 </h5>
|
||||||
@ -69,9 +69,22 @@
|
|||||||
<ul class="nav nav-tabs">
|
<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 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/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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<table class="table table-striped table-bordered table-hover ">
|
<table class="table table-striped table-bordered table-hover ">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div id="ibox-content" class="ibox-title">
|
<div id="ibox-content" class="ibox-title">
|
||||||
<h5> 用户{{ username }}日志详细信息列表 </h5>
|
<h5> 用户{{ username }}日志详细信息列表 </h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> {{ user_group.name }}授权修改</h5>
|
<h5> {{ user_group.name }}授权修改</h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> 查看小组</h5>
|
<h5> 查看小组</h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> 查看小组</h5>
|
<h5> 查看小组</h5>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<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">
|
<div class="ibox-tools">
|
||||||
<a class="collapse-link">
|
<a class="collapse-link">
|
||||||
<i class="fa fa-chevron-up"></i>
|
<i class="fa fa-chevron-up"></i>
|
||||||
@ -54,7 +54,7 @@
|
|||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<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">
|
<div class="ibox-tools">
|
||||||
<a class="collapse-link">
|
<a class="collapse-link">
|
||||||
<i class="fa fa-chevron-up"></i>
|
<i class="fa fa-chevron-up"></i>
|
||||||
@ -100,7 +100,7 @@
|
|||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<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">
|
<div class="ibox-tools">
|
||||||
<a class="collapse-link">
|
<a class="collapse-link">
|
||||||
<i class="fa fa-chevron-up"></i>
|
<i class="fa fa-chevron-up"></i>
|
||||||
@ -148,7 +148,7 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<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">
|
<div class="ibox-tools">
|
||||||
<a class="collapse-link">
|
<a class="collapse-link">
|
||||||
<i class="fa fa-chevron-up"></i>
|
<i class="fa fa-chevron-up"></i>
|
||||||
@ -175,7 +175,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">
|
<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>
|
||||||
<th class="text-center">主机</th>
|
<th class="text-center">主机</th>
|
||||||
<th class="text-center">密钥</th>
|
<th class="text-center">密钥</th>
|
||||||
@ -188,7 +188,7 @@
|
|||||||
{% for asset, info in pushed_asset.items %}
|
{% for asset, info in pushed_asset.items %}
|
||||||
<tr class="gradeX">
|
<tr class="gradeX">
|
||||||
<th class="text-center">
|
<th class="text-center">
|
||||||
<input type="checkbox" name="checked" value="{{ asset.id }}">
|
<input type="checkbox" name="asset_id" value="{{ asset.id }}">
|
||||||
</th>
|
</th>
|
||||||
<td class="text-center"> {{ asset.hostname }} </td>
|
<td class="text-center"> {{ asset.hostname }} </td>
|
||||||
<td class="text-center"> {{ info.key | yesno:"是,否,未知" }} </td>
|
<td class="text-center"> {{ info.key | yesno:"是,否,未知" }} </td>
|
||||||
@ -212,7 +212,7 @@
|
|||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<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">
|
<div class="ibox-tools">
|
||||||
<a class="collapse-link">
|
<a class="collapse-link">
|
||||||
<i class="fa fa-chevron-up"></i>
|
<i class="fa fa-chevron-up"></i>
|
||||||
@ -220,7 +220,6 @@
|
|||||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
<i class="fa fa-wrench"></i>
|
<i class="fa fa-wrench"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="close-link">
|
<a class="close-link">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
</a>
|
</a>
|
||||||
@ -229,9 +228,13 @@
|
|||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-left">
|
<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>
|
<thead>
|
||||||
<tr>
|
<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">主机</th>
|
||||||
<th class="text-center">IP</th>
|
<th class="text-center">IP</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -239,6 +242,9 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for asset in need_push_asset %}
|
{% for asset in need_push_asset %}
|
||||||
<tr class="gradeX">
|
<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.hostname }} </td>
|
||||||
<td class="text-center"> {{ asset.ip }} </td>
|
<td class="text-center"> {{ asset.ip }} </td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -276,10 +282,11 @@
|
|||||||
$('.del_muti').click(function(){
|
$('.del_muti').click(function(){
|
||||||
var check_array = [];
|
var check_array = [];
|
||||||
if (confirm("确定删除")) {
|
if (confirm("确定删除")) {
|
||||||
$(".gradeX input:checked").each(function() {
|
$(".gradeX input[name='asset_id']:checked").each(function() {
|
||||||
check_array.push($(this).attr("value"))
|
check_array.push($(this).attr("value"))
|
||||||
});
|
});
|
||||||
var url = '/jperm/role/recycle/?role_id={{ role.id }}&asset_id=' + check_array.join(',');
|
var url = '/jperm/role/recycle/?role_id={{ role.id }}&asset_id=' + check_array.join(',');
|
||||||
|
console.log(check_array);
|
||||||
$.get(url,
|
$.get(url,
|
||||||
{},
|
{},
|
||||||
function(data){
|
function(data){
|
||||||
@ -290,6 +297,15 @@
|
|||||||
return false;
|
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>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -9,7 +9,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div>
|
<div>
|
||||||
{% if error %}
|
{% if error %}
|
||||||
@ -50,19 +50,16 @@
|
|||||||
<th class="text-center">名称 </th>
|
<th class="text-center">名称 </th>
|
||||||
<th class="text-center">sudo别名</th>
|
<th class="text-center">sudo别名</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>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="edittbody">
|
<tbody id="edittbody">
|
||||||
{% for role in roles %}
|
{% for role in roles %}
|
||||||
<tr class="gradeX" id={{ role.id }}>
|
<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 | 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.date_added | date:"Y-m-d H:i:s"}} </td>
|
||||||
<td class="text-center"> {{ role.comment }} </td>
|
|
||||||
<td class="text-center">
|
<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/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>
|
<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>
|
<button onclick="remove_role({{ role.id }})" class="btn btn-xs btn-danger">删除</button>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
@ -45,7 +45,7 @@
|
|||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<select name="assets" id="assets" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
|
<select name="assets" id="assets" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
{% for asset in assets %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
@ -34,9 +34,9 @@
|
|||||||
<div class="alert alert-success text-center">{{ msg }}</div>
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="form-group">
|
<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">
|
<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>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
@ -52,10 +52,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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">
|
<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 %}
|
{% for user_group in user_groups %}
|
||||||
<option value="{{ user_group.id }}">{{ user_group.name }}</option>
|
<option value="{{ user_group.id }}">{{ user_group.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -75,9 +74,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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">
|
<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 %}
|
{% for asset_group in asset_groups %}
|
||||||
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
|
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -100,7 +99,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="comment" class="col-sm-2 control-label">备注</label>
|
<label for="comment" class="col-sm-2 control-label">备注</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
@ -133,17 +132,17 @@ $('#ruleForm').validator({
|
|||||||
},
|
},
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
"rulename": {
|
"name": {
|
||||||
rule: "required;check_name",
|
rule: "required;check_name",
|
||||||
tip: "输入规则名称",
|
tip: "输入规则名称",
|
||||||
msg: {required: "规则名称必填"}
|
msg: {required: "规则名称必填"}
|
||||||
},
|
},
|
||||||
"usergroup": {
|
"user_group": {
|
||||||
rule: "required(check_user)",
|
rule: "required(check_user)",
|
||||||
tip: "请选择用户组",
|
tip: "请选择用户组",
|
||||||
msg: {required: "用户和用户组必选一个!"}
|
msg: {required: "用户和用户组必选一个!"}
|
||||||
},
|
},
|
||||||
"assetgroup": {
|
"asset_group": {
|
||||||
rule: "required(check_asset)",
|
rule: "required(check_asset)",
|
||||||
tip: "输入资产组",
|
tip: "输入资产组",
|
||||||
msg: {required: "资产和资产组必选一个!"}
|
msg: {required: "资产和资产组必选一个!"}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-primary"><b>{{ rule.name }}</b></span>
|
<span class="label label-primary"><b>{{ rule.name }}</b></span>
|
||||||
@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-primary"><b>授权用户/用户组</b></span>
|
<span class="label label-primary"><b>授权用户/用户组</b></span>
|
||||||
@ -106,7 +106,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-primary"><b>授权主机/主机组</b></span>
|
<span class="label label-primary"><b>授权主机/主机组</b></span>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
@ -26,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<form method="post" id="userForm" class="form-horizontal" action="">
|
<form method="post" id="ruleForm" class="form-horizontal" action="">
|
||||||
{% if error %}
|
{% if error %}
|
||||||
<div class="alert alert-warning text-center">{{ error }}</div>
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -34,51 +34,51 @@
|
|||||||
<div class="alert alert-success text-center">{{ msg }}</div>
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="form-group">
|
<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">
|
<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>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
<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">
|
<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 %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
<span class="help-block m-b-none">用户和用户组必选一个</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
|
||||||
<div class="form-group">
|
<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">
|
<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 %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
<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">
|
<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 %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
<span class="help-block m-b-none">资产和资产组必选一个</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
|
||||||
<div class="form-group">
|
<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">
|
<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 %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -88,8 +88,8 @@
|
|||||||
<label for="role" class="col-sm-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">
|
<div class="col-sm-8">
|
||||||
<select name="role" data-placeholder="请选择角色" class="chosen-select form-control m-b" multiple tabindex="2">
|
<select name="role" data-placeholder="请选择角色" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
{% for role in roles %}
|
{% for role in roles %}
|
||||||
<option value="{{ role.id }}"{% if role in roles_select %} selected {% endif %}>{{ role.name }}</option>
|
<option value="{{ role.id }}"{% if role in rule.role.all %} selected {% endif %}>{{ role.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -99,7 +99,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="comment" class="col-sm-2 control-label">备注</label>
|
<label for="comment" class="col-sm-2 control-label">备注</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
@ -119,6 +119,47 @@
|
|||||||
{% block self_footer_js %}
|
{% block self_footer_js %}
|
||||||
<script>
|
<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 = {
|
var config = {
|
||||||
'.chosen-select' : {},
|
'.chosen-select' : {},
|
||||||
'.chosen-select-deselect' : {allow_single_deselect:true},
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div>
|
<div>
|
||||||
{% if error %}
|
{% if error %}
|
||||||
@ -35,7 +35,6 @@
|
|||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<div class="">
|
<div class="">
|
||||||
<a href="/jperm/perm_rule_add/" class="btn btn-sm btn-primary "> 添加规则 </a>
|
<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">
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control input-sm" id="search_input" name="search" placeholder="Search">
|
<input type="text" class="form-control input-sm" id="search_input" name="search" placeholder="Search">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
@ -69,7 +69,7 @@ $('#sudoForm').validator({
|
|||||||
timely: 2,
|
timely: 2,
|
||||||
theme: "yellow_right_effect",
|
theme: "yellow_right_effect",
|
||||||
rules: {
|
rules: {
|
||||||
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位']
|
check_name: [/^\w{2,20}$/, '大写字母,2-20位']
|
||||||
},
|
},
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div>
|
<div>
|
||||||
{% if error %}
|
{% if error %}
|
||||||
|
@ -10,11 +10,11 @@ cp ${sudo_file} ${sudo_file_bak}
|
|||||||
# Add Command Aliases
|
# Add Command Aliases
|
||||||
add_cmd_alias() {
|
add_cmd_alias() {
|
||||||
sudo_file=$1
|
sudo_file=$1
|
||||||
{% for sudo in sudo_alias %}
|
{% for sudo_name, sudo_cmd in sudo_alias.items %}
|
||||||
if $(grep '^Cmnd_Alias {{ sudo.name }}' ${sudo_file} &> /dev/null); then
|
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}
|
sed -i 's@^Cmnd_Alias.*{{ sudo_name }}.*@Cmnd_Alias {{ sudo_name }} = {{ sudo_cmd }}@g' ${sudo_file}
|
||||||
else
|
else
|
||||||
echo "Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }}" >> ${sudo_file}
|
echo "Cmnd_Alias {{ sudo_name }} = {{ sudo_cmd }}" >> ${sudo_file}
|
||||||
fi
|
fi
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
@ -23,11 +23,11 @@ add_cmd_alias() {
|
|||||||
# Add Command Aliases to role
|
# Add Command Aliases to role
|
||||||
add_role_chosen() {
|
add_role_chosen() {
|
||||||
sudo_file=$1
|
sudo_file=$1
|
||||||
{% for role, alias in role_chosen_aliase.items %}
|
{% for user, alias in sudo_user.items %}
|
||||||
if $(grep '^{{ role }}.*' ${sudo_file} &> /dev/null); then
|
if $(grep '^{{ user }}.*' ${sudo_file} &> /dev/null); then
|
||||||
sed -i 's@^{{ role }}.*@{{ role }} ALL = NOPASSWD: {{ alias }}@g' ${sudo_file}
|
sed -i 's@^{{ user }}.*@{{ user }} ALL = (root) NOPASSWD: {{ alias }}@g' ${sudo_file}
|
||||||
else
|
else
|
||||||
echo "{{ role }} ALL = NOPASSWD: {{ alias }}" >> ${sudo_file}
|
echo "{{ user }} ALL = (root) NOPASSWD: {{ alias }}" >> ${sudo_file}
|
||||||
fi
|
fi
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="panel blank-panel">
|
<div class="panel blank-panel">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="panel blank-panel">
|
<div class="panel blank-panel">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>编辑用户信息</h5>
|
<h5>编辑用户信息</h5>
|
||||||
@ -45,7 +45,7 @@
|
|||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<input id="password" name="password" placeholder="Password" type="password" class="form-control">
|
<input id="password" name="password" placeholder="Password" type="password" class="form-control">
|
||||||
<span class="help-block m-b-none">
|
<span class="help-block m-b-none">
|
||||||
登陆web的密码
|
登陆web的密码, 不修改请留空
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -88,10 +88,13 @@
|
|||||||
$('#userForm').validator({
|
$('#userForm').validator({
|
||||||
timely: 2,
|
timely: 2,
|
||||||
theme: "yellow_right_effect",
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_pass: [/^\w+$/, '数字和字符']
|
||||||
|
},
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
"password": {
|
"password": {
|
||||||
rule: "length[6~50]",
|
rule: "length[6~50];check_pass",
|
||||||
tip: "输入密码",
|
tip: "输入密码",
|
||||||
ok: "",
|
ok: "",
|
||||||
msg: {required: "必须填写!"}
|
msg: {required: "必须填写!"}
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
|
|
||||||
<body class="gray-bg">
|
<body class="gray-bg">
|
||||||
|
|
||||||
<div class="lock-word animated fadeInDown">
|
<div class="lock-word animated fadeInDown">
|
||||||
<span class="first-word">Jumperver</span>
|
<span class="first-word">Jumperver</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="middle-box text-center lockscreen animated fadeInDown">
|
<div class="middle-box text-center lockscreen animated fadeInDown">
|
||||||
<div>
|
<div>
|
||||||
<div class="m-b-md">
|
<div class="m-b-md">
|
||||||
@ -47,7 +47,5 @@
|
|||||||
<script src="/static/js/jquery-2.1.1.js"></script>
|
<script src="/static/js/jquery-2.1.1.js"></script>
|
||||||
<script src="/static/js/bootstrap.min.js"></script>
|
<script src="/static/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
</body></html>
|
|
@ -4,7 +4,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
|
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
|
@ -26,21 +26,16 @@
|
|||||||
<td class="text-center">角色</td>
|
<td class="text-center">角色</td>
|
||||||
<td class="text-center">Email</td>
|
<td class="text-center">Email</td>
|
||||||
<td class="text-center">激活</td>
|
<td class="text-center">激活</td>
|
||||||
{# <td class="text-center">添加时间</td>#}
|
|
||||||
{# <td class="text-center">最后登录</td>#}
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="gradeX">
|
<tr class="gradeX">
|
||||||
|
|
||||||
<td class="text-center">{{ user.id }}</td>
|
<td class="text-center">{{ user.id }}</td>
|
||||||
<td class="text-center">{{ user.username }}</td>
|
<td class="text-center">{{ user.username }}</td>
|
||||||
<td class="text-center">{{ user.name }}</td>
|
<td class="text-center">{{ user.name }}</td>
|
||||||
<td class="text-center">{{ user.id | get_role }}</td>
|
<td class="text-center">{{ user.id | get_role }}</td>
|
||||||
<td class="text-center">{{ user.email }}</td>
|
<td class="text-center">{{ user.email }}</td>
|
||||||
<td class="text-center">{{ user.is_active|bool2str }}</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>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">添加日期: </td>
|
<td class="text-center">添加日期: </td>
|
||||||
@ -56,23 +51,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</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>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<html><head>
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="/static/font-awesome/css/font-awesome.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/animate.css" rel="stylesheet">
|
||||||
<link href="/static/css/style.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">
|
<body class="gray-bg">
|
||||||
|
|
||||||
<div class="lock-word animated fadeInDown">
|
<div class="lock-word animated fadeInDown">
|
||||||
<span class="first-word">Jump</span><span>Server</span>
|
<span class="first-word">Jump</span><span>Server</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="middle-box text-center lockscreen animated fadeInDown">
|
<div class="middle-box text-center lockscreen animated fadeInDown">
|
||||||
<div>
|
<div>
|
||||||
<div class="m-b-md">
|
<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 %}
|
{% if error %}
|
||||||
<div class="alert alert-warning text-center">{{ error }}</div>
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -30,21 +30,44 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<h3>请输入新密码</h3>
|
<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">
|
<div class="form-group">
|
||||||
<input type="password" name='password' class="form-control" placeholder="New Password" required="">
|
<input type="password" name='password' class="form-control" placeholder="New Password">
|
||||||
<input type="password" name='password_confirm' class="form-control" placeholder="Password Confirm" required="">
|
<input type="password" name='password_confirm' class="form-control" placeholder="Password Confirm">
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary block full-width">确定</button>
|
<button type="submit" class="btn btn-primary block full-width">确定</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mainly scripts -->
|
<!-- Mainly scripts -->
|
||||||
<script src="/static/js/jquery-2.1.1.js"></script>
|
<script src="/static/js/jquery-2.1.1.js"></script>
|
||||||
<script src="/static/js/bootstrap.min.js"></script>
|
<script src="/static/js/bootstrap.min.js"></script>
|
||||||
|
<script src="/static/js/validator/jquery.validator.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$('#restForm').validator({
|
||||||
|
timely: 2,
|
||||||
</body></html>
|
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="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> 命令批量执行 </h5>
|
<h5> 命令批量执行 </h5>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>填写基本信息</h5>
|
<h5>填写基本信息</h5>
|
||||||
@ -99,11 +99,11 @@
|
|||||||
<label><input type="checkbox" value="0" name="extra" >禁用 </label>
|
<label><input type="checkbox" value="0" name="extra" >禁用 </label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2">
|
{# <div class="col-sm-2">#}
|
||||||
<div class="checkbox i-checks">
|
{# <div class="checkbox i-checks">#}
|
||||||
<label><input type="checkbox" value="1" name="extra">ssh key登录 </label>
|
{# <label><input type="checkbox" value="1" name="extra">ssh key登录 </label>#}
|
||||||
</div>
|
{# </div>#}
|
||||||
</div>
|
{# </div>#}
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
<div class="checkbox i-checks">
|
<div class="checkbox i-checks">
|
||||||
<label><input type="checkbox" value="2" name="extra">发送邮件 </label>
|
<label><input type="checkbox" value="2" name="extra">发送邮件 </label>
|
||||||
@ -167,16 +167,16 @@ $('#userForm').validator({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("document").ready(function(){
|
{#$("document").ready(function(){#}
|
||||||
$("input.role").click(function(){
|
{# $("input.role").click(function(){#}
|
||||||
if($("input.role[value=GA]").is( ":checked" )){
|
{# if($("input.role[value=GA]").is( ":checked" )){#}
|
||||||
$("#admin_groups").css("display", 'block');
|
{# $("#admin_groups").css("display", 'block');#}
|
||||||
}
|
{# }#}
|
||||||
else {
|
{# else {#}
|
||||||
$("#admin_groups").css("display", 'none');
|
{# $("#admin_groups").css("display", 'none');#}
|
||||||
}
|
{# }#}
|
||||||
})
|
{# })#}
|
||||||
})
|
{#})#}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -6,7 +6,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<span class="label label-primary"><b>{{ user.name }}</b></span>
|
<span class="label label-primary"><b>{{ user.name }}</b></span>
|
||||||
@ -45,9 +45,17 @@
|
|||||||
<td>{{ user.name }}</td>
|
<td>{{ user.name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-navy">角色</td>
|
<td class="text-navy">权限</td>
|
||||||
<td>{{ user.id | get_role }}</td>
|
<td>{{ user.id | get_role }}</td>
|
||||||
</tr>
|
</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>
|
<tr>
|
||||||
<td class="text-navy">Email</td>
|
<td class="text-navy">Email</td>
|
||||||
<td>{{ user.email }}</td>
|
<td>{{ user.email }}</td>
|
||||||
@ -76,13 +84,43 @@
|
|||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</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>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>授权主机/组</h5>
|
<h5>授权主机/组</h5>
|
||||||
@ -94,10 +132,6 @@
|
|||||||
<i class="fa fa-wrench"></i>
|
<i class="fa fa-wrench"></i>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu dropdown-user">
|
<ul class="dropdown-menu dropdown-user">
|
||||||
<li><a href="#">Config option 1</a>
|
|
||||||
</li>
|
|
||||||
<li><a href="#">Config option 2</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<a class="close-link">
|
<a class="close-link">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
@ -106,38 +140,40 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ibox-content ibox-heading">
|
<div class="ibox-content ibox-heading">
|
||||||
<h3>用户的所有授权主机</h3>
|
<h3>用户的所有授权主机</h3>
|
||||||
<small><i class="fa fa-map-marker"></i> 这里包含了用户所有的主机组和组下的主机.</small>
|
<small><i class="fa fa-map-marker"></i> 这里包含了用户授权角色和角色下的主机.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="ibox-content inspinia-timeline">
|
<div class="ibox-content inspinia-timeline">
|
||||||
{# {% for group in user|get_user_asset_group %}#}
|
{% for role, assets in role_assets.items %}
|
||||||
{# <div class="timeline-item">#}
|
<div class="timeline-item">
|
||||||
{# <div class="row">#}
|
<div class="row">
|
||||||
{# <div class="col-xs-3 date">#}
|
<div class="col-xs-3 date">
|
||||||
{# <i class="fa fa-repeat"></i>#}
|
<i class="fa fa-info"></i>
|
||||||
{# <b><a href="/jperm/perm_list/?uid={{ user.id }}&agid={{ group.id }}">{{ group.name }}</a></b>#}
|
<b>
|
||||||
{# <br>#}
|
<a href="/jperm/role/&id={{ role.id }}">{{ role.name }}</a></b>
|
||||||
{# <small class="text-navy">共: {{ group | group_asset_list_count }}台</small>#}
|
<br>
|
||||||
{# </div>#}
|
<small class="text-navy">共: {{ assets.asset | length }}台</small>
|
||||||
{# <div class="col-xs-7 content no-top-border">#}
|
</div>
|
||||||
{# <p class="m-b-xs"><strong>{{ group.comment }}</strong></p>#}
|
<div class="col-xs-7 content no-top-border">
|
||||||
{# <p>#}
|
<p class="m-b-xs">
|
||||||
{# {% for asset in group|group_asset_list %}#}
|
<strong>{{ role.comment }}</strong></p>
|
||||||
{# {{ asset.ip }}<br>#}
|
<p>
|
||||||
{# {% endfor %}#}
|
{% for asset in assets.asset %}
|
||||||
{# </p>#}
|
<a href="/jasset/asset_list/?id={{ asset.id }}">{{ asset.hostname }}</a><br>
|
||||||
{# <p></p>#}
|
{% endfor %}
|
||||||
{# </div>#}
|
</p>
|
||||||
{# </div>#}
|
<p></p>
|
||||||
{# </div>#}
|
</div>
|
||||||
{# {% endfor %}#}
|
</div>
|
||||||
{# {% if not user|get_user_asset_group %}#}
|
</div>
|
||||||
{# (无)#}
|
{% endfor %}
|
||||||
{# {% endif %}#}
|
{% ifequal '{{ asset.asset | length }}' 0 %}
|
||||||
|
(无)
|
||||||
|
{% endifequal %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-4">
|
<div class="col-sm-4">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>登录记录</h5>
|
<h5>登录记录</h5>
|
||||||
@ -147,8 +183,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<div id="last">
|
<div id="last">
|
||||||
|
|
||||||
<div class="feed-activity-list" >
|
<div class="feed-activity-list" >
|
||||||
{% for log in logs_last %}
|
{% for log in user_log_ten %}
|
||||||
<div class="feed-element">
|
<div class="feed-element">
|
||||||
<a href="profile.html" class="pull-left">
|
<a href="profile.html" class="pull-left">
|
||||||
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
|
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
|
||||||
@ -161,18 +198,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if not logs_last %}
|
{% if not user_log_ten %}
|
||||||
(暂无)
|
(暂无)
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if logs_num > 10 %}
|
{% 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 All</button>
|
<button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> Show More</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="all" style="display: none">
|
<div id="all" style="display: none">
|
||||||
<div class="feed-activity-list" >
|
<div class="feed-activity-list" >
|
||||||
{% for log in logs_all %}
|
{% for log in user_log_last %}
|
||||||
<div class="feed-element">
|
<div class="feed-element">
|
||||||
<a href="profile.html" class="pull-left">
|
<a href="profile.html" class="pull-left">
|
||||||
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
|
<img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>编辑用户信息</h5>
|
<h5>编辑用户信息</h5>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
<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">
|
<div class="col-sm-8">
|
||||||
<select id="groups" name="groups" class="form-control m-b" multiple size="12">
|
<select id="groups" name="groups" class="form-control m-b" multiple size="12">
|
||||||
{% for group in group_all %}
|
{% for group in group_all %}
|
||||||
@ -70,7 +70,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
<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">
|
<div class="col-sm-8">
|
||||||
{% for r, role_name in user_role.items %}
|
{% for r, role_name in user_role.items %}
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
@ -140,13 +140,9 @@
|
|||||||
$('#userForm').validator({
|
$('#userForm').validator({
|
||||||
timely: 2,
|
timely: 2,
|
||||||
theme: "yellow_right_effect",
|
theme: "yellow_right_effect",
|
||||||
{# rules: {#}
|
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_pass: [/^\w+$/, '数字和字符']
|
||||||
{# check_port: [/^\d{1,5}$/, '端口号不正确'],#}
|
},
|
||||||
{# type_m: function (element) {#}
|
|
||||||
{# return $("#M").is(":checked");#}
|
|
||||||
{# }#}
|
|
||||||
{# },#}
|
|
||||||
fields: {
|
fields: {
|
||||||
"username": {
|
"username": {
|
||||||
rule: "required",
|
rule: "required",
|
||||||
@ -155,7 +151,7 @@ $('#userForm').validator({
|
|||||||
msg: {required: "必须填写!"}
|
msg: {required: "必须填写!"}
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
rule: "length[6~50]",
|
rule: "length[6~50];check_pass",
|
||||||
tip: "输入密码",
|
tip: "输入密码",
|
||||||
ok: "",
|
ok: "",
|
||||||
msg: {required: "必须填写!"}
|
msg: {required: "必须填写!"}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> 查看用户 </h5>
|
<h5> 查看用户 </h5>
|
||||||
@ -24,30 +24,31 @@
|
|||||||
|
|
||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<div class="">
|
<div class="">
|
||||||
<a target="_blank" href="/juser/user_add/" class="btn btn-sm btn-primary "> 添加用户 </a>
|
<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>
|
<a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>
|
||||||
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control input-sm" id="search_input" name="keyword" placeholder="Search">
|
<input type="text" class="form-control input-sm" id="search_input" name="keyword" placeholder="Search">
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
-搜索-
|
-搜索-
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table table-striped table-bordered table-hover " id="editable" >
|
<table class="table table-striped table-bordered table-hover " id="editable" >
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">
|
<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>
|
||||||
<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>
|
||||||
<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">
|
<td class="text-center">
|
||||||
<input type="checkbox" name="checked" value="{{ user.id }}">
|
<input type="checkbox" name="checked" value="{{ user.id }}">
|
||||||
</td>
|
</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"> {{ 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" 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>
|
<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">{{ user.is_active | bool2str }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
{% if user.username|key_exist %}
|
{% if user.username|key_exist %}
|
||||||
<a href="/juser/down_key/?id={{ user.id }}" >下载</a>
|
<a href="/juser/down_key/?uuid={{ user.uuid }}" >下载</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span style="color: #586b7d">下载</span>
|
<span style="color: #586b7d">下载</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<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 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.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>
|
<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() {
|
$(".gradeX input:checked").each(function() {
|
||||||
check_array.push($(this).attr("value"))
|
check_array.push($(this).attr("value"))
|
||||||
});
|
});
|
||||||
|
console.log(check_array.join(","));
|
||||||
$.post("/juser/user_del/",
|
$.post("/juser/user_del/",
|
||||||
{id: check_array.join(",")},
|
{id: check_array.join(",")},
|
||||||
function(data){
|
function(data){
|
||||||
|
@ -99,7 +99,7 @@
|
|||||||
{# <div id="message"></div>#}
|
{# <div id="message"></div>#}
|
||||||
{# </div>#}
|
{# </div>#}
|
||||||
{# </div>#}
|
{# </div>#}
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-12">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title" style="border: solid">
|
<div class="ibox-title" style="border: solid">
|
||||||
<h5> 实时监控 </h5>
|
<h5> 实时监控 </h5>
|
||||||
|
@ -9,37 +9,42 @@
|
|||||||
<li id="juser">
|
<li id="juser">
|
||||||
<a href="#"><i class="fa fa-group"></i> <span class="nav-label">用户管理</span><span class="fa arrow"></span></a>
|
<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">
|
<ul class="nav nav-second-level">
|
||||||
<li class="group_list group_edit"><a href="/juser/group_list/">查看用户组</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"><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_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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li id="jasset">
|
<li id="jasset">
|
||||||
<a><i class="fa fa-inbox"></i> <span class="nav-label">资产管理</span><span class="fa arrow"></span></a>
|
<a><i class="fa fa-inbox"></i> <span class="nav-label">资产管理</span><span class="fa arrow"></span></a>
|
||||||
<ul class="nav nav-second-level">
|
<ul class="nav nav-second-level">
|
||||||
<li class="group_list group_detail group_edit"><a href="/jasset/group_list/">查看资产组</a></li>
|
<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>
|
<li class="idc_list idc_detail idc_edit"><a href="/jasset/idc_list/">查看机房</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li id="jperm">
|
<li id="jperm">
|
||||||
<a href="#"><i class="fa fa-edit"></i> <span class="nav-label">授权管理</span><span class="fa arrow"></span></a>
|
<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">
|
<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>
|
<a href="/jperm/rule/">授权规则</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="role">
|
<li class="role">
|
||||||
<a href="/jperm/role/">系统角色</a>
|
<a href="/jperm/role/">系统角色</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sudo">
|
<li class="sudo">
|
||||||
<a href="/jperm/sudo/">Sudo命令</a>
|
<a href="/jperm/sudo/">Sudo命令</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li id="jlog">
|
<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>
|
<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>
|
||||||
|
<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">
|
<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>
|
<a href="/setting/"><i class="fa fa-gears"></i> <span class="nav-label">设置</span><span class="label label-info pull-right"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@ -51,87 +56,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
{% endif %}
|
{% 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 %}
|
{% if request.session.role_id == 0 %}
|
||||||
<nav class="navbar-default navbar-static-side" role="navigation">
|
<nav class="navbar-default navbar-static-side" role="navigation">
|
||||||
<div class="sidebar-collapse">
|
<div class="sidebar-collapse">
|
||||||
<ul class="nav" id="side-menu">
|
<ul class="nav" id="side-menu">
|
||||||
{% include 'nav_li_profile.html' %}
|
{% include 'nav_li_profile.html' %}
|
||||||
<li id="index">
|
<li id="juser">
|
||||||
<a href="/"><i class="fa fa-th-large"></i> <span class="nav-label">仪表盘</span><span class="label label-info pull-right"></span></a>
|
<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 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>
|
</li>
|
||||||
<li id="jasset">
|
<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>
|
||||||
<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">
|
<li id="file">
|
||||||
<a href="#"><i class="fa fa-download"></i> <span class="nav-label">上传下载</span><span class="fa arrow"></span></a>
|
<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">
|
<ul class="nav nav-second-level">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="row wrapper border-bottom white-bg page-heading">
|
<div class="row wrapper border-bottom white-bg page-heading">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<h2>{{ header_title }}</h2>
|
<h2>{{ header_title }}</h2>
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li>
|
<li>
|
||||||
@ -17,6 +17,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-2">
|
<div class="col-sm-2">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
<ul class="dropdown-menu animated fadeInRight m-t-xs">
|
<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 value="/juser/profile/?id={{ session_user_id }}" class="iframe_user">个人信息</a></li>
|
||||||
<li><a href="/juser/change_info/">修改信息</a></li>
|
<li><a href="/juser/change_info/">修改信息</a></li>
|
||||||
{% if not user.role == 'CU' %}
|
{# {% if not user.role == 'CU' %}#}
|
||||||
{% if request.session.role_id == 0 %}
|
{# {% if request.session.role_id == 0 %}#}
|
||||||
<li><a href="/juser/change_role/">系统后台</a></li>
|
{# <li><a href="/juser/change_role/">系统后台</a></li>#}
|
||||||
{% else %}
|
{# {% else %}#}
|
||||||
<li><a href="/juser/change_role/">主机控制台</a></li>
|
{# <li><a href="/juser/change_role/">主机控制台</a></li>#}
|
||||||
{% endif %}
|
{# {% endif %}#}
|
||||||
{% endif %}
|
{# {% endif %}#}
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li><a href="/logout/">注销</a></li>
|
<li><a href="/logout/">注销</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5> 项目设置 </h5>
|
<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' %}
|
{% extends 'base.html' %}
|
||||||
{% load mytags %}
|
{% 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 %}
|
{% block content %}
|
||||||
{% include 'nav_cat_bar.html' %}
|
{% include 'nav_cat_bar.html' %}
|
||||||
<style>
|
<style>
|
||||||
@ -45,7 +50,7 @@
|
|||||||
</style>
|
</style>
|
||||||
<div class="wrapper wrapper-content animated fadeIn">
|
<div class="wrapper wrapper-content animated fadeIn">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-sm-10">
|
||||||
<div class="ibox float-e-margins">
|
<div class="ibox float-e-margins">
|
||||||
<div class="ibox-title">
|
<div class="ibox-title">
|
||||||
<h5>上传文件</h5>
|
<h5>上传文件</h5>
|
||||||
@ -53,15 +58,6 @@
|
|||||||
<a class="collapse-link">
|
<a class="collapse-link">
|
||||||
<i class="fa fa-chevron-up"></i>
|
<i class="fa fa-chevron-up"></i>
|
||||||
</a>
|
</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">
|
<a class="close-link">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
</a>
|
</a>
|
||||||
@ -69,36 +65,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ibox-content">
|
<div class="ibox-content">
|
||||||
<div>
|
<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">
|
<div class="dropzone-previews">
|
||||||
<input id="hosts" name="hosts" type="text" class="form-control" required="不能为空"
|
<div class="form-group">
|
||||||
placeholder="输入主机地址,逗号隔开,确保你有输入主机地址的权限" size="80%">
|
<div class="col-sm-10">
|
||||||
<select name="assetgroup" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
|
<select name="asset_ids" id="assets" data-placeholder="请选择上传的主机" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
{% for asset_group in asset_groups %}
|
{% for asset in assets %}
|
||||||
<option value="{{ asset_group.name }}">{{ asset_group.name }}</option>
|
<option value="{{ asset.id }}">{{ asset.hostname }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
{# <div id="hosts_list" style="position:absolute;display: none;z-index:999;">#}
|
</div>
|
||||||
{# 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>
|
||||||
<button type="submit" class="btn btn-primary pull-right">全部上传</button>
|
<button type="submit" class="btn btn-primary pull-right">全部上传</button>
|
||||||
</form>
|
</form>
|
||||||
@ -111,13 +90,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block self_footer_js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
Dropzone.options.myAwesomeDropzone = {
|
Dropzone.options.myAwesomeDropzone = {
|
||||||
|
|
||||||
autoProcessQueue: false,
|
autoProcessQueue: false,
|
||||||
uploadMultiple: true,
|
uploadMultiple: true,
|
||||||
parallelUploads: 100,
|
parallelUploads: 100,
|
||||||
@ -127,7 +105,6 @@
|
|||||||
// Dropzone settings
|
// Dropzone settings
|
||||||
init: function() {
|
init: function() {
|
||||||
var myDropzone = this;
|
var myDropzone = this;
|
||||||
|
|
||||||
this.element.querySelector("button[type=submit]").addEventListener("click", function(e) {
|
this.element.querySelector("button[type=submit]").addEventListener("click", function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -136,29 +113,38 @@
|
|||||||
this.on("sendingmultiple", function() {
|
this.on("sendingmultiple", function() {
|
||||||
});
|
});
|
||||||
this.on("successmultiple", function(files, response) {
|
this.on("successmultiple", function(files, response) {
|
||||||
alert(response)
|
$('#msg').css('display', 'block');
|
||||||
|
$('#msg').html(response)
|
||||||
});
|
});
|
||||||
this.on("errormultiple", function(files, response) {
|
this.on("errormultiple", function(files, response) {
|
||||||
|
$('#error').css('display', 'block');
|
||||||
|
$('#error').html(response)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
{# $("#hosts")[0].onfocus = function () {#}
|
|
||||||
{# TODO: by liuzheng#}
|
var config = {
|
||||||
{# $("#hosts_list").show()#}
|
'.chosen-select' : {},
|
||||||
{# };#}
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
{# $("#hosts")[0].focusout = function () {#}
|
'.chosen-select-no-single' : {disable_search_threshold:10},
|
||||||
{# $("#hosts_list").hide()#}
|
'.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({
|
$('#my-awesome-dropzone').validator({
|
||||||
timely: 2,
|
timely: 2,
|
||||||
theme: "yellow_right_effect",
|
theme: "yellow_right_effect",
|
||||||
fields: {
|
fields: {
|
||||||
"hosts": {
|
"hosts": {
|
||||||
rule: "required",
|
rule: "required",
|
||||||
tip: "输入上传的Host",
|
tip: "输入上传的主机",
|
||||||
ok: "",
|
ok: "",
|
||||||
msg: {required: "必须填写!"}
|
msg: {required: "必须填写!"}
|
||||||
}
|
}
|
||||||
@ -168,6 +154,7 @@ $('#my-awesome-dropzone').validator({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||||
|
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user