Commit a0684985 authored by yumaojun's avatar yumaojun

Merge branch 'dev' of https://git.coding.net/jumpserver/jumpserver into dev

# Conflicts:
#	jperm/ansible_api.py
parents bf98aa54 d337b929
...@@ -14,21 +14,25 @@ import getpass ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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)
...@@ -180,6 +208,13 @@ class Tty(object): ...@@ -180,6 +208,13 @@ class Tty(object):
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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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
len_x = len(x)
while index < len_x:
try:
n = os.write(sys.stdout.fileno(), x[index:])
sys.stdout.flush() 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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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,8 +553,10 @@ class Nav(object): ...@@ -575,8 +553,10 @@ 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()
if len(roles) > 1: # 授权角色数大于1
color_print('[%-2s] %-15s' % ('ID', '角色'), 'info')
role_check = dict(zip(range(len(roles)), roles)) role_check = dict(zip(range(len(roles)), roles))
for i, r in role_check.items(): for i, r in role_check.items():
...@@ -588,85 +568,158 @@ class Nav(object): ...@@ -588,85 +568,158 @@ class Nav(object):
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
role = roles[0]
assets = list(self.user_perm.get('role', {}).get(role).get('asset')) # 获取该用户,角色授权主机
print "该角色有权限的所有主机" print "该角色有权限的所有主机"
for asset in assets: for asset in assets:
print asset.hostname print ' %s' % asset.hostname
print print
print "请输入主机名、IP或ansile支持的pattern, q退出" print "请输入主机名、IP或ansile支持的pattern, 多个主机:分隔, q退出"
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip() pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
if pattern == 'q': if pattern == 'q':
break break
else: else:
res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm) res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm)
cmd = Command(res) runner = MyRunner(res)
logger.debug("批量执行res: %s" % res)
asset_name_str = '' asset_name_str = ''
for inv in cmd.inventory.get_hosts(pattern=pattern): print "匹配主机:"
print inv.name for inv in runner.inventory.get_hosts(pattern=pattern):
asset_name_str += inv.name print ' %s' % inv.name
asset_name_str += '%s ' % inv.name
print print
log_file_f, log_time_f, log = self.get_exec_log(asset_name_str)
pre_timestamp = time.time()
while True: while True:
print "请输入执行的命令, 按q退出" 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() 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': if command == 'q':
log.is_finished = True
log.end_time = datetime.datetime.now()
log.save()
break break
result = cmd.run(module_name='shell', command=command, pattern=pattern) runner.run('shell', command, pattern=pattern)
for k, v in result.items(): 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': if k == 'ok':
for host, output in v.items(): for host, output in v.items():
header = color_print("%s => %s" % (host, 'Ok'), 'green') color_print("%s => %s" % (host, 'Ok'), 'green')
print output 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 print
else: else:
for host, output in v.items(): for host, output in v.items():
header = color_print("%s => %s" % (host, k), 'red') color_print("%s => %s" % (host, k), 'red')
output = color_print(output, 'red') 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
print "=" * 20 print "~o~ Task finished ~o~"
print print
except (IndexError, KeyError): def upload(self):
color_print('ID输入错误') 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 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 EOFError: 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 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 break
finally: runner.run('fetch', module_args='src=%s dest=%s' % (file_path, tmp_dir), pattern=pattern)
log.is_finished = True ret = runner.results
log.end_time = datetime.datetime.now() 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(): ...@@ -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(): ...@@ -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
......
#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 @@ ...@@ -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: 默认
#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()
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
\ No newline at end of file django_crontab==0.6.0
\ No newline at end of file
# 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 * ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 = {}
disk_all = setup_info.get("ansible_devices")
if disk_all:
for disk_name, disk_info in disk_all.iteritems(): 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'): if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'):
disk_need[disk_name] = disk_info.get("size") 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): ...@@ -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=''): ...@@ -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=''): ...@@ -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=''): ...@@ -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): ...@@ -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): ...@@ -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 * ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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:
if user_perm != 0:
asset_find = Asset.objects.all() 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): ...@@ -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): ...@@ -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)
if user_perm != 0:
return my_render('jasset/asset_list.html', locals(), request) 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): ...@@ -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 ...@@ -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): ...@@ -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)
...@@ -3,11 +3,11 @@ from django.conf.urls import patterns, include, url ...@@ -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),
) )
\ No newline at end of file
...@@ -8,7 +8,7 @@ from jperm.perm_api import user_have_perm ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 = {} @property
def result(self):
result = {}
for k, v in self.results_raw.items():
if k == 'dark':
for host, info in v.items():
result[host] = {'dark': info.get('msg')}
elif k == 'contacted':
for host, info in v.items():
result[host] = {}
if info.get('stdout'):
result[host]['stdout'] = info.get('stdout')
elif info.get('stderr'):
result[host]['stderr'] = info.get('stderr')
return result
@property
def state(self):
result = {}
if self.stdout: if self.stdout:
data['ok'] = self.stdout result['ok'] = self.stdout
if self.stderr: if self.stderr:
data['err'] = self.stderr result['err'] = self.stderr
if self.dark: if self.dark:
data['dark'] = self.dark result['dark'] = self.dark
return result
return data
@property
def raw_results(self):
"""
get the ansible raw results.
"""
return self.results
@property @property
def exec_time(self): def exec_time(self):
...@@ -193,7 +233,7 @@ class Command(MyInventory): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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):
"""
add initail users: SA, DBA, DEV
"""
results = {}
action = results["action_info"] = {}
users = {"SA": get_rand_pass(), "DBA": get_rand_pass(), "DEV": get_rand_pass()}
for user, password in users.iteritems():
ret = self.add_user(user, password)
action[user] = ret
results["user_info"] = users
return results
def del_init_users(self): @staticmethod
""" def gen_sudo_script(role_list, sudo_list):
delete 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 = {}
for user in ["SA", "DBA", "DEV"]: for sudo in sudo_list:
ret = self.del_user(user) sudo_alias[sudo.name] = sudo.commands
action[user] = ret
return results
def get_host_info(self): for role in role_list:
""" sudo_user[role.name] = ','.join(sudo_alias.keys())
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 = {} sudo_j2 = get_template('jperm/role_sudo.j2')
all = self.results.get("contacted") sudo_content = sudo_j2.render(Context({"sudo_alias": sudo_alias, "sudo_user": sudo_user}))
for key, value in all.iteritems(): sudo_file = NamedTemporaryFile(delete=False)
setup =value.get("ansible_facts") sudo_file.write(sudo_content)
# get disk informations sudo_file.close()
disk_all = setup.get("ansible_devices") return sudo_file.name
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] = { def push_sudo_file(self, role_list, sudo_list):
"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): ...@@ -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): ...@@ -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,12 +164,14 @@ def gen_resource(ob, perm=None): ...@@ -161,12 +164,14 @@ 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:
roles = perm.get('role', {}).keys() # 获取用户所有授权角色
if role not in roles: if role not in roles:
return {} return {}
role_assets_all = perm.get('role').get(role).get('asset') role_assets_all = perm.get('role').get(role).get('asset') # 获取用户该角色所有授权主机
assets = set(role_assets_all) & set(asset_r) assets = set(role_assets_all) & set(asset_r) # 获取用户提交中合法的主机
for asset in assets: for asset in assets:
asset_info = get_asset_info(asset) asset_info = get_asset_info(asset)
...@@ -178,6 +183,23 @@ def gen_resource(ob, perm=None): ...@@ -178,6 +183,23 @@ def gen_resource(ob, perm=None):
'ssh_key': get_role_key(user, role) 'ssh_key': get_role_key(user, role)
} }
res.append(info) 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): ...@@ -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): ...@@ -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', ...@@ -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 ...@@ -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=""): ...@@ -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()
......
...@@ -10,10 +10,9 @@ from jasset.models import Asset, AssetGroup ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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,38 +147,36 @@ def perm_rule_edit(request): ...@@ -140,38 +147,36 @@ 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]
...@@ -181,6 +186,13 @@ def perm_rule_edit(request): ...@@ -181,6 +186,13 @@ def perm_rule_edit(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()
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
...@@ -191,10 +203,12 @@ def perm_rule_edit(request): ...@@ -191,10 +203,12 @@ def perm_rule_edit(request):
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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 ...@@ -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): ...@@ -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): ...@@ -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('', ...@@ -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 * ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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! @login_required(login_url='/login')
# by liuzheng def exec_cmd(request):
filepath = 'upload/' + str(request.user.id)+'/'+path role = request.GET.get('role')
return HttpResponse(filepath) check_assets = request.GET.get('check_assets', '')
web_terminal_uri = 'ws://%s/exec?role=%s' % (WEB_SOCKET_HOST, role)
def node_auth(request): return my_render('exec_cmd.html', locals(), request)
return HttpResponse('nothing')
def httperror(request):
return HttpResponse('nothing')
def base(request):
return HttpResponse('nothing')
def install(request):
return HttpResponse('nothing')
####################### liuzheng's test(end) ########################
\ No newline at end of file
...@@ -25,9 +25,6 @@ urlpatterns = patterns('juser.views', ...@@ -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='', ...@@ -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): ...@@ -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 ...@@ -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
......
...@@ -4,23 +4,15 @@ ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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', '')
# if request.session.get('role_id') == 1:
# user, dept = get_session_user_dept(request)
# if not validate(request, user=[user_id]):
# return HttpResponseRedirect('/')
user_id = request.GET.get('id', '') user_id = request.GET.get('id', '')
if not user_id:
user = get_object(User, id=user_id)
if not user:
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): ...@@ -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): ...@@ -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&timestamp=%s&hash=%s' % (uuid, timestamp, hash_encode) action = '/juser/reset_password/?uuid=%s&timestamp=%s&hash=%s' % (uuid_r, timestamp, hash_encode)
if request.method == 'POST': 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): ...@@ -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): ...@@ -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): ...@@ -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:
if is_role_request(request, 'user'): uuid_r = request.user.uuid
user_id = request.user.id
if user_id: if uuid_r:
user = get_object(User, id=user_id) user = get_object(User, uuid=uuid_r)
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): ...@@ -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))
\ No newline at end of file
...@@ -8,7 +8,7 @@ import sys ...@@ -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 ...@@ -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'): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 { ...@@ -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;
......
...@@ -16,9 +16,9 @@ function check_all(form) { ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
......
{% 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 @@ ...@@ -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 %}
{% if msg %}
<div class="alert alert-success text-center">{{ msg }}</div>
{% endif %}
<div class="form-group">
<label for="file_path" class="col-sm-2 control-label">文件路径<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<input id="file_path" name="file_path" placeholder="File Path" type="text" class="form-control">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="user" class="col-sm-2 control-label">选择主机<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<select name="asset_ids" id="asset_ids" data-placeholder="请输入" class="chosen-select form-control m-b" multiple tabindex="2">
{% for asset in assets %}
<option value="{{ asset.id }}">{{ asset.hostname }}</option>
{% endfor %} {% endfor %}
</select>
</div> </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 %}
\ No newline at end of file
<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>
\ No newline at end of file
<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 &copy; 2014-2015 <strong>Copyright</strong> Jumpserver.org Team &copy; 2014-2015
......
...@@ -6,7 +6,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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,7 +5,8 @@ ...@@ -5,7 +5,8 @@
<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="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>
...@@ -28,177 +29,63 @@ ...@@ -28,177 +29,63 @@
登录后根据提示进行操作。跳板机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">
<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> </div>
<div class="ibox-content"> <div class="ibox-content">
<form id="asset_form"> <div id="last">
<div class="col-sm-2" style="padding-left: 0px"> <div class="feed-activity-list" >
<label> {% for log in logs_last %}
<select name="idc" class="form-control m-b" onchange="change_info()"> <div class="feed-element">
<option value="">IDC机房</option> <a href="profile.html" class="pull-left">
{% for idc in idc_all %} <img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
{% ifequal idc.name idc_name %} </a>
<option value="{{ idc.name }}" selected> {{ idc.name }}</option> <div class="media-body ">
{% else %} {# <small class="pull-right">{{ log.start_time|time_delta }}</small>#}
<option value="{{ idc.name }}"> {{ idc.name }}</option> <small class="pull-right">{{ log.start_time }}</small>
{% endifequal %} <strong>{{ log.user }}</strong> 登录了 <span class="text-navy">{{ log.host }}. </span><br>
{% endfor %} <small class="text-muted">{{ log.start_time|date:"Y-m-d H:i:s" }}</small>
</select>
</label>
</div> </div>
<div class="col-sm-2">
<label>
<select name="group" class="form-control m-b" onchange="change_info()">
<option value="">主机组</option>
{% for asset_group in asset_group_all %}
{% ifequal asset_group.name group_name %}
<option value="{{ asset_group.name }}"
selected> {{ asset_group.name }} </option>
{% else %}
<option value="{{ asset_group.name }}"> {{ asset_group.name }} </option>
{% endifequal %}
{% endfor %}
</select>
</label>
</div> </div>
<div class="col-sm-2">
<label>
<select name="asset_type" class="form-control m-b" onchange="change_info()">
<option value="">所有类型</option>
{% for type in asset_types %}
{% ifequal type.0|int2str asset_type %}
<option value="{{ type.0 }}" selected> {{ type.1 }}</option>
{% else %}
<option value="{{ type.0 }}"> {{ type.1 }}</option>
{% endifequal %}
{% endfor %} {% endfor %}
</select> {% if not logs_last %}
</label> (暂无)
{% endif %}
</div> </div>
<div class="col-sm-2"> {% if logs_num > 10 %}
<label> <button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> Show All</button>
<select name="status" class="form-control m-b" onchange="change_info()"> {% endif %}
<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>
<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"> <div id="all" style="display: none">
<button id='search_btn' href="/jasset/asset_list/?search=true" type="button" <div class="feed-activity-list" >
class="btn btn-xm btn-primary search-btn" onclick="change_info()"> {% for log in logs_all %}
- 搜索 - <div class="feed-element">
</button> <a href="profile.html" class="pull-left">
<button type="button" href="/jasset/asset_list/?export=true" name="export" <img alt="image" class="img-circle" src="/static/img/{{ session_role_id | to_avatar }}.png">
class="btn btn-xm btn-success search-btn-excel" onclick="return false"> </a>
- 导出 - <div class="media-body ">
</button> <small class="pull-right">{{ log.start_time }}</small>
</div> {# <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>
</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 %} {% 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> </div>
{% include 'paginator.html' %}
</div> </div>
</form> </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">
<span class="label label-primary"><b>{{ user.username }}</b></span> <span class="label label-primary"><b>{{ user.username }}</b></span>
......
...@@ -5,7 +5,7 @@ ...@@ -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>
...@@ -118,6 +118,13 @@ ...@@ -118,6 +118,13 @@
{% block self_footer_js %} {% block self_footer_js %}
<script> <script>
$('document').ready(function(){ $('document').ready(function(){
var check_default = "{{ default_setting.name }}";
console.log(check_default);
if (check_default != 'default'){
$('#id_use_default_auth').attr('disabled', true);
$('#id_use_default_auth').attr('checked', false);
$('#admin_account').css('display', 'block');
} else {
$('#id_use_default_auth').click(function(){ $('#id_use_default_auth').click(function(){
if ($(this).is(':checked')){ if ($(this).is(':checked')){
$('#admin_account').css('display', 'none'); $('#admin_account').css('display', 'none');
...@@ -126,6 +133,8 @@ ...@@ -126,6 +133,8 @@
$('#admin_account').css('display', 'block'); $('#admin_account').css('display', 'block');
} }
}) })
}
}); });
var required_fields = ["id_hostname", "id_port"]; var required_fields = ["id_hostname", "id_port"];
......
...@@ -8,7 +8,7 @@ ...@@ -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>
......
{% 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 %}
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
{% ifnotequal session_role_id 0 %}
<a href="/jasset/asset_edit/?id={{ asset.id }}" class="btn btn-xs btn-info">编辑</a> <a href="/jasset/asset_edit/?id={{ asset.id }}" class="btn btn-xs btn-info">编辑</a>
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a> <a value="{{ 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> <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 @@ ...@@ -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 @@ ...@@ -196,19 +173,67 @@
} }
}); });
$('#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(){ $('.conn').click(function(){
var url='/jlog/get_role_name/?id=' + $(this).attr('value'); var url='/jperm/role/get/?id=' + $(this).attr('value'); // 获取用户有权限的角色
var href = $(this).attr('href'); var href = $(this).attr('href');
var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role='; var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role='; // webterminal socket url
var hostname = $(this).closest('tr').find('.hostname')[0].innerHTML; var hostname = $(this).closest('tr').find('.hostname a')[0].innerHTML;
$.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(',');
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 @@ ...@@ -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 @@ ...@@ -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,38 +351,41 @@ ...@@ -311,38 +351,41 @@
$('#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}); layer.msg('玩命更新中...', {time: 200000});
$.ajax({ $.ajax({
type: "post", type: "post",
data: {asset_id_all: asset_id_all}, url: "/jasset/asset_update_batch/?arg=all",
url: "/jasset/asset_update_batch/",
success: function () { success: function () {
parent.location.reload(); parent.location.reload();
} }
}); });
}); }
}
{# function update_tips(){#} else {
{# layer.tips('我是另外一个tips,只不过我长得跟之前那位稍有些不一样。', '吸附元素选择器', {#}
{# tips: [1, '#3595CC'],#}
{# time: 4000#}
{# });#}
{# }#}
$('#asset_update_all').click(function () {
layer.msg('玩命更新中...', {time: 200000}); layer.msg('玩命更新中...', {time: 200000});
$.ajax({ $.ajax({
type: "post", type: "post",
url: "/jasset/asset_update_batch/?arg=all", data: {asset_id_all: asset_id_all},
url: "/jasset/asset_update_batch/",
success: function () { success: function () {
parent.location.reload(); 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();
window.location = "/jasset/asset_list/?" + args window.location = "/jasset/asset_list/?" + args
......
...@@ -19,7 +19,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
......
{% 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 %}
\ No newline at end of file
{% 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 %}
\ No newline at end of file
{% 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 %}
{% 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 @@ ...@@ -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>
......
<!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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 %}
\ No newline at end of file
...@@ -9,7 +9,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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({ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
...@@ -89,7 +89,7 @@ ...@@ -89,7 +89,7 @@
<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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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({ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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} ...@@ -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() { ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
\ No newline at end of file
</body></html>
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
......
<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 @@ ...@@ -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 @@ ...@@ -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",
\ No newline at end of file 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>
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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({ ...@@ -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 %}
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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({ ...@@ -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 @@ ...@@ -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>
...@@ -42,12 +42,13 @@ ...@@ -42,12 +42,13 @@
<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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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,97 +9,50 @@ ...@@ -9,97 +9,50 @@
<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="setting"> <li id="file">
<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="#"><i class="fa fa-download"></i> <span class="nav-label">上传下载</span><span class="fa arrow"></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 == 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"> <ul class="nav nav-second-level">
<li class="perm_list perm_edit perm_detail"> <li class="upload"><a href="/file/upload/">文件上传</a></li>
<a href="/jperm/perm_list/">小组授权</a> <li class="download"><a href="/file/download/">文件下载</a></li>
</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> </ul>
</li> </li>
<li id="jlog"> <li id="setting">
<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="/setting/"><i class="fa fa-gears"></i> <span class="nav-label">设置</span><span class="label label-info pull-right"></span></a>
</li> </li>
<li class="special_link"> <li class="special_link">
<a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i> <span class="nav-label">访问官网</span></a> <a href="http://www.jumpserver.org" target="_blank"><i class="fa fa-database"></i> <span class="nav-label">访问官网</span></a>
</li> </li>
</ul> </ul>
</div> </div>
</nav> </nav>
{% endif %} {% endif %}
...@@ -109,29 +62,12 @@ ...@@ -109,29 +62,12 @@
<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">
......
<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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
......
<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>
{% 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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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({ ...@@ -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 %}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment