完成web批量命令执行

pull/26/head
ibuler 2015-12-02 19:16:05 +08:00
parent 104ee779e7
commit 1f26e49fb8
6 changed files with 106 additions and 75 deletions

View File

@ -24,7 +24,7 @@ if django.get_version() != '1.6':
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info, get_role from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info, get_role
from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR, bash, get_tmp_dir from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR, bash, get_tmp_dir
from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm, PermRole
from jumpserver.settings import LOG_DIR from jumpserver.settings import LOG_DIR
from jperm.ansible_api import Command, MyRunner from jperm.ansible_api import Command, MyRunner
from jlog.log_api import escapeString from jlog.log_api import escapeString

View File

@ -8,6 +8,7 @@ urlpatterns = patterns('',
(r'^skin_config/$', 'jumpserver.views.skin_config'), (r'^skin_config/$', 'jumpserver.views.skin_config'),
(r'^login/$', 'jumpserver.views.Login'), (r'^login/$', 'jumpserver.views.Login'),
(r'^logout/$', 'jumpserver.views.Logout'), (r'^logout/$', 'jumpserver.views.Logout'),
(r'^exec_cmd/$', 'jumpserver.views.exec_cmd'),
(r'^file/upload/$', 'jumpserver.views.upload'), (r'^file/upload/$', 'jumpserver.views.upload'),
(r'^file/download/$', 'jumpserver.views.download'), (r'^file/download/$', 'jumpserver.views.download'),
(r'^setting', 'jumpserver.views.setting'), (r'^setting', 'jumpserver.views.setting'),

View File

@ -360,3 +360,8 @@ def download(request):
return render_to_response('download.html', locals(), context_instance=RequestContext(request)) return render_to_response('download.html', locals(), context_instance=RequestContext(request))
@login_required(login_url='/login')
def exec_cmd(request):
role_name = request.GET.get('role_name')
web_terminal_uri = 'ws://%s/exec?role=%s' % (WEB_SOCKET_HOST, role_name)
return my_render('exec_cmd.html', locals(), request)

View File

@ -23,8 +23,8 @@ from tornado.options import define, options
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier
import select import select
from connect import Tty, User, Asset, PermRole, logger, get_object from connect import Tty, User, Asset, PermRole, logger, get_object, PermRole, gen_resource
from connect import TtyLog, Log, Session, user_have_perm from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, Command
try: try:
import simplejson as json import simplejson as json
@ -67,22 +67,6 @@ def require_auth(role='user'):
except AttributeError: except AttributeError:
pass pass
logger.warning('Websocket: Request auth failed.') logger.warning('Websocket: Request auth failed.')
# asset_id = int(request.get_argument('id', 9999))
# print asset_id
# asset = Asset.objects.filter(id=asset_id)
# if asset:
# asset = asset[0]
# request.asset = asset
# else:
# request.close()
#
# if user:
# user = user[0]
# request.user = user
#
# else:
# print("No session user.")
# request.close()
return _deco2 return _deco2
return _deco return _deco
@ -138,6 +122,7 @@ class Application(tornado.web.Application):
(r'/monitor', MonitorHandler), (r'/monitor', MonitorHandler),
(r'/terminal', WebTerminalHandler), (r'/terminal', WebTerminalHandler),
(r'/kill', WebTerminalKillHandler), (r'/kill', WebTerminalKillHandler),
(r'/exec', ExecHandler),
] ]
setting = { setting = {
@ -225,6 +210,61 @@ class WebTerminalKillHandler(tornado.web.RequestHandler):
logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients)) logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients))
class ExecHandler(tornado.websocket.WebSocketHandler):
clients = []
tasks = []
def __init__(self, *args, **kwargs):
self.id = 0
self.user = None
self.role = None
self.cmd = None
self.assets = []
self.perm = {}
super(ExecHandler, self).__init__(*args, **kwargs)
def check_origin(self, origin):
return True
@require_auth('user')
def open(self):
logger.debug('Websocket: Open exec request')
role_name = self.get_argument('role', 'dev')
self.role = get_object(PermRole, name=role_name)
self.perm = get_group_user_perm(self.user)
roles = self.perm.get('role').keys()
if self.role not in roles:
self.write_message('No perm that role %s' % role_name)
self.close()
self.assets = self.perm.get('role').get(self.role).get('asset')
res = gen_resource({'user': self.user, 'asset': self.assets, 'role': self.role})
logger.debug('Web执行命令res: %s' % res)
self.cmd = Command(res)
message = '有权限的主机:' + ', '.join([asset.hostname for asset in self.assets])
self.write_message(message)
def on_message(self, message):
data = json.loads(message)
pattern = data.get('pattern', '')
command = data.get('command', '')
asset_name_str = '匹配主机: '
if pattern and command:
for inv in self.cmd.inventory.get_hosts(pattern=pattern):
asset_name_str += '\n%s' % inv.name
self.write_message(asset_name_str)
self.write_message('<span style="color: yellow">Ansible> %s</span>\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 = "<span style='color: green'>[ %s => %s]</span>\n" % (host, 'Ok')
else:
header = "<span style='color: red'>[ %s => %s]</span>\n" % (host, 'failed')
self.write_message(header)
self.write_message(output)
self.write_message('\n\n')
class WebTerminalHandler(tornado.websocket.WebSocketHandler): class WebTerminalHandler(tornado.websocket.WebSocketHandler):
clients = [] clients = []
tasks = [] tasks = []

View File

@ -148,6 +148,7 @@
<a value="/jasset/asset_edit_batch/" type="button" class="btn btn-sm btn-warning iframe">修改</a> <a value="/jasset/asset_edit_batch/" type="button" class="btn btn-sm btn-warning iframe">修改</a>
<input type="button" id="asset_update" class="btn btn-info btn-sm" name="update_button" value="更新"/> <input type="button" id="asset_update" class="btn btn-info btn-sm" name="update_button" value="更新"/>
<input type="button" id="asset_update_all" class="btn btn-primary btn-sm" name="update_button" value="更新全部"/> <input type="button" id="asset_update_all" class="btn btn-primary btn-sm" name="update_button" value="更新全部"/>
<input type="button" id="exec_cmd" class="btn btn-sm btn-danger" name="exec_cmd" value="执行命令"/>
</div> </div>
{% include 'paginator.html' %} {% include 'paginator.html' %}
</div> </div>
@ -175,6 +176,46 @@
} }
}); });
$('#exec_cmd').click(function(){
var url='/jlog/get_role_name/?id={{ user.id }}';
var href = $(this).attr('href');
$.ajax({
type: 'GET',
url: url,
data: {},
success: function(data){
var dataArray = data.split(',');
if (dataArray.length == 1 && data != 'error'){
var title = 'Jumpserver Exec Terminal';
layer.open({
type: 2,
title: title,
maxmin: true,
shade: false,
area: ['628px', '452px'],
content: new_url+data
});
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
} else if (dataArray.length == '1' && data == 'error'){
layer.alert('没有授权角色')
} else {
aUrl = '';
$.each(dataArray, function(index, value){
aUrl += '<a onclick="windowOpen(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + ' value=' + hostname + '>' + value + '</a> '
});
layer.alert(aUrl, {
skin: 'layui-layer-molv',
title: '多个角色,请选择一个连接',
shade: false,
closeBtn: 0
})
}
}
});
return false
});
$('.conn').click(function(){ $('.conn').click(function(){
var url='/jlog/get_role_name/?id=' + $(this).attr('value'); var url='/jlog/get_role_name/?id=' + $(this).attr('value');
var href = $(this).attr('href'); var href = $(this).attr('href');

View File

@ -1,56 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jumpserver | 开源跳板机系统</title>
<link rel="shortcut icon" href="/static/img/facio.ico" type="image/x-icon">
{% include 'link_css.html' %}
{% include 'head_script.html' %}
</head>
<body>
<div id="wrapper">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5> 实时监控 </h5>
</div>
<div class="ibox-content blank-panel" id="content" style="background-color: #0b0b0b; color: #006621; height: 500px; padding: 20px;">
你好<br>
</div>
</div>
</div>
</div>
</body>
{% block self_footer_js %}
<script>
function monitor(){
var wsUri = 'ws://j:8080/send';
var ws = new WebSocket(wsUri);
ws.onopen = function(evt){
$('#content').append('Connect websocket success' + '<br />');
ws.send('Start')
};
ws.onmessage = function(evt){
console.log(evt.data);
$('#content').append(evt.data.replace(/\n|\r|(\r\n)|(\u0085)|(\u2028)|(\u2029)/g, '<br>'));
};
ws.onclose = function(evt){
$('#content').append('Disconnect with websocket')
}
}
monitor();
</script>
{% endblock %}
</html>