Web堡垒机

前言:

如何向Leader体现出运维人员的工作价值?工单!如何自动记录下他们的操作,堡垒机!我看了网上有说 GateOne是一款开源的堡垒机解决方案,但是部署上之后发现了一个痛点, 我如何在不使用 公钥、私钥的前提下,基于web shh 实现 点击按钮进行一键登录--------》使用xshell一样使用Linux ------》退出之后记录操作日志,我可以修改GateOne的源码!但感觉自己实力不足,所以当下我想利用Django+websocket+paramiko+gevent.....能否zzzzzz实现?

一、WebSocket配合terms.js 

terms.js是在前端模拟ssh终端的开源框架

把terms.js和websocket框架的回调函数

onmessage()

socket.onclose()

onclose()

send()

结合起来!

{% extends "arya/layout.html" %}
{% block out_js %}
    <script src="/static/pligin/datatables/jquery.dataTables.min.js"></script>
    <script src="/static/pligin/datatables/dataTables.bootstrap.min.js"></script>
    <script src="/static/pligin/term.js"></script>
    <script src="/static/iron_ssh.js"></script>  <!---引入打开WebSocke的JavaScript代码  IronSSHClient类-->
{% endblock %}
{% block content %}
    <div class="table-responsive">
        <div id="page-content">
            <div class="panel col-lg-9">
                <div class="panel-heading">
                    <h3 class="panel-title">主机列表</h3>
                </div>
                <div class="panel-body">
                    <div class="table-responsive">
                        <table id="host_table" class="table table-hover table-bordered table-striped">
                            <thead>
                            <tr>
                                <th>IDC</th>
                                <th>Hostname</th>
                                <th>IP</th>
                                <th>Port</th>
                                <th>Username</th>
                                <th>操作</th>
                            </tr>
                            </thead>
                            <tbody id="hostlist">
                            {% for host in hosts %}
                                <tr>
                                    <td>{{ host.host.idc }}</td>
                                    <td>{{ host.host.hostname }}</td>
                                    <td>{{ host.host.ip_addr }}</td>
                                    <td>{{ host.host.port }}</td>
                                    <td>{{ host.host_user.username }}</td>
                                    <td>
                                        <button onclick="open_websocket({{ host.pk }},this)" type="button"
                                                class="btn btn-success">连接
                                        </button>
                                    </td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>

                </div>
            </div>

        </div>
    </div>
    <div id="term">

    </div>
    <div hidden="hidden" id="disconnect">
        <button type="button" class="btn btn-danger" id="close_connect" onclick="close_ssh_termial()">关闭连接</button>
    </div>
{% endblock %}
{% block in_js %}
    <script>
        /* Datatables是一款jquery表格插件。它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能。 */
        function set_tables() {
            $('#host_table').DataTable({
                "paging": true, <!-- 允许分页 -->
                "lengthChange": true, <!-- 允许改变每页显示的行数 -->
                "searching": true, <!-- 允许内容搜索 -->
                "ordering": true, <!-- 允许排序 -->
                "info": true, <!-- 显示信息 -->
                "autoWidth": true
            });
        }

        set_tables();
        CUURENT_WEB_SOCKEY='';
        function open_terminal(options) {
            $('#page-content').hide(); //点击连接按钮隐藏表格
            $('#disconnect').show();
            var client = new IronSSHClient();  //这里相当于执行了iron_ssh.js中的代码
            CUURENT_WEB_SOCKEY=client;
            var term = new Terminal(
                {
                    cols: 80,
                    rows: 24,
                    handler: function (key) {
                        client.send(key);
                    },
                    screenKeys: true,
                    useStyle: true,
                    cursorBlink: true
                });
            term.open(); //打开ssh终端
            $('.terminal').detach().appendTo('#term'); //把ssh终端放入 #term div标签中
            term.write('开始连接......');
            client.connect( //调用connect连接方法,把option的方法扩展了传进去
                $.extend(options,
                    {
                        onError: function (error) {
                            term.write('错误: ' + error + '
');
                        },
                        onConnect: function () {
                            term.write('
');
                        },
                        onClose: function () {
                            client.close_web_soket();
                            term.write('对方断开了连接.......');

{#                            close_ssh_termial() //关闭ssh命令 终端#}

                        }, //term.destroy();
                        onData: function (data) {
                            term.write(data);
                        }
                    }
                )
            );

        }

        function open_websocket(pk, self) { //点击连接按钮创建web_ssh 通道
            var options = {host_id: pk};
            open_terminal(options)//打开1个模块ssh的终端
        }

        function close_ssh_termial() {//关闭ssh命令终端
            CUURENT_WEB_SOCKEY.close_web_soket();
            $('#term').empty();
            $('#page-content').show(); //点击连接按钮隐藏表格
            $('#disconnect').hide();


        }

    </script>




{% endblock %}
模板语言

---------------------------------------------------

//定义1个js的原型
function IronSSHClient() {
}

//增加生成URL的方法
IronSSHClient.prototype._generateURL = function (options) {
    if (window.location.protocol == 'https:'){
        var protocol = 'wss://';
    }
    else {
        var protocol = 'ws://';
    }
    // ws://192.168.1.108:8000/host/3/
    var url = protocol + window.location.host + '/audit/host/'+ encodeURIComponent(options.host_id) + '/';
    return url;
};

//连接websocket
IronSSHClient.prototype.connect = function (options) {
    var server_socket = this._generateURL(options);
    if (window.WebSocket) {
        this._web_socket = new WebSocket(server_socket) //创建1个websocket对象
    }
    else if (window.MozWebSocket) {
        this._web_socket = new new MozWebSocket(server_socket) //如果是火狐浏览器使用这种方式:创建1个websocket对象
    }
    else {
        options.onError('当前浏览器不支持WebSocket'); //如果用户的浏览器不支持 websocket
        return
    }

    this._web_socket.onopen = function () {  //连接建立时触发
        options.onConnect();
    };

    this._web_socket.onmessage = function (event) {  //客户端接收服务端数据时触发(event)
        var data = JSON.parse(event.data.toString());
        // console.log(data);
        if (data.error !== undefined) { //如果发过来的错误信息
            options.onError(data.error);//执行opetion中的error方法
        }
        else {                        //正常数据
            options.onData(data.data);
        }

    };
    this._web_socket.onclose = function (event) {  //关闭websocket的方法
        options.onClose();



    };

};


//websocket 发送数据
IronSSHClient.prototype.send = function (data) {  //websocket发送数据的方法
    this._web_socket.send(JSON.stringify({'data':data})); //注意O,我发得可是字典!!

};

IronSSHClient.prototype.close_web_soket = function (data) {  //websocket发送数据的方法
    this._web_socket.close();

};

// web_socket_client = new IronSSHClient();
iron_ssh.js

二、Django+dwebsocket

@accept_websocket
def connect_host(request,user_bind_host_id):
    # print(request.environ)
    try:
        if request.is_websocket():
            while True:
                    message = request.websocket.wait()#一直等待前端发生数据过来!!
                    if message:
                        request.websocket.send(message)
Django接收websocket请求

三、paramiko交互式

import paramiko
import time

trans = paramiko.Transport(('172.17.10.113', 22))  # 【坑1】 如果你使用 paramiko.SSHClient() cd后会回到连接的初始状态
trans.start_client()
# 用户名密码方式
trans.auth_password(username='root', password='xxxxxx123')
# 打开一个通道
channel = trans.open_session()
channel.settimeout(7200)
# 获取一个终端
channel.get_pty()
# 激活器
channel.invoke_shell()

while True:
    cmd = input('--------->        ').strip()
    channel.send(cmd + '
')
    time.sleep(0.2)
    rst = channel.recv(1024)
    rst = rst.decode('utf-8')
    print(rst)
    # 通过命令执行提示符来判断命令是否执行完成
    if 'yes/no' in rst:
        channel.send('yes
')  # 【坑3】 如果你使用绝对路径,则会在home路径建立文件夹导致与预期不符
        time.sleep(0.5)
        ret = channel.recv(1024)
        ret = ret.decode('utf-8')
        print(ret)
        break

channel.close()
trans.close()
paramiko交互式

 四、WebSocket+Paramiko交互式(同步)

import json,time,paramiko
from . import models
from dwebsocket.decorators import accept_websocket
from django.shortcuts import render,HttpResponse,redirect

def hosts_list(request):
    current_user=models.UserInfo.objects.get(username=request.session.get('username'))
    current_audit__user =models.Account.objects.filter(user=current_user).first()
    if current_user:
        hosts=current_audit__user.host_user_binds.all()
    return render(request,'hosts_list.html',locals())


@accept_websocket
def connect_host(request,user_bind_host_id):
    # print(request.environ)
    try:
        if request.is_websocket(): #来了1个WebSocket创建1个SSHSocket,在它们两个开始同步 对话
            ssh_socket = paramiko.Transport(('172.17.10.113', 22))
            ssh_socket.start_client()
            ssh_socket.auth_password(username='root', password='xxxxxx123')
            channel = ssh_socket.open_session()
            channel.get_pty()
            channel.invoke_shell()
            while True:
                    message = request.websocket.wait()#一直等待前端发生数据过来!!
                    if len(message)>1:
                        cmd=json.loads(message)
                        #-------------------------------
                        channel.send(cmd['data'])
                        #---------------------------------
                        data = channel.recv(1024)
                        if len(data)>1:
                            request.websocket.send(json.dumps({'data': data.decode()}))  # 把前端发送的数据,返回前段的数据
                            # time.sleep(2)
    except Exception:
        print('客户端已经断开了连接!')
同步堡垒机

 五、WebSocket+Paramiko交互式+Gevent模块(协程异步)

本来打算使用Gevent模块开协程进行切换的,但是gevent的模块的from gevent import monkey;monkey.patch_all()Django项目中所有用到得库,还得换uwsgi,为避免牵一发而动全身的,我采用了保守的方式(线程)

import paramiko
import threading
import json

class Web_and_SSH(object):
    def __init__(self,host_user_bind_obj,websocket):
        self.host_user_bind_obj=host_user_bind_obj
        self.ip=self.host_user_bind_obj.host.ip_addr
        self.port=int(self.host_user_bind_obj.host.port)
        self.login_user=self.host_user_bind_obj. host_user.username
        self.password=self.host_user_bind_obj.host_user.password
        self.web_socket = websocket
        self.cmd_string = ''

    def open_shh_socket(self):
        try:
            # trans = paramiko.Transport(('172.17.10.113', 22))  # 【坑1】 如果你使用 paramiko.SSHClient() cd后会回到连接的初始状态
            # print(self.ip,)
            trans = paramiko.Transport((self.ip,self.port))  # 【坑1】 如果你使用 paramiko.SSHClient() cd后会回到连接的初始状态
            trans.start_client()
            # 用户名密码方式
            # print(self.login_user,self.password) #xxxxxx123
            trans.auth_password(username=self.login_user,password=self.password)
            # 打开一个通道
            channel = trans.open_session()
            # 获取一个终端
            channel.get_pty()
            channel.invoke_shell()
            self.ssh_socket=channel
            # print(self.ssh_socket)
        except Exception as e:
            print(e)
            self.web_socket.send(json.dumps({'error':str(e)},ensure_ascii=False))
            self.ssh_socket.close()
            raise

    def web_to_ssh(self):
        # print('--------------->')
        try:
            while True:
                message= self.web_socket.wait()
                if not message:
                    return
                cmd = json.loads(message)
                if 'data' in cmd:
                    self.ssh_socket.send(cmd['data'])
                    self.cmd_string += cmd['data']
        finally:
            self.close()

    def ssh_to_web(self):
        # print('<-------------------')
        try:
            while True:
                data = self.ssh_socket.recv(1024)
                if not data:
                    return
                self.web_socket.send(json.dumps({'data':data.decode()}))
                # print(self.cmd_string)

        finally:
            self.close()

    def _bridge(self):
        t1 = threading.Thread(target=self.web_to_ssh)
        t2 = threading.Thread(target=self.ssh_to_web)
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    def shell(self):
        self.open_shh_socket()
        self._bridge()
        self.close()


    def close(self):
        self.ssh_socket.close()
Bridge.py
from audit import Bridge
from . import models
from dwebsocket.decorators import accept_websocket
from django.shortcuts import render,HttpResponse,redirect

def hosts_list(request):
    current_user=models.UserInfo.objects.get(username=request.session.get('username'))
    current_audit__user =models.Account.objects.filter(user=current_user).first()
    if current_user:
        hosts=current_audit__user.host_user_binds.all()
    return render(request,'hosts_list.html',locals())


@accept_websocket
def connect_host(request,user_bind_host_id):
        if request.is_websocket(): #来了1个WebSocket创建1个SSHSocket,在它们两个开始同步 对话
            user_bind_host_id=models.HostUserBind.objects.get(pk=user_bind_host_id)
            obj=Bridge.Web_and_SSH(user_bind_host_id,request.websocket)
            obj.open_shh_socket()
            obj.shell()
视图

六.用户行为日志+运维日志

我在想怎么在使用了web socket的前提下 记录用户输入的command,这样做的痛点是使用了web socket协议之后 数据传输是 水流式的( 如果你执行了1个df命令,就会有d 、f 、 传输到后端),还要继续做数据处理,即便我拿到这些命令意义也不是很大;

突然我放弃了,我不这么搞了,我要这么搞!

我记录web socket响应给前端的数据,其实这样也可以把堡垒机用户所有操作记录下来而且较为详细; 

用户行为日志

用户操作日志

from django.db import models
from cmdb.models import UserInfo
# Create your models here.


class IDC(models.Model):
    name = models.CharField(max_length=64,unique=True)
    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural = "IDC机房"

class Host(models.Model):
    """存储所有主机信息"""
    hostname = models.CharField(max_length=64,unique=True)
    ip_addr = models.GenericIPAddressField(unique=True)
    port = models.IntegerField(default=22)
    idc = models.ForeignKey("IDC")
    enabled = models.BooleanField(default=True)
    def __str__(self):
        return "%s-%s" %(self.hostname,self.ip_addr)
    class Meta:
        verbose_name_plural = "主机"

class HostGroup(models.Model):
    """主机组"""
    name = models.CharField(max_length=64,unique=True)
    host_user_binds  = models.ManyToManyField("HostUserBind")
    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural = "主机组"


class HostUser(models.Model):
    """存储远程主机的用户信息
    root 123
    root abc
    root sfsfs
    """
    auth_type_choices = ((0,'ssh-password'),(1,'ssh-key'))
    auth_type = models.SmallIntegerField(choices=auth_type_choices)
    username = models.CharField(max_length=32)
    password = models.CharField(blank=True,null=True,max_length=128)

    def __str__(self):
        return "%s-%s-%s" %(self.get_auth_type_display(),self.username,self.password)

    class Meta:
        unique_together = ('username','password')
        verbose_name_plural = "用户+密码表"


class HostUserBind(models.Model):
    """绑定主机和用户"""
    host = models.ForeignKey("Host")
    host_user = models.ForeignKey("HostUser")
    def __str__(self):
        return "%s-%s" %(self.host,self.host_user)

    class Meta:
        unique_together = ('host','host_user')
        verbose_name_plural = "主机+用户+密码表"

class SessionLog(models.Model):
    ''' 记录每个用户 每次操作的记录 '''
    account=models.ForeignKey('Account',verbose_name='执行任务的用户')
    host_user_bind=models.ForeignKey('HostUserBind',verbose_name='执行的任务所在服务器')
    operation_type_choices= ((0, '交互式操作'), (1, '批量操作'))
    operation_type=models.SmallIntegerField(choices=operation_type_choices,default=0,verbose_name='操作类型')
    start_date=models.CharField(max_length=255,verbose_name='开始时间')
    end_date=models.DateTimeField(auto_now_add=True,verbose_name='结束时间')
    is_work_order=models.BooleanField(default=False)
    def __str__(self):
        return '%s %s-%s-%s-%s'%(self.start_date,self.account,self.host_user_bind.host.ip_addr,self.host_user_bind.host_user.username,self.get_operation_type_display())
    class Meta:
        verbose_name_plural = '操作记录'

class AuditLog(models.Model):
    """记录用户 每次操作执行的命令"""
    session = models.ForeignKey("SessionLog")
    cmd = models.TextField(verbose_name='执行了哪些命令')
    date = models.DateTimeField(auto_now_add=True)
    def __str__(self):
        return "%s-%s" %(self.session,self.cmd)
    class Meta:
        verbose_name_plural = '操作执行的命令'


class Account(models.Model):
    """堡垒机账户
    user.account.host_user_bind
    """
    user = models.OneToOneField(UserInfo,verbose_name='运维平台用户')
    enabled = models.BooleanField(default=True,verbose_name='当前用户是否被禁用')
    host_user_binds = models.ManyToManyField("HostUserBind",blank=True,verbose_name='用户下的权限')
    host_groups = models.ManyToManyField("HostGroup",blank=True,verbose_name='用户下的权限组')
    def __str__(self):
        return "%s" %(self.user.username)
    class Meta:
        verbose_name_plural = '堡垒机用户'

class CronTable(models.Model):
    '''主机的Cron 任务表'''
    host_user=models.ForeignKey('HostUserBind',verbose_name='1服务器+1用户+1cron+1行记录')
    task_name=models.CharField(max_length=255,verbose_name='任务名称',blank=True,null=True)
    task_tag= models.CharField(max_length=255, verbose_name='任务功能说明',blank=True, null=True)
    cron_expression = models.CharField(max_length=255, verbose_name='任务表达式', blank=True, null=True)
    available=models.BooleanField(verbose_name='当前cron任务是否可用')
    last_execute_available = models.BooleanField(default=True, verbose_name='上一次执行是否执行成功')
    last_execute_log = models.TextField(verbose_name='上次次执行日志', blank=True, null=True)
    next_execute_time = models.CharField(max_length=255, verbose_name='下次执行时间',blank=True, null=True)
    cron_execute=((0,'shell'),(1,'http-get'))
    pass1 = models.CharField(max_length=255,verbose_name='预留字段1',blank=True, null=True)
    pass2 = models.CharField(max_length=255,verbose_name='预留字段2',blank=True, null=True)
    # class Meta:
    #     verbose_name_plural = 'crontab表'
models.py
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^hosts_list/$',views.hosts_list,name='hosts_list'),#/audit/hosts_list/
    #('host/<int:user_bind_host_id>/', views.connect  #(?P<n1>d+)/
    url(r'^host/(?P<user_bind_host_id>d+)/$',views.connect_host,name='connect_host'),#/audit/hosts_list/
    url(r'^user/activity/logs/$',views.activity_log, name='users_activity_log_url'),#/audit//user/operation/logs/
    url(r'^user/operation/logs/$',views.operation_log, name='users_operation_log_url')
   ]
urls.py
from audit import Bridge
from . import models

from dwebsocket.decorators import accept_websocket
from django.shortcuts import render,HttpResponse,redirect

def hosts_list(request):
    current_user=models.UserInfo.objects.get(username=request.session.get('username'))
    current_audit__user =models.Account.objects.filter(user=current_user).first()
    if current_user:
        hosts=current_audit__user.host_user_binds.all()
    return render(request,'hosts_list.html',locals())





@accept_websocket
def connect_host(request,user_bind_host_id):
        if request.is_websocket(): #来了1个WebSocket创建1个SSHSocket,django在它们2个之间, 协调异步对话
            user_bind_host_id=models.HostUserBind.objects.get(pk=user_bind_host_id)
            obj=Bridge.Web_and_SSH(user_bind_host_id,request.websocket,request,models)
            obj.open_shh_socket()
            obj.shell()
            obj.add_logs()


def activity_log(request):#用户行为日志
    pk=request.GET.get('pk')
    host_user_bind_pk=pk
    SessionLogs=models.SessionLog.objects.filter(host_user_bind__pk=pk).order_by('-pk')
    return render(request,'activity_logs.html',locals())

def operation_log(request):#用户操作日
    pk = request.GET.get('pk')
    host_user_bind_pk=request.GET.get('next')
    AuditLogs = models.AuditLog.objects.filter(session__pk=pk).order_by('-pk')
    return render(request,'operation_logs.html',locals())

def generate_work_order(request):#运维日志生成工单
    return HttpResponse('ok')
views.py

------------------------------------------------------------------

{% extends "arya/layout.html" %}
{% block out_js %}
    <script src="/static/pligin/datatables/jquery.dataTables.min.js"></script>
    <script src="/static/pligin/datatables/dataTables.bootstrap.min.js"></script>
    <script src="/static/pligin/term.js"></script>
    <script src="/static/iron_ssh.js"></script>  <!---引入打开WebSocke的JavaScript代码  IronSSHClient类-->
{% endblock %}
{% block content %}
    <div class="table-responsive">

        <div id="page-content">
            <div class="panel col-lg-9">
                <div class="panel-heading">
                    <h3 class="panel-title">主机列表</h3>
                </div>
                <div class="panel-body">
                    <div class="table-responsive">
                        <table id="host_table" class="table table-hover table-bordered table-striped">
                            <thead>
                            <tr>
                                <th>IDC</th>
                                <th>Hostname</th>
                                <th>IP</th>
                                <th>Port</th>
                                <th>Username</th>
                                <th>操作</th>
                            </tr>
                            </thead>
                            <tbody id="hostlist">
                            {% for host in hosts %}
                                <tr>
                                    <td>{{ host.host.idc }}</td>
                                    <td>{{ host.host.hostname }}</td>
                                    <td><a href="{% url 'users_activity_log_url'%}?pk={{ host.pk }}">{{ host.host.ip_addr }}</a> </td>
                                    <td>{{ host.host.port }}</td>
                                    <td>{{ host.host_user.username }}</td>
                                    <td>
                                        <button onclick="open_websocket({{ host.pk }},this)" type="button"
                                                class="btn btn-success">连接
                                        </button>
                                    </td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>

                </div>
            </div>

        </div>
    </div>
    <div id="term">

    </div>
    <div hidden="hidden" id="disconnect">
        <button type="button" class="btn btn-danger" id="close_connect" onclick="close_ssh_termial()">关闭连接</button>
    </div>
{% endblock %}
{% block in_js %}
    <script>
        /* Datatables是一款jquery表格插件。它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能。 */
        function set_tables() {
            $('#host_table').DataTable({
                "paging": true, <!-- 允许分页 -->
                "lengthChange": true, <!-- 允许改变每页显示的行数 -->
                "searching": true, <!-- 允许内容搜索 -->
                "ordering": true, <!-- 允许排序 -->
                "info": true, <!-- 显示信息 -->
                "autoWidth": true
            });
        }

        set_tables();
        CUURENT_WEB_SOCKEY='';
        function open_terminal(options) {
            $('#page-content').hide(); //点击连接按钮隐藏表格
            $('#disconnect').show();
            var client = new IronSSHClient();  //这里相当于执行了iron_ssh.js中的代码
            CUURENT_WEB_SOCKEY=client;
            var term = new Terminal(
                {
                    cols: 80,
                    rows: 24,
                    handler: function (key) {
                        client.send(key);
                    },
                    screenKeys: true,
                    useStyle: true,
                    cursorBlink: true
                });
            term.open(); //打开ssh终端
            $('.terminal').detach().appendTo('#term'); //把ssh终端放入 #term div标签中
            term.write('开始连接......');
            client.connect( //调用connect连接方法,把option的方法扩展了传进去
                $.extend(options,
                    {
                        onError: function (error) {
                            term.write('错误: ' + error + '
');
                        },
                        onConnect: function () {
                            term.write('
');
                        },
                        onClose: function () {
                            client.close_web_soket();
                            term.write('对方断开了连接.......');

{#                            close_ssh_termial() //关闭ssh命令 终端#}

                        }, //term.destroy();
                        onData: function (data) {
                            term.write(data);
                        }
                    }
                )
            );

        }

        function open_websocket(pk, self) { //点击连接按钮创建web_ssh 通道
            var options = {host_id: pk};
            open_terminal(options)//打开1个模块ssh的终端
        }

        function close_ssh_termial() {//关闭ssh命令终端
            CUURENT_WEB_SOCKEY.close_web_soket();
            $('#term').empty();
            $('#page-content').show(); //点击连接按钮隐藏表格
            $('#disconnect').hide();


        }

    </script>




{% endblock %}
hosts_list.html
{% extends "arya/layout.html" %}
{% block out_js %}
    <script src="/static/pligin/datatables/jquery.dataTables.min.js"></script>
    <script src="/static/pligin/datatables/dataTables.bootstrap.min.js"></script>
{% endblock %}
{% block content %}
    <a class='btn btn-primary btn-sm' href="/audit/hosts_list/">返回</a>
    <div class="table-responsive">
        <div id="page-content">
            <div class="panel col-lg-9">
                <div class="panel-heading">
                    <h3 class="panel-title">用户行为日志</h3>
                </div>
                <div class="panel-body">
                    <div class="table-responsive">
                        <table id="users_activity_log_show" class="table table-hover table-bordered table-striped">
                            <thead>
                            <tr>
                                <th>开始时间</th>
                                <th>结束时间</th>
                                <th>运维用户</th>
                                <th>方式</th>
                                <th>登录</th>
                                <th>服务器</th>
                                <th>操作</th>
                            </tr>
                            </thead>
                            <tbody>
                            {% for log in SessionLogs %}
                                <tr>
                                    <td>{{ log.start_date }}</td>
                                    <td>{{ log.end_date }}</td>
                                    <td> {{ log.account.user.username }}</td>
                                    <td> {{ log.get_operation_type_display }}</td>
                                    <td>{{ log.host_user_bind.host_user.username }}</td>
                                    <td>{{ log.host_user_bind.host.ip_addr }}</td>
                                    <td style="text-align: center">
                                        <a class='btn btn-primary btn-sm'
                                           href="{% url 'users_operation_log_url' %}?pk={{ log.pk }}&next={{ host_user_bind_pk }}">更多</a>
                                        {% if request.session.username == log.account.user.username %}
                                            <a class='btn btn-success btn-sm'
                                               href="{% url 'users_operation_log_url' %}?pk={{ log.pk }}&next={{ host_user_bind_pk }}">工单</a>
                                            </td>
                                        {% endif %}

                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>

                </div>
            </div>

        </div>
    </div>
{% endblock %}

{% block in_js %}
    <script>
        function set_tables() {
            $('#users_activity_log_show').DataTable({
                "paging": true, <!-- 允许分页 -->
                "lengthChange": true, <!-- 允许改变每页显示的行数 -->
                "searching": true, <!-- 允许内容搜索 -->
                "ordering": true, <!-- 允许排序 -->
                "info": true, <!-- 显示信息 -->
                "autoWidth": true
            });
        }

        set_tables()
    </script>

{% endblock %}
activity_logs.html
{% extends "arya/layout.html" %}
{% block content %}
    <a class='btn btn-primary btn-sm' href="{% url 'users_activity_log_url'%}?pk={{ host_user_bind_pk}}">返回</a>
    <div class="table-responsive">
        <div id="page-content">
            <div class="panel col-lg-9">
                <div class="panel-heading">
                    <h3 class="panel-title">运维日志</h3>
                </div>
                {% for log in AuditLogs %}
                    <h3>{{ log.date }}</h3>
                    <pre style="background-color: black;color: white">
                     {{ log.cmd }}
                    </pre>
                {% endfor %}
            </div>

        </div>
    </div>



{% endblock %}
operation_logs.html

参考

批量任务执行

Django支持WebSocker协议

Paramiko实现shell交互

原文地址:https://www.cnblogs.com/sss4/p/11016699.html