mirror of https://github.com/jumpserver/jumpserver
完成web批量命令执行
parent
104ee779e7
commit
1f26e49fb8
|
@ -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
|
||||||
|
|
|
@ -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'),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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>
|
|
Loading…
Reference in New Issue