一、实际生产的架构图

主机管理+堡垒机系统开发:前端批量命令结果(十二)

1、生产环境为什么要这样干

  1. 解耦
  2. 异步

 2、常用的queue软件

  1. redis, rabbitmq
  2. github ,
  3. restful API
  4. celery

二、我们今天如何实现?

主机管理+堡垒机系统开发:前端批量命令结果(十二)

1、实现思路

问题:views和web之间已返回,线程这里就断开了,那是因为你用django又启了一个线程

怎样才启动一个独立的进程,和django是没有关系,只不过是用django启动的,由操作系统来管理

三、目录结构

主机管理+堡垒机系统开发:前端批量命令结果(十二)

四、代码实现

1、backend

1、main

import subprocess
from web import models
from django.contrib.auth import authenticate
import random,string,uuid


class HostManager(object):
    """用户登陆堡垒机后的交互程序"""
    def __init__(self):
        self.user = None
    def get_session_id(self,bind_host_obj,tag):
        '''apply  session id'''
        session_obj = models.Session(user_id = self.user.id,bind_host=bind_host_obj,tag=tag)

        session_obj.save()
        return session_obj
    
    def interactive(self):
        """交互脚本"""
        print("----run---------")

        count = 0
        while count <3:
            username = input("Username:").strip()
            password = input("Password:").strip()
            user = authenticate(username=username,password=password)
            if user:
                print("Welcome %s".center(50,'-') % user.name )
                self.user = user
                break
            else:
                print("Wrong username or password!")

            count += 1

        else:
            exit("Too many attempts, bye.")

        if self.user: #验证成功
            while True:
                for index,host_group  in enumerate(self.user.host_groups.all()): #select_related()
                    print("%s.\t%s[%s]" %(index,host_group.name, host_group.bind_hosts.count()))
                print("z.\t未分组主机[%s]" %(self.user.bind_hosts.count()))


                choice = input("%s>>:"% self.user.name).strip()
                if len(choice) == 0:continue
                selected_host_group = None

                if choice.isdigit():
                    choice = int(choice)
                    if choice >=0 and choice <= index: #合法选项
                        selected_host_group = self.user.host_groups.all()[choice]
                elif choice == 'z':
                    selected_host_group = self.user

                if selected_host_group:
                    print("selected host group", selected_host_group)
                    while True:
                        for index, bind_host in enumerate(selected_host_group.bind_hosts.all()):
                            print("%s.\t%s" % (index, bind_host))
                        choice = choice = input("%s>>>:" % self.user.name).strip()
                        if choice.isdigit():
                            choice = int(choice)
                            if choice >= 0 and choice <= index:  # 合法选项
                                print("going to logon ....", selected_host_group.bind_hosts.all()[choice])
                                bind_host = selected_host_group.bind_hosts.all()[choice]
                                ssh_tag  = uuid.uuid4()
                                session_obj =  self.get_session_id(bind_host,ssh_tag)

                                monitor_script = subprocess.Popen("sh /opt/CrazyEye/backend/session_tracker.sh %s %s" % (ssh_tag,session_obj.id),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                                
                                #print(monitor_script.stderr.read()) 

                                subprocess.run('sshpass -p %s ssh %s@%s -E %s -o  StrictHostKeyChecking=no' %(bind_host.remote_user.password,
                                                                           bind_host.remote_user.username,
                                                                           bind_host.host.ip_addr ,ssh_tag ), shell=True)
                                

                        elif choice == 'b':
                            break

2、task_manager

import json,os ,subprocess
from django import conf
from web import models

class MultiTaskManger(object):
    """负责解析并触发批量任务"""

    def __init__(self,request):
        self.request = request

        self.call_task()

    def task_parser(self):
        """解析任务"""
        self.task_data = json.loads(self.request.POST.get("task_data"))

    def call_task(self):

        self.task_parser()

        if self.task_data['task_type'] == 0:#cmd
            self.cmd_task()

        elif self.task_data['task_type'] == 1:#file transfer
            self.file_transfer_task()


    def cmd_task(self):
        """
        1.生产任务id
        2.触发任务
        3.返回任务id
        :return:
        """

        task_obj = models.Task.objects.create(user=self.request.user,
                                              task_type=self.task_data['task_type'],
                                              content = self.task_data["cmd"])

        sub_task_objs = []

        for host_id in self.task_data['selected_host_ids']       :
            sub_task_objs.append(models.TaskLogDetail(task=task_obj,bind_host_id=host_id,result='init...',status=2))


        models.TaskLogDetail.objects.bulk_create(sub_task_objs)

        task_script_obj = subprocess.Popen("python %s %s" %(conf.settings.MULTITASK_SCRIPT,task_obj.id),
                                           shell=True,stdout=subprocess.PIPE)



        self.task = task_obj





    def file_transfer_task(self):
        """
         1.生产任务记录
         2.触发任务
         3. 返回任务id
        :return:
        """


        task_obj = models.Task.objects.create(user=self.request.user,
                                              task_type=self.task_data['task_type'],
                                              content=json.dumps(self.task_data))

        sub_task_objs = []


        for host_id in self.task_data['selected_host_ids']:
            sub_task_objs.append(models.TaskLogDetail(task=task_obj, bind_host_id=host_id, result='init...', status=2))

        models.TaskLogDetail.objects.bulk_create(sub_task_objs)


        task_script_obj = subprocess.Popen("python %s %s" % (conf.settings.MULTITASK_SCRIPT, task_obj.id),
                                           shell=True, stdout=subprocess.PIPE)

        self.task = task_obj

3、task_runner  

import sys ,os
import time,json
from concurrent.futures  import ThreadPoolExecutor

import paramiko

def  ssh_cmd(task_log_obj):
    host = task_log_obj.bind_host.host
    user_obj = task_log_obj.bind_host.remote_user

    try:

        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(host.ip_addr, host.port, user_obj.username, user_obj.password,timeout=10)

        stdin, stdout, stderr = ssh.exec_command(task_log_obj.task.content)

        stdout_res = stdout.read()
        stderr_res = stderr.read()

        result = stdout_res + stderr_res
        print(result)
        task_log_obj.result = result

        task_log_obj.status = 0
        ssh.close()

    except Exception as e :
        task_log_obj.result = e
        task_log_obj.status = 1

    task_log_obj.save()

def file_transfer(task_log_obj):
    host = task_log_obj.bind_host.host
    user_obj = task_log_obj.bind_host.remote_user
    try:

        t = paramiko.Transport((host.ip_addr, host.port))

        t.connect(username=user_obj.username, password=user_obj.password)

        sftp = paramiko.SFTPClient.from_transport(t)

        task_data = json.loads(task_log_obj.task.content)


        if task_data['file_transfer_type'] == 'send':
            sftp.put(task_data['local_file_path'],task_data['remote_file_path'])
            task_log_obj.result = "send local file [%s] to remote [%s] succeeded!" %(task_data['local_file_path'],
                                                                                     task_data['remote_file_path'])

        else: #get

            local_file_path = "%s/%s" %(django.conf.settings.DOWNLOAD_DIR,task_log_obj.task.id)
            if not os.path.isdir(local_file_path):
                os.mkdir(local_file_path)
            file_name = task_data['remote_file_path'].split('/')[-1]
            sftp.get(task_data['remote_file_path'], "%s/%s.%s" %(local_file_path,host.ip_addr,file_name))
            task_log_obj.result = "get remote file [%s] succeeded" %(task_data['remote_file_path'])

        t.close()
        task_log_obj.status = 0

    except Exception as e:
        task_log_obj.result = e
        task_log_obj.status = 1
    task_log_obj.save()


if __name__ == '__main__':

    base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(base_dir)
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CrazyEye.settings")
    import django
    django.setup()
    from django import conf

    from web import models


    if len(sys.argv) ==  1:
        exit("error:must provide task_id!")
    task_id = sys.argv[1]

    task_obj = models.Task.objects.get(id=task_id)


    #1. 生产多线程
    pool = ThreadPoolExecutor(10)


    if task_obj.task_type == 0:#cmd
        thread_func = ssh_cmd
    else: #file_transfer
        thread_func = file_transfer

    for task_log_detail_obj in task_obj.tasklogdetail_set.all():
        pool.submit(thread_func,task_log_detail_obj)

        #ssh_cmd(task_log_detail_obj)

    pool.shutdown(wait=True)

2、CrazyEye

1、settings

  1 import os
  2 
  3 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
  4 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  5 
  6 
  7 # Quick-start development settings - unsuitable for production
  8 # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
  9 
 10 # SECURITY WARNING: keep the secret key used in production secret!
 11 SECRET_KEY = 'c+lq-s!5j1($4zj+_3icw1xwr)yt#%x%&um#!e!b*-*5x(0&3a'
 12 
 13 # SECURITY WARNING: don't run with debug turned on in production!
 14 DEBUG = True
 15 
 16 ALLOWED_HOSTS = ["*"]
 17 
 18 
 19 # Application definition
 20 
 21 INSTALLED_APPS = [
 22     'django.contrib.admin',
 23     'django.contrib.auth',
 24     'django.contrib.contenttypes',
 25     'django.contrib.sessions',
 26     'django.contrib.messages',
 27     'django.contrib.staticfiles',
 28     'web',
 29 ]
 30 
 31 MIDDLEWARE = [
 32     'django.middleware.security.SecurityMiddleware',
 33     'django.contrib.sessions.middleware.SessionMiddleware',
 34     'django.middleware.common.CommonMiddleware',
 35     'django.middleware.csrf.CsrfViewMiddleware',
 36     'django.contrib.auth.middleware.AuthenticationMiddleware',
 37     'django.contrib.messages.middleware.MessageMiddleware',
 38     'django.middleware.clickjacking.XFrameOptionsMiddleware',
 39 ]
 40 
 41 ROOT_URLCONF = 'CrazyEye.urls'
 42 
 43 TEMPLATES = [
 44     {
 45         'BACKEND': 'django.template.backends.django.DjangoTemplates',
 46         'DIRS': [os.path.join(BASE_DIR, 'templates')]
 47         ,
 48         'APP_DIRS': True,
 49         'OPTIONS': {
 50             'context_processors': [
 51                 'django.template.context_processors.debug',
 52                 'django.template.context_processors.request',
 53                 'django.contrib.auth.context_processors.auth',
 54                 'django.contrib.messages.context_processors.messages',
 55             ],
 56         },
 57     },
 58 ]
 59 
 60 WSGI_APPLICATION = 'CrazyEye.wsgi.application'
 61 
 62 
 63 # Database
 64 # https://docs.djangoproject.com/en/1.10/ref/settings/#databases
 65 
 66 DATABASES = {
 67     'default': {
 68         'ENGINE': 'django.db.backends.sqlite3',
 69         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
 70     }
 71 }
 72 
 73 
 74 # Password validation
 75 # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
 76 
 77 AUTH_PASSWORD_VALIDATORS = [
 78     {
 79         'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
 80     },
 81     {
 82         'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
 83     },
 84     {
 85         'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
 86     },
 87     {
 88         'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
 89     },
 90 ]
 91 
 92 
 93 # Internationalization
 94 # https://docs.djangoproject.com/en/1.10/topics/i18n/
 95 
 96 LANGUAGE_CODE = 'en-us'
 97 
 98 TIME_ZONE = 'UTC'
 99 
100 USE_I18N = True
101 
102 USE_L10N = True
103 
104 USE_TZ = True
105 
106 
107 # Static files (CSS, JavaScript, Images)
108 # https://docs.djangoproject.com/en/1.10/howto/static-files/
109 
110 STATIC_URL = '/static/'
111 
112 STATICFILES_DIRS = (
113     os.path.join(BASE_DIR,'statics'),
114 )
115 
116 
117 AUTH_USER_MODEL = 'web.UserProfile'
118 
119 AUDIT_LOG_DIR = os.path.join(BASE_DIR,'log')
120 MULTITASK_SCRIPT= os.path.join(BASE_DIR,'backend/task_runner.py')
121 
122 DOWNLOAD_DIR = os.path.join(BASE_DIR,'downloads')
123 
124 
125 LOGIN_URL = "/login/"
settings

相关文章:

  • 2021-08-26
  • 2021-07-16
  • 2021-11-23
  • 2021-12-17
  • 2021-08-16
  • 2021-12-23
  • 2021-07-20
  • 2021-12-27
猜你喜欢
  • 2021-08-02
  • 2021-06-20
  • 2021-11-19
  • 2021-11-13
  • 2021-11-19
  • 2021-09-15
  • 2021-10-30
相关资源
相似解决方案