zoukankan      html  css  js  c++  java
  • 主机管理+堡垒机系统开发:前端批量命令结果(十二)

    一、实际生产的架构图

    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.	%s[%s]" %(index,host_group.name, host_group.bind_hosts.count()))
                    print("z.	未分组主机[%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.	%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

    2、urls

    from django.conf.urls import url
    from django.contrib import admin
    from web import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^$', views.dashboard),
        url(r'^user_audit/$', views.user_audit,name="user_audit"),
        url(r'^audit_log/(w+-w+-w+)/$', views.audit_log_date,name="audit_log_date"),
        url(r'^audit_log/(w+-w+-w+)/(d+)/$', views.audit_log_detail,name="audit_log_detail"),
        url(r'^webssh/$', views.webssh,name="webssh"),
        url(r'^multitask/cmd/$', views.multitask_cmd,name="multitask_cmd"),
        url(r'^multitask/file_transfer/$', views.multitask_file_transfer,name="multitask_file_transfer"),
        url(r'^multitask/$', views.multitask,name="multitask"),
        url(r'^multitask/result/$', views.multitask_result,name="task_result"),
        url(r'^login/$', views.acc_login),
        url(r'^logout/$', views.acc_logout,name="logout"),
    
    ]
    

    3、templates

    1、multitack_cmd.html

    {% extends 'index.html' %}
    
    
    {% block page-title %}主机管理|批量命令{% endblock %}
    
    {% block page-content %}
        {% csrf_token %}
    
    <div class="row">
    
        {% include 'multitask_host_list_component.html' %}
    
        <div class="col-lg-8">
            <div class="panel">
                <div class="panel-heading">
                    <h3 class="panel-title">命令操作</h3>
                </div>
                <div class="panel-body">
                    <textarea id="cmd_text" class="form-control"></textarea>
                    <input type="button" id='post_task_btn'  onclick="PostTask(this,'cmd')" class="btn btn-success pull-right" value="执行命令">
                </div>
            </div>
            <div class="panel">
                <div class="panel-heading">
                    <h3 class="panel-title">任务结果</h3>
                </div>
                <div class="panel-body">
                    <ul id="task_result_container"></ul>
                </div>
            </div>
        </div>
    
    </div>
    
    
    {% include 'multitask_js_component.html' %}
    
    {% endblock %}

    2、multitask_host_list_component

        <div class="col-lg-4">
            <div class="panel">
                <div class="panel-heading">
                    <h3 class="panel-title">主机列表</h3>
                </div>
                <div class="panel-body">
                        <div class="list-group bord-no">
                            <a onclick="HostListDisplayToggle(this)" class="list-group-item " href="#">
                                <input type="checkbox" onclick="SelectGroup(this)">
                                未分组主机
                                <span class="badge badge-primary">{{ request.user.bind_hosts.count }}</span>
                            </a>
                            <ol class="hide">
                                {% for bind_host in request.user.bind_hosts.all %}
                                    <li><input type="checkbox"  select_host="true" value="{{ bind_host.id }}">{{ bind_host.host.hostname }}({{ bind_host.host.ip_addr }})@{{ bind_host.remote_user.username}}</li>
                                {% endfor %}
                            </ol>
    
    
    
                            {% for host_group in request.user.host_groups.select_related %}
    
                                <a onclick="HostListDisplayToggle(this)" class="list-group-item " href="#">
                                    <input type="checkbox" onclick="SelectGroup(this)">
                                    {{ host_group.name }}
                                    <span class="badge badge-primary">{{ host_group.bind_hosts.count }}</span>
                                </a>
                                <ol class="hide">
                                    {% for bind_host in host_group.bind_hosts.all %}
                                        <li><input type="checkbox"  select_host="true" value="{{ bind_host.id }}">{{ bind_host.host.hostname }}({{ bind_host.host.ip_addr }})@{{ bind_host.remote_user.username}}</li>
                                    {% endfor %}
                                </ol>
    
    
                            {% endfor %}
                        </div>
                </div>
            </div>
    
        </div>

    3、multitask_js_component

    <script>
    
    
        function SelectFileTransferType(ele) {
            if ($(ele).val() == 'get'){
    
                $("#local_file_path").addClass("hide");
            }else {
                $("#local_file_path").removeClass("hide");
            }
        }
    
        function HostListDisplayToggle(ele) {
    
            $(ele).next().toggleClass("hide");
    
    
        }
    
        function SelectGroup(ele) {
    
            $(ele).parent().next().find("input").prop("checked",$(ele).prop("checked"))
    
        }
    
        function  GetTaskResult(task_id) {
    
            $.getJSON( "{% url 'task_result'  %}" ,{'task_id':task_id},function(callback){
    
               console.log( callback);
               var all_task_done = true;
               $.each(callback,function (index,ele) {
                   var li_ele = $("li[bind_host_id='"+  ele['id'] +"']");
                   li_ele.next().text(ele['result']);
                   $(li_ele.children()[0]).text(ele.status);
                   if ( ele.status == 2 ){
                       all_task_done = false; //有任务未完成
                   }
    
               })
    
               if (all_task_done){
    
                   clearInterval(ResultRefreshObj);
                   $("#post_task_btn").removeClass("disabled");
               }
    
            });//end getJSON
        }
    
    
        function PostTask(ele,task_type) {
    
            var selected_host_ids  = [];
    
            $("input[select_host]:checked").each(function () {
                selected_host_ids.push( $(this).val()  );
            })
    
            console.log(selected_host_ids)
            if (selected_host_ids.length == 0){
                alert("必须选择主机!")
                return false
            }
    
            if (task_type == "cmd"){
                var cmd_text = $("#cmd_text").val().trim();
    
                if (cmd_text.length == 0){
    
                    alert("必须输入要执行的命令!")
                    return false
                }
    
                var task_arguments = {
                    'selected_host_ids' : selected_host_ids,
                    'task_type':0 ,//cmd
                    'cmd': cmd_text,
    
                }
    
            }else {
    
                var file_transfer_type = $("select[name='file_transfer_type']").val()
                var local_file_path = $("#local_file_path").val().trim()
                var remote_file_path = $("#remote_file_path").val().trim()
                if (file_transfer_type == "send"){
                    if (local_file_path.length == 0){
                        alert("必须输入本地文件路径!")
                        return false
                    }
    
                }
    
                if (remote_file_path.length == 0){
                        alert("必须输入远程文件路径!")
                        return false
                }
    
                var task_arguments = {
                    'selected_host_ids' : selected_host_ids,
                    'task_type':1 ,//file_transfer
                    'file_transfer_type': file_transfer_type,
                    'local_file_path':local_file_path,
                    'remote_file_path':remote_file_path
    
                }
    
            }
    
    
    
    
    
    
            //再此任务执行完成前,不允许再提交新任务
            $(ele).addClass("disabled")
            //提交新任务之前情况任务结果面版
            $("#task_result_container").empty();
    
    
            $.post("{%  url 'multitask' %}"  , {'task_data':JSON.stringify(task_arguments),'csrfmiddlewaretoken':$("input[name='csrfmiddlewaretoken']").val() },function(callback){
    
                console.log(callback);
    
                var callback = JSON.parse(callback);
                $.each(callback.selected_hosts,function (index,ele) {
                    var li_ele = "<li bind_host_id='"+ ele['id'] +"'>Host:" + ele.bind_host__host__hostname + "(" +ele.bind_host__host__ip_addr +")----------------<span></span></li><pre>sdff</pre>" ;
                    $("#task_result_container").append(li_ele);
    
                })
    
    
    
                //去后端定时拿结果
                ResultRefreshObj = setInterval(function () {
    
                        GetTaskResult(callback.task_id);
    
    
                    },2000);
    
            });//end post
    
    
    
        }
    
    </script>
    

    4、web

    1、admin

      1 from django.contrib import admin
      2 
      3 from web import models
      4 # Register your models here.
      5 
      6 
      7 from django import forms
      8 from django.contrib import admin
      9 from django.contrib.auth.models import Group
     10 from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
     11 from django.contrib.auth.forms import ReadOnlyPasswordHashField
     12 
     13 from web.models import UserProfile
     14 
     15 
     16 class UserCreationForm(forms.ModelForm):
     17     """A form for creating new users. Includes all the required
     18     fields, plus a repeated password."""
     19     password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
     20     password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
     21 
     22     class Meta:
     23         model = UserProfile
     24         fields = ('email', 'name')
     25 
     26     def clean_password2(self):
     27         # Check that the two password entries match
     28         password1 = self.cleaned_data.get("password1")
     29         password2 = self.cleaned_data.get("password2")
     30         if password1 and password2 and password1 != password2:
     31             raise forms.ValidationError("Passwords don't match")
     32         return password2
     33 
     34     def save(self, commit=True):
     35         # Save the provided password in hashed format
     36         user = super(UserCreationForm, self).save(commit=False)
     37         user.set_password(self.cleaned_data["password1"])
     38         if commit:
     39             user.save()
     40         return user
     41 
     42 
     43 class UserChangeForm(forms.ModelForm):
     44     """A form for updating users. Includes all the fields on
     45     the user, but replaces the password field with admin's
     46     password hash display field.
     47     """
     48     password = ReadOnlyPasswordHashField()
     49 
     50     class Meta:
     51         model = UserProfile
     52         fields = ('email', 'password', 'name', 'is_active', 'is_admin')
     53 
     54     def clean_password(self):
     55         # Regardless of what the user provides, return the initial value.
     56         # This is done here, rather than on the field, because the
     57         # field does not have access to the initial value
     58         return self.initial["password"]
     59 
     60 
     61 class UserProfileAdmin(BaseUserAdmin):
     62     # The forms to add and change user instances
     63     form = UserChangeForm
     64     add_form = UserCreationForm
     65 
     66     # The fields to be used in displaying the User model.
     67     # These override the definitions on the base UserAdmin
     68     # that reference specific fields on auth.User.
     69     list_display = ('email', 'name','is_staff', 'is_admin')
     70     list_filter = ('is_admin','is_staff')
     71     fieldsets = (
     72         (None, {'fields': ('email', 'password')}),
     73         ('Personal info', {'fields': ('name',)}),
     74         ('堡垒机主机授权', {'fields': ('bind_hosts','host_groups')}),
     75         ('Permissions', {'fields': ('is_admin','is_staff','user_permissions','groups')}),
     76     )
     77     # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
     78     # overrides get_fieldsets to use this attribute when creating a user.
     79     add_fieldsets = (
     80         (None, {
     81             'classes': ('wide',),
     82             'fields': ('email', 'name', 'password1', 'password2')}
     83         ),
     84     )
     85     search_fields = ('email',)
     86     ordering = ('email',)
     87     filter_horizontal = ('user_permissions','groups','bind_hosts','host_groups')
     88 
     89 # Now register the new UserAdmin...
     90 admin.site.register(models.UserProfile, UserProfileAdmin)
     91 # ... and, since we're not using Django's built-in permissions,
     92 # unregister the Group model from admin.
     93 admin.site.unregister(Group)
     94 
     95 
     96 
     97 class RemoteUserAdmin(admin.ModelAdmin):
     98     list_display = ('username','auth_type','password')
     99 
    100 
    101 class TaskAdmin(admin.ModelAdmin):
    102     list_display = ['id','user','task_type','content','date']
    103 
    104 
    105 class TaskLogDetailAdmin(admin.ModelAdmin):
    106     list_display = ['id','task','bind_host','result','status','start_date','end_date']
    107 
    108 
    109 admin.site.register(models.Host)
    110 admin.site.register(models.HostGroup)
    111 admin.site.register(models.BindHost)
    112 admin.site.register(models.RemoteUser,RemoteUserAdmin)
    113 admin.site.register(models.IDC)
    114 admin.site.register(models.Session)
    115 admin.site.register(models.Task,TaskAdmin)
    116 admin.site.register(models.TaskLogDetail,TaskLogDetailAdmin)
    View Code

    2、models

      1 from django.db import models
      2 from django.contrib.auth.models import User
      3 
      4 from django.contrib.auth.models import (
      5     BaseUserManager, AbstractBaseUser,PermissionsMixin
      6 )
      7 
      8 
      9 
     10 # Create your models here.
     11 
     12 class IDC(models.Model):
     13     name = models.CharField(max_length=64,unique=True)
     14 
     15     def __str__(self):
     16         return self.name
     17 
     18 class Host(models.Model):
     19     """存储所有主机"""
     20     hostname = models.CharField(max_length=64)
     21     ip_addr = models.GenericIPAddressField(unique=True)
     22     port = models.PositiveSmallIntegerField(default=22)
     23     idc = models.ForeignKey("IDC")
     24 
     25     enabled = models.BooleanField(default=True)
     26 
     27     def __str__(self):
     28         return self.ip_addr
     29 
     30 
     31 
     32 class HostGroup(models.Model):
     33     """主机组"""
     34     name = models.CharField(max_length=64, unique=True)
     35     bind_hosts  = models.ManyToManyField("BindHost")
     36     def __str__(self):
     37         return self.name
     38 
     39 
     40 
     41 class RemoteUser(models.Model):
     42     """存储远程用户名密码"""
     43     username = models.CharField(max_length=64)
     44     auth_type_choices = ((0,'ssh/password'),(1,'ssh/key'))
     45     auth_type = models.SmallIntegerField(choices=auth_type_choices,default=0)
     46     password = models.CharField(max_length=128,blank=True,null=True)
     47 
     48     #hosts = models.ManyToManyField("Host")
     49 
     50     def __str__(self):
     51         return "%s(%s)%s" %( self.username,self.get_auth_type_display(),self.password)
     52 
     53     class Meta:
     54         unique_together = ('username','auth_type','password')
     55 
     56 class BindHost(models.Model):
     57     """绑定远程主机和远程用户的对应关系"""
     58     host = models.ForeignKey("Host")
     59     remote_user = models.ForeignKey("RemoteUser")
     60 
     61     def __str__(self):
     62         return "%s -> %s" %(self.host,self.remote_user)
     63     class Meta:
     64         unique_together = ("host","remote_user")
     65 
     66 
     67 
     68 class UserProfileManager(BaseUserManager):
     69     def create_user(self, email, name, password=None):
     70         """
     71         Creates and saves a User with the given email, date of
     72         birth and password.
     73         """
     74         if not email:
     75             raise ValueError('Users must have an email address')
     76 
     77         user = self.model(
     78             email=self.normalize_email(email),
     79             name=name,
     80         )
     81 
     82         user.set_password(password)
     83         user.save(using=self._db)
     84         return user
     85 
     86     def create_superuser(self, email, name, password):
     87         """
     88         Creates and saves a superuser with the given email, date of
     89         birth and password.
     90         """
     91         user = self.create_user(
     92             email,
     93             password=password,
     94             name=name,
     95         )
     96         user.is_admin = True
     97         user.is_staff = True
     98         user.save(using=self._db)
     99         return user
    100 
    101 
    102 class UserProfile(AbstractBaseUser,PermissionsMixin):
    103     email = models.EmailField(
    104         verbose_name='email address',
    105         max_length=255,
    106         unique=True,
    107     )
    108     name = models.CharField(max_length=64)
    109     is_active = models.BooleanField(default=True)
    110     is_admin = models.BooleanField(default=False)
    111     is_staff = models.BooleanField(
    112         ('staff status'),
    113         default=False,
    114         help_text=('Designates whether the user can log into this admin site.'),
    115     )
    116 
    117     bind_hosts = models.ManyToManyField("BindHost",blank=True)
    118     host_groups = models.ManyToManyField("HostGroup",blank=True)
    119 
    120     objects = UserProfileManager()
    121 
    122     USERNAME_FIELD = 'email'
    123     REQUIRED_FIELDS = ['name',]
    124 
    125     def get_full_name(self):
    126         # The user is identified by their email address
    127         return self.email
    128 
    129     def get_short_name(self):
    130         # The user is identified by their email address
    131         return self.email
    132 
    133     def __str__(self):              # __unicode__ on Python 2
    134         return self.email
    135 
    136     def has_perm(self, perm, obj=None):
    137         "Does the user have a specific permission?"
    138         # Simplest possible answer: Yes, always
    139         return True
    140 
    141     def has_module_perms(self, app_label):
    142         "Does the user have permissions to view the app `app_label`?"
    143         # Simplest possible answer: Yes, always
    144         return True
    145 
    146 
    147 
    148 
    149 class Session(models.Model):
    150     '''生成用户操作session id '''
    151     user = models.ForeignKey('UserProfile')
    152     bind_host = models.ForeignKey('BindHost')
    153     tag = models.CharField(max_length=128,default='n/a')
    154     closed = models.BooleanField(default=False)
    155     cmd_count = models.IntegerField(default=0) #命令执行数量
    156     stay_time = models.IntegerField(default=0, help_text="每次刷新自动计算停留时间",verbose_name="停留时长(seconds)")
    157     date = models.DateTimeField(auto_now_add=True)
    158 
    159     def __str__(self):
    160         return '<id:%s user:%s bind_host:%s>' % (self.id,self.user.email,self.bind_host.host)
    161     class Meta:
    162         verbose_name = '审计日志'
    163         verbose_name_plural = '审计日志'
    164 
    165 
    166 
    167 class Task(models.Model):
    168     """批量任务记录表"""
    169     user = models.ForeignKey("UserProfile")
    170     task_type_choices = ((0,'cmd'),(1,'file_transfer'))
    171     task_type = models.SmallIntegerField(choices=task_type_choices)
    172     content = models.TextField(verbose_name="任务内容")
    173     #hosts = models.ManyToManyField("BindHost")
    174     date  = models.DateTimeField(auto_now_add=True)
    175 
    176     def __str__(self):
    177         return "%s %s" %(self.task_type,self.content)
    178 
    179 
    180 class TaskLogDetail(models.Model):
    181     task = models.ForeignKey("Task")
    182     bind_host = models.ForeignKey("BindHost")
    183     result = models.TextField()
    184 
    185     status_choices = ((0,'success'),(1,'failed'),(2,'init'))
    186     status = models.SmallIntegerField(choices=status_choices)
    187 
    188     start_date = models.DateTimeField(auto_now_add=True)
    189     end_date = models.DateTimeField(blank=True,null=True)
    190 
    191 
    192     def __str__(self):
    193         return "%s %s" %(self.bind_host,self.status)
    models

    3、views

    from django.shortcuts import render,redirect,HttpResponse
    from django.contrib.auth.decorators import login_required
    from django.contrib.auth import authenticate,logout,login
    from  django.conf import settings
    import os,re,json
    from web import models
    from backend.task_manager import  MultiTaskManger
    
    from backend import audit
    # Create your views here.
    
    def json_date_handler(obj):
        if hasattr(obj, 'isoformat'):
            return obj.strftime("%Y-%m-%d %T")
    
    
    
    @login_required
    def dashboard(request):
        return render(request,'index.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("/")
    
            else:
                error_msg = "Wrong username or password!"
        return render(request,"login.html",{'error_msg':error_msg})
    
    
    def acc_logout(request):
    
        logout(request)
    
        return redirect("/login/")
    
    
    
    @login_required
    def webssh(request):
        return render(request,'web_ssh.html')
    
    @login_required
    def user_audit(request):
    
        log_dirs = os.listdir(settings.AUDIT_LOG_DIR)
    
    
        return render(request,'user_audit.html',locals())
    
    
    @login_required
    def audit_log_date(request,log_date):
        log_date_path = "%s/%s" %(settings.AUDIT_LOG_DIR,log_date)
        log_file_dirs = os.listdir(log_date_path)
        session_ids = [re.search("d+",i).group() for i in log_file_dirs ]
    
        session_objs = models.Session.objects.filter(id__in=session_ids)
    
        return render(request, 'user_audit_file_list.html', locals())
    
    
    @login_required
    def multitask_cmd(request):
    
        return render(request,"multitask_cmd.html")
    
    @login_required
    def multitask_file_transfer(request):
        return render(request,'multitask_file_transfer.html')
    
    
    @login_required
    def multitask_result(request):
        task_id = request.GET.get('task_id')
        task_obj = models.Task.objects.get(id=task_id)
        task_log_results = list(task_obj.tasklogdetail_set.values('id', 'result','status','start_date','end_date'))
    
        return  HttpResponse(json.dumps(task_log_results,default=json_date_handler))
    
    @login_required
    def multitask(request):
    
        print("--->",request.POST)
        task_data = json.loads(request.POST.get('task_data'))
        print("--->selcted hosts",task_data)
    
        task_obj= MultiTaskManger(request)
        selected_hosts = list(task_obj.task.tasklogdetail_set.all().values('id', 'bind_host__host__ip_addr',
                                                                 'bind_host__host__hostname', 'bind_host__remote_user__username'))
    
    
    
        return HttpResponse(
            json.dumps({'task_id':task_obj.task.id,'selected_hosts':selected_hosts})
        )
    
    
    @login_required
    def audit_log_detail(request,log_date,session_id):
        log_date_path = "%s/%s" % (settings.AUDIT_LOG_DIR, log_date)
        log_file_path = "%s/session_%s.log" %(log_date_path,session_id)
    
        log_parser = audit.AuditLogHandler(log_file_path)
        cmd_list = log_parser.parse()
    
        return render(request,"user_audit_detail.html",locals())

    五、测试截图

    1、执行一条命令

    1、web端

    2、admin后台

    2、执行多条命令

    1、web前端

    2、admin后台

    4、连接不上的命令截图

    连接不上直接排除timeout

    3、后台触发一条命令

    1、后台触发创建

    2、控制台截图

    3、admin后台截图

    4、执行结果前端web显示

    1、任务结果前端显示hostid

    2、任务结果限制执行结果

     3、超时处理

     4、机器连接不通

    5、超时状态数字变化

    6、执行成功状态变化

  • 相关阅读:
    一张图片入门Python
    4.1. 如何在Windows环境下开发Python
    你必须知道的EF知识和经验
    XUnit的使用
    如何使用NUnit
    Entity Framework 不支持DefaultValue
    Have You Ever Wondered About the Difference Between NOT NULL and DEFAULT?
    Validation failed for one or more entities. See 'EntityValidationErrors' property for more details
    Entity Framework 与多线程
    sqlite中的自增主键
  • 原文地址:https://www.cnblogs.com/luoahong/p/9501791.html
Copyright © 2011-2022 走看看