云服务器

简单分析实现运维利器---批量操作bashshell

2021-07-08 09:10:20 9

背景

为了进一步完善自己写的小运维系统,今天就继续来补充一个批量操作bashshell,并记录操作用于审计!

 

一、思路

实现批量bashshell操作思路其实挺简单,同样是用到paramiko库,如果只写小脚本实现的可以参考我的另一篇文章Python

搞定繁琐运维之批量执行Linux命令,如果运用在web应用上,则需要websocket的帮助。

具体思路:

.打开websocket通道2.打开ssh通道3.执行shell4.反馈执行结果.5.保存操作记录

这个流程在Django运维系统基础功能之—web远程ssh终端这篇文章也讲述的很清楚。

但是我在想,实现批量操作需不需要做到像web终端一样?所有数据都要返回?我的答案是否定的,因为我觉得,要实行批量操作的时候,一般都只是做配置、备份、部署应用等。

因此只需要做到标准输入->标准输出或者错误输出即可。

 

二、实现

其实有上面的思路以及上述的两篇文章已经把细节讲的非常清楚了,在这里就不过多的阐述了,不清楚的可以看看上面提到的两篇文章,直接上代码供大家参考吧。

def batch(request):
    global hostnum, hostip, hostport, hostuser, hostpass
    if request.session.get('login')==None:
        return redirect('/sys/login/')
    if not request.is_websocket():
        dataa = models.host.objects.all()
        hostlist = (request.POST.get(f'{data.hostaddress}') for data in dataa)
        hostnum = 0
        hostip = []
        hostport = []
        hostuser = []
        hostpass = []
        for data in dataa:
            host = request.POST.get(f'{data.hostaddress}')
            if host != None:
                hostnum = hostnum + 1
                hostip.append(host)
                hostport.append(models.host.objects.get(hostaddress=host).hostport)
                hostuser.append(models.host.objects.get(hostaddress=host).hostuser)
                hostpass.append(models.host.objects.get(hostaddress=host).hostpass)
        return render(request,'html/batch.html',locals())
    else:
        if hostnum == 0 :
            nummessage = '没有选择主机.'
            request.websocket.send(nummessage.encode('utf-8'))
        else:
            wamessage = '等待主机连接ing...'
            request.websocket.send(wamessage.encode('utf-8'))
        a = []
        b = []
        c = []
        d = []
        for i in range(hostnum):
            a.append('client' + str(i))
            b.append('stdin' + str(i))
            c.append('stdout' + str(i))
            d.append('stderr' + str(i))
            a[i] = paramiko.SSHClient()  # 创建num个连接对象
            # client2 = paramiko.SSHClient()
            a[i].set_missing_host_key_policy(paramiko.AutoAddPolicy)  # 添加num个主机名及主机密钥到本地HostKeys对象
            # client2.set_missing_host_key_policy(paramiko.AutoAddPolicy)
            try:
                a[i].connect(hostname=hostip[i], port=hostport[i], username=hostuser[i], password=hostpass[i])  # num个连接
                # client2.connect(hostname=ip[1],port=port[1],username=username[1],password=password[1])
                print(f'主机{hostip[i]}连接成功!')
                message = f'主机{hostip[i]}连接成功!'
                request.websocket.send(message.encode('utf-8'))
            except:
                print(f'主机{hostip[i]}连接失败,请确认列表信息!')
                message = f'主机{hostip[i]}连接失败,请确认列表信息!'
                request.websocket.send(message.encode('utf-8'))

    for shell in request.websocket:
        addactionshell = models.batchaction()
        addactionshell.hostaddress = hostip
        addactionshell.username = request.session['username']
        addactionshell.actionshell = shell.decode('utf-8')
        addactionshell.starttime = time.strftime("%Y%m%d%H%M%S")
        addactionshell.save()
        for i in range(hostnum):
            b[i], c[i], d[i] = a[i].exec_command(shell)  # num个对象执行e命令
            # print(f'----------------------{hostip[i]}执行结果----------------------')
            # print(c[i].read().decode('utf-8'))  # 打印正确输出
            # print(d[i].read().decode('utf-8'))  # 打印错误输出
            output = '-'*97 + f'{hostip[i]}执行结果' + '-'*90 + c[i].read().decode('utf-8')  + d[i].read().decode('utf-8')
            request.websocket.send(output.encode('utf-8'))</code></pre>

 

html页面:

{% extends 'base.html' %}
{% load static %}
{% block title %}批量执行{% endblock %}
{% block css %}
     <link rel="stylesheet" href="{% static 'adminlet-2.4.10/bower_components/datatables.net-bs/css/dataTables.bootstrap.css' %}">
    <link rel="stylesheet" href="{% static 'asciinemaplayer/asciinema-player.css' %}">
{% endblock %}

<!-- 顶部内容 --> {% block breadcrumb %} <!-- Content Header (Page header) --> <section class="content-header"> <h1>批量执行</h1> </section> {% endblock %}

<!-- 身体内容 --> {% block content %} <div class="col-md-4"> <button type="button" style="float: left" class="btn btn-default" data-toggle="modal" data-target="#selecthost">选择主机</button> </div> <!-- Modal选择主机模态框 --> <div class="modal fade bs-example-modeal-sm" id="selecthost" tabindex="-1" role="dialog" aria-labelledby="selecthostLabel"> <div class="modal-dialog modal-sm" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <h4 class="modal-title" id="selecthostlLabel">请选择主机</h4> </div> <form action="{% url 'batch' %}" method="post"> {% csrf_token %} <div class="modal-body"> <table class="table"> <tbody> {% for data in dataa %} <tr> <td> <label class="checkbox-inline"> <input type="checkbox" name="{{data.hostaddress}}" value="{{data.hostaddress}}">{{ data.hostaddress }} </label> </td> </tr> {% endfor %} </tbody> </table>

  &lt;/div&gt;

  &lt;div class="modal-footer"&gt;
    &lt;button type="button" class="btn btn-default" data-dismiss="modal"&gt;关闭&lt;/button&gt;
    &lt;button type="submit" class="btn btn-primary" id="selhostip"&gt;确定&lt;/button&gt;
  &lt;/div&gt;
&lt;/form&gt;
&lt;/div&gt;

</div> </div> <br><br>

&lt;!-- 批量连接box --&gt;
&lt;div class="col-md-4"&gt;

<div class="box box-info" > <div class="box-header with-border"> <h3 class="box-title">执行的主机</h3> </div> <div class="box-body"> <table> {% for i in hostlist %} {% if i != None %} <tr> <td>{{i}}</td> </tr> {% endif %} {% endfor %} </table> </div> <div class="box-footer"> <a style="float: right" class="btn btn-success" id="conwebsocket">连接</a> </div> </div> <label class="form-inline" >执行的命令: <input class="form-control" type="text" name="acshell" id="shell" value=''> <a class="btn btn-success" id="sendshell">确定</a><br>(只支持标准输入-标准输出-标准错误模式) </label> </div>

<div class="col-md-8"> <div class="box box-info" > <div class="box-header with-border"> <h3 class="box-title">执行结果</h3> </div> <div class="box-body" id="showmess">

    &lt;/div&gt;
  &lt;/div&gt;

</div>

{% endblock %}

<!-- JS内容 --> {% block script %} <script src="{% static 'adminlet-2.4.10/bower_components/datatables.net/js/jquery.dataTables.min.js' %}"></script> <script src="{% static 'adminlet-2.4.10/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js' %}"></script> <script src="/static/xterm_/jquery.js"></script> <script type="text/javascript"> $(function () { $('#conwebsocket').click(function () { if (window.s) { window.s.close() } /创建socket连接/ var socket = new WebSocket("ws://" + window.location.host + "{% url 'batch' %}"); socket.onopen = function () { console.log('WebSocket open');//成功连接上Websocket }; socket.onmessage = function (mess) { console.log('message: ' + mess.data);//打印出服务端返回过来的数据 $('#showmess').prepend('<p>' + mess.data + '</p>'); }; // Call onopen directly if socket is already open if (socket.readyState == WebSocket.OPEN) socket.onopen(); window.s = socket; }); $('#sendshell').click(function () { //如果未连接到websocket if (!window.s) { $('#showmess').prepend('没有连接主机'); } else { window.s.send($('#shell').val());//通过websocket发送数据 } }); $('#close_websocket').click(function () { if (window.s) { window.s.close();//关闭websocket console.log('websocket已关闭'); } });

});
&lt;/script&gt;

{% endblock %}

 

models:

class batchaction(models.Model):
    hostaddress = models.CharField(max_length=255)
    username = models.CharField(max_length=255)
    actionshell = models.CharField(max_length=255)
    starttime = models.CharField(max_length=255)

 

 

审计html,在录像的页面基础上添加:

  <!-- datatable 批量操作审计-->
  <div class="box box-default">

&lt;div class="box-header with-border"&gt;
  &lt;h3 class="box-title"&gt;批量操作列表&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="box-body"&gt;

<table id="batchlist" class="display" style="width:100%"> <thead> <tr> <th>主机地址</th> <th>操作人</th> <th>执行shell</th> <th>开始时间</th> </tr> </thead> <tbody> {% for batchacction in batchactionlist %} <tr> <td>{{ batchacction.hostaddress }}</td> <td>{{ batchacction.username }}</td> <td>{{ batchacction.actionshell }}</td> <td>{{ batchacction.starttime }}</td> </tr> {% endfor %} </tbody> </table> </div> </div>

 

最终效果:



 

三、结束

好啦,Django运维系统之—批量操作bashshell,并记录操作用于审计的功能做到这里就结束咯。下次看看再补充一个执行shell脚本的功能吧。

执行shell脚本功能出来了好像上传文件的功能也差不多出来了哈哈…

上一篇: 无

微信关注

获取更多技术咨询