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、执行成功状态变化

  • 相关阅读:
    Mybatis深入浅出之工作原理
    Mybatis深入浅出之缓存机制
    Error :Unable to access jarfile *.jar
    Mysql与JDBC版本兼容性问题
    找工作的正确方法
    关于制作云主机基准镜像
    笔记分享
    Android4.0.1找不到R.java
    android 反编译出错 can not merge I and Z
    [论文理解] Improving the imporved training of Wasserstesin GANS: A consistency term and its dual effect
  • 原文地址:https://www.cnblogs.com/luoahong/p/9501791.html
Copyright © 2011-2022 走看看