diff --git a/connect.py b/connect.py
index 83db6b855..4ab1f4934 100644
--- a/connect.py
+++ b/connect.py
@@ -27,9 +27,11 @@ from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR, bash, get
from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm, PermRole
from jumpserver.settings import LOG_DIR
from jperm.ansible_api import Command, 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())
+remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n')
try:
import termios
@@ -227,8 +229,6 @@ class Tty(object):
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir))
try:
- # log_file_f = copen(log_file_path + '.log', mode='at', encoding='utf-8', errors='replace')
- # log_time_f = copen(log_file_path + '.time', mode='at', encoding='utf-8', errors='replace')
log_file_f = open(log_file_path + '.log', 'a')
log_time_f = open(log_file_path + '.time', 'a')
except IOError:
@@ -237,13 +237,12 @@ class Tty(object):
if self.login_type == 'ssh': # 如果是ssh连接过来,记录connect.py的pid,web terminal记录为日志的id
pid = os.getpid()
- self.remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n') # 获取远端IP
+ self.remote_ip = remote_ip # 获取远端IP
else:
pid = 0
log = Log(user=self.username, host=self.asset_name, remote_ip=self.remote_ip, login_type=self.login_type,
log_path=log_file_path, start_time=date_today, pid=pid)
-
log.save()
if self.login_type == 'web':
log.pid = log.id
@@ -421,9 +420,6 @@ class SshTty(Tty):
Connect server.
连接服务器
"""
- ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip
- login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip
-
# 发起ssh连接请求 Make a ssh connection
ssh = self.get_connection()
@@ -435,20 +431,6 @@ class SshTty(Tty):
signal.signal(signal.SIGWINCH, self.set_win_size)
except:
pass
-
- # 设置PS1并提示 Set PS1 and msg it
- #channel.send(ps1)
- #channel.send(login_msg)
- # channel.send('echo ${SSH_TTY}\n')
- # global SSH_TTY
- # while not channel.recv_ready():
- # time.sleep(1)
- # tmp = channel.recv(1024)
- #print 'ok'+tmp+'ok'
- # SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip()
- # SSH_TTY = ''
- # channel.send('clear\n')
- # Make ssh interactive tunnel
self.posix_shell()
# Shutdown channel socket
@@ -468,16 +450,6 @@ class Nav(object):
Print prompt
打印提示导航
"""
- msg = """\n\033[1;32m### Welcome To Use JumpServer, A Open Source System . ### \033[0m
- 1) Type \033[32mID\033[0m To Login.
- 2) Type \033[32m/\033[0m + \033[32mIP, Host Name, Host Alias or Comments \033[0mTo Search.
- 3) Type \033[32mP/p\033[0m To Print The Servers You Available.
- 4) Type \033[32mG/g\033[0m To Print The Server Groups You Available.
- 5) Type \033[32mG/g\033[0m\033[0m + \033[32mGroup ID\033[0m To Print The Server Group You Available.
- 6) Type \033[32mE/e\033[0m To Execute Command On Several Servers.
- 7) Type \033[32mQ/q\033[0m To Quit.
- """
-
msg = """\n\033[1;32m### 欢迎使用Jumpserver开源跳板机 ### \033[0m
1) 输入 \033[32mID\033[0m 直接登录.
2) 输入 \033[32m/\033[0m + \033[32mIP, 主机名, 主机别名 or 备注 \033[0m搜索.
@@ -542,35 +514,6 @@ class Nav(object):
print '[%-3s] %-15s' % (asset_group.id, asset_group.name)
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):
"""
批量执行命令
@@ -578,104 +521,75 @@ class Nav(object):
while True:
if not self.user_perm:
self.user_perm = get_group_user_perm(self.user)
- print '\033[32m[%-2s] %-15s \033[0m' % ('ID', '角色')
+
roles = self.user_perm.get('role').keys()
- role_check = dict(zip(range(len(roles)), roles))
+ if len(roles) > 1: # 授权角色数大于1
+ print '\033[32m[%-2s] %-15s \033[0m' % ('ID', '角色')
+ role_check = dict(zip(range(len(roles)), roles))
- for i, r in role_check.items():
- print '[%-2s] %-15s' % (i, r.name)
- print
- print "请输入运行命令角色的ID, q退出"
+ for i, r in role_check.items():
+ print '[%-2s] %-15s' % (i, r.name)
+ print
+ print "请输入运行命令角色的ID, q退出"
- try:
- role_id = raw_input("\033[1;32mRole>:\033[0m ").strip()
- if role_id == 'q':
- break
+ try:
+ role_id = raw_input("\033[1;32mRole>:\033[0m ").strip()
+ if role_id == 'q':
+ break
+ except (IndexError, ValueError):
+ color_print('错误输入')
else:
role = role_check[int(role_id)]
- assets = list(self.user_perm.get('role', {}).get(role).get('asset'))
- print "该角色有权限的所有主机"
- for asset in assets:
- print asset.hostname
- print
- print "请输入主机名、IP或ansile支持的pattern, q退出"
- pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
- if pattern == 'q':
- break
- else:
- res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm)
- cmd = Command(res)
- logger.debug("批量执行res: %s" % res)
- asset_name_str = ''
- for inv in cmd.inventory.get_hosts(pattern=pattern):
- print inv.name
- asset_name_str += inv.name
- print
-
- log_file_f, log_time_f, log = self.get_exec_log(asset_name_str)
- pre_timestamp = time.time()
- while True:
- print "请输入执行的命令, 按q退出"
- data = 'ansible> '
- write_log(log_file_f, data)
- now_timestamp = time.time()
- write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
- pre_timestamp = now_timestamp
- command = raw_input("\033[1;32mCmds>:\033[0m ").strip()
- data = '%s\r\n' % command
- write_log(log_file_f, data)
- now_timestamp = time.time()
- write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
- pre_timestamp = now_timestamp
- TtyLog(log=log, cmd=command, datetime=datetime.datetime.now()).save()
- if command == 'q':
- log.is_finished = True
- log.end_time = datetime.datetime.now()
- log.save()
- break
- result = cmd.run(module_name='shell', command=command, pattern=pattern)
- for k, v in result.items():
- if k == 'ok':
- for host, output in v.items():
- header = color_print("%s => %s" % (host, 'Ok'), 'green')
- print output
- output = re.sub(r'[\r\n]', '\r\n', output)
- data = '%s\r\n%s\r\n' % (header, output)
- now_timestamp = time.time()
- write_log(log_file_f, data)
- write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
- pre_timestamp = now_timestamp
- print
- else:
- for host, output in v.items():
- header = color_print("%s => %s" % (host, k), 'red')
- output = color_print(output, 'red')
- output = re.sub(r'[\r\n]', '\r\n', output)
- data = '%s\r\n%s\r\n' % (header, output)
- now_timestamp = time.time()
- write_log(log_file_f, data)
- write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
- pre_timestamp = now_timestamp
- print
- print "=" * 20
- print
-
- except (IndexError, KeyError):
- color_print('ID输入错误')
- continue
-
- except EOFError:
- print
+ elif len(roles) == 1: # 授权角色数为1
+ role = roles[0]
+ assets = list(self.user_perm.get('role', {}).get(role).get('asset')) # 获取该用户,角色授权主机
+ print "该角色有权限的所有主机"
+ for asset in assets:
+ print ' %s' % asset.hostname
+ print
+ print "请输入主机名、IP或ansile支持的pattern, q退出"
+ pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
+ if pattern == 'q':
break
- finally:
- log.is_finished = True
- log.end_time = datetime.datetime.now()
+ else:
+ res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm)
+ runner = MyRunner(res)
+ logger.debug("批量执行res: %s" % res)
+ asset_name_str = ''
+ print "匹配主机:"
+ for inv in runner.inventory.get_hosts(pattern=pattern):
+ print ' %s' % inv.name
+ asset_name_str += '%s ' % inv.name
+ print
+
+ while True:
+ print "请输入执行的命令, 按q退出"
+ command = raw_input("\033[1;32mCmds>:\033[0m ").strip()
+ if command == 'q':
+ break
+ runner.run('shell', command, pattern=pattern)
+ ExecLog(host=asset_name_str, user=self.user.username, cmd=command, remote_ip=remote_ip,
+ result=runner.results).save()
+ for k, v in runner.results.items():
+ if k == 'ok':
+ for host, output in v.items():
+ color_print("%s => %s" % (host, 'Ok'), 'green')
+ print output
+ print
+ else:
+ for host, output in v.items():
+ color_print("%s => %s" % (host, k), 'red')
+ color_print(output, 'red')
+ print
+ print "~o~ Task finished ~o~"
+ print
def upload(self):
while True:
if not self.user_perm:
self.user_perm = get_group_user_perm(self.user)
try:
+ print "进入批量上传模式"
print "请输入主机名、IP或ansile支持的pattern, q退出"
pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
if pattern == 'q':
@@ -684,33 +598,39 @@ class Nav(object):
assets = self.user_perm.get('asset').keys()
res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm)
runner = MyRunner(res)
- logger.debug("Muti upload file res: %s" % res)
asset_name_str = ''
+ print "匹配主机:\n"
for inv in runner.inventory.get_hosts(pattern=pattern):
print inv.name
- asset_name_str += inv.name
- print
+ asset_name_str += '%s ' % inv.name
+
+ if not asset_name_str:
+ color_print('没有匹配主机')
+ continue
tmp_dir = get_tmp_dir()
logger.debug('Upload tmp dir: %s' % tmp_dir)
os.chdir(tmp_dir)
bash('rz')
- check_notempty = os.listdir(tmp_dir)
- if not check_notempty:
- print color_print("上传文件为空")
+ 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.get_result()
- logger.debug(ret)
+ 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'):
- print ret
error = '上传目录: %s \n上传失败: [ %s ] \n上传成功 [ %s ]' % (tmp_dir,
', '.join(ret.get('failed').keys()),
- ', '.join(ret.get('ok')))
+ ', '.join(ret.get('ok').keys()))
color_print(error)
else:
- msg = '上传目录: %s \n传送成功 [ %s ]' % (tmp_dir, ', '.join(ret.get('ok')))
+ msg = '上传目录: %s \n传送成功 [ %s ]' % (tmp_dir, ', '.join(ret.get('ok').keys()))
color_print(msg, 'green')
print
@@ -731,30 +651,41 @@ class Nav(object):
assets = self.user_perm.get('asset').keys()
res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm)
runner = MyRunner(res)
- logger.debug("Muti Muti file res: %s" % res)
+ logger.debug("Muti download file res: %s" % res)
+ asset_name_str = ''
+ print "匹配用户:\n"
for inv in runner.inventory.get_hosts(pattern=pattern):
- print inv.name
+ asset_name_str += '%s ' % inv.name
+ print ' %s' % inv.name
+ if not asset_name_str:
+ color_print('没有匹配主机')
+ continue
print
- tmp_dir = get_tmp_dir()
- logger.debug('Download tmp dir: %s' % tmp_dir)
while True:
+ tmp_dir = get_tmp_dir()
+ logger.debug('Download tmp dir: %s' % tmp_dir)
print "请输入文件路径(不支持目录)"
file_path = raw_input("\033[1;32mPath>:\033[0m ").strip()
if file_path == 'q':
break
runner.run('fetch', module_args='src=%s dest=%s' % (file_path, tmp_dir), pattern=pattern)
- ret = runner.get_result()
+ ret = runner.results
+ FileLog(user=self.user.name, host=asset_name_str, filename=file_path, type='download',
+ remote_ip=remote_ip, result=ret).save()
+ logger.debug('Download file result: %s' % ret)
os.chdir('/tmp')
tmp_dir_name = os.path.basename(tmp_dir)
- bash('tar czf %s.tar.gz %s ' % (tmp_dir, tmp_dir_name))
+ 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'):
- print ret
- error = '文件名称: %s 下载失败: [ %s ] \n下载成功 [ %s ]' % \
- ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('failed').keys()), ', '.join(ret.get('ok')))
+ 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 下载成功 [ %s ]' % ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('ok')))
+ msg = '文件名称: %s \n下载成功 [ %s ]' % ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('ok').keys()))
color_print(msg, 'green')
print
except IndexError:
diff --git a/jlog/models.py b/jlog/models.py
index 2b43d3e52..c8ffd77a2 100644
--- a/jlog/models.py
+++ b/jlog/models.py
@@ -3,7 +3,7 @@ from django.db import models
class Log(models.Model):
user = models.CharField(max_length=20, null=True)
- host = models.CharField(max_length=20, null=True)
+ host = models.CharField(max_length=200, null=True)
remote_ip = models.CharField(max_length=100)
login_type = models.CharField(max_length=100)
log_path = models.CharField(max_length=100)
@@ -24,5 +24,26 @@ class Alert(models.Model):
class TtyLog(models.Model):
log = models.ForeignKey(Log)
- datetime = models.DateTimeField()
+ datetime = models.DateTimeField(auto_now=True)
cmd = models.CharField(max_length=200)
+
+
+class ExecLog(models.Model):
+ user = models.CharField(max_length=100)
+ host = models.TextField()
+ cmd = models.TextField()
+ remote_ip = models.CharField(max_length=100)
+ result = models.TextField(default='')
+ datetime = models.DateTimeField(auto_now=True)
+
+
+class FileLog(models.Model):
+ user = models.CharField(max_length=100)
+ host = models.TextField()
+ filename = models.TextField()
+ type = models.CharField(max_length=20)
+ remote_ip = models.CharField(max_length=100)
+ result = models.TextField(default='')
+ datetime = models.DateTimeField(auto_now=True)
+
+
diff --git a/jlog/urls.py b/jlog/urls.py
index e9a3063bd..a490f46eb 100644
--- a/jlog/urls.py
+++ b/jlog/urls.py
@@ -3,11 +3,11 @@ from django.conf.urls import patterns, include, url
from jlog.views import *
urlpatterns = patterns('',
- url(r'^$', log_list),
- url(r'^log_list/(\w+)/$', log_list),
- url(r'^history/$', log_history),
- url(r'^log_kill/', log_kill),
- url(r'^record/$', log_record),
- url(r'^web_terminal/$', web_terminal),
-
+ (r'^$', log_list),
+ (r'^log_list/(\w+)/$', log_list),
+ (r'^log_detail/(\w+)/$', log_detail),
+ (r'^history/$', log_history),
+ (r'^log_kill/', log_kill),
+ (r'^record/$', log_record),
+ (r'^web_terminal/$', web_terminal),
)
\ No newline at end of file
diff --git a/jlog/views.py b/jlog/views.py
index 462fdf1e8..a6564cbfb 100644
--- a/jlog/views.py
+++ b/jlog/views.py
@@ -8,7 +8,7 @@ from jperm.perm_api import user_have_perm
from django.http import HttpResponseNotFound
from jlog.log_api import renderTemplate
-from models import Log
+from jlog.models import Log, ExecLog, FileLog
from jumpserver.settings import WEB_SOCKET_HOST
@@ -21,9 +21,19 @@ def log_list(request, offset):
username_list = request.GET.getlist('username', [])
host_list = request.GET.getlist('host', [])
cmd = request.GET.get('cmd', '')
- print date_seven_day, date_now_str
+
if offset == 'online':
posts = Log.objects.filter(is_finished=False).order_by('-start_time')
+ elif offset == 'exec':
+ posts = ExecLog.objects.all().order_by('-id')
+ keyword = request.GET.get('keyword', '')
+ if keyword:
+ posts = posts.filter(Q(user__icontains=keyword)|Q(host__icontains=keyword)|Q(cmd__icontains=keyword))
+ elif offset == 'file':
+ posts = FileLog.objects.all().order_by('-id')
+ keyword = request.GET.get('keyword', '')
+ if keyword:
+ posts = posts.filter(Q(user__icontains=keyword)|Q(host__icontains=keyword)|Q(filename__icontains=keyword))
else:
posts = Log.objects.filter(is_finished=True).order_by('-start_time')
username_all = set([log.user for log in Log.objects.all()])
@@ -57,6 +67,11 @@ def log_list(request, offset):
return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request))
+@require_role('admin')
+def log_detail(request):
+ return my_render('jlog/exec_detail.html', locals(), request)
+
+
@require_role('admin')
def log_kill(request):
""" 杀掉connect进程 """
@@ -114,3 +129,21 @@ def web_terminal(request):
web_terminal_uri = 'ws://%s/terminal?id=%s&role=%s' % (WEB_SOCKET_HOST, asset_id, role_name)
return render_to_response('jlog/web_terminal.html', locals())
+
+@require_role('admin')
+def log_detail(request, offset):
+ log_id = request.GET.get('id')
+ if offset == 'exec':
+ log = get_object(ExecLog, id=log_id)
+ assets_hostname = log.host.split(' ')
+ result = eval(str(log.result))
+ return my_render('jlog/exec_detail.html', locals(), request)
+ elif offset == 'file':
+ log = get_object(FileLog, id=log_id)
+ assets_hostname = log.host.split(' ')
+ file_list = log.filename.split(' ')
+ try:
+ result = eval(str(log.result))
+ except (SyntaxError, NameError):
+ result = {}
+ return my_render('jlog/file_detail.html', locals(), request)
diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py
index 8ce990146..422c65a03 100644
--- a/jperm/ansible_api.py
+++ b/jperm/ansible_api.py
@@ -117,9 +117,9 @@ class MyRunner(MyInventory):
"""
def __init__(self, *args, **kwargs):
super(MyRunner, self).__init__(*args, **kwargs)
- self.results = {}
+ self.results_raw = {}
- def run(self, module_name, module_args='', timeout=10, forks=10, pattern='',
+ def run(self, module_name='shell', module_args='', timeout=10, forks=10, pattern='',
sudo=False, sudo_user='root', sudo_pass=''):
"""
run module from andible ad-hoc.
@@ -137,23 +137,29 @@ class MyRunner(MyInventory):
become_user=sudo_user,
become_pass=sudo_pass
)
- self.results = hoc.run()
- return self.results
+ self.results_raw = hoc.run()
+ return self.results_raw
- def get_result(self):
- result = {'failed': {}, 'ok': []}
- dark = self.results.get('dark')
- contacted = self.results.get('contacted')
+ @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('msg'):
- result['failed'][host] = info.get('msg')
+ if info.get('failed'):
+ result['failed'][host] = info.get('msg') + info.get('stderr', '')
+ elif info.get('stderr'):
+ result['failed'][host] = info.get('stderr') + str(info.get('warnings'))
else:
- result['ok'].append(host)
+ result['ok'][host] = info.get('stdout')
return result
@@ -163,9 +169,9 @@ class Command(MyInventory):
"""
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
- self.results = {}
+ self.results_raw = {}
- def run(self, command, module_name="command", timeout=10, forks=10, pattern='*'):
+ def run(self, command, module_name="command", timeout=10, forks=10, pattern=''):
"""
run command from andible ad-hoc.
command : 必须是一个需要执行的命令字符串, 比如
@@ -183,25 +189,34 @@ class Command(MyInventory):
pattern=pattern,
forks=forks,
)
- self.results = hoc.run()
-
- ret = {}
- if self.stdout:
- data['ok'] = self.stdout
- if self.stderr:
- data['err'] = self.stderr
- if self.dark:
- data['dark'] = self.dark
-
- return data
-
+ self.results_raw = hoc.run()
@property
- def raw_results(self):
- """
- get the ansible raw results.
- """
- return self.results
+ def result(self):
+ result = {}
+ for k, v in self.results_raw.items():
+ if k == 'dark':
+ for host, info in v.items():
+ result[host] = {'dark': info.get('msg')}
+ elif k == 'contacted':
+ for host, info in v.items():
+ result[host] = {}
+ if info.get('stdout'):
+ result[host]['stdout'] = info.get('stdout')
+ elif info.get('stderr'):
+ result[host]['stderr'] = info.get('stderr')
+ return result
+
+ @property
+ def state(self):
+ result = {}
+ if self.stdout:
+ result['ok'] = self.stdout
+ if self.stderr:
+ result['err'] = self.stderr
+ if self.dark:
+ result['dark'] = self.dark
+ return result
@property
def exec_time(self):
@@ -209,7 +224,7 @@ class Command(MyInventory):
get the command execute time.
"""
result = {}
- all = self.results.get("contacted")
+ all = self.results_raw.get("contacted")
for key, value in all.iteritems():
result[key] = {
"start": value.get("start"),
@@ -223,7 +238,7 @@ class Command(MyInventory):
get the comamnd standard output.
"""
result = {}
- all = self.results.get("contacted")
+ all = self.results_raw.get("contacted")
for key, value in all.iteritems():
result[key] = value.get("stdout")
return result
@@ -234,7 +249,7 @@ class Command(MyInventory):
get the command standard error.
"""
result = {}
- all = self.results.get("contacted")
+ all = self.results_raw.get("contacted")
for key, value in all.iteritems():
if value.get("stderr") or value.get("warnings"):
result[key] = {
@@ -247,7 +262,7 @@ class Command(MyInventory):
"""
get the dark results.
"""
- return self.results.get("dark")
+ return self.results_raw.get("dark")
class Tasks(Command):
diff --git a/jperm/models.py b/jperm/models.py
index 3a280762e..00f43a1ca 100644
--- a/jperm/models.py
+++ b/jperm/models.py
@@ -55,6 +55,6 @@ class PermPush(models.Model):
is_public_key = models.BooleanField(default=False)
is_password = models.BooleanField(default=False)
success = models.BooleanField(default=False)
- result = models.TextField()
+ result = models.TextField(default='')
date_added = models.DateTimeField(auto_now=True)
diff --git a/jperm/perm_api.py b/jperm/perm_api.py
index f64f32189..7366ea284 100644
--- a/jperm/perm_api.py
+++ b/jperm/perm_api.py
@@ -217,6 +217,7 @@ def gen_resource(ob, perm=None):
for asset in ob:
info = get_asset_info(asset)
res.append(info)
+ logger.debug('生成res: %s' % res)
return res
@@ -295,9 +296,11 @@ def get_role_push_host(role):
asset_all = Asset.objects.all()
asset_pushed = {}
for push in pushs:
+ print push.result
asset_pushed[push.asset] = {'success': push.success, 'key': push.is_public_key, 'password': push.is_password,
'result': push.result}
asset_no_push = set(asset_all) - set(asset_pushed.keys())
+ print asset_no_push, asset_pushed
return asset_pushed, asset_no_push
diff --git a/jperm/views.py b/jperm/views.py
index da33d7ff7..110b36c76 100644
--- a/jperm/views.py
+++ b/jperm/views.py
@@ -78,11 +78,11 @@ def perm_rule_add(request):
if request.method == 'POST':
# 获取用户选择的 用户,用户组,资产,资产组,用户角色
- users_select = request.POST.getlist('user', [])
- user_groups_select = request.POST.getlist('usergroup', [])
- assets_select = request.POST.getlist('asset', [])
- asset_groups_select = request.POST.getlist('assetgroup', [])
- roles_select = request.POST.getlist('role', [])
+ users_select = request.POST.getlist('user', []) # 需要授权用户
+ user_groups_select = request.POST.getlist('usergroup', []) # 需要授权用户组
+ assets_select = request.POST.getlist('asset', []) # 需要授权资产
+ asset_groups_select = request.POST.getlist('assetgroup', []) # 需要授权资产组
+ roles_select = request.POST.getlist('role', []) # 需要授权角色
rule_name = request.POST.get('rulename')
rule_comment = request.POST.get('rule_comment')
@@ -94,8 +94,10 @@ def perm_rule_add(request):
# 获取需要授权的主机列表
assets_obj = [Asset.objects.get(id=asset_id) for asset_id in assets_select]
asset_groups_obj = [AssetGroup.objects.get(id=group_id) for group_id in asset_groups_select]
- group_assets_obj = [asset for asset in [group.asset_set.all() for group in asset_groups_obj]]
- calc_assets = set(group_assets_obj) | set(assets_obj)
+ group_assets_obj = []
+ for asset_group in asset_groups_obj:
+ group_assets_obj.extend(list(asset_group.asset_set.all()))
+ calc_assets = set(group_assets_obj) | set(assets_obj) # 授权资产和资产组包含的资产
# 获取需要授权的用户列表
users_obj = [User.objects.get(id=user_id) for user_id in users_select]
@@ -106,8 +108,9 @@ def perm_rule_add(request):
# 获取授予的角色列表
roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select]
need_push_asset = set()
+
for role in roles_obj:
- asset_no_push = get_role_push_host(role=role)[1]
+ asset_no_push = get_role_push_host(role=role)[0] # 获取某角色已经推送的资产
need_push_asset.update(set(calc_assets) - set(asset_no_push))
if need_push_asset:
raise ServerError(u'没有推送角色 %s 的主机 %s'
@@ -444,13 +447,14 @@ def perm_role_push(request):
if password_push or key_push:
role_chosen_aliase = {} # {'dev': 'NETWORKING, SHUTDOWN'}
sudo_alias = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
- role_chosen_aliase[role.name] = ','.join(sudo.name for sudo in sudo_alias)
- add_sudo_script = get_add_sudo_script(role_chosen_aliase, sudo_alias)
- ret['sudo'] = task.push_sudo_file(add_sudo_script)
+ if sudo_alias:
+ role_chosen_aliase[role.name] = ','.join(sudo.name for sudo in sudo_alias if sudo.name)
+ 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)
+ 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_failed)
diff --git a/jumpserver/api.py b/jumpserver/api.py
index d73611d59..3d7cd7f0e 100644
--- a/jumpserver/api.py
+++ b/jumpserver/api.py
@@ -96,7 +96,7 @@ def get_role_key(user, role):
with open(os.path.join(role.key_path, 'id_rsa')) as fk:
with open(user_role_key_path, 'w') as fu:
fu.write(fk.read())
- logger.debug("创建新的用户角色key %s" % user_role_key_path)
+ logger.debug(u"创建新的用户角色key %s" % user_role_key_path)
chown(user_role_key_path, user.username)
os.chmod(user_role_key_path, 0600)
return user_role_key_path
diff --git a/jumpserver/views.py b/jumpserver/views.py
index 900d39fc8..dfa312819 100644
--- a/jumpserver/views.py
+++ b/jumpserver/views.py
@@ -15,7 +15,7 @@ from jumpserver.api import *
from jumpserver.models import Setting
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
-from jlog.models import Log
+from jlog.models import Log, FileLog
from jperm.perm_api import get_group_user_perm, gen_resource
from jasset.models import Asset, IDC
from jperm.ansible_api import MyRunner
@@ -287,14 +287,14 @@ def setting(request):
def upload(request):
user = request.user
assets = get_group_user_perm(user).get('asset').keys()
-
asset_select = []
if request.method == 'POST':
+ remote_ip = request.META.get('REMOTE_ADDR')
asset_ids = request.POST.getlist('asset_ids', '')
upload_files = request.FILES.getlist('file[]', None)
date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
upload_dir = get_tmp_dir()
- filenames = {}
+ # file_dict = {}
for asset_id in asset_ids:
asset_select.append(get_object(Asset, id=asset_id))
@@ -302,9 +302,10 @@ def upload(request):
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)
- filenames[upload_file.name] = file_path
with open(file_path, 'w') as f:
for chunk in upload_file.chunks():
f.write(chunk)
@@ -313,14 +314,17 @@ def upload(request):
runner = MyRunner(res)
runner.run('copy', module_args='src=%s dest=%s directory_mode'
% (upload_dir, upload_dir), pattern='*')
- ret = runner.get_result()
+ 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 = '上传目录: %s
上传失败: [ %s ]
上传成功 [ %s ]' % (upload_dir,
', '.join(ret.get('failed').keys()),
- ', '.join(ret.get('ok')))
+ ', '.join(ret.get('ok').keys()))
return HttpResponse(error, status=500)
- msg = '上传目录: %s
传送成功 [ %s ]' % (upload_dir, ', '.join(ret.get('ok')))
+ msg = '上传目录: %s
传送成功 [ %s ]' % (upload_dir, ', '.join(ret.get('ok')).keys())
return HttpResponse(msg)
return my_render('upload.html', locals(), request)
@@ -329,9 +333,9 @@ def upload(request):
def download(request):
user = request.user
assets = get_group_user_perm(user).get('asset').keys()
-
asset_select = []
if request.method == 'POST':
+ remote_ip = request.META.get('REMOTE_ADDR')
asset_ids = request.POST.getlist('asset_ids', '')
file_path = request.POST.get('file_path')
date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
@@ -342,14 +346,17 @@ def download(request):
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]))
+
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='*')
- logger.debug(runner.get_result())
+ 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 && sz %s.tar.gz' % (tar_file, tmp_dir_name, upload_dir))
+ bash('tar czf %s %s' % (tar_file, tmp_dir_name))
f = open(tar_file)
data = f.read()
f.close()
diff --git a/run_websocket.py b/run_websocket.py
index 1d0b44f07..fd8090813 100644
--- a/run_websocket.py
+++ b/run_websocket.py
@@ -8,7 +8,7 @@ import sys
import os.path
import threading
import datetime
-import urllib
+import re
import tornado.ioloop
import tornado.options
@@ -24,7 +24,7 @@ from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE
import select
from connect import Tty, User, Asset, PermRole, logger, get_object, PermRole, gen_resource
-from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, Command
+from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, MyRunner, ExecLog
try:
import simplejson as json
@@ -218,9 +218,10 @@ class ExecHandler(tornado.websocket.WebSocketHandler):
self.id = 0
self.user = None
self.role = None
- self.cmd = None
+ self.runner = None
self.assets = []
self.perm = {}
+ self.remote_ip = ''
super(ExecHandler, self).__init__(*args, **kwargs)
def check_origin(self, origin):
@@ -230,6 +231,7 @@ class ExecHandler(tornado.websocket.WebSocketHandler):
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)
@@ -238,32 +240,50 @@ class ExecHandler(tornado.websocket.WebSocketHandler):
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})
- logger.debug('Web执行命令res: %s' % res)
- self.cmd = Command(res)
- message = '有权限的主机:' + ', '.join([asset.hostname for asset in self.assets])
+ 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 = '匹配主机: '
+ asset_name_str = ''
if pattern and command:
- for inv in self.cmd.inventory.get_hosts(pattern=pattern):
- asset_name_str += '\n%s' % inv.name
- self.write_message(asset_name_str)
+ 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('Ansible> %s\n\n' % command)
- result = self.cmd.run(module_name='shell', command=command, pattern=pattern)
- for k, v in result.items():
- for host, output in v.items():
- if k == 'ok':
- header = "[ %s => %s]\n" % (host, 'Ok')
- else:
- header = "[ %s => %s]\n" % (host, 'failed')
- self.write_message(header)
- self.write_message(output)
- self.write_message('\n~o~ Task finished ~o~\n')
+ 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 = "[ %s => %s]\n" % (host, 'Ok')
+ else:
+ header = "[ %s => %s]\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):
diff --git a/static/css/style.css b/static/css/style.css
index b8a15d909..2dc7a2591 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -2822,7 +2822,9 @@ body.body-small .footer.fixed {
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td {
- border-top: 1px solid #e7eaec;
+ /*border-top: 1px solid #e7eaec;*/
+ border-bottom: 1px solid #e7eaec;
+ border-top: none;
line-height: 1.42857;
padding: 8px;
vertical-align: top;
diff --git a/templates/download.html b/templates/download.html
index 0c001d8d6..3be809660 100644
--- a/templates/download.html
+++ b/templates/download.html
@@ -10,7 +10,7 @@