当前位置:   article > 正文

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

{% for i in groups.all %}

一、实际生产的架构

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

  1. import subprocess
  2. from web import models
  3. from django.contrib.auth import authenticate
  4. import random,string,uuid
  5. class HostManager(object):
  6. """用户登陆堡垒机后的交互程序"""
  7. def __init__(self):
  8. self.user = None
  9. def get_session_id(self,bind_host_obj,tag):
  10. '''apply session id'''
  11. session_obj = models.Session(user_id = self.user.id,bind_host=bind_host_obj,tag=tag)
  12. session_obj.save()
  13. return session_obj
  14. def interactive(self):
  15. """交互脚本"""
  16. print("----run---------")
  17. count = 0
  18. while count <3:
  19. username = input("Username:").strip()
  20. password = input("Password:").strip()
  21. user = authenticate(username=username,password=password)
  22. if user:
  23. print("Welcome %s".center(50,'-') % user.name )
  24. self.user = user
  25. break
  26. else:
  27. print("Wrong username or password!")
  28. count += 1
  29. else:
  30. exit("Too many attempts, bye.")
  31. if self.user: #验证成功
  32. while True:
  33. for index,host_group in enumerate(self.user.host_groups.all()): #select_related()
  34. print("%s.\t%s[%s]" %(index,host_group.name, host_group.bind_hosts.count()))
  35. print("z.\t未分组主机[%s]" %(self.user.bind_hosts.count()))
  36. choice = input("%s>>:"% self.user.name).strip()
  37. if len(choice) == 0:continue
  38. selected_host_group = None
  39. if choice.isdigit():
  40. choice = int(choice)
  41. if choice >=0 and choice <= index: #合法选项
  42. selected_host_group = self.user.host_groups.all()[choice]
  43. elif choice == 'z':
  44. selected_host_group = self.user
  45. if selected_host_group:
  46. print("selected host group", selected_host_group)
  47. while True:
  48. for index, bind_host in enumerate(selected_host_group.bind_hosts.all()):
  49. print("%s.\t%s" % (index, bind_host))
  50. choice = choice = input("%s>>>:" % self.user.name).strip()
  51. if choice.isdigit():
  52. choice = int(choice)
  53. if choice >= 0 and choice <= index: # 合法选项
  54. print("going to logon ....", selected_host_group.bind_hosts.all()[choice])
  55. bind_host = selected_host_group.bind_hosts.all()[choice]
  56. ssh_tag = uuid.uuid4()
  57. session_obj = self.get_session_id(bind_host,ssh_tag)
  58. 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)
  59. #print(monitor_script.stderr.read())
  60. subprocess.run('sshpass -p %s ssh %s@%s -E %s -o StrictHostKeyChecking=no' %(bind_host.remote_user.password,
  61. bind_host.remote_user.username,
  62. bind_host.host.ip_addr ,ssh_tag ), shell=True)
  63. elif choice == 'b':
  64. break

2、task_manager

  1. import json,os ,subprocess
  2. from django import conf
  3. from web import models
  4. class MultiTaskManger(object):
  5. """负责解析并触发批量任务"""
  6. def __init__(self,request):
  7. self.request = request
  8. self.call_task()
  9. def task_parser(self):
  10. """解析任务"""
  11. self.task_data = json.loads(self.request.POST.get("task_data"))
  12. def call_task(self):
  13. self.task_parser()
  14. if self.task_data['task_type'] == 0:#cmd
  15. self.cmd_task()
  16. elif self.task_data['task_type'] == 1:#file transfer
  17. self.file_transfer_task()
  18. def cmd_task(self):
  19. """
  20. 1.生产任务id
  21. 2.触发任务
  22. 3.返回任务id
  23. :return:
  24. """
  25. task_obj = models.Task.objects.create(user=self.request.user,
  26. task_type=self.task_data['task_type'],
  27. content = self.task_data["cmd"])
  28. sub_task_objs = []
  29. for host_id in self.task_data['selected_host_ids'] :
  30. sub_task_objs.append(models.TaskLogDetail(task=task_obj,bind_host_id=host_id,result='init...',status=2))
  31. models.TaskLogDetail.objects.bulk_create(sub_task_objs)
  32. task_script_obj = subprocess.Popen("python %s %s" %(conf.settings.MULTITASK_SCRIPT,task_obj.id),
  33. shell=True,stdout=subprocess.PIPE)
  34. self.task = task_obj
  35. def file_transfer_task(self):
  36. """
  37. 1.生产任务记录
  38. 2.触发任务
  39. 3. 返回任务id
  40. :return:
  41. """
  42. task_obj = models.Task.objects.create(user=self.request.user,
  43. task_type=self.task_data['task_type'],
  44. content=json.dumps(self.task_data))
  45. sub_task_objs = []
  46. for host_id in self.task_data['selected_host_ids']:
  47. sub_task_objs.append(models.TaskLogDetail(task=task_obj, bind_host_id=host_id, result='init...', status=2))
  48. models.TaskLogDetail.objects.bulk_create(sub_task_objs)
  49. task_script_obj = subprocess.Popen("python %s %s" % (conf.settings.MULTITASK_SCRIPT, task_obj.id),
  50. shell=True, stdout=subprocess.PIPE)
  51. self.task = task_obj

3、task_runner  

  1. import sys ,os
  2. import time,json
  3. from concurrent.futures import ThreadPoolExecutor
  4. import paramiko
  5. def ssh_cmd(task_log_obj):
  6. host = task_log_obj.bind_host.host
  7. user_obj = task_log_obj.bind_host.remote_user
  8. try:
  9. ssh = paramiko.SSHClient()
  10. ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  11. ssh.connect(host.ip_addr, host.port, user_obj.username, user_obj.password,timeout=10)
  12. stdin, stdout, stderr = ssh.exec_command(task_log_obj.task.content)
  13. stdout_res = stdout.read()
  14. stderr_res = stderr.read()
  15. result = stdout_res + stderr_res
  16. print(result)
  17. task_log_obj.result = result
  18. task_log_obj.status = 0
  19. ssh.close()
  20. except Exception as e :
  21. task_log_obj.result = e
  22. task_log_obj.status = 1
  23. task_log_obj.save()
  24. def file_transfer(task_log_obj):
  25. host = task_log_obj.bind_host.host
  26. user_obj = task_log_obj.bind_host.remote_user
  27. try:
  28. t = paramiko.Transport((host.ip_addr, host.port))
  29. t.connect(username=user_obj.username, password=user_obj.password)
  30. sftp = paramiko.SFTPClient.from_transport(t)
  31. task_data = json.loads(task_log_obj.task.content)
  32. if task_data['file_transfer_type'] == 'send':
  33. sftp.put(task_data['local_file_path'],task_data['remote_file_path'])
  34. task_log_obj.result = "send local file [%s] to remote [%s] succeeded!" %(task_data['local_file_path'],
  35. task_data['remote_file_path'])
  36. else: #get
  37. local_file_path = "%s/%s" %(django.conf.settings.DOWNLOAD_DIR,task_log_obj.task.id)
  38. if not os.path.isdir(local_file_path):
  39. os.mkdir(local_file_path)
  40. file_name = task_data['remote_file_path'].split('/')[-1]
  41. sftp.get(task_data['remote_file_path'], "%s/%s.%s" %(local_file_path,host.ip_addr,file_name))
  42. task_log_obj.result = "get remote file [%s] succeeded" %(task_data['remote_file_path'])
  43. t.close()
  44. task_log_obj.status = 0
  45. except Exception as e:
  46. task_log_obj.result = e
  47. task_log_obj.status = 1
  48. task_log_obj.save()
  49. if __name__ == '__main__':
  50. base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  51. sys.path.append(base_dir)
  52. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CrazyEye.settings")
  53. import django
  54. django.setup()
  55. from django import conf
  56. from web import models
  57. if len(sys.argv) == 1:
  58. exit("error:must provide task_id!")
  59. task_id = sys.argv[1]
  60. task_obj = models.Task.objects.get(id=task_id)
  61. #1. 生产多线程
  62. pool = ThreadPoolExecutor(10)
  63. if task_obj.task_type == 0:#cmd
  64. thread_func = ssh_cmd
  65. else: #file_transfer
  66. thread_func = file_transfer
  67. for task_log_detail_obj in task_obj.tasklogdetail_set.all():
  68. pool.submit(thread_func,task_log_detail_obj)
  69. #ssh_cmd(task_log_detail_obj)
  70. 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

  1. from django.conf.urls import url
  2. from django.contrib import admin
  3. from web import views
  4. urlpatterns = [
  5. url(r'^admin/', admin.site.urls),
  6. url(r'^$', views.dashboard),
  7. url(r'^user_audit/$', views.user_audit,name="user_audit"),
  8. url(r'^audit_log/(\w+-\w+-\w+)/$', views.audit_log_date,name="audit_log_date"),
  9. url(r'^audit_log/(\w+-\w+-\w+)/(\d+)/$', views.audit_log_detail,name="audit_log_detail"),
  10. url(r'^webssh/$', views.webssh,name="webssh"),
  11. url(r'^multitask/cmd/$', views.multitask_cmd,name="multitask_cmd"),
  12. url(r'^multitask/file_transfer/$', views.multitask_file_transfer,name="multitask_file_transfer"),
  13. url(r'^multitask/$', views.multitask,name="multitask"),
  14. url(r'^multitask/result/$', views.multitask_result,name="task_result"),
  15. url(r'^login/$', views.acc_login),
  16. url(r'^logout/$', views.acc_logout,name="logout"),
  17. ]

3、templates

1、multitack_cmd.html

  1. {% extends 'index.html' %}
  2. {% block page-title %}主机管理|批量命令{% endblock %}
  3. {% block page-content %}
  4. {% csrf_token %}
  5. <div class="row">
  6. {% include 'multitask_host_list_component.html' %}
  7. <div class="col-lg-8">
  8. <div class="panel">
  9. <div class="panel-heading">
  10. <h3 class="panel-title">命令操作</h3>
  11. </div>
  12. <div class="panel-body">
  13. <textarea id="cmd_text" class="form-control"></textarea>
  14. <input type="button" id='post_task_btn' οnclick="PostTask(this,'cmd')" class="btn btn-success pull-right" value="执行命令">
  15. </div>
  16. </div>
  17. <div class="panel">
  18. <div class="panel-heading">
  19. <h3 class="panel-title">任务结果</h3>
  20. </div>
  21. <div class="panel-body">
  22. <ul id="task_result_container"></ul>
  23. </div>
  24. </div>
  25. </div>
  26. </div>
  27. {% include 'multitask_js_component.html' %}
  28. {% endblock %}

2、multitask_host_list_component

  1. <div class="col-lg-4">
  2. <div class="panel">
  3. <div class="panel-heading">
  4. <h3 class="panel-title">主机列表</h3>
  5. </div>
  6. <div class="panel-body">
  7. <div class="list-group bord-no">
  8. <a οnclick="HostListDisplayToggle(this)" class="list-group-item " href="#">
  9. <input type="checkbox" οnclick="SelectGroup(this)">
  10. 未分组主机
  11. <span class="badge badge-primary">{{ request.user.bind_hosts.count }}</span>
  12. </a>
  13. <ol class="hide">
  14. {% for bind_host in request.user.bind_hosts.all %}
  15. <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>
  16. {% endfor %}
  17. </ol>
  18. {% for host_group in request.user.host_groups.select_related %}
  19. <a οnclick="HostListDisplayToggle(this)" class="list-group-item " href="#">
  20. <input type="checkbox" οnclick="SelectGroup(this)">
  21. {{ host_group.name }}
  22. <span class="badge badge-primary">{{ host_group.bind_hosts.count }}</span>
  23. </a>
  24. <ol class="hide">
  25. {% for bind_host in host_group.bind_hosts.all %}
  26. <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>
  27. {% endfor %}
  28. </ol>
  29. {% endfor %}
  30. </div>
  31. </div>
  32. </div>
  33. </div>

3、multitask_js_component

  1. <script>
  2. function SelectFileTransferType(ele) {
  3. if ($(ele).val() == 'get'){
  4. $("#local_file_path").addClass("hide");
  5. }else {
  6. $("#local_file_path").removeClass("hide");
  7. }
  8. }
  9. function HostListDisplayToggle(ele) {
  10. $(ele).next().toggleClass("hide");
  11. }
  12. function SelectGroup(ele) {
  13. $(ele).parent().next().find("input").prop("checked",$(ele).prop("checked"))
  14. }
  15. function GetTaskResult(task_id) {
  16. $.getJSON( "{% url 'task_result' %}" ,{'task_id':task_id},function(callback){
  17. console.log( callback);
  18. var all_task_done = true;
  19. $.each(callback,function (index,ele) {
  20. var li_ele = $("li[bind_host_id='"+ ele['id'] +"']");
  21. li_ele.next().text(ele['result']);
  22. $(li_ele.children()[0]).text(ele.status);
  23. if ( ele.status == 2 ){
  24. all_task_done = false; //有任务未完成
  25. }
  26. })
  27. if (all_task_done){
  28. clearInterval(ResultRefreshObj);
  29. $("#post_task_btn").removeClass("disabled");
  30. }
  31. });//end getJSON
  32. }
  33. function PostTask(ele,task_type) {
  34. var selected_host_ids = [];
  35. $("input[select_host]:checked").each(function () {
  36. selected_host_ids.push( $(this).val() );
  37. })
  38. console.log(selected_host_ids)
  39. if (selected_host_ids.length == 0){
  40. alert("必须选择主机!")
  41. return false
  42. }
  43. if (task_type == "cmd"){
  44. var cmd_text = $("#cmd_text").val().trim();
  45. if (cmd_text.length == 0){
  46. alert("必须输入要执行的命令!")
  47. return false
  48. }
  49. var task_arguments = {
  50. 'selected_host_ids' : selected_host_ids,
  51. 'task_type':0 ,//cmd
  52. 'cmd': cmd_text,
  53. }
  54. }else {
  55. var file_transfer_type = $("select[name='file_transfer_type']").val()
  56. var local_file_path = $("#local_file_path").val().trim()
  57. var remote_file_path = $("#remote_file_path").val().trim()
  58. if (file_transfer_type == "send"){
  59. if (local_file_path.length == 0){
  60. alert("必须输入本地文件路径!")
  61. return false
  62. }
  63. }
  64. if (remote_file_path.length == 0){
  65. alert("必须输入远程文件路径!")
  66. return false
  67. }
  68. var task_arguments = {
  69. 'selected_host_ids' : selected_host_ids,
  70. 'task_type':1 ,//file_transfer
  71. 'file_transfer_type': file_transfer_type,
  72. 'local_file_path':local_file_path,
  73. 'remote_file_path':remote_file_path
  74. }
  75. }
  76. //再此任务执行完成前,不允许再提交新任务
  77. $(ele).addClass("disabled")
  78. //提交新任务之前情况任务结果面版
  79. $("#task_result_container").empty();
  80. $.post("{% url 'multitask' %}" , {'task_data':JSON.stringify(task_arguments),'csrfmiddlewaretoken':$("input[name='csrfmiddlewaretoken']").val() },function(callback){
  81. console.log(callback);
  82. var callback = JSON.parse(callback);
  83. $.each(callback.selected_hosts,function (index,ele) {
  84. 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>" ;
  85. $("#task_result_container").append(li_ele);
  86. })
  87. //去后端定时拿结果
  88. ResultRefreshObj = setInterval(function () {
  89. GetTaskResult(callback.task_id);
  90. },2000);
  91. });//end post
  92. }
  93. </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

  1. from django.shortcuts import render,redirect,HttpResponse
  2. from django.contrib.auth.decorators import login_required
  3. from django.contrib.auth import authenticate,logout,login
  4. from django.conf import settings
  5. import os,re,json
  6. from web import models
  7. from backend.task_manager import MultiTaskManger
  8. from backend import audit
  9. # Create your views here.
  10. def json_date_handler(obj):
  11. if hasattr(obj, 'isoformat'):
  12. return obj.strftime("%Y-%m-%d %T")
  13. @login_required
  14. def dashboard(request):
  15. return render(request,'index.html')
  16. def acc_login(request):
  17. error_msg = ''
  18. if request.method == "POST":
  19. username = request.POST.get('username')
  20. password = request.POST.get('password')
  21. user = authenticate(username=username,password=password)
  22. if user:
  23. login(request,user)
  24. return redirect("/")
  25. else:
  26. error_msg = "Wrong username or password!"
  27. return render(request,"login.html",{'error_msg':error_msg})
  28. def acc_logout(request):
  29. logout(request)
  30. return redirect("/login/")
  31. @login_required
  32. def webssh(request):
  33. return render(request,'web_ssh.html')
  34. @login_required
  35. def user_audit(request):
  36. log_dirs = os.listdir(settings.AUDIT_LOG_DIR)
  37. return render(request,'user_audit.html',locals())
  38. @login_required
  39. def audit_log_date(request,log_date):
  40. log_date_path = "%s/%s" %(settings.AUDIT_LOG_DIR,log_date)
  41. log_file_dirs = os.listdir(log_date_path)
  42. session_ids = [re.search("\d+",i).group() for i in log_file_dirs ]
  43. session_objs = models.Session.objects.filter(id__in=session_ids)
  44. return render(request, 'user_audit_file_list.html', locals())
  45. @login_required
  46. def multitask_cmd(request):
  47. return render(request,"multitask_cmd.html")
  48. @login_required
  49. def multitask_file_transfer(request):
  50. return render(request,'multitask_file_transfer.html')
  51. @login_required
  52. def multitask_result(request):
  53. task_id = request.GET.get('task_id')
  54. task_obj = models.Task.objects.get(id=task_id)
  55. task_log_results = list(task_obj.tasklogdetail_set.values('id', 'result','status','start_date','end_date'))
  56. return HttpResponse(json.dumps(task_log_results,default=json_date_handler))
  57. @login_required
  58. def multitask(request):
  59. print("--->",request.POST)
  60. task_data = json.loads(request.POST.get('task_data'))
  61. print("--->selcted hosts",task_data)
  62. task_obj= MultiTaskManger(request)
  63. selected_hosts = list(task_obj.task.tasklogdetail_set.all().values('id', 'bind_host__host__ip_addr',
  64. 'bind_host__host__hostname', 'bind_host__remote_user__username'))
  65. return HttpResponse(
  66. json.dumps({'task_id':task_obj.task.id,'selected_hosts':selected_hosts})
  67. )
  68. @login_required
  69. def audit_log_detail(request,log_date,session_id):
  70. log_date_path = "%s/%s" % (settings.AUDIT_LOG_DIR, log_date)
  71. log_file_path = "%s/session_%s.log" %(log_date_path,session_id)
  72. log_parser = audit.AuditLogHandler(log_file_path)
  73. cmd_list = log_parser.parse()
  74. 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、执行成功状态变化

 

转载于:https://www.cnblogs.com/luoahong/p/9501791.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/598065
推荐阅读
相关标签
  

闽ICP备14008679号