• python---堡垒机开发


     一:堡垒机需求分析

    注意:

    虽然我们在中间使用防火墙服务器对流量进行拦截和转发也可以起到过滤作用,但是我们无法去获取到完整,正确的操作记录。因为无论是客户端还是服务器端(管理员可能会去修改记录,而且可能会出现一个账号多人用,无法知道是谁操作了这台服务器)我们都无法完全控制。所以,我们可以使用中间件,替客户去执行命令,并且记录操作记录到堡垒机的数据库中

    history        可以查看操作记录
    history -c     清空记录

    注意:

    由于所有密码都存在堡垒机中,所以我们要保证其环境的安全性,,最好还有一个备份堡垒机

    补充:远程登录服务器的方法

    (1)账号+密码

    (2)A将公钥放在对方B服务器,A就可以直接登录B服务器

    公钥登录方法测试:

    1.在本机生成密匙,公钥(后缀.pub)和私钥

    2.将公钥远程拷贝或发生到对方服务器

     

     

    3.要想公钥在对方服务器生效,需要将发送过来的公钥放在用户家目录下的.ssh隐藏目录,若是不存在该目录,需要先生成该目录,然后再加文件拷贝进入

    4.将公钥在对方服务器中放入用户的家目录下的.ssh目录,并修改名为authorized_keys,才能生效

    5.测试远程ssh登录

     二:数据表结构设计

    from django.db import models
    from django.contrib.auth.models import (
        BaseUserManager, AbstractBaseUser,PermissionsMixin
    )
    #主机表:含有主机名,ip地址,端口,外联IDC机房
    class Host(models.Model):
        name = models.CharField(max_length=64,unique=True)
        ip_addr = models.GenericIPAddressField(unique=True)
        port = models.SmallIntegerField(default=22)
        idc = models.ForeignKey("IDC")
    
        def __str__(self):
            return self.name
    #用户表,用于登录上面主机,若是只有用户密码,那么是一对多,但是包含公钥登录,所以结构变为多对多,第三张表我们需要自己去创建
    class
    RemoteUser(models.Model): '''远程登录用户:1.私钥,秘钥,2.用户密码''' 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(max_length=64,blank=True,null=True) class Meta: unique_together = ("auth_type","username","password") def __str__(self): return "%s:%s"%(self.username,self.password)
    #主机对用户的多对多,第三张表,用于和堡垒机的账号表多对多关联。也会和主机组多对多关联
    class
    HostToRemoteUser(models.Model): host = models.ForeignKey("Host") remote_user = models.ForeignKey("RemoteUser") class Meta: unique_together = ("host","remote_user") def __str__(self): return "%s %s"%(self.host,self.remote_user)
    #主机组,类似于角色分组。对各个主机进行分组管理
    class
    HostGroup(models.Model): """存储主机组""" name = models.CharField(max_length=64,unique=True) #hosts = models.ManyToManyField("Host") host_to_remote_users = models.ManyToManyField("HostToRemoteUser") def __str__(self): return self.name
    #堡垒机的账号管理,包括了权限管理
    class UserProfileManager(BaseUserManager): def create_user(self, email, name, password=None): """ Creates and saves a User with the given email, date of birth and password. """ if not email: raise ValueError('Users must have an email address') user = self.model( email=self.normalize_email(email), name=name, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, name, password): """ Creates and saves a superuser with the given email, date of birth and password. """ user = self.create_user( email, password=password, name=name, ) user.is_superuser = True user.save(using=self._db) return user #账号管理,关联主机分为单个主机用户表HostToRemoteUser(主要是对某些用户的分配主机只有一台或者过少,不需要分配主机用户组),和主机用户组HostGroup class UserProfile(AbstractBaseUser,PermissionsMixin): """堡垒机账号""" email = models.EmailField( verbose_name='email address', max_length=255, unique=True, ) name = models.CharField(max_length=64, verbose_name="姓名") is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=True) objects = UserProfileManager() host_to_remote_users = models.ManyToManyField("HostToRemoteUser",blank=True,null=True) host_groups = models.ManyToManyField("HostGroup",blank=True,null=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['name'] def get_full_name(self): # The user is identified by their email address return self.email def get_short_name(self): # The user is identified by their email address return self.email def __str__(self): # __unicode__ on Python 2 return self.email

     完善操作记录表

    class AuditLog(models.Model):
        '''日志记录'''
        user = models.ForeignKey("UserProfile",verbose_name="堡垒机账号",blank=True,null=True)
        host_to_remote_user = models.ForeignKey("HostToRemoteUser",verbose_name="主机账号",blank=True,null=True)
        log_type_choices = (
            (0,"login"),
            (1,"cmd"),
            (2,"logout")
        )
        log_type = models.SmallIntegerField(choices=log_type_choices,default=0)
        content = models.CharField(max_length=255,blank=True,null=True)
        datetime = models.DateTimeField(auto_now_add=True,blank=True,null=True)
    
        def __str__(self):
            return "%s %s %s"%(self.user,self.host_to_remote_user,self.content)

     三:业务调用

    (1)backend_manage.py:配置Django环境,便于使用

    # coding:utf8
    # __author:  Administrator
    # date:      2018/6/9 0009
    # /usr/bin/env python
    import sys,os
    
    if __name__ == "__main__":
        #Django环境配置
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crazyeye.settings")
        import django
        django.setup()
    
        from backend import main  #注意项目中的模块导入,需要在Django环境配置之后,不然不会调用到Django环境而报错
        interactive_obj = main.ArgvHandler(sys.argv)

    进入backend目录

    (2)main.py:对用户参数进行解析

    # coding:utf8
    # __author:  Administrator
    # date:      2018/6/9 0009
    # /usr/bin/env python
    from backend import ssh_interactive
    
    class ArgvHandler(object):
        def __init__(self,sys_argv):
            self.sys_argv = sys_argv
            self.call()
    
        def help_msg(self,error=''):
            msg = '''
            %s
            run     启动用户交互程序
            '''
            exit(msg%error)
    
        def call(self):
            '''根据用户参数,调用对应方法'''
            if len(self.sys_argv) == 1:
                self.help_msg()
            if hasattr(self,self.sys_argv[1]):
                getattr(self,self.sys_argv[1])(self.sys_argv[2:])
            else:
                self.help_msg(error="调用方法[%s]不存在"%self.sys_argv[1])
    
        def run(self,*args,**kwargs):
            ssh_interactive.SshHandler(self)  #开始交互

    (3)ssh_interactive.py:启动堡垒机交互脚本

    # coding:utf8
    # __author:  Administrator
    # date:      2018/6/9 0009
    # /usr/bin/env python
    from django.contrib.auth import authenticate
    from backend import paramiko_ssh
    from repository import models
    import getpass
    
    
    class SshHandler(object):
        '''
        启动堡垒机交互脚本
        '''
        def __init__(self,argv_handler_instance):
            self.argv_handler_instance = argv_handler_instance
            self.interactive()
    
        def auth(self):
            '''登录堡垒机账号'''
            count = 0
            while count < 3:
                username = input("堡垒机账号>>>:").strip()
                password = getpass.getpass("Password>>>:")
                user = authenticate(username=username,password=password)
                if user:
                    self.user = user
                    return True
                count += 1
            return False
    
        def show_host_group(self):
            '''显示所有可以操作的主机组和未分组'''
            msg = '''
            HostGroup_List:
            %s
            '''
            Hgroup_list = self.user.host_groups.all()
            g_list = []
            for index,group in enumerate(Hgroup_list):
                g_list.append("[%s]	%s(%s台)"%(index,group.name,group.host_to_remote_users.count()))
    
            g_list.append('[z]	未分组主机(%s台)'%(self.user.host_to_remote_users.count()))
    
            while True:
                print(msg % ('
    	'.join(g_list)))
                choice = input("请选择主机组>>>:").strip()
                if choice.isdigit():
                    choice = int(choice)
                    try:
                        selected_group = Hgroup_list[choice]
                    except IndexError:
                        continue
                    self.show_host_list(selected_group)
                elif choice == "z":
                    self.show_host_list(None,False)
                elif choice == "exit":
                    exit(0)
    
        def show_host_list(self,selected_group,group_type=True):
            '''显示主机组下面的主机'''
            if group_type:
                H2R_ulist = selected_group.host_to_remote_users.all()
            else:
                H2R_ulist = self.user.host_to_remote_users.all()
    
            msg = '''
            HostToRemoteUser_List:
            %s
            '''
            h_list = []
            for index,h2r in enumerate(H2R_ulist):
                h_list.append("[%s]	%s"%(index,h2r))
            while True:
                print(msg % ('
    	'.join(h_list)))
                choice = input("请选择操作主机>>>:").strip()
                if choice.isdigit():
                    choice = int(choice)
                    try:
                        selected_host = H2R_ulist[choice]
                    except IndexError:
                        continue
                    return self.link(selected_host)
                elif choice == "b":
                    return True
                elif choice == "exit":
                    exit(0)
    
        def link(self,selected_host):
            '''连接主机'''
            self.models = models
            paramiko_ssh.ssh_connect(self, selected_host)
    
        def interactive(self):
            '''启动交互脚本'''
            if self.auth():
                print("登录成功")
                self.show_host_group()
            else:
                print("登录失败")

    (4)paramiko_ssh.py:开始连接远程主机,并将登陆,退出信息记录到数据库中

    #!/usr/bin/env python
    
    import base64
    from binascii import hexlify
    import getpass
    import os
    import select
    import socket
    import sys
    import time
    import traceback
    from paramiko.py3compat import input
    
    import paramiko
    
    try:
        import interactive
    except ImportError:
        from . import interactive
    
    
    def agent_auth(transport, username):
        """
        Attempt to authenticate to the given transport using any of the private
        keys available from an SSH agent.
        """
    
        agent = paramiko.Agent()
        agent_keys = agent.get_keys()
        if len(agent_keys) == 0:
            return
    
        for key in agent_keys:
            print("Trying ssh-agent key %s" % hexlify(key.get_fingerprint()))
            try:
                transport.auth_publickey(username, key)
                print("... success!")
                return
            except paramiko.SSHException:
                print("... nope.")
    
    
    def manual_auth(username, hostname,password,t):
        default_auth = "p"
        # auth = input(
        #     "Auth by (p)assword, (r)sa key, or (d)ss key? [%s] " % default_auth
        # )
        auth = default_auth
    
        if len(auth) == 0:
            auth = default_auth
    
        if auth == "r":
            default_path = os.path.join(os.environ["HOME"], ".ssh", "id_rsa")
            path = input("RSA key [%s]: " % default_path)
            if len(path) == 0:
                path = default_path
            try:
                key = paramiko.RSAKey.from_private_key_file(path)
            except paramiko.PasswordRequiredException:
                password = getpass.getpass("RSA key password: ")
                key = paramiko.RSAKey.from_private_key_file(path, password)
            t.auth_publickey(username, key)
        elif auth == "d":
            default_path = os.path.join(os.environ["HOME"], ".ssh", "id_dsa")
            path = input("DSS key [%s]: " % default_path)
            if len(path) == 0:
                path = default_path
            try:
                key = paramiko.DSSKey.from_private_key_file(path)
            except paramiko.PasswordRequiredException:
                password = getpass.getpass("DSS key password: ")
                key = paramiko.DSSKey.from_private_key_file(path, password)
            t.auth_publickey(username, key)
        else:
            #pw = getpass.getpass("Password for %s@%s: " % (username, hostname))
            '''动态密码'''
            t.auth_password(username, password)
    
    
    def ssh_connect(ssh_interactive_handler,selected_host):  #获取ssh_interactive_handler句柄,含有models等信息,selected_host是用户选择的主机账号
        '''动态获取hostname,port,name,password,ssh_key'''
        hostname = selected_host.host.ip_addr
        port = selected_host.host.port
        username = selected_host.remote_user.username
        password = selected_host.remote_user.password
    
        # now connect
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((hostname, port))
        except Exception as e:
            print("*** Connect failed: " + str(e))
            traceback.print_exc()
            sys.exit(1)
    
        try:
            t = paramiko.Transport(sock)
            try:
                t.start_client()
            except paramiko.SSHException:
                print("*** SSH negotiation failed.")
                sys.exit(1)
    
            try:
                keys = paramiko.util.load_host_keys(
                    os.path.expanduser("~/.ssh/known_hosts")
                )
            except IOError:
                try:
                    keys = paramiko.util.load_host_keys(
                        os.path.expanduser("~/ssh/known_hosts")
                    )
                except IOError:
                    print("*** Unable to open host keys file")
                    keys = {}
    
            # check server's host key -- this is important.
            key = t.get_remote_server_key()
            if hostname not in keys:
                print("*** WARNING: Unknown host key!")
            elif key.get_name() not in keys[hostname]:
                print("*** WARNING: Unknown host key!")
            elif keys[hostname][key.get_name()] != key:
                print("*** WARNING: Host key has changed!!!")
                sys.exit(1)
            else:
                print("*** Host key OK.")
    
            if not t.is_authenticated():
                manual_auth(username, hostname,password,t)
            if not t.is_authenticated():
                print("*** Authentication failed. :(")
                t.close()
                sys.exit(1)
    
            chan = t.open_session()
            chan.get_pty()
            chan.invoke_shell()
            chan.ssh_handler = ssh_interactive_handler
            chan.selected_host = selected_host
            print("*** Here we go!
    ")
         #登陆 ssh_interactive_handler.models.AuditLog.objects.create( user
    =ssh_interactive_handler.user, host_to_remote_user=selected_host, log_type=0, content="*** Login ***" )
    interactive.interactive_shell(chan)  #启动会话 chan.close() t.close()
         #退出 ssh_interactive_handler.models.AuditLog.objects.create( user
    =chan.ssh_handler.user, host_to_remote_user=chan.selected_host, log_type=2, content="*** Logout ***" ) except Exception as e: print("*** Caught exception: " + str(e.__class__) + ": " + str(e)) traceback.print_exc() try: t.close() except: pass sys.exit(1)

    (5)interactive.py:启动会话,和主机进行交互记录命令到数据库

    # Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
    #
    # This file is part of paramiko.
    #
    # Paramiko is free software; you can redistribute it and/or modify it under the
    # terms of the GNU Lesser General Public License as published by the Free
    # Software Foundation; either version 2.1 of the License, or (at your option)
    # any later version.
    #
    # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
    # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
    # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    # details.
    #
    # You should have received a copy of the GNU Lesser General Public License
    # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
    # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
    
    
    import socket
    import sys
    from paramiko.py3compat import u
    import time
    
    # windows does not have termios...
    try:
        import termios
        import tty
    
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan):
        if has_termios:
            posix_shell(chan)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan):  #linux使用select框架循环
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
    
            cmd = []
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write("
    *** EOF
    ")
                            break
                        sys.stdout.write(x)
                        sys.stdout.flush()
                    except socket.timeout:
                        pass
                if sys.stdin in r:
                    x = sys.stdin.read(1)
                    if x == "
    ":
                        print('input>',"".join(cmd))
                        chan.ssh_handler.models.AuditLog.objects.create(
                            user=chan.ssh_handler.user,
                            host_to_remote_user=chan.selected_host,
                            log_type=1,
                            content=''.join(cmd)
                        )
                        cmd.clear()
                    if len(x) == 0:
                        break
                    cmd.append(x)
                    chan.send(x)
        except (EOFError,OSError):
            pass
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    # thanks to Mike Looijmans for this code
    def windows_shell(chan):  #windows使用多线程和socket方式交互
        import threading
    
        sys.stdout.write(
            "Line-buffered terminal emulation. Press F6 or ^Z to send EOF.
    
    "
        )
    
        def writeall(sock):
            while True:
                data = sock.recv(256)
                if not data:
                    sys.stdout.write("
    *** EOF ***
    
    ")
                    sys.stdout.flush()
                    break
                sys.stdout.write(data.decode())
                sys.stdout.flush()
    
        writer = threading.Thread(target=writeall, args=(chan,))
        writer.start()
    
        try:
            cmd = []
            while True:
                d = sys.stdin.read(1)
                if d == "
    ":
                    chan.ssh_handler.models.AuditLog.objects.create(
                        user=chan.ssh_handler.user,
                        host_to_remote_user=chan.selected_host,
                        log_type=1,
                        content=''.join(cmd)
                    )
                    cmd.clear()
                cmd.append(d)
                if not d:
                    break
                chan.send(d)
        except (EOFError,OSError):
            pass

    四:linux服务器上测试

    (1)为项目创建一个公共用户,为堡垒机用户提供。密码设置简单

    useradd crazyeye
    passwd crazyeye
    输入密码:123456

    (2)正常启用项目

    python backend_manage.py run

    (3)需要用户登录账号后立即执行程序,减少用户权限

    去操作用户家目录下的.bashrc文件,进行配置

    # .bashrc
    
    # Source global definitions
    if [ -f /etc/bashrc ]; then
            . /etc/bashrc
    fi
    
    # User specific aliases and functions
    python3 /home/crazyeye/crazyeye/backend_manage.py run  #用户登录后自动执行
    exit  #防止用户出现不可预期的错误导致上面的程序退出,而去操作账号下的其他数据,我们需要让上面程序结束后,账号退出即可

     五.实现web页面对堡垒机进行操作

    linux下安装shellinabox实现web登录服务器

    {% extends "index.html" %}
    {% block right-content-container %}
               <!--Page Title-->
                    <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
                    <div id="page-title">
                        <h1 class="page-header text-overflow">Web Ssh</h1>
    
                        <!--Searchbox-->
                        <div class="searchbox">
                            <div class="input-group custom-search-form">
                                <input type="text" class="form-control" placeholder="Search..">
                                <span class="input-group-btn">
                                    <button class="text-muted" type="button"><i class="demo-pli-magnifi-glass"></i></button>
                                </span>
                            </div>
                        </div>
                    </div>
                    <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
                    <!--End page title-->
    
                    <!--Page content-->
                    <!--===================================================-->
                    <div id="page-content">
                        <div class="row">
                            <div class="col-lg-12">
                                 <iframe src="http://192.168.218.129:4200/" width="100%" style="padding-bottom:100%;background-color:white;"></iframe>
                            </div>
                        </div>
                    </div>
                    <!--===================================================-->
                    <!--End page content-->
    {% endblock %}
    前端代码:使用iframe

    六.实现批量命令和文件功能

    (0)添加数据表和业务流程图

    1..数据表增加

    class Task(models.Model):
        '''批量任务'''
        task_type_choice = (
            ('cmd',"批量命令"),
            ('file_transfer','文件传输'),
        )
    
        task_type = models.CharField(choices=task_type_choice,max_length=32)
        user = models.ForeignKey("UserProfile")
        content = models.CharField(max_length=255)
    
        date = models.DateTimeField(auto_now_add=True)
        def __str__(self):
            return "%s %s"%(self.task_type,self.content)
    
    class TaskLogDetail(models.Model):
        '''存储每台主机执行结果'''
        task = models.ForeignKey("Task")
        host_to_remote_user = models.ForeignKey("HostToRemoteUser")
        result = models.TextField(verbose_name="任务结果")
    
        status_choices = ((0, 'initialized'), (1, 'sucess'), (2, 'failed'), (3, 'timeout'))
        status = models.SmallIntegerField(choices=status_choices)
    
        date = models.DateTimeField(auto_now_add=True)

    2.流程图

    (1)批量命令展示

    (2)批量文件操作展示

     (3)全部代码展示

    1.公共组件

    <div class="col-lg-3">
                                <div class="panel">
                                    <div class="panel-heading">
                                        <div class="panel-control">
                                            <a title="" data-html="true" data-container="body" data-original-title="<p class='h4 text-semibold'>Information</p><p style='150px'>This is an information bubble to help the user.</p>" href="#" class="demo-psi-information icon-lg icon-fw unselectable text-info add-tooltip"></a>
                                        </div>
                                        <h3 class="panel-title">主机列表</h3>
                                    </div>
                                    <div class="bord-btm">
                                        <div class="list-group bord-no">
                                            {% for hostGroup in request.user.host_groups.all %}
                                            <a class="list-group-item" onclick="toggel_host(this);" href="#">{{ hostGroup.name }}<span class="badge badge-success">{{ hostGroup.host_to_remote_users.count }}</span></a>
                                            <ul class="list-group" style="margin-bottom: 0px;display: none;">
                                                {% for hostUser in hostGroup.host_to_remote_users.all %}
                                                <li class="list-group-item">
                                                    <input id="demo-form-checkbox-{{ hostUser.id }}" tag="host-selected" class="magic-checkbox" type="checkbox" value="{{ hostUser.id }}">
                                                    <label for="demo-form-checkbox-{{ hostUser.id }}">{{ hostUser.host }}@{{ hostUser.remote_user.username }}</label>
                                                </li>
                                                {% endfor %}
                                            </ul>
                                            {% endfor %}
                                            <a class="list-group-item" onclick="toggel_host(this);" href="#">未分组主机<span class="badge badge-success">{{ request.user.host_to_remote_users.count }}</span></a>
                                            <ul class="list-group" style="margin-bottom: 0px;display: none;">
                                                {% for hostUser in request.user.host_to_remote_users.all %}
                                                <li class="list-group-item">
                                                    <input id="demo-form-checkbox-{{ hostUser.id }}" tag="host-selected" class="magic-checkbox" type="checkbox" value="{{ hostUser.id }}">
                                                    <label for="demo-form-checkbox-{{ hostUser.id }}">{{ hostUser.host }}@{{ hostUser.remote_user.username }}</label>
                                                </li>
                                                {% endfor %}
                                            </ul>
                                        </div>
                                    </div>
    
                                </div>
    
                            </div>
    host_list_compentent.html主机列表
    <script>
        function toggel_host(ths){
            $(ths).next().toggle();
        }
    
        function ChangeFileType(ths){
            if ($(ths).val() == "send"){
                $("#local_file").show();
            }else{
                $("#local_file").hide();
            }
        }
    
        function ShowError(title,content,type) {
            $(".modal-toggle").find(".modal-title").html(title);
            $(".modal-toggle").find(".bootbox-body").html(content);
            $(".modal-toggle").find(type).show();
            $(".modal-toggle").find(".modal").show();
            $(".modal-toggle").show();
        }
    
        function submitData(ths,cmd_type){
            if ($(ths).hasClass('disabled')){
                return false;
            }
            var host_list = [];
            $("[tag='host-selected']:checked").each(function(){
                host_list.push($(this).val());
            })
    
            if (host_list.length == 0){
                ShowError("Error Before Submit","<h3>未选中主机</h1>",".btn-danger");
                return false;
            }
    
            var task_arguments = {};
    
            if (cmd_type == 'cmd'){
                var cmd = $("input[name='cmd']").val().trim()
                if(cmd.length == 0){
                    ShowError("Error Before Submit","<h3>请输入要执行的命令</h1>",".btn-danger");
                    return false;
                }
    
                task_arguments = {
                    'task_type' : 'cmd',
                    'cmd': cmd,
                }
            }else{
                task_arguments['task_type'] = 'file_transfer';
    
                if ($("#server_file_path").val().trim().length == 0){
                    ShowError("Error Before Submit","<h3>请输入服务端文件路径</h1>",".btn-danger");
                    return false;
                }
    
                if ($("#select_file_type").val() == "recv"){
                    task_arguments['transfer_type'] = "recv";
                    task_arguments['server_file_path'] = $("#server_file_path").val().trim();
                }else{
                    if ($("#local_file_path").val().trim().length == 0){
                        ShowError("Error Before Submit","<h3>请输入本地文件路径</h1>",".btn-danger");
                        return false;
                    }
                    task_arguments['transfer_type'] = "send";
                    task_arguments['server_file_path'] = $("#server_file_path").val().trim();
                    task_arguments['local_file_path'] = $("#local_file_path").val().trim();
                }
            }
    
            task_arguments['selected_hosts'] = host_list;
            $(ths).addClass('disabled');
    
            $.post(
                "{% url 'batch_task_mgr' %}",
                {'task_data':JSON.stringify(task_arguments),'csrfmiddlewaretoken':'{{ csrf_token  }}'},
                function(callback){
                    $("#task_result_container").empty();
                    $.each(callback['selected_hosts'],function(index,obj){
                        var ul_inner = '<li class="list-group-item" for="'+obj['id']+'"><span class="badge badge-primary">wait</span>'+obj['host_to_remote_user__host__ip_addr']+' '+obj['host_to_remote_user__host__name']+' '+obj['host_to_remote_user__remote_user__username']+'</li><pre>initilize...</pre>';
                        $("#task_result_container").append(ul_inner);
                    });
                    TimerFlag = setInterval(GetTask,2000,callback['task_id']);
                },
                'json'
            )
        }
    
        function GetTask(task_id){
            $.get(
                '{% url "get_task" %}',
                {'id':task_id},
                function(callback){
                    var All_get = true;
                    $.each(callback,function(index,obj){
                        $ele = $("#task_result_container li[for='"+obj['id']+"']");
                        $ele.next().text(obj['result']);
                        if(obj['status'] == 1){
                            $ele.children().first().removeClass("badge-primary").addClass("badge-success").text("finished");
                        }else if (obj['status'] == 2){
                             $ele.children().first().removeClass("badge-primary").addClass("badge-warning").text("warning");
                        }else{
                            All_get = false;
                        }
                    });
                    if (All_get){
                        $("#submit_btn").removeClass("disabled");
                        clearInterval(TimerFlag);
                    }
                },
                'json'
            );
        }
    
        $(function(){
            $(".modal-toggle").find(".btn-danger").click(function(){
                $(this).parents(".modal-toggle").hide();
            });
        })
    </script>
    multitask_js_compentent.html共用js
    <div class="col-lg-7">
        <div class="panel">
            <div class="panel-heading">
                <h3 class="panel-title">批量执行命令结果展示</h3>
            </div>
            <div class="panel-body">
                <ul class="list-group" id="task_result_container">
                </ul>
            </div>
        </div>
    </div>
    task_result_compentent.html任务结果展示

    2.前端展示:批量命令,批量文件

    {% extends "index.html" %}
    {% block right-content-container %}
               <!--Page Title-->
                    <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
                    <div id="page-title">
                        <h1 class="page-header text-overflow">Host Manage</h1>
    
                        <!--Searchbox-->
                        <div class="searchbox">
                            <div class="input-group custom-search-form">
                                <input type="text" class="form-control" placeholder="Search..">
                                <span class="input-group-btn">
                                    <button class="text-muted" type="button"><i class="demo-pli-magnifi-glass"></i></button>
                                </span>
                            </div>
                        </div>
                    </div>
                    <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
                    <!--End page title-->
    
                    <!--Page content-->
                    <!--===================================================-->
                    <div id="page-content">
                        <div class="row">
                            {% include "include/host_list_compentent.html" %}
                            <div class="col-lg-7">
                                <div class="panel">
                                    <div class="panel-heading">
                                        <h3 class="panel-title">批量执行命令</h3>
                                    </div>
                                    <div class="panel-body">
                                        <form class="form-inline">
                                            <div class="input-group mar-btm col-lg-12">
                                                <input type="text" name="cmd" placeholder="input your cmd...." class="form-control">
                                                <span class="input-group-btn" style="32px;">
                                                    <button class="btn btn-mint" id="submit_btn" onclick="submitData(this,'cmd');" type="button">执行命令</button>
                                                </span>
                                            </div>
                                        </form>
                                    </div>
                                </div>
                            </div>
                            {% include "include/task_result_compentent.html" %}
                        </div>
                    </div>
                    <!--===================================================-->
                    <!--End page content-->
    
                    <div class="modal-toggle" style="display: none;">
                        <div class="bootbox modal fade in" tabindex="-1" role="dialog" style="padding-right: 17px;">
                            <div class="modal-dialog">
                                <div class="modal-content">
                                    <div class="modal-header">
                                        <button type="button" class="close" data-dismiss="modal">
                                            <i class="pci-cross pci-circle"></i>
                                        </button>
                                        <h4 class="modal-title"></h4>
                                    </div>
                                    <div class="modal-body">
                                        <div class="bootbox-body"></div>
                                    </div>
                                    <div class="modal-footer">
                                        <button data-bb-handler="success" type="button" class="btn btn-success" style="display: none;">Success!</button>
                                        <button data-bb-handler="danger" type="button" class="btn btn-danger" style="display: none;">Danger!</button>
                                        <button data-bb-handler="main" type="button" class="btn btn-primary" style="display: none;">Confirm</button>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="modal-backdrop fade in"></div>
                    </div>
    
                    {% include "include/multitask_js_compentent.html" %}
    {% endblock %}
    host_mgr.html前端批量命令
    {% extends "index.html" %}
    {% block right-content-container %}
               <!--Page Title-->
                    <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
                    <div id="page-title">
                        <h1 class="page-header text-overflow">File Tranfer</h1>
    
                        <!--Searchbox-->
                        <div class="searchbox">
                            <div class="input-group custom-search-form">
                                <input type="text" class="form-control" placeholder="Search..">
                                <span class="input-group-btn">
                                    <button class="text-muted" type="button"><i class="demo-pli-magnifi-glass"></i></button>
                                </span>
                            </div>
                        </div>
                    </div>
                    <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
                    <!--End page title-->
    
                    <!--Page content-->
                    <!--===================================================-->
                    <div id="page-content">
                        <div class="row">
                            {% include "include/host_list_compentent.html" %}
                            <div class="col-lg-7">
                                <div class="panel">
                                    <div class="panel-heading">
                                        <h3 class="panel-title">批量操作文件</h3>
                                    </div>
                                    <div class="panel-body">
                                        <form class="form-horizontal">
                                              <div class="form-group">
                                                <label class="col-sm-2 control-label">文件操作:</label>
                                                <div class="col-sm-10">
                                                    <select class="form-control" onchange="ChangeFileType(this);" id="select_file_type">
                                                      <option value="recv">接收文件</option>
                                                      <option value="send">发送文件</option>
                                                    </select>
                                                </div>
                                              </div>
                                            <div class="form-group" id="server_file">
                                                <label class="col-sm-2 control-label">服务器文件路径:</label>
                                                <div class="col-sm-10">
                                                    <input type="text" class="form-control" id="server_file_path" placeholder="Service file path">
                                                </div>
                                            </div>
                                            <div class="form-group" style="display: none;" id="local_file" >
                                                <label class="col-sm-2 control-label">本地文件路径:</label>
                                                <div class="col-sm-10">
                                                    <input type="text" class="form-control" id="local_file_path" placeholder="Local file path">
                                                </div>
                                            </div>
                                            <div class="panel-footer text-right">
                                                <button class="btn btn-success" onclick="submitData(this,'file_transfer');" id="submit_btn"  type="button">Submit</button>
                                            </div>
                                        </form>
                                    </div>
                                </div>
                            </div>
                            {% include "include/task_result_compentent.html" %}
                        </div>
                    </div>
                    <!--===================================================-->
                    <!--End page content-->
    
                    <div class="modal-toggle" style="display: none;">
                        <div class="bootbox modal fade in" tabindex="-1" role="dialog" style="padding-right: 17px;">
                            <div class="modal-dialog">
                                <div class="modal-content">
                                    <div class="modal-header">
                                        <button type="button" class="close" data-dismiss="modal">
                                            <i class="pci-cross pci-circle"></i>
                                        </button>
                                        <h4 class="modal-title"></h4>
                                    </div>
                                    <div class="modal-body">
                                        <div class="bootbox-body"></div>
                                    </div>
                                    <div class="modal-footer">
                                        <button data-bb-handler="success" type="button" class="btn btn-success" style="display: none;">Success!</button>
                                        <button data-bb-handler="danger" type="button" class="btn btn-danger" style="display: none;">Danger!</button>
                                        <button data-bb-handler="main" type="button" class="btn btn-primary" style="display: none;">Confirm</button>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="modal-backdrop fade in"></div>
                    </div>
    
                    {% include "include/multitask_js_compentent.html" %}
    {% endblock %}
    file_transfer.html批量文件

    3.url和views视图文件

    from django.conf.urls import url
    from web import views
    
    
    urlpatterns = [
        url(r"dashbroad.html",views.dashbroad),
        url(r"login.html", views.acc_login),
        url(r"web_ssh.html", views.web_ssh,name="web_ssh"),
        url(r"host_mgr.html", views.host_mgr, name="host_mgr"),
        url(r"file_transfer.html", views.file_transfer, name="file_transfer"),
        url(r"batch_task_mgr.html", views.batch_task_mgr, name="batch_task_mgr"),
        url(r"batch_task_mgr.html", views.batch_task_mgr, name="batch_task_mgr"),
        url(r"get_task.html", views.get_task, name="get_task"),
    ]
    urls.py
    from django.shortcuts import render,redirect,HttpResponse
    from django.contrib.auth import authenticate,login,logout
    from django.contrib.auth.decorators import login_required
    from repository import models
    import json
    # Create your views here.
    
    @login_required
    def dashbroad(request):
    
        return render(request,"web/dashbroad.html")
    
    
    def acc_login(request):
        error_msg = ""
        if request.method == "POST":
            username = request.POST.get("username")
            password = request.POST.get("password")
            user = authenticate(username=username,password=password)
            if user:
                login(request,user)
                return redirect("/web/dashbroad.html")
            else:
                error_msg = "Wrong Username Or Password"
    
        return render(request,"login.html",{"error_msg":error_msg,})
    
    
    @login_required
    def web_ssh(request):
    
        return render(request,"web/web_ssh.html")
    
    
    @login_required
    def host_mgr(request):
    
        return render(request,"web/host_mgr.html")
    
    
    @login_required
    def file_transfer(request):
    
    
        return render(request,"web/file_transfer.html")
    
    
    def conv(date_obj):
            return date_obj.strftime("%Y-%m-%d %H:%M:%S")
    
    
    @login_required
    def batch_task_mgr(request):
        from backend.multitask import Multitask
    
        task_obj = Multitask(request)
    
        respone = {
            'task_id':task_obj.task_obj.id,
            'selected_hosts':list(task_obj.task_obj.tasklogdetail_set.all().values(
                'id',
                'host_to_remote_user__host__ip_addr',
                'host_to_remote_user__host__name',
                'host_to_remote_user__remote_user__username'
            ))
        }
    
        return HttpResponse(json.dumps(respone))
    
    
    @login_required
    def get_task(request):
        task_log_obj = models.TaskLogDetail.objects.filter(task_id=request.GET.get("id")).values("id","status","result",'date')
    
        log_data = json.dumps(list(task_log_obj),default=conv)
    
        return HttpResponse(log_data)
    views.py

     4.后台backend模块的多任务管理类

    # coding:utf8
    # __author:  Administrator
    # date:      2018/6/14 0014
    # /usr/bin/env python
    from repository import models
    import json,subprocess
    from django import conf
    
    class Multitask(object):
        def __init__(self,request):
            self.request = request
            self.run_task()
    
        def run_task(self):
            '''解析参数,调用方法'''
            self.task_data = json.loads(self.request.POST.get("task_data"))
            task_type = self.task_data.get("task_type")
            if hasattr(self,task_type):
                task_func = getattr(self,task_type)
                task_func()
            else:
                print("cannot find task ",task_type)
    
        def cmd(self):
            '''执行批量命令'''
            #先将任务添加到Task中
            task_obj = models.Task.objects.create(
                task_type = "cmd",
                user = self.request.user,
                content=self.task_data.get('cmd')
            )
            #向TaskLogDetail中批量添加数据
            task_log_list = []
            for host_remote_user_id in set(self.task_data.get("selected_hosts")):
                task_log_list.append(
                    models.TaskLogDetail(
                        task=task_obj,
                        host_to_remote_user_id=host_remote_user_id,
                        result="init...",
                        status=0
                    )
                )
    
    
            models.TaskLogDetail.objects.bulk_create(task_log_list)
    
            shell_cmd = "python %s/backend/task_runner.py %s"%(conf.settings.BASE_DIR,task_obj.id)
            cmd_process = subprocess.Popen(shell_cmd,shell=True)
    
            self.task_obj = task_obj
    
        def file_transfer(self):
            '''批量操作文件'''
            # 先将任务添加到Task中
            task_obj = models.Task.objects.create(
                task_type="file_transfer",
                user=self.request.user,
                content=json.dumps(self.task_data)
            )
            # 向TaskLogDetail中批量添加数据
            task_log_list = []
            for host_remote_user_id in set(self.task_data.get("selected_hosts")):
                task_log_list.append(
                    models.TaskLogDetail(
                        task=task_obj,
                        host_to_remote_user_id=host_remote_user_id,
                        result="init...",
                        status=0
                    )
                )
    
            models.TaskLogDetail.objects.bulk_create(task_log_list)
    
            shell_cmd = "python %s/backend/task_runner.py %s" % (conf.settings.BASE_DIR, task_obj.id)
            cmd_process = subprocess.Popen(shell_cmd, shell=True)
    
            self.task_obj = task_obj
    multitask.py

    5.脚本task_runner.py

    # coding:utf8
    # __author:  Administrator
    # date:      2018/6/14 0014
    # /usr/bin/env python
    import paramiko,os,sys,json
    from concurrent.futures import ThreadPoolExecutor
    
    def ssh_cmd(task_obj):
        host_to_user_obj = task_obj.host_to_remote_user
    
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
        try:
            ssh.connect(host_to_user_obj.host.ip_addr, host_to_user_obj.host.port, host_to_user_obj.remote_user.username, host_to_user_obj.remote_user.password,timeout=5)
            stdin, stdout, stderr = ssh.exec_command(task_obj.task.content)
            stdout_res = stdout.read()
            stderr_res = stderr.read()
    
            task_obj.result = stdout_res + stderr_res
    
            if stderr_res:
                task_obj.status = 2
            else:
                task_obj.status = 1
        except Exception as e:
            task_obj.status = 2
            task_obj.result = e
        finally:
            ssh.close()
            task_obj.save()
    
    
    def ssh_file(task_log_obj,task_obj):
        file_cmd = json.loads(task_obj.content)
        opration_res = ''
    
        try:
            t = paramiko.Transport(
                (task_log_obj.host_to_remote_user.host.ip_addr, task_log_obj.host_to_remote_user.host.port))
            t.connect(username=task_log_obj.host_to_remote_user.remote_user.username,
                      password=task_log_obj.host_to_remote_user.remote_user.password)
            sftp = paramiko.SFTPClient.from_transport(t)
            if file_cmd.get("transfer_type") == "recv":
                file_name = "%s-%s-%s"%(task_log_obj.host_to_remote_user.host.ip_addr,task_log_obj.host_to_remote_user.remote_user.username,os.path.basename(file_cmd.get("server_file_path")))
                local_path = os.path.join(conf.settings.DOWN_FILE_PATH,str(task_obj.id),file_name)
                if not os.path.exists(local_path):
                    try:
                        os.makedirs(os.path.dirname(local_path))
                    except Exception:
                        pass
                sftp.get(file_cmd.get("server_file_path"), local_path)
                t.close()
                opration_res = "file [%s] recv success! path [%s]"%(file_cmd.get("server_file_path"),local_path)
            else:
                file_path = file_cmd.get("local_file_path")
                if os.path.isdir(file_path):
                    file_path = os.path.join(file_path,os.path.basename(file_cmd.get("server_file_path")))
                sftp.put(file_path,file_cmd.get("server_file_path"))
                t.close()
                opration_res = "file [%s] send [%s] success!"%(file_path,file_cmd.get("server_file_path"))
    
            task_log_obj.result = opration_res
            task_log_obj.status = 1
        except Exception as e:
            print(e)
            task_log_obj.status = 2
            task_log_obj.result = e
        finally:
            task_log_obj.save()
    
    
    if __name__ == "__main__":
    
        BaseDir = os.path.dirname(os.path.dirname(os.path.abspath(os.path.abspath(__file__))))
        #将路径放入系统路径
        sys.path.append(BaseDir)
        # 加载Django环境
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crazyeye.settings")
        import django
        django.setup()
    
        from django import conf
    
        if len(sys.argv) == 1:
            exit("task id not provided!")
        else:
            task_id = sys.argv[1]
        from repository import models
    
        task_obj = models.Task.objects.get(id=task_id)
    
        pool = ThreadPoolExecutor(10)
    
        if task_obj.task_type == "cmd":
            for task_log_obj in task_obj.tasklogdetail_set.all():
                pool.submit(ssh_cmd,task_log_obj)
        else:
            for task_log_obj in task_obj.tasklogdetail_set.all():
                pool.submit(ssh_file,task_log_obj,task_obj)
    
        pool.shutdown(wait=True)
    task_runner.py
  • 相关阅读:
    Dubbo服务提供者Provider启动流程下(四)
    修改超链接的默认格式
    给 3Com 3C940 Gigabit LOM 安装Windows Server 2008 驱动
    Express版SQL定时备份BAK文件(SQL2005,SQL2008)
    DataGridView的行、列调整
    更新DNS缓存
    VB.Net类型转换——全角半角间转换(StrConv)
    VB.Net 获取或者转换时间不受系统时间格式影响
    Active Desktop
    VB.Net类型转换——全角半角间转换(StrConv)
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9157586.html
Copyright © 2020-2023  润新知