mirror of https://github.com/jumpserver/jumpserver
Merge branch 'dev' of https://git.coding.net/jumpserver/jumpserver into NormalUserPageLZ
commit
175f2702d1
138
connect.py
138
connect.py
|
@ -19,14 +19,14 @@ import struct, fcntl, signal, socket, select
|
|||
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
|
||||
if django.get_version() != '1.6':
|
||||
django.setup()
|
||||
from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object, mkdir, get_asset_info, get_role
|
||||
from jumpserver.api import logger, Log, TtyLog, get_role_key
|
||||
from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm
|
||||
from django.contrib.sessions.models import Session
|
||||
from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info, get_role
|
||||
from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR
|
||||
from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm
|
||||
from jumpserver.settings import LOG_DIR
|
||||
from jperm.ansible_api import Command
|
||||
|
||||
login_user = get_object(User, username=getpass.getuser())
|
||||
VIM_FLAG = False
|
||||
|
||||
try:
|
||||
import termios
|
||||
|
@ -69,6 +69,9 @@ class Tty(object):
|
|||
self.ssh = None
|
||||
self.connect_info = None
|
||||
self.login_type = 'ssh'
|
||||
self.vim_flag = False
|
||||
self.ps1_pattern = re.compile('\[.*@.*\][\$#]')
|
||||
self.vim_data = ''
|
||||
|
||||
@staticmethod
|
||||
def is_output(strings):
|
||||
|
@ -78,25 +81,24 @@ class Tty(object):
|
|||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def deal_command(str_r):
|
||||
def deal_command(self, str_r):
|
||||
"""
|
||||
处理命令中特殊字符
|
||||
"""
|
||||
str_r = re.sub('\x07','',str_r) #删除响铃
|
||||
patch_char = re.compile('\x08\x1b\[C') #删除方向左右一起的按键
|
||||
str_r = re.sub('\x07', '', str_r) # 删除响铃
|
||||
patch_char = re.compile('\x08\x1b\[C') # 删除方向左右一起的按键
|
||||
while patch_char.search(str_r):
|
||||
str_r = patch_char.sub('', str_r.rstrip())
|
||||
|
||||
result_command = '' #最后的结果
|
||||
backspace_num = 0 #光标移动的个数
|
||||
reach_backspace_flag = False #没有检测到光标键则为true
|
||||
pattern_str=''
|
||||
result_command = '' # 最后的结果
|
||||
backspace_num = 0 # 光标移动的个数
|
||||
reach_backspace_flag = False # 没有检测到光标键则为true
|
||||
pattern_str = ''
|
||||
while str_r:
|
||||
tmp = re.match(r'\s*\w+\s*', str_r)
|
||||
if tmp:
|
||||
if reach_backspace_flag :
|
||||
pattern_str +=str(tmp.group(0))
|
||||
if reach_backspace_flag:
|
||||
pattern_str += str(tmp.group(0))
|
||||
str_r = str_r[len(str(tmp.group(0))):]
|
||||
continue
|
||||
else:
|
||||
|
@ -107,7 +109,7 @@ class Tty(object):
|
|||
tmp = re.match(r'\x1b\[K[\x08]*', str_r)
|
||||
if tmp:
|
||||
if backspace_num > 0:
|
||||
if backspace_num > len(result_command) :
|
||||
if backspace_num > len(result_command):
|
||||
result_command += pattern_str
|
||||
result_command = result_command[0:-backspace_num]
|
||||
else:
|
||||
|
@ -117,8 +119,8 @@ class Tty(object):
|
|||
if del_len > 0:
|
||||
result_command = result_command[0:-del_len]
|
||||
reach_backspace_flag = False
|
||||
backspace_num =0
|
||||
pattern_str=''
|
||||
backspace_num = 0
|
||||
pattern_str = ''
|
||||
str_r = str_r[len(str(tmp.group(0))):]
|
||||
continue
|
||||
|
||||
|
@ -136,13 +138,13 @@ class Tty(object):
|
|||
else:
|
||||
break
|
||||
|
||||
if reach_backspace_flag :
|
||||
pattern_str +=str_r[0]
|
||||
else :
|
||||
if reach_backspace_flag:
|
||||
pattern_str += str_r[0]
|
||||
else:
|
||||
result_command += str_r[0]
|
||||
str_r = str_r[1:]
|
||||
|
||||
if backspace_num > 0 :
|
||||
if backspace_num > 0:
|
||||
result_command = result_command[0:-backspace_num] + pattern_str
|
||||
|
||||
control_char = re.compile(r"""
|
||||
|
@ -155,35 +157,13 @@ class Tty(object):
|
|||
[\x80-\x9f] | (?:\x1b\]0.*) | \[.*@.*\][\$#] | (.*mysql>.*) #匹配 所有控制字符
|
||||
""", re.X)
|
||||
result_command = control_char.sub('', result_command.strip())
|
||||
global VIM_FLAG
|
||||
if not VIM_FLAG:
|
||||
if result_command.startswith('vi'):
|
||||
VIM_FLAG = True
|
||||
return result_command.decode('utf8',"ignore")
|
||||
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 ''
|
||||
|
||||
@staticmethod
|
||||
def remove_control_char(str_r):
|
||||
"""
|
||||
处理日志特殊字符
|
||||
"""
|
||||
control_char = re.compile(r"""
|
||||
\x1b[ #%()*+\-.\/]. |
|
||||
\r | #匹配 回车符(CR)
|
||||
(?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd
|
||||
(?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL)
|
||||
(?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST)
|
||||
\x1b. #匹配 转义过后的字符
|
||||
[\x80-\x9f] #匹配 所有控制字符
|
||||
""", re.X)
|
||||
backspace = re.compile(r"[^\b][\b]")
|
||||
line_filtered = control_char.sub('', str_r.rstrip())
|
||||
while backspace.search(line_filtered):
|
||||
line_filtered = backspace.sub('', line_filtered)
|
||||
|
||||
return line_filtered
|
||||
|
||||
def get_log(self):
|
||||
"""
|
||||
Logging user command and output.
|
||||
|
@ -230,15 +210,17 @@ class Tty(object):
|
|||
"""
|
||||
获取需要登陆的主机的信息和映射用户的账号密码
|
||||
"""
|
||||
|
||||
# 1. get ip, port
|
||||
# 2. get 映射用户
|
||||
# 3. get 映射用户的账号,密码或者key
|
||||
# self.connect_info = {'user': '', 'asset': '', 'ip': '', 'port': 0, 'role_name': '', 'role_pass': '', 'role_key': ''}
|
||||
asset_info = get_asset_info(self.asset)
|
||||
role_key = get_role_key(self.user, self.role)
|
||||
role_pass = CRYPTOR.decrypt(self.role.password)
|
||||
self.connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'),
|
||||
'port': int(asset_info.get('port')), 'role_name': self.role.name,
|
||||
'role_pass': self.role.password, 'role_key': self.role.key_path}
|
||||
'role_pass': role_pass, 'role_key': role_key}
|
||||
logger.debug("Connect: Host: %s Port: %s User: %s Pass: %s Key: %s" % (asset_info.get('ip'),
|
||||
asset_info.get('port'),
|
||||
self.role.name,
|
||||
role_pass,
|
||||
role_key))
|
||||
return self.connect_info
|
||||
|
||||
def get_connection(self):
|
||||
|
@ -252,7 +234,7 @@ class Tty(object):
|
|||
ssh.load_system_host_keys()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
role_key = get_role_key(self.user, self.role)
|
||||
role_key = connect_info.get('role_key')
|
||||
if role_key and os.path.isfile(role_key):
|
||||
try:
|
||||
ssh.connect(connect_info.get('ip'),
|
||||
|
@ -262,7 +244,8 @@ class Tty(object):
|
|||
look_for_keys=False)
|
||||
self.ssh = 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)
|
||||
pass
|
||||
|
||||
ssh.connect(connect_info.get('ip'),
|
||||
|
@ -319,12 +302,8 @@ class SshTty(Tty):
|
|||
log_file_f, log_time_f, log = self.get_log()
|
||||
old_tty = termios.tcgetattr(sys.stdin)
|
||||
pre_timestamp = time.time()
|
||||
pattern = re.compile('\[.*@.*\][\$#]')
|
||||
data = ''
|
||||
chan_str = ''
|
||||
input_mode = False
|
||||
global VIM_FLAG
|
||||
|
||||
try:
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
tty.setcbreak(sys.stdin.fileno())
|
||||
|
@ -341,8 +320,8 @@ class SshTty(Tty):
|
|||
x = self.channel.recv(1024)
|
||||
if len(x) == 0:
|
||||
break
|
||||
if VIM_FLAG:
|
||||
chan_str += x
|
||||
if self.vim_flag:
|
||||
self.vim_data += x
|
||||
sys.stdout.write(x)
|
||||
sys.stdout.flush()
|
||||
now_timestamp = time.time()
|
||||
|
@ -361,21 +340,20 @@ class SshTty(Tty):
|
|||
if sys.stdin in r:
|
||||
x = os.read(sys.stdin.fileno(), 1)
|
||||
input_mode = True
|
||||
|
||||
if str(x) in ['\r', '\n', '\r\n']:
|
||||
if VIM_FLAG:
|
||||
match = pattern.search(chan_str)
|
||||
if self.vim_flag:
|
||||
match = self.ps1_pattern.search(self.vim_data)
|
||||
if match:
|
||||
VIM_FLAG = False
|
||||
data = self.deal_command(data)
|
||||
self.vim_flag = False
|
||||
data = self.deal_command(data)[0:200]
|
||||
if len(data) > 0:
|
||||
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
|
||||
else:
|
||||
data = self.deal_command(data)
|
||||
data = self.deal_command(data)[0:200]
|
||||
if len(data) > 0:
|
||||
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
|
||||
data = ''
|
||||
chan_str = ''
|
||||
self.vim_data = ''
|
||||
input_mode = False
|
||||
|
||||
if len(x) == 0:
|
||||
|
@ -465,24 +443,33 @@ class Nav(object):
|
|||
|
||||
def search(self, str_r=''):
|
||||
gid_pattern = re.compile(r'^g\d+$')
|
||||
# 获取用户授权的所有主机信息
|
||||
if not self.user_perm:
|
||||
self.user_perm = get_group_user_perm(self.user)
|
||||
user_asset_all = self.user_perm.get('asset').keys()
|
||||
# 搜索结果保存
|
||||
user_asset_search = []
|
||||
if str_r:
|
||||
# 资产组组id匹配
|
||||
if gid_pattern.match(str_r):
|
||||
user_asset_search = list(Asset.objects.all())
|
||||
gid = int(str_r.lstrip('g'))
|
||||
# 获取资产组包含的资产
|
||||
user_asset_search = get_object(AssetGroup, id=gid).asset_set.all()
|
||||
else:
|
||||
# 匹配 ip, hostname, 备注
|
||||
for asset in user_asset_all:
|
||||
if str_r in asset.ip or str_r in str(asset.comment):
|
||||
if str_r in asset.ip or str_r in str(asset.hostname) or str_r in str(asset.comment):
|
||||
user_asset_search.append(asset)
|
||||
else:
|
||||
# 如果没有输入就展现所有
|
||||
user_asset_search = user_asset_all
|
||||
|
||||
self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search))
|
||||
print '\033[32m[%-3s] %-15s %-15s %-5s %-10s %s \033[0m' % ('ID', 'AssetName', 'IP', 'Port', 'Role', 'Comment')
|
||||
for index, asset in self.search_result.items():
|
||||
# 获取该资产信息
|
||||
asset_info = get_asset_info(asset)
|
||||
# 获取该资产包含的角色
|
||||
role = [str(role.name) for role in self.user_perm.get('asset').get(asset).get('role')]
|
||||
if asset.comment:
|
||||
print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'),
|
||||
|
@ -491,9 +478,11 @@ class Nav(object):
|
|||
print '[%-3s] %-15s %-15s %-5s %-10s' % (index, asset.hostname, asset.ip, asset_info.get('port'), role)
|
||||
print
|
||||
|
||||
@staticmethod
|
||||
def print_asset_group():
|
||||
user_asset_group_all = AssetGroup.objects.all()
|
||||
def print_asset_group(self):
|
||||
"""
|
||||
打印用户授权的资产组
|
||||
"""
|
||||
user_asset_group_all = get_group_user_perm(self.user).get('asset_group', [])
|
||||
|
||||
print '\033[32m[%-3s] %-15s %s \033[0m' % ('ID', 'GroupName', 'Comment')
|
||||
for asset_group in user_asset_group_all:
|
||||
|
@ -504,6 +493,9 @@ class Nav(object):
|
|||
print
|
||||
|
||||
def exec_cmd(self):
|
||||
"""
|
||||
批量执行命令
|
||||
"""
|
||||
self.search()
|
||||
while True:
|
||||
print "请输入主机名、IP或ansile支持的pattern, q退出"
|
||||
|
|
|
@ -1,459 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ansible.inventory.group import Group
|
||||
from ansible.inventory.host import Host
|
||||
from ansible.inventory import Inventory
|
||||
from ansible.runner import Runner
|
||||
from ansible.playbook import PlayBook
|
||||
|
||||
from ansible import callbacks
|
||||
from ansible import utils
|
||||
from passlib.hash import sha512_crypt
|
||||
|
||||
# from utils import get_rand_pass
|
||||
|
||||
import random
|
||||
import os.path
|
||||
API_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ANSIBLE_DIR = os.path.join(API_DIR, 'playbooks')
|
||||
|
||||
def get_rand_pass():
|
||||
"""
|
||||
get a reandom password.
|
||||
"""
|
||||
lower = [chr(i) for i in range(97,123)]
|
||||
upper = [chr(i).upper() for i in range(97,123)]
|
||||
digit = [str(i) for i in range(10)]
|
||||
password_pool = []
|
||||
password_pool.extend(lower)
|
||||
password_pool.extend(upper)
|
||||
password_pool.extend(digit)
|
||||
pass_list = [random.choice(password_pool) for i in range(1,14)]
|
||||
pass_list.insert(random.choice(range(1,14)), '@')
|
||||
pass_list.insert(random.choice(range(1,14)), random.choice(digit))
|
||||
password = ''.join(pass_list)
|
||||
return password
|
||||
|
||||
class AnsibleError(StandardError):
|
||||
"""
|
||||
the base AnsibleError which contains error(required),
|
||||
data(optional) and message(optional).
|
||||
存储所有Ansible 异常对象
|
||||
"""
|
||||
def __init__(self, error, data='', message=''):
|
||||
super(AnsibleError, self).__init__(message)
|
||||
self.error = error
|
||||
self.data = data
|
||||
self.message = message
|
||||
|
||||
|
||||
class CommandValueError(AnsibleError):
|
||||
"""
|
||||
indicate the input value has error or invalid.
|
||||
the data specifies the error field of input form.
|
||||
输入不合法 异常对象
|
||||
"""
|
||||
def __init__(self, field, message=''):
|
||||
super(CommandValueError, self).__init__('value:invalid', field, message)
|
||||
|
||||
|
||||
class MyInventory(object):
|
||||
"""
|
||||
this is my ansible inventory object.
|
||||
"""
|
||||
def __init__(self, resource):
|
||||
"""
|
||||
resource的数据格式是一个列表字典,比如
|
||||
{
|
||||
"group1": {
|
||||
"hosts": [{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...],
|
||||
"vars": {"var1": value1, "var2": value2, ...}
|
||||
}
|
||||
}
|
||||
|
||||
如果你只传入1个列表,这默认该列表内的所有主机属于my_group组,比如
|
||||
[{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...]
|
||||
"""
|
||||
self.resource = resource
|
||||
self.inventory = Inventory()
|
||||
self.gen_inventory()
|
||||
|
||||
def add_group(self, hosts, groupname, groupvars=None):
|
||||
"""
|
||||
add hosts to a group
|
||||
"""
|
||||
my_group = Group(name=groupname)
|
||||
|
||||
# if group variables exists, add them to group
|
||||
if groupvars:
|
||||
for key, value in groupvars.iteritems():
|
||||
my_group.set_variable(key, value)
|
||||
|
||||
# add hosts to group
|
||||
for host in hosts:
|
||||
# set connection variables
|
||||
hostname = host.get("hostname")
|
||||
hostport = host.get("port")
|
||||
username = host.get("username")
|
||||
password = host.get("password")
|
||||
my_host = Host(name=hostname, port=hostport)
|
||||
my_host.set_variable('ansible_ssh_host', hostname)
|
||||
my_host.set_variable('ansible_ssh_port', hostport)
|
||||
my_host.set_variable('ansible_ssh_user', username)
|
||||
my_host.set_variable('ansible_ssh_pass', password)
|
||||
# set other variables
|
||||
for key, value in host.iteritems():
|
||||
if key not in ["hostname", "port", "username", "password"]:
|
||||
my_host.set_variable(key, value)
|
||||
# add to group
|
||||
my_group.add_host(my_host)
|
||||
|
||||
self.inventory.add_group(my_group)
|
||||
|
||||
def gen_inventory(self):
|
||||
"""
|
||||
add hosts to inventory.
|
||||
"""
|
||||
if isinstance(self.resource, list):
|
||||
self.add_group(self.resource, 'my_group')
|
||||
elif isinstance(self.resource, dict):
|
||||
for groupname, hosts_and_vars in self.resource.iteritems():
|
||||
self.add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars"))
|
||||
|
||||
|
||||
class Command(MyInventory):
|
||||
"""
|
||||
this is a command object for parallel execute command.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Command, self).__init__(*args, **kwargs)
|
||||
self.results = ''
|
||||
|
||||
def run(self, command, module_name="command", timeout=5, forks=10, group='my_group'):
|
||||
"""
|
||||
run command from andible ad-hoc.
|
||||
command : 必须是一个需要执行的命令字符串, 比如
|
||||
'uname -a'
|
||||
"""
|
||||
if module_name not in ["raw", "command", "shell"]:
|
||||
raise CommandValueError("module_name",
|
||||
"module_name must be of the 'raw, command, shell'")
|
||||
hoc = Runner(module_name=module_name,
|
||||
module_args=command,
|
||||
timeout=timeout,
|
||||
inventory=self.inventory,
|
||||
subset=group,
|
||||
forks=forks
|
||||
)
|
||||
self.results = hoc.run()
|
||||
|
||||
if self.stdout:
|
||||
return {"ok": self.stdout}
|
||||
else:
|
||||
msg = []
|
||||
if self.stderr:
|
||||
msg.append(self.stderr)
|
||||
if self.dark:
|
||||
msg.append(self.dark)
|
||||
return {"failed": msg}
|
||||
|
||||
@property
|
||||
def raw_results(self):
|
||||
"""
|
||||
get the ansible raw results.
|
||||
"""
|
||||
return self.results
|
||||
|
||||
@property
|
||||
def exec_time(self):
|
||||
"""
|
||||
get the command execute time.
|
||||
"""
|
||||
result = {}
|
||||
all = self.results.get("contacted")
|
||||
for key, value in all.iteritems():
|
||||
result[key] = {
|
||||
"start": value.get("start"),
|
||||
"end" : value.get("end"),
|
||||
"delta": value.get("delta"),}
|
||||
return result
|
||||
|
||||
@property
|
||||
def stdout(self):
|
||||
"""
|
||||
get the comamnd standard output.
|
||||
"""
|
||||
result = {}
|
||||
all = self.results.get("contacted")
|
||||
for key, value in all.iteritems():
|
||||
result[key] = value.get("stdout")
|
||||
return result
|
||||
|
||||
@property
|
||||
def stderr(self):
|
||||
"""
|
||||
get the command standard error.
|
||||
"""
|
||||
result = {}
|
||||
all = self.results.get("contacted")
|
||||
for key, value in all.iteritems():
|
||||
result[key] = {
|
||||
"stderr": value.get("stderr"),
|
||||
"warnings": value.get("warnings"),}
|
||||
return result
|
||||
|
||||
@property
|
||||
def dark(self):
|
||||
"""
|
||||
get the dark results.
|
||||
"""
|
||||
return self.results.get("dark")
|
||||
|
||||
|
||||
class Tasks(Command):
|
||||
"""
|
||||
this is a tasks object for include the common command.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Tasks, self).__init__(*args, **kwargs)
|
||||
|
||||
def __run(self, module_args, module_name="command", timeout=5, forks=10, group='my_group'):
|
||||
"""
|
||||
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,
|
||||
forks=forks
|
||||
)
|
||||
|
||||
self.results = hoc.run()
|
||||
|
||||
@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):
|
||||
"""
|
||||
push the ssh authorized key to target.
|
||||
"""
|
||||
module_args = 'user="%s" key="{{ lookup("file", "%s") }}"' % (user, key_path)
|
||||
self.__run(module_args, "authorized_key")
|
||||
|
||||
return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"}
|
||||
|
||||
def del_key(self, user, key_path):
|
||||
"""
|
||||
push the ssh authorized key to target.
|
||||
"""
|
||||
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path)
|
||||
self.__run(module_args, "authorized_key")
|
||||
|
||||
return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"}
|
||||
|
||||
def add_user(self, username, password):
|
||||
"""
|
||||
add a host user.
|
||||
"""
|
||||
encrypt_pass = sha512_crypt.encrypt(password)
|
||||
module_args = 'name=%s shell=/bin/bash password=%s' % (username, encrypt_pass)
|
||||
self.__run(module_args, "user")
|
||||
|
||||
return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"}
|
||||
|
||||
def add_multi_user(self, *args):
|
||||
"""
|
||||
add multi user
|
||||
:param args:
|
||||
user
|
||||
:return:
|
||||
"""
|
||||
results = {}
|
||||
users = {}
|
||||
action = results["action_info"] = {}
|
||||
for user in args:
|
||||
users[user] = 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_user(self, username):
|
||||
"""
|
||||
delete a host user.
|
||||
"""
|
||||
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % (username)
|
||||
self.__run(module_args, "user")
|
||||
|
||||
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):
|
||||
"""
|
||||
delete initail users: SA, DBA, DEV
|
||||
"""
|
||||
results = {}
|
||||
action = results["action_info"] = {}
|
||||
for user in ["SA", "DBA", "DEV"]:
|
||||
ret = self.del_user(user)
|
||||
action[user] = ret
|
||||
return results
|
||||
|
||||
def get_host_info(self):
|
||||
"""
|
||||
use the setup module get host informations
|
||||
:return:
|
||||
all_ip is list
|
||||
processor_count is int
|
||||
system_dist_version is string
|
||||
system_type is string
|
||||
disk is dict (device_name: device_size}
|
||||
system_dist is string
|
||||
processor_type is string
|
||||
default_ip is string
|
||||
hostname is string
|
||||
product_sn is string
|
||||
memory_total is int (MB)
|
||||
default_mac is string
|
||||
product_name is string
|
||||
"""
|
||||
self.__run('', 'setup')
|
||||
|
||||
result = {}
|
||||
all = self.results.get("contacted")
|
||||
for key, value in all.iteritems():
|
||||
setup =value.get("ansible_facts")
|
||||
# get disk informations
|
||||
disk_all = setup.get("ansible_devices")
|
||||
disk_need = {}
|
||||
for disk_name, disk_info in disk_all.iteritems():
|
||||
if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'):
|
||||
disk_need[disk_name] = disk_info.get("size")
|
||||
|
||||
result[key] = {
|
||||
"other_ip": setup.get("ansible_all_ipv4_addresses"),
|
||||
"hostname": setup.get("ansible_hostname" ),
|
||||
"ip": setup.get("ansible_default_ipv4").get("address"),
|
||||
"mac": setup.get("ansible_default_ipv4").get("macaddress"),
|
||||
"brand": setup.get("ansible_product_name"),
|
||||
"cpu_type": setup.get("ansible_processor"),
|
||||
"cpu_cores": setup.get("ansible_processor_count"),
|
||||
"memory": setup.get("ansible_memtotal_mb"),
|
||||
"disk": disk_need,
|
||||
"system_type": setup.get("ansible_distribution"),
|
||||
"system_version": setup.get("ansible_distribution_version"),
|
||||
"asset_type": setup.get("ansible_system"),
|
||||
"sn": setup.get("ansible_product_serial")
|
||||
}
|
||||
|
||||
return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok", "result": result}
|
||||
|
||||
|
||||
class CustomAggregateStats(callbacks.AggregateStats):
|
||||
"""
|
||||
Holds stats about per-host activity during playbook runs.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(CustomAggregateStats, self).__init__()
|
||||
self.results = []
|
||||
|
||||
def compute(self, runner_results, setup=False, poll=False,
|
||||
ignore_errors=False):
|
||||
"""
|
||||
Walk through all results and increment stats.
|
||||
"""
|
||||
super(CustomAggregateStats, self).compute(runner_results, setup, poll,
|
||||
ignore_errors)
|
||||
|
||||
self.results.append(runner_results)
|
||||
|
||||
|
||||
def summarize(self, host):
|
||||
"""
|
||||
Return information about a particular host
|
||||
"""
|
||||
summarized_info = super(CustomAggregateStats, self).summarize(host)
|
||||
|
||||
# Adding the info I need
|
||||
summarized_info['result'] = self.results
|
||||
|
||||
return summarized_info
|
||||
|
||||
|
||||
class MyPlaybook(MyInventory):
|
||||
"""
|
||||
this is my playbook object for execute playbook.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MyPlaybook, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def run(self, playbook_relational_path, extra_vars=None):
|
||||
"""
|
||||
run ansible playbook,
|
||||
only surport relational path.
|
||||
"""
|
||||
stats = callbacks.AggregateStats()
|
||||
playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
|
||||
runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
|
||||
playbook_path = os.path.join(ANSIBLE_DIR, playbook_relational_path)
|
||||
|
||||
pb = PlayBook(
|
||||
playbook = playbook_path,
|
||||
stats = stats,
|
||||
callbacks = playbook_cb,
|
||||
runner_callbacks = runner_cb,
|
||||
inventory = self.inventory,
|
||||
extra_vars = extra_vars,
|
||||
check=False)
|
||||
|
||||
self.results = pb.run()
|
||||
|
||||
@property
|
||||
def raw_results(self):
|
||||
"""
|
||||
get the raw results after playbook run.
|
||||
"""
|
||||
return self.results
|
||||
|
||||
|
||||
class App(MyPlaybook):
|
||||
"""
|
||||
this is a app object for inclue the common playbook.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(App, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
|
|
@ -4,6 +4,8 @@ import xlsxwriter
|
|||
from django.db.models import AutoField
|
||||
from jumpserver.api import *
|
||||
from jasset.models import ASSET_STATUS, ASSET_TYPE, ASSET_ENV, IDC, AssetRecord
|
||||
from jperm.ansible_api import MyRunner
|
||||
from jperm.perm_api import gen_resource
|
||||
|
||||
|
||||
def group_add_asset(group, asset_id=None, asset_ip=None):
|
||||
|
@ -359,7 +361,6 @@ def ansible_record(asset, ansible_dic, username):
|
|||
old = asset_dic.get(field)
|
||||
new = ansible_dic.get(field)
|
||||
if unicode(old) != unicode(new):
|
||||
print old, new, type(old), type(new)
|
||||
setattr(asset, field, value)
|
||||
asset.save()
|
||||
alert_dic[field] = [old, new]
|
||||
|
@ -384,16 +385,17 @@ def excel_to_db(excel_file):
|
|||
row = table.row_values(row_num)
|
||||
if row:
|
||||
ip, port, hostname, use_default_auth, username, password, group = row
|
||||
use_default_auth = 1 if use_default_auth == u'默认' else 0
|
||||
if get_object(Asset, hostname=hostname):
|
||||
continue
|
||||
use_default_auth = 1 if use_default_auth == u'默认' else 0
|
||||
password_encode = CRYPTOR.encrypt(password) if password else ''
|
||||
if hostname:
|
||||
asset = Asset(ip=ip,
|
||||
port=port,
|
||||
hostname=hostname,
|
||||
use_default_auth=use_default_auth,
|
||||
username=username,
|
||||
password=password
|
||||
password=password_encode
|
||||
)
|
||||
asset.save()
|
||||
group_list = group.split('/')
|
||||
|
@ -406,3 +408,64 @@ def excel_to_db(excel_file):
|
|||
asset.group = group_instance
|
||||
asset.save()
|
||||
return True
|
||||
|
||||
|
||||
def get_ansible_asset_info(asset_ip, setup_info):
|
||||
disk_all = setup_info.get("ansible_devices")
|
||||
disk_need = {}
|
||||
for disk_name, disk_info in disk_all.iteritems():
|
||||
if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'):
|
||||
disk_need[disk_name] = disk_info.get("size")
|
||||
|
||||
all_ip = setup_info.get("ansible_all_ipv4_addresses")
|
||||
other_ip_list = all_ip.remove(asset_ip) if asset_ip in all_ip else []
|
||||
other_ip = ','.join(other_ip_list) if other_ip_list else ''
|
||||
# hostname = setup_info.get("ansible_hostname")
|
||||
# ip = setup_info.get("ansible_default_ipv4").get("address")
|
||||
mac = setup_info.get("ansible_default_ipv4").get("macaddress")
|
||||
brand = setup_info.get("ansible_product_name")
|
||||
cpu_type = setup_info.get("ansible_processor")[1]
|
||||
cpu_cores = setup_info.get("ansible_processor_count")
|
||||
cpu = cpu_type + ' * ' + unicode(cpu_cores)
|
||||
memory = setup_info.get("ansible_memtotal_mb")
|
||||
disk = disk_need
|
||||
system_type = setup_info.get("ansible_distribution")
|
||||
system_version = setup_info.get("ansible_distribution_version")
|
||||
# asset_type = setup_info.get("ansible_system")
|
||||
sn = setup_info.get("ansible_product_serial")
|
||||
asset_info = [other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand]
|
||||
|
||||
return asset_info
|
||||
|
||||
|
||||
def asset_ansible_update(obj_list, name=''):
|
||||
resource = gen_resource(obj_list)
|
||||
ansible_instance = MyRunner(resource)
|
||||
ansible_asset_info = ansible_instance.run(module_name='setup', pattern='*')
|
||||
for asset in obj_list:
|
||||
try:
|
||||
setup_info = ansible_asset_info['contacted'][asset.hostname]['ansible_facts']
|
||||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
asset_info = get_ansible_asset_info(asset.ip, setup_info)
|
||||
other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand = asset_info
|
||||
asset_dic = {"other_ip": other_ip,
|
||||
"mac": mac,
|
||||
"cpu": cpu,
|
||||
"memory": memory,
|
||||
"disk": disk,
|
||||
"sn": sn,
|
||||
"system_type": system_type,
|
||||
"system_version": system_version,
|
||||
"brand": brand
|
||||
}
|
||||
|
||||
ansible_record(asset, asset_dic, name)
|
||||
|
||||
|
||||
def asset_ansible_update_all():
|
||||
name = u'定时更新'
|
||||
asset_all = Asset.objects.all()
|
||||
asset_ansible_update(asset_all, name)
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ class Asset(models.Model):
|
|||
"""
|
||||
asset modle
|
||||
"""
|
||||
ip = models.GenericIPAddressField(blank=True, null=True, verbose_name=u"主机IP")
|
||||
ip = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"主机IP")
|
||||
other_ip = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"其他IP")
|
||||
hostname = models.CharField(unique=True, max_length=128, verbose_name=u"主机名")
|
||||
port = models.IntegerField(blank=True, null=True, verbose_name=u"端口号")
|
||||
|
@ -73,7 +73,7 @@ class Asset(models.Model):
|
|||
memory = 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_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"系统版本号")
|
||||
cabinet = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'机柜号')
|
||||
position = models.IntegerField(blank=True, null=True, verbose_name=u'机器位置')
|
||||
number = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'资产编号')
|
||||
|
|
|
@ -11,6 +11,7 @@ urlpatterns = patterns('',
|
|||
url(r"^asset_detail/$", asset_detail),
|
||||
url(r'^asset_edit/$', asset_edit),
|
||||
url(r'^asset_update/$', asset_update),
|
||||
url(r'^asset_update_batch/$', asset_update_batch),
|
||||
# url(r'^search/$', host_search),
|
||||
# url(r"^show_all_ajax/$", show_all_ajax),
|
||||
url(r'^group_add/$', group_add),
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# coding:utf-8
|
||||
|
||||
import ast
|
||||
from django.db.models import Q
|
||||
from jasset.asset_api import *
|
||||
from jumpserver.api import *
|
||||
from jumpserver.models import Setting
|
||||
from jasset.forms import AssetForm, IdcForm
|
||||
from jasset.models import Asset, IDC, AssetGroup, ASSET_TYPE, ASSET_STATUS
|
||||
from ansible_api import Tasks
|
||||
from jperm.ansible_api import Tasks, MyRunner
|
||||
from jperm.perm_api import gen_resource
|
||||
|
||||
|
||||
@require_role('admin')
|
||||
|
@ -95,8 +95,6 @@ def group_list(request):
|
|||
"""
|
||||
header_title, path1, path2 = u'查看资产组', u'资产管理', u'查看资产组'
|
||||
keyword = request.GET.get('keyword', '')
|
||||
gid = request.GET.get('gid')
|
||||
sid = request.GET.get('sid')
|
||||
asset_group_list = AssetGroup.objects.all()
|
||||
|
||||
if keyword:
|
||||
|
@ -132,6 +130,7 @@ def asset_add(request):
|
|||
af = AssetForm()
|
||||
if request.method == 'POST':
|
||||
af_post = AssetForm(request.POST)
|
||||
print af_post
|
||||
ip = request.POST.get('ip', '')
|
||||
hostname = request.POST.get('hostname', '')
|
||||
is_active = True if request.POST.get('is_active') == '1' else False
|
||||
|
@ -200,7 +199,7 @@ def asset_edit(request):
|
|||
header_title, path1, path2 = u'修改资产', u'资产管理', u'修改资产'
|
||||
|
||||
asset_id = request.GET.get('id', '')
|
||||
username = request.session.get('username', 'admin')
|
||||
username = request.user.username
|
||||
asset = get_object(Asset, id=asset_id)
|
||||
if asset:
|
||||
password_old = asset.password
|
||||
|
@ -211,13 +210,13 @@ def asset_edit(request):
|
|||
ip = request.POST.get('ip', '')
|
||||
hostname = request.POST.get('hostname', '')
|
||||
password = request.POST.get('password', '')
|
||||
is_active = True if request.POST.get('is_active') == '1' else False
|
||||
use_default_auth = request.POST.get('use_default_auth', '')
|
||||
|
||||
try:
|
||||
asset_test = get_object(Asset, hostname=hostname)
|
||||
if asset_test and asset_id != unicode(asset_test.id):
|
||||
error = u'该主机名 %s 已存在!' % hostname
|
||||
raise ServerError(error)
|
||||
emg = u'该主机名 %s 已存在!' % hostname
|
||||
raise ServerError(emg)
|
||||
except ServerError:
|
||||
pass
|
||||
else:
|
||||
|
@ -226,10 +225,12 @@ def asset_edit(request):
|
|||
if use_default_auth:
|
||||
af_save.username = ''
|
||||
af_save.password = ''
|
||||
af_save.port = None
|
||||
else:
|
||||
if password_old != password:
|
||||
password_encode = CRYPTOR.encrypt(password)
|
||||
af_save.password = password_encode
|
||||
af_save.is_active = True if is_active else False
|
||||
af_save.save()
|
||||
af_post.save_m2m()
|
||||
# asset_new = get_object(Asset, id=asset_id)
|
||||
|
@ -237,9 +238,10 @@ def asset_edit(request):
|
|||
info = asset_diff(af_post.__dict__.get('initial'), request.POST)
|
||||
db_asset_alert(asset, username, info)
|
||||
|
||||
msg = u'主机 %s 修改成功' % ip
|
||||
smg = u'主机 %s 修改成功' % ip
|
||||
else:
|
||||
emg = u'主机 %s 修改失败' % ip
|
||||
return my_render('jasset/error.html', locals(), request)
|
||||
return HttpResponseRedirect('/jasset/asset_detail/?id=%s' % asset_id)
|
||||
|
||||
return my_render('jasset/asset_edit.html', locals(), request)
|
||||
|
@ -250,6 +252,7 @@ def asset_list(request):
|
|||
"""
|
||||
asset list view
|
||||
"""
|
||||
header_title, path1, path2 = u'查看资产', u'资产管理', u'查看资产'
|
||||
idc_all = IDC.objects.filter()
|
||||
asset_group_all = AssetGroup.objects.all()
|
||||
asset_types = ASSET_TYPE
|
||||
|
@ -311,7 +314,7 @@ def asset_list(request):
|
|||
@require_role('admin')
|
||||
def asset_edit_batch(request):
|
||||
af = AssetForm()
|
||||
name = request.session.get('username', 'admin')
|
||||
name = request.user.username
|
||||
asset_group_all = AssetGroup.objects.all()
|
||||
|
||||
if request.method == 'POST':
|
||||
|
@ -382,9 +385,8 @@ def asset_edit_batch(request):
|
|||
asset.save()
|
||||
|
||||
if alert_list:
|
||||
username = unicode(name) + ' - ' + u'批量'
|
||||
print alert_list
|
||||
AssetRecord.objects.create(asset=asset, username=username, content=alert_list)
|
||||
recode_name = unicode(name) + ' - ' + u'批量'
|
||||
AssetRecord.objects.create(asset=asset, username=recode_name, content=alert_list)
|
||||
return HttpResponse('ok')
|
||||
|
||||
return my_render('jasset/asset_edit_batch.html', locals(), request)
|
||||
|
@ -410,53 +412,34 @@ def asset_update(request):
|
|||
"""
|
||||
asset_id = request.GET.get('id', '')
|
||||
asset = get_object(Asset, id=asset_id)
|
||||
name = request.user.username
|
||||
if not asset:
|
||||
return HttpResponseRedirect('/jasset/asset_detail/?id=%s' % asset_id)
|
||||
name = request.session.get('username', 'admin')
|
||||
if asset.use_default_auth:
|
||||
default = Setting.objects.all()
|
||||
if default:
|
||||
default = default[0]
|
||||
username = default.default_user
|
||||
password = default.default_password
|
||||
port = default.default_port
|
||||
else:
|
||||
return HttpResponse(u'没有设置默认用户名和密码!')
|
||||
else:
|
||||
username = asset.username
|
||||
password = asset.password
|
||||
port = asset.port
|
||||
|
||||
resource = [{"hostname": asset.ip, "port": port,
|
||||
"username": username, "password": password}]
|
||||
|
||||
ansible_instance = Tasks(resource)
|
||||
ansible_asset_info = ansible_instance.get_host_info()
|
||||
if ansible_asset_info['status'] == 'ok':
|
||||
asset_info = ansible_asset_info['result'][asset.ip]
|
||||
if asset_info:
|
||||
hostname = asset_info.get('hostname')
|
||||
other_ip = ','.join(asset_info.get('other_ip'))
|
||||
cpu_type = asset_info.get('cpu_type')[1]
|
||||
cpu_cores = asset_info.get('cpu_cores')
|
||||
cpu = cpu_type + ' * ' + unicode(cpu_cores)
|
||||
memory = asset_info.get('memory')
|
||||
disk = asset_info.get('disk')
|
||||
sn = asset_info.get('sn')
|
||||
brand = asset_info.get('brand')
|
||||
system_type = asset_info.get('system_type')
|
||||
system_version = asset_info.get('system_version')
|
||||
|
||||
asset_dic = {"hostname": hostname, "other_ip": other_ip, "cpu": cpu,
|
||||
"memory": memory, "disk": disk, "system_type": system_type,
|
||||
"system_version": system_version, "brand": brand, "sn": sn
|
||||
}
|
||||
|
||||
ansible_record(asset, asset_dic, name)
|
||||
|
||||
asset_ansible_update([asset], name)
|
||||
return HttpResponseRedirect('/jasset/asset_detail/?id=%s' % asset_id)
|
||||
|
||||
|
||||
@require_role('admin')
|
||||
def asset_update_batch(request):
|
||||
if request.method == 'POST':
|
||||
arg = request.GET.get('arg', '')
|
||||
name = unicode(request.user.username) + ' - ' + u'自动更新'
|
||||
if arg == 'all':
|
||||
asset_list = Asset.objects.all()
|
||||
else:
|
||||
asset_list = []
|
||||
asset_id_all = unicode(request.POST.get('asset_id_all', ''))
|
||||
asset_id_all = asset_id_all.split(',')
|
||||
for asset_id in asset_id_all:
|
||||
asset = get_object(Asset, id=asset_id)
|
||||
if asset:
|
||||
asset_list.append(asset)
|
||||
asset_ansible_update(asset_list, name)
|
||||
return HttpResponse(u'批量更新成功!')
|
||||
return HttpResponse(u'批量更新成功!')
|
||||
|
||||
|
||||
@require_role('admin')
|
||||
def idc_add(request):
|
||||
"""
|
||||
|
@ -477,9 +460,7 @@ def idc_add(request):
|
|||
return HttpResponseRedirect("/jasset/idc_list/")
|
||||
else:
|
||||
idc_form = IdcForm()
|
||||
return render_to_response('jasset/idc_add.html',
|
||||
locals(),
|
||||
context_instance=RequestContext(request))
|
||||
return my_render('jasset/idc_add.html', locals(), request)
|
||||
|
||||
|
||||
@require_role('admin')
|
||||
|
@ -495,9 +476,7 @@ def idc_list(request):
|
|||
else:
|
||||
posts = IDC.objects.exclude(name='ALL').order_by('id')
|
||||
contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
|
||||
return render_to_response('jasset/idc_list.html',
|
||||
locals(),
|
||||
context_instance=RequestContext(request))
|
||||
return my_render('jasset/idc_list.html', locals(), request)
|
||||
|
||||
|
||||
@require_role('admin')
|
||||
|
|
|
@ -9,4 +9,5 @@ urlpatterns = patterns('',
|
|||
url(r'^log_kill/', log_kill),
|
||||
url(r'^record/$', log_record),
|
||||
url(r'^web_terminal/$', web_terminal),
|
||||
url(r'^get_role_name/$', get_role_name),
|
||||
)
|
|
@ -4,6 +4,7 @@ from django.template import RequestContext
|
|||
from django.shortcuts import render_to_response
|
||||
|
||||
from jumpserver.api import *
|
||||
from jperm.perm_api import user_have_perm
|
||||
from django.http import HttpResponseNotFound
|
||||
from jlog.log_api import renderTemplate
|
||||
|
||||
|
@ -50,6 +51,7 @@ def log_list(request, offset):
|
|||
|
||||
web_monitor_uri = 'ws://%s/monitor' % WEB_SOCKET_HOST
|
||||
web_kill_uri = 'http://%s/kill' % WEB_SOCKET_HOST
|
||||
session_id = request.session.session_key
|
||||
return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
|
@ -103,11 +105,20 @@ def log_record(request):
|
|||
return HttpResponse('无日志记录!')
|
||||
|
||||
|
||||
@require_role('user')
|
||||
def get_role_name(request):
|
||||
asset_id = request.GET.get('id', 9999)
|
||||
asset = get_object(Asset, id=asset_id)
|
||||
if asset:
|
||||
role = user_have_perm(request.user, asset=asset)
|
||||
return HttpResponse(','.join([i.name for i in role]))
|
||||
return HttpResponse('error')
|
||||
|
||||
|
||||
@require_role('user')
|
||||
def web_terminal(request):
|
||||
#username = get_session.get('username', '')
|
||||
token = request.COOKIES.get('sessionid')
|
||||
username = request.user.username
|
||||
asset_name = '127.0.0.1'
|
||||
web_terminal_uri = 'ws://%s/terminal?username=%s&asset_name=%s&token=%s' % (WEB_SOCKET_HOST, username, asset_name, token)
|
||||
asset_id = request.GET.get('id')
|
||||
role_name = request.GET.get('role')
|
||||
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())
|
||||
|
||||
|
|
|
@ -132,6 +132,15 @@ def get_group_asset_perm(ob):
|
|||
return perm
|
||||
|
||||
|
||||
def user_have_perm(user, asset):
|
||||
user_perm_all = get_group_user_perm(user)
|
||||
user_assets = user_perm_all.get('asset').keys()
|
||||
if asset in user_assets:
|
||||
return user_perm_all.get('asset').get(asset).get('role')
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def gen_resource(ob, ex='', perm=None):
|
||||
"""
|
||||
ob为用户或资产列表或资产queryset, 如果同时输入用户和资产,则获取用户在这些资产上的信息
|
||||
|
|
|
@ -46,7 +46,7 @@ def gen_keys():
|
|||
"""
|
||||
key_basename = "key-" + uuid4().hex
|
||||
key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename)
|
||||
mkdir(key_path_dir, 0755)
|
||||
mkdir(key_path_dir, mode=0755)
|
||||
key = RSAKey.generate(2048)
|
||||
private_key = os.path.join(key_path_dir, 'id_rsa')
|
||||
public_key = os.path.join(key_path_dir, 'id_rsa.pub')
|
||||
|
|
|
@ -13,7 +13,7 @@ password = mysql234
|
|||
database = jumpserver
|
||||
|
||||
[websocket]
|
||||
web_socket_host = j:3000
|
||||
web_socket_host = js:3000
|
||||
|
||||
[mail]
|
||||
mail_enable = 1
|
||||
|
|
|
@ -54,12 +54,15 @@ def get_asset_info(asset):
|
|||
if default:
|
||||
info['port'] = default.default_port
|
||||
info['username'] = default.default_user
|
||||
try:
|
||||
info['password'] = CRYPTOR.decrypt(default.default_password)
|
||||
except ServerError:
|
||||
pass
|
||||
info['ssh_key'] = default.default_pri_key_path
|
||||
else:
|
||||
info['port'] = asset.port
|
||||
info['username'] = asset.username
|
||||
info['password'] = asset.password
|
||||
info['password'] = CRYPTOR.decrypt(asset.password)
|
||||
|
||||
return info
|
||||
|
||||
|
|
|
@ -5,16 +5,12 @@ from jumpserver.api import *
|
|||
|
||||
def name_proc(request):
|
||||
user_id = request.user.id
|
||||
# role_id = request.session.get('role_id')
|
||||
role_id = {'SU':2,'GA':1,'CU':0}.get(request.user.role,0)
|
||||
# if role_id == 2:
|
||||
role_id = {'SU': 2, 'GA': 1, 'CU': 0}.get(request.user.role, 0)
|
||||
# role_id = 'SU'
|
||||
user_total_num = User.objects.all().count()
|
||||
user_active_num = User.objects.filter().count()
|
||||
host_total_num = Asset.objects.all().count()
|
||||
host_active_num = Asset.objects.filter(is_active=True).count()
|
||||
# else:
|
||||
# pass
|
||||
|
||||
request.session.set_expiry(3600)
|
||||
|
||||
info_dic = {'session_user_id': user_id,
|
||||
|
|
|
@ -66,6 +66,7 @@ INSTALLED_APPS = (
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.humanize',
|
||||
'django_crontab',
|
||||
'bootstrapform',
|
||||
'jumpserver',
|
||||
'juser',
|
||||
|
@ -149,3 +150,7 @@ USE_TZ = False
|
|||
STATIC_URL = '/static/'
|
||||
|
||||
BOOTSTRAP_COLUMN_COUNT = 10
|
||||
|
||||
CRONJOBS = [
|
||||
('0 1 * * *', 'jasset.asset_api.asset_ansible_update_all')
|
||||
]
|
||||
|
|
|
@ -209,7 +209,11 @@ def str_to_dic(info):
|
|||
"""
|
||||
str to list
|
||||
"""
|
||||
return ast.literal_eval(info).iteritems()
|
||||
if '{' in info:
|
||||
info_dic = ast.literal_eval(info).iteritems()
|
||||
else:
|
||||
info_dic = {}
|
||||
return info_dic
|
||||
|
||||
|
||||
@register.filter(name='str_to_code')
|
||||
|
@ -237,3 +241,12 @@ def key_exist(username):
|
|||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@register.filter(name='check_role')
|
||||
def check_role(asset_id, user):
|
||||
"""
|
||||
ssh key is exist or not
|
||||
"""
|
||||
return user
|
||||
|
||||
|
|
146
run_websocket.py
146
run_websocket.py
|
@ -7,6 +7,7 @@ import os
|
|||
import sys
|
||||
import os.path
|
||||
import threading
|
||||
import datetime
|
||||
import urllib
|
||||
|
||||
import tornado.ioloop
|
||||
|
@ -20,16 +21,10 @@ from tornado.websocket import WebSocketClosedError
|
|||
|
||||
from tornado.options import define, options
|
||||
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier
|
||||
import select
|
||||
|
||||
# from gevent import monkey
|
||||
# monkey.patch_all()
|
||||
# import gevent
|
||||
# from gevent.socket import wait_read, wait_write
|
||||
import struct, fcntl, signal, socket, select, fnmatch
|
||||
|
||||
import paramiko
|
||||
from connect import Tty
|
||||
from connect import TtyLog, Log
|
||||
from connect import Tty, User, Asset, PermRole, logger, get_object
|
||||
from connect import TtyLog, Log, Session, user_have_perm
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
|
@ -41,17 +36,54 @@ define("port", default=3000, help="run on the given port", type=int)
|
|||
define("host", default='0.0.0.0', help="run port on", type=str)
|
||||
|
||||
|
||||
def require_auth(func):
|
||||
def _deco(request, *args, **kwargs):
|
||||
username = request.get_argument('username', '')
|
||||
asset_name = request.get_argument('asset_name', '')
|
||||
token = request.get_argument('token', '')
|
||||
print username, asset_name, token
|
||||
client = tornado.httpclient.HTTPClient()
|
||||
# response = client.fetch('http://some/url') + urllib.urlencode({'username': username,
|
||||
# 'asset_name': asset_name, 'token': token})
|
||||
# return request.close()
|
||||
def require_auth(role='user'):
|
||||
def _deco(func):
|
||||
def _deco2(request, *args, **kwargs):
|
||||
if request.get_cookie('sessionid'):
|
||||
session_key = request.get_cookie('sessionid')
|
||||
else:
|
||||
session_key = request.get_argument('sessionid', '')
|
||||
|
||||
logger.debug('Websocket: session_key: %s' % session_key)
|
||||
if session_key:
|
||||
session = get_object(Session, session_key=session_key)
|
||||
logger.debug('Websocket: session: %s' % session)
|
||||
if session and datetime.datetime.now() < session.expire_date:
|
||||
user_id = session.get_decoded().get('_auth_user_id')
|
||||
user = get_object(User, id=user_id)
|
||||
if user:
|
||||
logger.debug('Websocket: user [ %s ] request websocket' % user.username)
|
||||
request.user = user
|
||||
if role == 'admin':
|
||||
if user.role in ['SU', 'GA']:
|
||||
return func(request, *args, **kwargs)
|
||||
logger.debug('Websocket: user [ %s ] is not admin.' % user.username)
|
||||
else:
|
||||
return func(request, *args, **kwargs)
|
||||
else:
|
||||
logger.debug('Websocket: session expired: %s' % session_key)
|
||||
try:
|
||||
request.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
logger.warning('Websocket: Request auth failed.')
|
||||
# asset_id = int(request.get_argument('id', 9999))
|
||||
# print asset_id
|
||||
# asset = Asset.objects.filter(id=asset_id)
|
||||
# if asset:
|
||||
# asset = asset[0]
|
||||
# request.asset = asset
|
||||
# else:
|
||||
# request.close()
|
||||
#
|
||||
# if user:
|
||||
# user = user[0]
|
||||
# request.user = user
|
||||
#
|
||||
# else:
|
||||
# print("No session user.")
|
||||
# request.close()
|
||||
return _deco2
|
||||
return _deco
|
||||
|
||||
|
||||
|
@ -70,14 +102,7 @@ class EventHandler(ProcessEvent):
|
|||
def __init__(self, client=None):
|
||||
self.client = client
|
||||
|
||||
def process_IN_CREATE(self, event):
|
||||
print "Create file:%s." % os.path.join(event.path, event.name)
|
||||
|
||||
def process_IN_DELETE(self, event):
|
||||
print "Delete file:%s." % os.path.join(event.path, event.name)
|
||||
|
||||
def process_IN_MODIFY(self, event):
|
||||
print "Modify file:%s." % os.path.join(event.path, event.name)
|
||||
self.client.write_message(f.read())
|
||||
|
||||
|
||||
|
@ -87,10 +112,10 @@ def file_monitor(path='.', client=None):
|
|||
notifier = AsyncNotifier(wm, EventHandler(client))
|
||||
wm.add_watch(path, mask, auto_add=True, rec=True)
|
||||
if not os.path.isfile(path):
|
||||
print "You should monitor a file"
|
||||
logger.debug("File %s does not exist." % path)
|
||||
sys.exit(3)
|
||||
else:
|
||||
print "now starting monitor %s." % path
|
||||
logger.debug("Now starting monitor file %s." % path)
|
||||
global f
|
||||
f = open(path, 'r')
|
||||
st_size = os.stat(path)[6]
|
||||
|
@ -136,7 +161,7 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
|
|||
def check_origin(self, origin):
|
||||
return True
|
||||
|
||||
@require_auth
|
||||
@require_auth('admin')
|
||||
def open(self):
|
||||
# 获取监控的path
|
||||
self.file_path = self.get_argument('file_path', '')
|
||||
|
@ -158,7 +183,8 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
|
|||
MonitorHandler.clients.remove(self)
|
||||
MonitorHandler.threads.remove(MonitorHandler.threads[client_index])
|
||||
|
||||
print len(MonitorHandler.threads), len(MonitorHandler.clients)
|
||||
logger.debug("Websocket: Monitor client num: %s, thread num: %s" % (len(MonitorHandler.clients),
|
||||
len(MonitorHandler.threads)))
|
||||
|
||||
def on_message(self, message):
|
||||
# 监控日志,发生变动发向客户端
|
||||
|
@ -168,10 +194,13 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
|
|||
# 客户端主动关闭
|
||||
# self.close()
|
||||
|
||||
print "Close websocket."
|
||||
logger.debug("Websocket: Monitor client close request")
|
||||
try:
|
||||
client_index = MonitorHandler.clients.index(self)
|
||||
MonitorHandler.clients.remove(self)
|
||||
MonitorHandler.threads.remove(MonitorHandler.threads[client_index])
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
class WebTty(Tty):
|
||||
|
@ -184,16 +213,16 @@ class WebTty(Tty):
|
|||
|
||||
|
||||
class WebTerminalKillHandler(tornado.web.RequestHandler):
|
||||
@require_auth('admin')
|
||||
def get(self):
|
||||
ws_id = self.get_argument('id')
|
||||
Log.objects.filter(id=ws_id).update(is_finished=True)
|
||||
for ws in WebTerminalHandler.clients:
|
||||
print ws.id
|
||||
if ws.id == int(ws_id):
|
||||
print "killed"
|
||||
logger.debug("Kill log id %s" % ws_id)
|
||||
ws.log.save()
|
||||
ws.close()
|
||||
print len(WebTerminalHandler.clients)
|
||||
logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients))
|
||||
|
||||
|
||||
class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
||||
|
@ -206,18 +235,38 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||
self.log_time_f = None
|
||||
self.log = None
|
||||
self.id = 0
|
||||
self.user = None
|
||||
super(WebTerminalHandler, self).__init__(*args, **kwargs)
|
||||
|
||||
def check_origin(self, origin):
|
||||
return True
|
||||
|
||||
@require_auth
|
||||
@require_auth('user')
|
||||
def open(self):
|
||||
asset_name = self.get_argument('asset_name', '')
|
||||
username = self.get_argument('username', '')
|
||||
token = self.get_argument('token', '')
|
||||
print asset_name, username, token
|
||||
self.term = WebTty('a', 'b')
|
||||
logger.debug('Websocket: Open request')
|
||||
role_name = self.get_argument('role', 'sb')
|
||||
asset_id = self.get_argument('id', 9999)
|
||||
asset = get_object(Asset, id=asset_id)
|
||||
if asset:
|
||||
roles = user_have_perm(self.user, asset)
|
||||
logger.debug(roles)
|
||||
login_role = ''
|
||||
for role in roles:
|
||||
if role.name == role_name:
|
||||
login_role = role
|
||||
break
|
||||
if not login_role:
|
||||
logger.warning('Websocket: Not that Role %s for Host: %s User: %s ' % (role_name, asset.hostname,
|
||||
self.user.username))
|
||||
self.close()
|
||||
return
|
||||
else:
|
||||
logger.warning('Websocket: No that Host: %s User: %s ' % (asset_id, self.user.username))
|
||||
self.close()
|
||||
return
|
||||
logger.debug('Websocket: request web terminal Host: %s User: %s Role: %s' % (asset.hostname, self.user.username,
|
||||
login_role.name))
|
||||
self.term = WebTty(self.user, asset, login_role)
|
||||
self.term.get_connection()
|
||||
self.term.channel = self.term.ssh.invoke_shell(term='xterm')
|
||||
WebTerminalHandler.tasks.append(MyThread(target=self.forward_outbound))
|
||||
|
@ -236,13 +285,23 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||
if data.get('data'):
|
||||
self.term.input_mode = True
|
||||
if str(data['data']) in ['\r', '\n', '\r\n']:
|
||||
TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=self.term.deal_command(self.term.data, self.term.ssh)).save()
|
||||
if self.term.vim_flag:
|
||||
match = self.term.ps1_pattern.search(self.term.vim_data)
|
||||
if match:
|
||||
self.term.vim_flag = False
|
||||
vim_data = self.term.deal_command(self.term.vim_data)[0:200]
|
||||
if len(data) > 0:
|
||||
TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=vim_data).save()
|
||||
|
||||
TtyLog(log=self.log, datetime=datetime.datetime.now(),
|
||||
cmd=self.term.deal_command(self.term.data)[0:200]).save()
|
||||
self.term.vim_data = ''
|
||||
self.term.data = ''
|
||||
self.term.input_mode = False
|
||||
self.term.channel.send(data['data'])
|
||||
|
||||
def on_close(self):
|
||||
print 'On_close'
|
||||
logger.debug('Websocket: Close request')
|
||||
if self in WebTerminalHandler.clients:
|
||||
WebTerminalHandler.clients.remove(self)
|
||||
try:
|
||||
|
@ -267,6 +326,8 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||
if not len(recv):
|
||||
return
|
||||
data += recv
|
||||
if self.term.vim_flag:
|
||||
self.term.vim_data += recv
|
||||
try:
|
||||
self.write_message(json.dumps({'data': data}))
|
||||
now_timestamp = time.time()
|
||||
|
@ -290,4 +351,5 @@ if __name__ == '__main__':
|
|||
server.bind(options.port, options.host)
|
||||
# server.listen(options.port)
|
||||
server.start(num_processes=1)
|
||||
print "Run server on %s:%s" % (options.host, options.port)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -55,7 +55,7 @@
|
|||
<div class="col-sm-2">
|
||||
<div class="radio i-checks">
|
||||
<label>
|
||||
<input type="checkbox" checked="" id="id_use_default_auth" name="use_default_auth"><span> 使用默认 </span>
|
||||
<input type="checkbox" checked="checked" id="id_use_default_auth" name="use_default_auth"><span> 使用默认 </span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -142,26 +142,46 @@
|
|||
rules: {
|
||||
check_ip: [/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/, 'ip地址不正确'],
|
||||
check_port: [/^\d{1,5}$/, '端口号不正确'],
|
||||
use_default_auth: function() {
|
||||
var str1 = $("#id_use_default_auth").is(":checked");
|
||||
if (str1 == true){
|
||||
var decide = false;
|
||||
} else {
|
||||
var decide = true;
|
||||
}
|
||||
return decide}
|
||||
},
|
||||
fields: {
|
||||
{# "ip": {#}
|
||||
{# rule: "required;check_ip",#}
|
||||
{# tip: "输入IP",#}
|
||||
{# ok: "",#}
|
||||
{# msg: {required: "必须填写!"}#}
|
||||
{# },#}
|
||||
"ip": {
|
||||
rule: "check_ip;",
|
||||
tip: "输入IP",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
},
|
||||
"hostname": {
|
||||
rule: "required",
|
||||
tip: "填写主机名",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
},
|
||||
{# "port": {#}
|
||||
{# rule: "required;check_port",#}
|
||||
{# tip: "输入端口号",#}
|
||||
{# ok: "",#}
|
||||
{# msg: {required: "必须填写!"}#}
|
||||
{# }#}
|
||||
"port": {
|
||||
rule: "required(use_default_auth)",
|
||||
tip: "输入端口号",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
},
|
||||
"username": {
|
||||
rule: "required(use_default_auth)",
|
||||
tip: "输入用户名",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
},
|
||||
"password": {
|
||||
rule: "required(use_default_auth)",
|
||||
tip: "输入密码",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
}
|
||||
},
|
||||
valid: function(form) {
|
||||
form.submit();
|
||||
|
|
|
@ -11,9 +11,15 @@
|
|||
<div class="ibox-title">
|
||||
<span class="text text-primary"><b>{{ asset.ip }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="" href="/jasset/asset_update/?id={{ asset.id }}">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</a>
|
||||
<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">
|
||||
|
@ -29,14 +35,14 @@
|
|||
<div>
|
||||
<div class="text-left">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td class="text-navy">IP</td>
|
||||
<td>{{ asset.ip|default_if_none:"" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-navy">主机名</td>
|
||||
<td>{{ asset.hostname|default_if_none:"" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-navy">IP</td>
|
||||
<td>{{ asset.ip|default_if_none:"" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-navy">其他IP</td>
|
||||
<td>
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<div class="col-sm-2">
|
||||
<div class="radio i-checks">
|
||||
<label>
|
||||
<input type="checkbox" {% if asset.use_default_auth %} checked="" {% endif %} id="id_use_default_auth" name="use_default_auth"><span> 使用默认 </span>
|
||||
<input type="checkbox" {% if asset.use_default_auth %} checked="checked" {% endif %} id="id_use_default_auth" name="use_default_auth"><span> 使用默认 </span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -77,7 +77,7 @@
|
|||
<div class="hr-line-dashed"></div>
|
||||
<label class="col-sm-2 control-label"> 端口<span class="red-fonts">*</span> </label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" placeholder="Port" value="{{ asset.port }}" name="port" class="form-control">
|
||||
<input type="text" placeholder="Port" value="{{ asset.port|default_if_none:"" }}" name="port" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -99,6 +99,12 @@
|
|||
<div class="hr-line-dashed"></div>
|
||||
{{ af.disk|bootstrap_horizontal }}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
{{ af.system_type|bootstrap_horizontal }}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
{{ af.system_version|bootstrap_horizontal }}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
{{ af.number|bootstrap_horizontal }}
|
||||
|
||||
|
@ -186,6 +192,14 @@
|
|||
rules: {
|
||||
check_ip: [/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/, 'ip地址不正确'],
|
||||
check_port: [/^\d{1,5}$/, '端口号不正确'],
|
||||
use_default_auth: function() {
|
||||
var str1 = $("#id_use_default_auth").is(":checked");
|
||||
if (str1 == true){
|
||||
var decide = false;
|
||||
} else {
|
||||
var decide = true;
|
||||
}
|
||||
return decide}
|
||||
},
|
||||
fields: {
|
||||
"hostname": {
|
||||
|
@ -194,17 +208,24 @@
|
|||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
},
|
||||
{# "ip": {#}
|
||||
{# rule: "required;check_ip",#}
|
||||
{# tip: "输入IP",#}
|
||||
{# ok: "",#}
|
||||
{# msg: {required: "必须填写!"}#}
|
||||
{# },#}
|
||||
{# "port": {#}
|
||||
{# rule: "required;check_port",#}
|
||||
{# tip: "输入端口号",#}
|
||||
{# ok: "",#}
|
||||
{# msg: {required: "必须填写!"}#}
|
||||
"port": {
|
||||
rule: "required(use_default_auth)",
|
||||
tip: "输入端口号",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
},
|
||||
"username": {
|
||||
rule: "required(use_default_auth)",
|
||||
tip: "输入用户名",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
},
|
||||
"password": {
|
||||
rule: "required(use_default_auth)",
|
||||
tip: "输入密码",
|
||||
ok: "",
|
||||
msg: {required: "必须填写!"}
|
||||
}
|
||||
},
|
||||
valid: function(form) {
|
||||
form.submit();
|
||||
|
|
|
@ -54,29 +54,17 @@
|
|||
<div class="col-sm-2">
|
||||
<div class="radio i-checks">
|
||||
<label>
|
||||
<<<<<<< HEAD
|
||||
<input type="radio" checked="" value="no_action" id="no" name="use_default_auth" class="auth"><span> 不修改 </span>
|
||||
=======
|
||||
<input type="radio" checked="" value="" id="no" name="use_default_auth" class="auth"><span> 不修改 </span>
|
||||
>>>>>>> cmdb
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio i-checks">
|
||||
<label>
|
||||
<<<<<<< HEAD
|
||||
<input type="radio" id="default" name="use_default_auth" class="auth"><span> 使用默认 </span>
|
||||
=======
|
||||
<input type="radio" id="default" name="use_default_auth" class="auth" value="default"><span> 使用默认 </span>
|
||||
>>>>>>> cmdb
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio i-checks">
|
||||
<label>
|
||||
<<<<<<< HEAD
|
||||
<input type="radio" id="pass" name="use_default_auth" class="auth"><span> 用户名密码 </span>
|
||||
=======
|
||||
<input type="radio" id="pass" name="use_default_auth" class="auth" value="user_passwd"><span> 用户名密码 </span>
|
||||
>>>>>>> cmdb
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -138,21 +126,6 @@
|
|||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
<<<<<<< HEAD
|
||||
$('#host_edit').click(function () {
|
||||
var args = {};
|
||||
var match = null;
|
||||
var uuid = decodeURIComponent(location.search.substring(1));
|
||||
var reg = /(?:([^&]+)=([^&]+))/g;
|
||||
while((match = reg.exec(uuid))!==null){
|
||||
args[match[1]] = match[2];
|
||||
}
|
||||
var ids = args['uuid'];
|
||||
$('#uuid').val(ids)
|
||||
});
|
||||
|
||||
=======
|
||||
>>>>>>> cmdb
|
||||
$('.auth').click(function(){
|
||||
if ($(this).attr('id') == 'pass'){
|
||||
$('#admin_account').css('display', 'block')
|
||||
|
|
|
@ -130,7 +130,7 @@
|
|||
<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_update/?id={{ asset.id }}" class="btn btn-xs btn-info">更新</a>
|
||||
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
|
||||
<a value="/jasset/asset_del/?id={{ asset.id }}" class="btn btn-xs btn-danger asset_del">删除</a>
|
||||
{% endifnotequal %}
|
||||
</td>
|
||||
|
@ -142,6 +142,8 @@
|
|||
<div class="col-sm-6">
|
||||
<input type="button" id="asset_del" class="btn btn-danger btn-sm" name="del_button" value="删除"/>
|
||||
<a value="/jasset/asset_edit_batch/" type="button" class="btn btn-sm btn-warning iframe">修改</a>
|
||||
<input type="button" id="asset_update" class="btn btn-info btn-sm" name="update_button" value="更新"/>
|
||||
<input type="button" id="asset_update_all" class="btn btn-primary btn-sm" name="update_button" value="更新全部"/>
|
||||
</div>
|
||||
{% include 'paginator.html' %}
|
||||
</div>
|
||||
|
@ -155,23 +157,10 @@
|
|||
|
||||
{% block self_footer_js %}
|
||||
<script>
|
||||
$('table td').on('change', function(env, id){
|
||||
var url = "/jasset/show_all_ajax/?env=" + env + "&id=" + id;
|
||||
console.log(url);
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
// data: $("#search_form").serialize(),
|
||||
success: function (data) {
|
||||
$("#j_dept_"+id).html(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).ready(function(){
|
||||
$('.asset_del').click(function(){
|
||||
var row = $(this).closest('tr');
|
||||
if (confirm("确定删除")) {
|
||||
if (confirm("确定删除?")) {
|
||||
$.get(
|
||||
$(this).attr('value'),
|
||||
{},
|
||||
|
@ -180,9 +169,46 @@
|
|||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$('.conn').click(function(){
|
||||
var url='/jlog/get_role_name/?id=' + $(this).attr('value');
|
||||
var href = $(this).attr('href');
|
||||
var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role=';
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: url,
|
||||
data: {},
|
||||
success: function(data){
|
||||
var dataArray = data.split(',');
|
||||
if (dataArray.length == 1 && data != 'error'){
|
||||
console.log('one');
|
||||
window.open(new_url + data, '', 'height=400, width=600, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,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 + '</a> '
|
||||
});
|
||||
layer.alert(aUrl, {
|
||||
skin: 'layui-layer-molv',
|
||||
title: '多个角色,请选择一个连接',
|
||||
closeBtn: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
return false
|
||||
});
|
||||
});
|
||||
|
||||
function windowOpen(aTab){
|
||||
var new_url = aTab.href;
|
||||
window.open(new_url, '', 'height=400, width=600, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');
|
||||
return false
|
||||
}
|
||||
|
||||
$(".iframe").on('click', function(){
|
||||
var asset_id_all = getIDall();
|
||||
if (asset_id_all == ''){
|
||||
|
@ -219,14 +245,15 @@
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('#asset_del').click(function () {
|
||||
var asset_id_all = getIDall();
|
||||
console.log(asset_id_all);
|
||||
if (asset_id_all == ''){
|
||||
alert("请至少选择一行!");
|
||||
return false;
|
||||
}
|
||||
if (confirm("确定删除")) {
|
||||
if (confirm("确定删除?")) {
|
||||
$.ajax({
|
||||
type: "post",
|
||||
data: {asset_id_all: asset_id_all},
|
||||
|
@ -238,6 +265,40 @@
|
|||
}
|
||||
});
|
||||
|
||||
$('#asset_update').click(function () {
|
||||
var asset_id_all = getIDall();
|
||||
if (asset_id_all == ''){
|
||||
alert("请至少选择一行!");
|
||||
return false;
|
||||
}
|
||||
layer.msg('玩命更新中...', {time: 200000});
|
||||
$.ajax({
|
||||
type: "post",
|
||||
data: {asset_id_all: asset_id_all},
|
||||
url: "/jasset/asset_update_batch/",
|
||||
success: function () {
|
||||
parent.location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
{# function update_tips(){#}
|
||||
{# layer.tips('我是另外一个tips,只不过我长得跟之前那位稍有些不一样。', '吸附元素选择器', {#}
|
||||
{# tips: [1, '#3595CC'],#}
|
||||
{# time: 4000#}
|
||||
{# });#}
|
||||
{# }#}
|
||||
|
||||
$('#asset_update_all').click(function () {
|
||||
layer.msg('玩命更新中...', {time: 200000});
|
||||
$.ajax({
|
||||
type: "post",
|
||||
url: "/jasset/asset_update_batch/?arg=all",
|
||||
success: function () {
|
||||
parent.location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function change_info(){
|
||||
var args = $("#asset_form").serialize();
|
||||
|
@ -249,19 +310,6 @@
|
|||
change_info()
|
||||
}
|
||||
});
|
||||
|
||||
function show_all(env, id) {
|
||||
var url = "/jasset/show_all_ajax/?env=" + env + "&id=" + id;
|
||||
console.log(url);
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
success: function (data) {
|
||||
$("#j_group_" + id).html(data);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -1,4 +1,4 @@
|
|||
{% for field in af %}
|
||||
{% for field in af_form %}
|
||||
<div class="alert alert-warning text-center"> {{ field.errors }}</div>
|
||||
{{ field.label_tag }}: {{ field }}
|
||||
{% endfor %}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<div class="ibox-content">
|
||||
<div class="">
|
||||
<a target="_blank" href="/jasset/idc_add" class="btn btn-sm btn-primary "> 添加IDC </a>
|
||||
<input type="button" id="del_check" class="btn btn-danger btn-sm" name="del_button" value="删除所选"/>
|
||||
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-sm" id="search_input" name="keyword" placeholder="Search">
|
||||
|
@ -78,10 +79,9 @@
|
|||
</table>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{% ifequal session_role_id 2 %}
|
||||
<input type="button" id="del_check" class="btn btn-danger btn-sm" name="del_button" value="删除"/>
|
||||
<!--<input type="button" id="alter_button" class="btn btn-warning btn-sm" name="alter_button" value="修改" onclick="alter('contents_form')" />-->
|
||||
{% endifequal %}
|
||||
<div class="dataTables_info" id="editable_info" role="status" aria-live="polite">
|
||||
Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries
|
||||
</div>
|
||||
</div>
|
||||
{% include 'paginator.html' %}
|
||||
</div>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<div class="col-lg-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div id="ibox-content" class="ibox-title">
|
||||
<h5> 用户日志详细信息列表 <input type="button" id="test_connect" class="btn btn-primary" value="测试连接 web terminal" /> </h5>
|
||||
<h5> 用户日志详细信息列表 </h5>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
|
@ -79,11 +79,9 @@
|
|||
<th class="text-center"> 用户名 </th>
|
||||
<th class="text-center"> 登录主机 </th>
|
||||
<th class="text-center"> 来源IP </th>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<th class="text-center"> 统计命令 </th>
|
||||
<th class="text-center"> 实时监控 </th>
|
||||
<th class="text-center"> 阻断 </th>
|
||||
{% endifnotequal %}
|
||||
<th class="text-center"> 登录时间 </th>
|
||||
|
||||
</tr>
|
||||
|
@ -94,11 +92,9 @@
|
|||
<td id="username" class="text-center"> {{ post.user }} </td>
|
||||
<td id="ip" class="text-center"> {{ post.host }} </td>
|
||||
<td id="remote_ip" class="text-center"> {{ post.remote_ip }} </td>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<td class="text-center"><a href="/jlog/history/?id={{ post.id }}" class="log_command"> 命令统计 </a></td>
|
||||
<td class="text-center"><a class="monitor" file_path="{{ post.log_path }}"> 监控 </a></td>
|
||||
<td class="text-center"><input type="button" id="cut" class="btn btn-danger btn-xs" name="cut" value="阻断" onclick='cut("{{ post.pid }}", "{{ post.remote_ip }}")' /></td>
|
||||
{% endifnotequal %}
|
||||
<td class="text-center" id="start_time"> {{ post.start_time|date:"Y-m-d H:i:s" }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -188,35 +184,19 @@
|
|||
}});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#test_connect').click(function(){
|
||||
window.open('/jlog/web_terminal/?asset_name="hello', '播放', 'height=400, width=600, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');
|
||||
});
|
||||
});
|
||||
|
||||
{# function log_search(){#}
|
||||
{# $.ajax({#}
|
||||
{# type: "GET",#}
|
||||
{# url: "/jlog/search/?env=online",#}
|
||||
{# data: $("#search_form").serialize(),#}
|
||||
{# success: function (data) {#}
|
||||
{# $(".tab-content").html(data);#}
|
||||
{# }#}
|
||||
{# });#}
|
||||
{# }#}
|
||||
|
||||
|
||||
function cut(num, host){
|
||||
console.log(host);
|
||||
if (host=='Web'){
|
||||
var g_url = '{{ web_kill_uri }}' + '?id=' + num;
|
||||
} else {
|
||||
g_url = "/jlog/log_kill/?id=" + num;
|
||||
var g_url = "/jlog/log_kill/?id=" + num;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: g_url,
|
||||
url: g_url+"&sessionid={{ session_id }}",
|
||||
success: window.open("/jlog/log_list/online/", "_self")
|
||||
});
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
<label for="role_name" class="col-sm-2 control-label">规则名称<span class="red-fonts">*</span></label>
|
||||
<label for="role_name" class="col-sm-2 control-label">角色名称<span class="red-fonts">*</span></label>
|
||||
<div class="col-sm-8">
|
||||
<input id="role_name" name="role_name" placeholder="Role Name" type="text" class="form-control">
|
||||
</div>
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
<li class="role">
|
||||
<a href="/jperm/role/">系统角色</a>
|
||||
</li>
|
||||
<li class="apply_show online"><a href="/jperm/apply_show/online/">权限审批</a></li>
|
||||
<li class="apply_show online"><a href="/jperm/log/">授权记录</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="jlog">
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<script>
|
||||
$(".iframe_user").on('click', function(){
|
||||
var url= $(this).attr("value");
|
||||
$.layer({
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: '个人信息',
|
||||
maxmin: true,
|
||||
|
@ -45,7 +45,7 @@
|
|||
shade: [0.5, '#000000'],
|
||||
shadeClose: true,
|
||||
area : ['800px' , '600px'],
|
||||
iframe: {src: url}
|
||||
content: url
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<li id="tab1" class="active"><a data-toggle="tab" href="#tab-default" aria-expanded="true">默认设置</a></li>
|
||||
<li id="tab2" class=""><a data-toggle="tab" href="#tab-email" aria-expanded="true">邮箱设置</a></li>
|
||||
{# <li id="tab2" class=""><a data-toggle="tab" href="#tab-email" aria-expanded="true">邮箱设置</a></li>#}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -82,15 +82,15 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div id="tab-email" class="tab-pane">
|
||||
<table class="table table-striped table-bordered table-hover " id="editable" >
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">组名</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
{# <div id="tab-email" class="tab-pane">#}
|
||||
{# <table class="table table-striped table-bordered table-hover " id="editable" >#}
|
||||
{# <thead>#}
|
||||
{# <tr>#}
|
||||
{# <th class="text-center">组名</th>#}
|
||||
{# </tr>#}
|
||||
{# </thead>#}
|
||||
{# </table>#}
|
||||
{# </div>#}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue