mirror of
https://github.com/RobbieHan/sandboxMP.git
synced 2026-02-10 22:24:56 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2646254e52 | ||
|
|
e3490b3af7 | ||
|
|
b58be33aac | ||
|
|
f1dde180fb | ||
|
|
6765fa8b66 | ||
|
|
ad74ed1802 | ||
|
|
a36f8d74f6 | ||
|
|
cea6fa7cba | ||
|
|
eb10ffe9be | ||
|
|
9c204933e8 | ||
|
|
371b1ebbe3 |
@@ -0,0 +1 @@
|
|||||||
|
default_app_config = 'cmdb.apps.CmdbConfig'
|
||||||
@@ -3,3 +3,6 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class CmdbConfig(AppConfig):
|
class CmdbConfig(AppConfig):
|
||||||
name = 'cmdb'
|
name = 'cmdb'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
from .signals import auto_delete_file
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from .models import Code
|
from .models import Code, DeviceInfo, ConnectionInfo, DeviceFile
|
||||||
|
|
||||||
|
|
||||||
class CodeCreateForm(forms.ModelForm):
|
class CodeCreateForm(forms.ModelForm):
|
||||||
@@ -43,4 +43,63 @@ class CodeUpdateForm(CodeCreateForm):
|
|||||||
raise forms.ValidationError(msg)
|
raise forms.ValidationError(msg)
|
||||||
if matching_code.filter(value=value).exists():
|
if matching_code.filter(value=value).exists():
|
||||||
msg = 'value:{} 已经存在'.format(value)
|
msg = 'value:{} 已经存在'.format(value)
|
||||||
raise forms.ValidationError(msg)
|
raise forms.ValidationError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceCreateForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = DeviceInfo
|
||||||
|
exclude = ['dev_connection']
|
||||||
|
error_messages = {
|
||||||
|
'hostname': {'required': '请填写设备地址'},
|
||||||
|
'buyDate': {'required': '请填写购买日期'},
|
||||||
|
'warrantyDate': {'required': '请填写到保日期'}
|
||||||
|
}
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super(DeviceCreateForm, self).clean()
|
||||||
|
hostname = cleaned_data.get('hostname')
|
||||||
|
|
||||||
|
if DeviceInfo.objects.filter(hostname=hostname).count():
|
||||||
|
raise forms.ValidationError('设备地址:{}已存在'.format(hostname))
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceUpdateForm(DeviceCreateForm):
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = self.cleaned_data
|
||||||
|
hostname = cleaned_data.get('hostname')
|
||||||
|
|
||||||
|
if self.instance:
|
||||||
|
matching_device = DeviceInfo.objects.exclude(pk=self.instance.pk)
|
||||||
|
if matching_device.filter(hostname=hostname).exists():
|
||||||
|
raise forms.ValidationError('设备地址:{}已存在'.format(hostname))
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionInfoForm(forms.ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConnectionInfo
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
error_messages = {
|
||||||
|
'port': {'required': '端口不能为空'},
|
||||||
|
}
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = self.cleaned_data
|
||||||
|
username = cleaned_data.get('username')
|
||||||
|
password = cleaned_data.get('password')
|
||||||
|
private_key = cleaned_data.get('private_key')
|
||||||
|
auth_type = cleaned_data.get('auth_type')
|
||||||
|
if len(username) == 0:
|
||||||
|
raise forms.ValidationError('用户名不能为空!')
|
||||||
|
if auth_type == 'password' and len(password) == 0:
|
||||||
|
raise forms.ValidationError('认证类型为[密码]时,必须设置密码信息!')
|
||||||
|
if auth_type == 'private_key' and len(private_key) == 0:
|
||||||
|
raise forms.ValidationError('认证类型为[密钥]时,必须设置密钥信息!')
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceFileUploadForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = DeviceFile
|
||||||
|
fields = '__all__'
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import os
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.dispatch import receiver
|
|
||||||
|
from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ class AbstractMode(models.Model):
|
|||||||
class Code(AbstractMode):
|
class Code(AbstractMode):
|
||||||
key = models.CharField(max_length=80, verbose_name='键')
|
key = models.CharField(max_length=80, verbose_name='键')
|
||||||
value = models.CharField(max_length=80, verbose_name='值')
|
value = models.CharField(max_length=80, verbose_name='值')
|
||||||
desc = models.BooleanField(default=True, verbose_name='备注')
|
desc = models.CharField(max_length=100, blank=True, default='', verbose_name='备注')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = '字典'
|
verbose_name = '字典'
|
||||||
@@ -53,11 +53,11 @@ class ConnectionAbstract(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceAbstract(models.Model):
|
class DeviceAbstract(models.Model):
|
||||||
sys_hostname = models.CharField(max_length=50, blank=True, default='', verbose_name='主机名')
|
sys_hostname = models.CharField(max_length=150, blank=True, default='', verbose_name='主机名')
|
||||||
mac_address = models.CharField(max_length=50, blank=True, default='', verbose_name='MAC地址')
|
mac_address = models.CharField(max_length=150, blank=True, default='', verbose_name='MAC地址')
|
||||||
sn_number = models.CharField(max_length=50, blank=True, default='', verbose_name='SN号码')
|
sn_number = models.CharField(max_length=150, blank=True, default='', verbose_name='SN号码')
|
||||||
os_type = models.CharField(max_length=50, blank=True, default='', verbose_name='系统类型')
|
os_type = models.CharField(max_length=150, blank=True, default='', verbose_name='系统类型')
|
||||||
device_type = models.CharField(max_length=50, blank=True, default='', verbose_name='设备类型')
|
device_type = models.CharField(max_length=150, blank=True, default='', verbose_name='设备类型')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
@@ -99,20 +99,24 @@ class DeviceInfo(AbstractMode, DeviceAbstract, TimeAbstract):
|
|||||||
buyDate = models.DateField(default=datetime.now, verbose_name="购买日期")
|
buyDate = models.DateField(default=datetime.now, verbose_name="购买日期")
|
||||||
warrantyDate = models.DateField(default=datetime.now, verbose_name="到保日期")
|
warrantyDate = models.DateField(default=datetime.now, verbose_name="到保日期")
|
||||||
desc = models.TextField(blank=True, default='', verbose_name='备注信息')
|
desc = models.TextField(blank=True, default='', verbose_name='备注信息')
|
||||||
|
changed_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
|
history = HistoricalRecords(excluded_fields=['add_time', 'modify_time', 'parent'])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = '设备信息'
|
verbose_name = '设备信息'
|
||||||
verbose_name_plural = verbose_name
|
verbose_name_plural = verbose_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _history_user(self):
|
||||||
|
return self.changed_by
|
||||||
|
|
||||||
|
@_history_user.setter
|
||||||
|
def _history_user(self, value):
|
||||||
|
self.changed_by = value
|
||||||
|
|
||||||
|
|
||||||
class DeviceFile(TimeAbstract):
|
class DeviceFile(TimeAbstract):
|
||||||
device = models.ForeignKey('DeviceInfo', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='设备')
|
device = models.ForeignKey('DeviceInfo', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='设备')
|
||||||
file_content = models.FileField(upload_to="asset_file/%Y/%m", null=True, blank=True, verbose_name="资产文件")
|
file_content = models.FileField(upload_to="asset_file/%Y/%m", null=True, blank=True, verbose_name="资产文件")
|
||||||
upload_user = models.CharField(max_length=20, verbose_name="上传人")
|
upload_user = models.CharField(max_length=20, verbose_name="上传人")
|
||||||
|
|
||||||
|
|
||||||
@receiver(models.signals.post_delete, sender=DeviceFile)
|
|
||||||
def auto_delete_file(sender, instance, **kwargs):
|
|
||||||
if instance.file_content:
|
|
||||||
if os.path.isfile(instance.file_content.path):
|
|
||||||
os.remove(instance.file_content.path)
|
|
||||||
|
|||||||
46
apps/cmdb/signals.py
Normal file
46
apps/cmdb/signals.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.db.models.signals import post_delete, post_save
|
||||||
|
|
||||||
|
from .models import DeviceFile, DeviceInfo, ConnectionInfo
|
||||||
|
from utils.db_utils import MongodbDriver
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_delete, sender=DeviceFile)
|
||||||
|
def auto_delete_file(sender, instance, **kwargs):
|
||||||
|
if instance.file_content:
|
||||||
|
if os.path.isfile(instance.file_content.path):
|
||||||
|
os.remove(instance.file_content.path)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=DeviceInfo)
|
||||||
|
def auto_compare_diff(sender, instance, **kwargs):
|
||||||
|
record = instance.history.latest()
|
||||||
|
prev_record = record.prev_record
|
||||||
|
ope_type = {'~': 'update', '+': 'create', '-': 'delete'}
|
||||||
|
compare_result = {
|
||||||
|
'id': record.id,
|
||||||
|
'changed_by': record.changed_by.name,
|
||||||
|
'history_type': ope_type[record.history_type],
|
||||||
|
'history_date': record.history_date
|
||||||
|
}
|
||||||
|
changes = {}
|
||||||
|
if prev_record is not None:
|
||||||
|
delta = record.diff_against(prev_record)
|
||||||
|
for change in delta.changes:
|
||||||
|
changes[change.field] = [change.old, change.new]
|
||||||
|
compare_result['changes'] = changes
|
||||||
|
if compare_result['changes'] or compare_result['history_type'] == 'create':
|
||||||
|
try:
|
||||||
|
mongo = MongodbDriver(collection='change_compare')
|
||||||
|
mongo.insert(compare_result)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_delete, sender=DeviceInfo)
|
||||||
|
def auto_delete_connection(sender, instance, **kwargs):
|
||||||
|
dev_connection = getattr(instance, 'dev_connection')
|
||||||
|
if dev_connection:
|
||||||
|
ConnectionInfo.objects.filter(id=dev_connection).delete()
|
||||||
56
apps/cmdb/tasks.py
Normal file
56
apps/cmdb/tasks.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from celery import shared_task
|
||||||
|
from celery_once import QueueOnce
|
||||||
|
|
||||||
|
from utils.sandbox_utils import SandboxScan, LoginExecution
|
||||||
|
from .models import DeviceScanInfo
|
||||||
|
|
||||||
|
info_logger = logging.getLogger('sandbox_info')
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(base=QueueOnce)
|
||||||
|
def scan_execution():
|
||||||
|
scan = SandboxScan()
|
||||||
|
execution = LoginExecution()
|
||||||
|
scan_type = execution.get_scan_type()
|
||||||
|
auth_type = execution.get_auth_type()
|
||||||
|
start_time = time.time()
|
||||||
|
if scan_type == 'basic_scan':
|
||||||
|
hosts = scan.basic_scan()
|
||||||
|
for host in hosts:
|
||||||
|
DeviceScanInfo.objects.update_or_create(
|
||||||
|
hostname=host,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
hosts = scan.os_scan()
|
||||||
|
login_hosts = [host for host in hosts if host['os'] in ['Linux', 'embedded']]
|
||||||
|
nologin_hosts = [host for host in hosts if host not in login_hosts]
|
||||||
|
for host in nologin_hosts:
|
||||||
|
DeviceScanInfo.objects.update_or_create(
|
||||||
|
hostname=host['host'],
|
||||||
|
defaults={
|
||||||
|
'os_type': host['os']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for host in login_hosts:
|
||||||
|
kwargs = {
|
||||||
|
'hostname': host['host'],
|
||||||
|
'username': execution.get_ssh_username(),
|
||||||
|
'port': execution.get_ssh_port(),
|
||||||
|
'password': execution.get_ssh_password(),
|
||||||
|
'private_key': execution.get_ssh_private_key()
|
||||||
|
}
|
||||||
|
defaults = execution.login_execution(auth_type=auth_type, **kwargs)
|
||||||
|
DeviceScanInfo.objects.update_or_create(
|
||||||
|
hostname=host['host'],
|
||||||
|
defaults=defaults
|
||||||
|
)
|
||||||
|
end_time = time.time()
|
||||||
|
msg = 'Scan task has been completed, execution time: %(time)s, %(num)s hosts are up.' % {
|
||||||
|
'time': end_time - start_time,
|
||||||
|
'num': len(hosts)
|
||||||
|
}
|
||||||
|
info_logger.info(msg)
|
||||||
|
return msg
|
||||||
3
apps/cmdb/templatetags/__init__.py
Normal file
3
apps/cmdb/templatetags/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# @Time : 2019/2/17 21:28
|
||||||
|
# @Author : RobbieHan
|
||||||
|
# @File : __init__.py.py
|
||||||
60
apps/cmdb/templatetags/extra_tags.py
Normal file
60
apps/cmdb/templatetags/extra_tags.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from django import template
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from cmdb.models import Code, Cabinet
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def get_con(context, arg, field):
|
||||||
|
if isinstance(context, QuerySet):
|
||||||
|
context = context.values()
|
||||||
|
instance = [con for con in context if con['id'] == arg]
|
||||||
|
if instance:
|
||||||
|
return instance[0][field]
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name='compare_result')
|
||||||
|
def get_change_compare(changes):
|
||||||
|
change_compare = []
|
||||||
|
for key, value in changes.items():
|
||||||
|
if key in ['network_type', 'service_type', 'operation_type']:
|
||||||
|
log = replace_log(key, value, Code, 'value')
|
||||||
|
elif key == 'dev_cabinet':
|
||||||
|
log = replace_log(key, value, Cabinet, 'number')
|
||||||
|
elif key == 'leader':
|
||||||
|
log = replace_log(key, value, User, 'name')
|
||||||
|
else:
|
||||||
|
log = '字段:"%(field)s",由:"%(old)s",变更为:"%(new)s"。' % {
|
||||||
|
'field': key,
|
||||||
|
'old': value[0],
|
||||||
|
'new': value[1]
|
||||||
|
}
|
||||||
|
change_compare.append(log)
|
||||||
|
return ','.join(str(i) for i in change_compare)
|
||||||
|
|
||||||
|
|
||||||
|
def replace_log(key, value, model, field):
|
||||||
|
old = value[0]
|
||||||
|
new = value[1]
|
||||||
|
log_format = '字段:"%(field)s",由:"%(old)s",变更为:"%(new)s"。'
|
||||||
|
try:
|
||||||
|
data = model.objects.filter(id=old).values()[0]
|
||||||
|
old_data = data[field]
|
||||||
|
except Exception:
|
||||||
|
old_data = old
|
||||||
|
try:
|
||||||
|
data = model.objects.filter(id=new).values()[0]
|
||||||
|
new_data = data[field]
|
||||||
|
except Exception:
|
||||||
|
new_data = new
|
||||||
|
return log_format % {
|
||||||
|
'field': key,
|
||||||
|
'old': old_data,
|
||||||
|
'new': new_data
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import CmdbView
|
from .views import CmdbView
|
||||||
from . import views_code, views_scan
|
from . import views_code, views_scan, views_eam
|
||||||
|
|
||||||
app_name = 'cmdb'
|
app_name = 'cmdb'
|
||||||
|
|
||||||
@@ -14,4 +14,28 @@ urlpatterns = [
|
|||||||
path('portal/code/delete/', views_code.CodeDeleteView.as_view(), name='portal-code-delete'),
|
path('portal/code/delete/', views_code.CodeDeleteView.as_view(), name='portal-code-delete'),
|
||||||
|
|
||||||
path('portal/scan_config/', views_scan.ScanConfigView.as_view(), name='portal-scan_config'),
|
path('portal/scan_config/', views_scan.ScanConfigView.as_view(), name='portal-scan_config'),
|
||||||
|
path('portal/device_scan/', views_scan.DeviceScanView.as_view(), name='portal-device_scan'),
|
||||||
|
path('portal/device_scan/list/', views_scan.DeviceScanListView.as_view(), name='portal-device_scan-list'),
|
||||||
|
path('portal/device_scan/detail/', views_scan.DeviceScanDetailView.as_view(), name='portal-device_scan-detail'),
|
||||||
|
path('portal/device_scan/delete/', views_scan.DeviceScanDeleteView.as_view(), name='portal-device_scan-delete'),
|
||||||
|
path('portal/device_scan/exec/', views_scan.DeviceScanExecView.as_view(), name='portal-device_scan-exec'),
|
||||||
|
path('portal/device_scan/inbound/', views_scan.DeviceScanInboundView.as_view(), name='portal-device_scan-inbound'),
|
||||||
|
|
||||||
|
path('eam/cabinet/', views_eam.CabinetView.as_view(), name='eam-cabinet'),
|
||||||
|
path('eam/cabinet/create/', views_eam.CabinetCreateView.as_view(), name='eam-cabinet-create'),
|
||||||
|
path('eam/cabinet/update/', views_eam.CabinetUpdateView.as_view(), name='eam-cabinet-update'),
|
||||||
|
path('eam/cabinet/list/', views_eam.CabinetListView.as_view(), name='eam-cabinet-list'),
|
||||||
|
path('eam/cabinet/delete/', views_eam.CabinetDeleteView.as_view(), name='eam-cabinet-delete'),
|
||||||
|
|
||||||
|
path('eam/device/', views_eam.DeviceView.as_view(), name='eam-device'),
|
||||||
|
path('eam/device/create/', views_eam.DeviceCreateView.as_view(), name='eam-device-create'),
|
||||||
|
path('eam/device/update/', views_eam.DeviceUpdateView.as_view(), name='eam-device-update'),
|
||||||
|
path('eam/device/list/', views_eam.DeviceListView.as_view(), name='eam-device-list'),
|
||||||
|
path('eam/device/delete/', views_eam.DeviceDeleteView.as_view(), name='eam-device-delete'),
|
||||||
|
path('eam/device/device2connection/', views_eam.Device2ConnectionView.as_view(), name='eam-device-device2connection'),
|
||||||
|
path('eam/device/detail/', views_eam.DeviceDetailView.as_view(), name='eam-device-detail'),
|
||||||
|
path('eam/device/upload/', views_eam.DeviceFileUploadView.as_view(), name='eam-device-upload'),
|
||||||
|
path('eam/device/file_delete/', views_eam.DeviceFileDeleteView.as_view(), name='eam-device-file_delete'),
|
||||||
|
path('eam/device/auto_update_device_info/', views_eam.AutoUpdateDeviceInfo.as_view(),
|
||||||
|
name='eam-device-auto_update_device_info'),
|
||||||
]
|
]
|
||||||
|
|||||||
235
apps/cmdb/views_eam.py
Normal file
235
apps/cmdb/views_eam.py
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from django.views.generic import TemplateView, View
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.forms.models import model_to_dict
|
||||||
|
|
||||||
|
from system.mixin import LoginRequiredMixin
|
||||||
|
from custom import (BreadcrumbMixin, SandboxDeleteView,
|
||||||
|
SandboxListView, SandboxUpdateView, SandboxCreateView)
|
||||||
|
from .models import Cabinet, DeviceInfo, Code, ConnectionInfo, DeviceFile
|
||||||
|
from .forms import DeviceCreateForm, DeviceUpdateForm, ConnectionInfoForm, DeviceFileUploadForm
|
||||||
|
from utils.db_utils import MongodbDriver
|
||||||
|
from utils.sandbox_utils import LoginExecution
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class CabinetView(LoginRequiredMixin, BreadcrumbMixin, TemplateView):
|
||||||
|
template_name = 'cmdb/cabinet.html'
|
||||||
|
|
||||||
|
|
||||||
|
class CabinetCreateView(SandboxCreateView):
|
||||||
|
model = Cabinet
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class CabinetUpdateView(SandboxUpdateView):
|
||||||
|
model = Cabinet
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class CabinetListView(SandboxListView):
|
||||||
|
model = Cabinet
|
||||||
|
fields = ['id', 'number', 'position', 'desc']
|
||||||
|
|
||||||
|
def get_filters(self):
|
||||||
|
data = self.request.GET
|
||||||
|
filters = {}
|
||||||
|
if 'number' in data and data['number']:
|
||||||
|
filters['number__icontains'] = data['number']
|
||||||
|
if 'position' in data and data['position']:
|
||||||
|
filters['position__icontains'] = data['position']
|
||||||
|
return filters
|
||||||
|
|
||||||
|
|
||||||
|
class CabinetDeleteView(SandboxDeleteView):
|
||||||
|
model = Cabinet
|
||||||
|
|
||||||
|
|
||||||
|
def get_device_public():
|
||||||
|
all_code = Code.objects.all()
|
||||||
|
all_cabinet = Cabinet.objects.all()
|
||||||
|
all_user = User.objects.all()
|
||||||
|
all_device = DeviceInfo.objects.all()
|
||||||
|
ret = {
|
||||||
|
'all_code': all_code,
|
||||||
|
'all_cabinet': all_cabinet,
|
||||||
|
'all_user': all_user,
|
||||||
|
'all_device': all_device,
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceView(LoginRequiredMixin, BreadcrumbMixin, TemplateView):
|
||||||
|
template_name = 'cmdb/deviceinfo.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
device_public = get_device_public()
|
||||||
|
kwargs.update(device_public)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceListView(SandboxListView):
|
||||||
|
model = DeviceInfo
|
||||||
|
fields = ['id', 'sys_hostname', 'sn_number', 'os_type', 'device_type', 'hostname', 'mac_address', 'leader']
|
||||||
|
|
||||||
|
def get_filters(self):
|
||||||
|
data = self.request.GET
|
||||||
|
filters = {}
|
||||||
|
if 'sys_hostname' in data and data['sys_hostname']:
|
||||||
|
filters['sys_hostname__icontains'] = data['sys_hostname']
|
||||||
|
if 'hostname' in data and data['hostname']:
|
||||||
|
filters['hostname__icontains'] = data['hostname']
|
||||||
|
if 'network_type' in data and data['network_type']:
|
||||||
|
filters['network_type'] = data['network_type']
|
||||||
|
if 'service_type' in data and data['service_type']:
|
||||||
|
filters['service_type'] = data['service_type']
|
||||||
|
if 'operation_type' in data and data['operation_type']:
|
||||||
|
filters['operation_type'] = data['operation_type']
|
||||||
|
return filters
|
||||||
|
|
||||||
|
def get_datatables_paginator(self, request):
|
||||||
|
context_data = super().get_datatables_paginator(request)
|
||||||
|
data = context_data['data']
|
||||||
|
for device in data:
|
||||||
|
user_id = device['leader']
|
||||||
|
device['leader'] = get_object_or_404(
|
||||||
|
User, pk=int(user_id)).name if user_id else ''
|
||||||
|
return context_data
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceCreateView(SandboxCreateView):
|
||||||
|
model = DeviceInfo
|
||||||
|
form_class = DeviceCreateForm
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
public_data = get_device_public()
|
||||||
|
kwargs.update(public_data)
|
||||||
|
print(public_data)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceUpdateView(SandboxUpdateView):
|
||||||
|
model = DeviceInfo
|
||||||
|
form_class = DeviceUpdateForm
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
public_data = get_device_public()
|
||||||
|
kwargs.update(public_data)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceDeleteView(SandboxDeleteView):
|
||||||
|
model = DeviceInfo
|
||||||
|
|
||||||
|
|
||||||
|
class Device2ConnectionView(LoginRequiredMixin, View):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
ret = dict()
|
||||||
|
if 'id' in request.GET and request.GET['id']:
|
||||||
|
device = get_object_or_404(DeviceInfo, pk=int(request.GET['id']))
|
||||||
|
ret['device'] = device
|
||||||
|
dev_connection = device.dev_connection
|
||||||
|
if dev_connection:
|
||||||
|
connection_info = get_object_or_404(
|
||||||
|
ConnectionInfo, pk=int(dev_connection)
|
||||||
|
)
|
||||||
|
ret['connection_info'] = connection_info
|
||||||
|
return render(request, 'cmdb/deviceinfo2connection.html', ret)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
res = dict(result=False)
|
||||||
|
con_info = ConnectionInfo()
|
||||||
|
if 'id' in request.POST and request.POST['id']:
|
||||||
|
con_info = get_object_or_404(ConnectionInfo, pk=request.POST['id'])
|
||||||
|
form = ConnectionInfoForm(request.POST, instance=con_info)
|
||||||
|
if form.is_valid():
|
||||||
|
instance = form.save()
|
||||||
|
con_id = getattr(instance, 'id')
|
||||||
|
device = get_object_or_404(DeviceInfo, hostname=request.POST['hostname'])
|
||||||
|
device.dev_connection = con_id
|
||||||
|
device.save()
|
||||||
|
res['result'] = True
|
||||||
|
else:
|
||||||
|
pattern = '<li>.*?<ul class=.*?><li>(.*?)</li>'
|
||||||
|
form_errors = str(form.errors)
|
||||||
|
errors = re.findall(pattern, form_errors)
|
||||||
|
res['error'] = errors[0]
|
||||||
|
return JsonResponse(res)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceDetailView(LoginRequiredMixin, BreadcrumbMixin, TemplateView):
|
||||||
|
template_name = 'cmdb/deviceinfo_detail.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
device = get_object_or_404(DeviceInfo, pk=int(self.request.GET['id']))
|
||||||
|
mongo = MongodbDriver()
|
||||||
|
logs = mongo.find(id=int(self.request.GET['id']), sort_by='history_date')
|
||||||
|
all_file = device.devicefile_set.all()
|
||||||
|
device_public = get_device_public()
|
||||||
|
kwargs['device'] = device
|
||||||
|
kwargs['logs'] = logs
|
||||||
|
kwargs['all_file'] = all_file
|
||||||
|
kwargs.update(device_public)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceFileUploadView(LoginRequiredMixin, View):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
ret = dict()
|
||||||
|
device = get_object_or_404(DeviceInfo, pk=request.GET['id'])
|
||||||
|
ret['device'] = device
|
||||||
|
return render(request, 'cmdb/deviceinfo_upload.html', ret)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
res = dict(result=False)
|
||||||
|
device_file = DeviceFile()
|
||||||
|
upload_form = DeviceFileUploadForm(
|
||||||
|
request.POST, request.FILES, instance=device_file
|
||||||
|
)
|
||||||
|
if upload_form.is_valid():
|
||||||
|
upload_form.save()
|
||||||
|
res['result'] = True
|
||||||
|
return JsonResponse(res)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceFileDeleteView(SandboxDeleteView):
|
||||||
|
model = DeviceFile
|
||||||
|
|
||||||
|
|
||||||
|
class AutoUpdateDeviceInfo(LoginRequiredMixin, View):
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
res = dict(status='fail')
|
||||||
|
if 'id' in request.POST and request.POST['id']:
|
||||||
|
device = get_object_or_404(DeviceInfo, pk=int(request.POST['id']))
|
||||||
|
con_id = device.dev_connection
|
||||||
|
conn = ConnectionInfo.objects.filter(id=con_id)
|
||||||
|
if con_id and conn:
|
||||||
|
try:
|
||||||
|
conn_info = conn.get()
|
||||||
|
kwargs = model_to_dict(conn_info, exclude=['id', 'auth_type'])
|
||||||
|
auth_type = conn_info.auth_type
|
||||||
|
le = LoginExecution()
|
||||||
|
data = le.login_execution(auth_type=auth_type, **kwargs)
|
||||||
|
conn_info.status = data['status']
|
||||||
|
conn_info.save()
|
||||||
|
if data['status'] == 'succeed':
|
||||||
|
device.sys_hostname = data['sys_hostname']
|
||||||
|
device.mac_address = data['mac_address']
|
||||||
|
device.sn_number = data['sn_number']
|
||||||
|
device.os_type = data['os_type']
|
||||||
|
device.device_type = data['device_type']
|
||||||
|
device.save()
|
||||||
|
res['status'] = 'success'
|
||||||
|
except conn.model.DoesNotExist:
|
||||||
|
res['status'] = 'con_empty'
|
||||||
|
else:
|
||||||
|
res['status'] = 'con_empty'
|
||||||
|
return JsonResponse(res)
|
||||||
@@ -6,15 +6,19 @@ import ast
|
|||||||
import logging
|
import logging
|
||||||
from ruamel import yaml
|
from ruamel import yaml
|
||||||
|
|
||||||
from django.views.generic import View
|
from django.views.generic import View, TemplateView
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
|
||||||
|
from celery_once import AlreadyQueued
|
||||||
|
|
||||||
from system.mixin import LoginRequiredMixin
|
from system.mixin import LoginRequiredMixin
|
||||||
from custom import BreadcrumbMixin
|
from custom import BreadcrumbMixin, SandboxListView, SandboxDeleteView
|
||||||
from utils.sandbox_utils import ConfigFileMixin
|
from utils.sandbox_utils import ConfigFileMixin
|
||||||
from system.models import Menu
|
from system.models import Menu
|
||||||
|
from .models import (DeviceScanInfo, ConnectionInfo, DeviceInfo,
|
||||||
|
ConnectionAbstract, DeviceAbstract)
|
||||||
|
from .tasks import scan_execution
|
||||||
|
|
||||||
error_logger = logging.getLogger('sandbox_error')
|
error_logger = logging.getLogger('sandbox_error')
|
||||||
|
|
||||||
@@ -52,3 +56,63 @@ class ScanConfigView(LoginRequiredMixin, BreadcrumbMixin, ConfigFileMixin, View)
|
|||||||
error_logger.error(e)
|
error_logger.error(e)
|
||||||
|
|
||||||
return JsonResponse(ret)
|
return JsonResponse(ret)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceScanView(LoginRequiredMixin, BreadcrumbMixin, TemplateView):
|
||||||
|
template_name = 'cmdb/device_scan.html'
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceScanListView(SandboxListView):
|
||||||
|
model = DeviceScanInfo
|
||||||
|
fields = ['id', 'sys_hostname', 'hostname', 'mac_address', 'auth_type', 'status', 'os_type', 'device_type']
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceScanDetailView(LoginRequiredMixin, View):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
ret = Menu.get_menu_by_request_url(request.path_info)
|
||||||
|
if 'id' in request.GET and request.GET['id']:
|
||||||
|
device = get_object_or_404(DeviceScanInfo, pk=int(request.GET['id']))
|
||||||
|
ret['device'] = device
|
||||||
|
return render(request, 'cmdb/device_scan_detail.html', ret)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceScanDeleteView(SandboxDeleteView):
|
||||||
|
model = DeviceScanInfo
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceScanExecView(LoginRequiredMixin, View):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
ret = dict(status='fail')
|
||||||
|
try:
|
||||||
|
scan_execution.delay()
|
||||||
|
ret['status'] = 'success'
|
||||||
|
except AlreadyQueued:
|
||||||
|
ret['status'] = 'already_queued'
|
||||||
|
return JsonResponse(ret)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceScanInboundView(LoginRequiredMixin, View):
|
||||||
|
def post(self, request):
|
||||||
|
ret = dict(result=False)
|
||||||
|
login_succeed = list(DeviceScanInfo.objects.filter(status='succeed').values())
|
||||||
|
connection_fields = [field.name for field in ConnectionAbstract._meta.fields if field.name is not 'id']
|
||||||
|
device_fields = [field.name for field in DeviceAbstract._meta.fields if field.name is not 'id']
|
||||||
|
device_fields.append('hostname')
|
||||||
|
for host in login_succeed:
|
||||||
|
connection_defaults = {key: host[key] for key in host.keys() & connection_fields}
|
||||||
|
device_defaults = {key: host[key] for key in host.keys() & device_fields}
|
||||||
|
connection_info, _ = ConnectionInfo.objects.update_or_create(
|
||||||
|
hostname=host['hostname'],
|
||||||
|
defaults=connection_defaults
|
||||||
|
)
|
||||||
|
connection_id = int(getattr(connection_info, 'id'))
|
||||||
|
device_defaults['dev_connection'] = connection_id
|
||||||
|
device_defaults['changed_by_id'] = request.user.id
|
||||||
|
DeviceInfo.objects.update_or_create(
|
||||||
|
hostname=host['hostname'],
|
||||||
|
defaults=device_defaults
|
||||||
|
)
|
||||||
|
ret['result'] = True
|
||||||
|
return JsonResponse(ret)
|
||||||
|
|||||||
@@ -84,9 +84,9 @@ class SandboxMultipleObjectMixin:
|
|||||||
filters = self.get_filters()
|
filters = self.get_filters()
|
||||||
fields = self.get_fields()
|
fields = self.get_fields()
|
||||||
if filters:
|
if filters:
|
||||||
queryset = queryset.filter(**self.filters)
|
queryset = queryset.filter(**filters)
|
||||||
if fields:
|
if fields:
|
||||||
queryset = queryset.values(*self.fields)
|
queryset = queryset.values(*fields)
|
||||||
|
|
||||||
record_filter_count = queryset.count()
|
record_filter_count = queryset.count()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
default_app_config = 'system.apps.SystemConfig'
|
||||||
|
|||||||
@@ -2,4 +2,10 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
|
|
||||||
class SystemConfig(AppConfig):
|
class SystemConfig(AppConfig):
|
||||||
name = 'apps.system'
|
name = 'system'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
from .signals import create_menu
|
||||||
|
from .signals import user_logged_in_handler
|
||||||
|
from .signals import user_logged_out_handler
|
||||||
|
from .signals import user_login_failed_handler
|
||||||
|
|||||||
50
apps/system/signals.py
Normal file
50
apps/system/signals.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
|
||||||
|
|
||||||
|
from .models import Role, Menu
|
||||||
|
|
||||||
|
error_logger = logging.getLogger('sandbox_error')
|
||||||
|
info_logger = logging.getLogger('sandbox_info')
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=Menu)
|
||||||
|
def create_menu(sender, instance, **kwargs):
|
||||||
|
queryset = Role.objects.filter(id=1)
|
||||||
|
try:
|
||||||
|
admin_role = queryset.get()
|
||||||
|
admin_role.permissions.add(instance)
|
||||||
|
except queryset.model.DoesNotExist as e:
|
||||||
|
error_logger.error(e)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(user_logged_in)
|
||||||
|
def user_logged_in_handler(sender, request, user, **kwargs):
|
||||||
|
ip = request.META.get('REMOTE_ADDR')
|
||||||
|
msg = 'login user: {user}, remote ip: {ip}, action: login, status: successed'.format(
|
||||||
|
user=user.username,
|
||||||
|
ip=ip,
|
||||||
|
)
|
||||||
|
info_logger.info(msg)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(user_logged_out)
|
||||||
|
def user_logged_out_handler(sender, request, user, **kwargs):
|
||||||
|
ip = request.META.get('REMOTE_ADDR')
|
||||||
|
msg = 'login user: {user}, remote ip: {ip}, action: logout, status: successed'.format(
|
||||||
|
user=user.username,
|
||||||
|
ip=ip,
|
||||||
|
)
|
||||||
|
info_logger.info(msg)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(user_login_failed)
|
||||||
|
def user_login_failed_handler(sender, credentials, request, **kwargs):
|
||||||
|
msg = 'logout failed for: {credentials}'.format(
|
||||||
|
credentials=credentials,
|
||||||
|
)
|
||||||
|
|
||||||
|
info_logger.info(msg)
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ class LoginView(View):
|
|||||||
redirect_to = request.GET.get('next', '/')
|
redirect_to = request.GET.get('next', '/')
|
||||||
login_form = LoginForm(request.POST)
|
login_form = LoginForm(request.POST)
|
||||||
ret = dict(login_form=login_form)
|
ret = dict(login_form=login_form)
|
||||||
|
print(request.META.get('REMOTE_ADDR'))
|
||||||
if login_form.is_valid():
|
if login_form.is_valid():
|
||||||
user_name = request.POST['username']
|
user_name = request.POST['username']
|
||||||
pass_word = request.POST['password']
|
pass_word = request.POST['password']
|
||||||
|
|||||||
18
apps/utils/db_utils.py
Normal file
18
apps/utils/db_utils.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import pymongo
|
||||||
|
|
||||||
|
|
||||||
|
class MongodbDriver(object):
|
||||||
|
|
||||||
|
def __init__(self, db='device', collection='change_compare'):
|
||||||
|
self.client = pymongo.MongoClient('127.0.0.1', 27017)
|
||||||
|
self.db = self.client[db]
|
||||||
|
self.col = self.db[collection]
|
||||||
|
|
||||||
|
def insert(self, content):
|
||||||
|
return self.col.insert(content)
|
||||||
|
|
||||||
|
def find(self, sort_by, **filters,):
|
||||||
|
data = self.col.find(filters)
|
||||||
|
if sort_by:
|
||||||
|
data.sort(sort_by, pymongo.DESCENDING)
|
||||||
|
return data
|
||||||
@@ -8,6 +8,8 @@ from django.conf import settings
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
|
import nmap
|
||||||
|
import paramiko
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sandboxMP.settings')
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sandboxMP.settings')
|
||||||
error_logger = logging.getLogger('sandbox_error')
|
error_logger = logging.getLogger('sandbox_error')
|
||||||
@@ -72,3 +74,136 @@ class ConfigFileMixin:
|
|||||||
"""
|
"""
|
||||||
key = ['hosts', 'net_address']
|
key = ['hosts', 'net_address']
|
||||||
return self.get_conf_content(*key)
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
|
||||||
|
class SandboxScan(ConfigFileMixin):
|
||||||
|
|
||||||
|
def basic_scan(self):
|
||||||
|
"""
|
||||||
|
Use ICMP discovery online hosts and return online hosts.
|
||||||
|
"""
|
||||||
|
hosts = self.get_net_address()
|
||||||
|
nm = nmap.PortScanner()
|
||||||
|
nm.scan(hosts=hosts, arguments='-n -sP -PE')
|
||||||
|
online_hosts = nm.all_hosts()
|
||||||
|
return online_hosts
|
||||||
|
|
||||||
|
def os_scan(self):
|
||||||
|
"""
|
||||||
|
Get the system type by nmap scan and return hosts list with os type.
|
||||||
|
"""
|
||||||
|
hosts = self.get_net_address()
|
||||||
|
nm = nmap.PortScanner()
|
||||||
|
nm.scan(hosts=hosts, arguments='-n sS -O')
|
||||||
|
online_hosts = []
|
||||||
|
for host in nm.all_hosts():
|
||||||
|
try:
|
||||||
|
os_type = nm[host]['osmatch'][0]['osclass'][0]['osfamily']
|
||||||
|
except Exception:
|
||||||
|
os_type = 'unknown'
|
||||||
|
host_dict = {'host': host, 'os': os_type}
|
||||||
|
online_hosts.append(host_dict)
|
||||||
|
return online_hosts
|
||||||
|
|
||||||
|
def get_net_address(self):
|
||||||
|
"""
|
||||||
|
Return the hosts that will be used to scan.
|
||||||
|
Subclasses can override this to return any hosts.`
|
||||||
|
"""
|
||||||
|
hosts_list = super().get_net_address()
|
||||||
|
hosts = ' '.join(str(i) for i in hosts_list)
|
||||||
|
return hosts
|
||||||
|
|
||||||
|
|
||||||
|
class LoginExecution(ConfigFileMixin):
|
||||||
|
|
||||||
|
def login_execution(self, auth_type='password', **kwargs):
|
||||||
|
"""
|
||||||
|
Support two authentication modes: password or private_key, and auth_type default is password.
|
||||||
|
"""
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
try:
|
||||||
|
if auth_type == 'password':
|
||||||
|
ssh.connect(
|
||||||
|
kwargs['hostname'],
|
||||||
|
kwargs['port'],
|
||||||
|
kwargs['username'],
|
||||||
|
kwargs['password'],
|
||||||
|
timeout=3,
|
||||||
|
)
|
||||||
|
kwargs['auth_type'] = 'password'
|
||||||
|
elif auth_type == 'private_key':
|
||||||
|
kwargs['auth_type'] = 'private_key'
|
||||||
|
private_key = paramiko.RSAKey.from_private_key_file(kwargs['private_key'])
|
||||||
|
ssh.connect(
|
||||||
|
kwargs['hostname'],
|
||||||
|
kwargs['port'],
|
||||||
|
kwargs['username'],
|
||||||
|
private_key,
|
||||||
|
timeout=3,
|
||||||
|
)
|
||||||
|
kwargs['status'] = 'succeed'
|
||||||
|
kwargs['error_message'] = ''
|
||||||
|
commands = self.get_commands()
|
||||||
|
for key, value in commands.items():
|
||||||
|
stdin, stdout, stderr = ssh.exec_command(value, timeout=5)
|
||||||
|
result = str(stdout.read()).strip('b').strip("'").strip('\\n')
|
||||||
|
kwargs[key] = result
|
||||||
|
except Exception as e:
|
||||||
|
msg = '%(exc)s hostname %(hostname)s' % {
|
||||||
|
'exc': e,
|
||||||
|
'hostname': kwargs['hostname']
|
||||||
|
}
|
||||||
|
error_logger.error(msg)
|
||||||
|
kwargs['status'] = 'failed'
|
||||||
|
kwargs['error_message'] = str(e)
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def password_login_execution(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Login to the remote system with a password.
|
||||||
|
Kwargs is a dict containing hostname, port, username and password.
|
||||||
|
Example: kwargs = {'hostname': '172.16.3.101', 'port': 22, 'username': 'root', 'password': 'paw123'}
|
||||||
|
"""
|
||||||
|
return self.login_execution(**kwargs)
|
||||||
|
|
||||||
|
def private_key_login_execution(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Login to the remote system with a private_key.
|
||||||
|
Kwargs is a dict containing hostname, port, username and private key.
|
||||||
|
Example:kwargs = {'hostname': '172.16.3.101', 'port': 22, 'username': 'root', 'private_key': '/root/.ssh/id_rsa'}
|
||||||
|
"""
|
||||||
|
return self.login_execution(auth_type='private_key', **kwargs)
|
||||||
|
|
||||||
|
def get_auth_type(self):
|
||||||
|
key = ['hosts', 'auth_type']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
def get_ssh_username(self):
|
||||||
|
key = ['hosts', 'ssh_username']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
def get_ssh_port(self):
|
||||||
|
key = ['hosts', 'ssh_port']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
def get_ssh_password(self):
|
||||||
|
key = ['hosts', 'ssh_password']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
def get_ssh_private_key(self):
|
||||||
|
key = ['hosts', 'ssh_private_key']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
def get_email(self):
|
||||||
|
key = ['hosts', 'email']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
def get_send_email(self):
|
||||||
|
key = ['hosts', 'send_email']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
|
|
||||||
|
def get_scan_type(self):
|
||||||
|
key = ['hosts', 'scan_type']
|
||||||
|
return self.get_conf_content(*key)
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
django==2.1.2
|
django==2.1.5
|
||||||
pillow==5.3.0
|
pillow==5.3.0
|
||||||
mysqlclient==1.3.13
|
mysqlclient==1.3.13
|
||||||
ipython==7.1.1
|
ipython==7.1.1
|
||||||
pyyaml==3.13
|
pyyaml==4.2b1
|
||||||
ruamel.yaml==0.15.80
|
ruamel.yaml==0.15.80
|
||||||
python-nmap==0.6.1
|
python-nmap==0.6.1
|
||||||
redis==3.0.1
|
redis==3.0.1
|
||||||
pymongo==3.7.1
|
pymongo==3.7.1
|
||||||
paramiko==2.4.2
|
paramiko==2.4.2
|
||||||
pycrypto==2.6.1
|
django-simple-history==2.6.0
|
||||||
celery==4.2.1
|
celery==4.2.1
|
||||||
celery-once==2.0.0
|
celery-once==2.0.0
|
||||||
flower==0.9.2
|
flower==0.9.2
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from .celery import app as celery_app
|
||||||
|
|
||||||
|
__all__ = ('celery_app')
|
||||||
38
sandboxMP/celery.py
Normal file
38
sandboxMP/celery.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
import os
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sandboxMP.settings')
|
||||||
|
|
||||||
|
app = Celery('sandbox')
|
||||||
|
|
||||||
|
app.config_from_object('django.conf:settings')
|
||||||
|
|
||||||
|
app.autodiscover_tasks()
|
||||||
|
|
||||||
|
BROKER_URL = 'redis://localhost:6379/0'
|
||||||
|
|
||||||
|
CELERY_RESULT_BACKEND = 'redis://localhost:6379/1'
|
||||||
|
|
||||||
|
CELERY_TIMEZONE = 'Asia/Shanghai'
|
||||||
|
|
||||||
|
CELERY_ENABLE_UTC = False
|
||||||
|
|
||||||
|
CELERYD_FORCE_EXECV = True
|
||||||
|
|
||||||
|
CELERYD_CONCURRENCY = 5
|
||||||
|
|
||||||
|
CELERY_ACKS_LATE = True
|
||||||
|
|
||||||
|
CELERYD_MAX_TASKS_PER_CHILD = 100
|
||||||
|
|
||||||
|
CELERYD_TASK_TIME_LIMIT = 60 * 5
|
||||||
|
|
||||||
|
|
||||||
|
app.conf.ONCE = {
|
||||||
|
'backend': 'celery_once.backends.Redis',
|
||||||
|
'settings': {
|
||||||
|
'url': 'redis://localhost:6379/2',
|
||||||
|
'default_timeout': 60 * 5
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ https://docs.djangoproject.com/en/2.1/ref/settings/
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from .celery import *
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
@@ -39,8 +41,10 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'simple_history',
|
||||||
'system',
|
'system',
|
||||||
'cmdb',
|
'cmdb',
|
||||||
|
'cmdb.templatetags',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -53,6 +57,7 @@ MIDDLEWARE = [
|
|||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'apps.system.middleware.MenuCollection',
|
'apps.system.middleware.MenuCollection',
|
||||||
'apps.system.middleware.RbacMiddleware',
|
'apps.system.middleware.RbacMiddleware',
|
||||||
|
'simple_history.middleware.HistoryRequestMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'sandboxMP.urls'
|
ROOT_URLCONF = 'sandboxMP.urls'
|
||||||
|
|||||||
2462
static/plugins/masonry/masonry.js
Normal file
2462
static/plugins/masonry/masonry.js
Normal file
File diff suppressed because it is too large
Load Diff
276
templates/cmdb/cabinet.html
Normal file
276
templates/cmdb/cabinet.html
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
{% extends "base-left.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/datatables/jquery.dataTables.min.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
|
<section class="content">
|
||||||
|
<div id="devlist">
|
||||||
|
<div class="box box-primary" id="liebiao">
|
||||||
|
<div class="box-header">
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnRefresh" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-repeat"></i>刷新
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group pull-left"> </div>
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnCreate" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-plus"></i>新增
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="btn-group pull-left"> </div>
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnDelete" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i>删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
<form class="form-inline" id="queryForm">
|
||||||
|
<div class="form-group searchArea margin-r-5 margin-top-5">
|
||||||
|
<label>机柜编号:</label>
|
||||||
|
<input type="text" name="number" class="form-control inputText" id="number">
|
||||||
|
</div>
|
||||||
|
<div class="form-group searchArea margin-r-5 margin-top-5">
|
||||||
|
<label>机柜位置:</label>
|
||||||
|
<input type="text" name="position" class="form-control inputText" id="position">
|
||||||
|
</div>
|
||||||
|
<button type="button" id="btnSearch" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-search"></i>查询
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box-body">
|
||||||
|
<table id="dtbList" class="display" cellspacing="0" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr valign="middle">
|
||||||
|
<th><input type="checkbox" id="checkAll"></th>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>机柜编号</th>
|
||||||
|
<th>机柜位置</th>
|
||||||
|
<th>备注信息</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br> <br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- /.content -->
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
|
||||||
|
<script src="{% static 'plugins/datatables/jquery.dataTables.min.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/datatables/dataTables.const-1.js' %}"></script>
|
||||||
|
<script src="{% static 'js/plugins/layer/layer.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// 菜单选中高亮
|
||||||
|
$(function () {
|
||||||
|
$('#CMDB-EAM').addClass('active');
|
||||||
|
$('#CMDB-EAM-CABINET').addClass('active');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// datatables 初始化配置
|
||||||
|
var oDataTable = null;
|
||||||
|
$(function () {
|
||||||
|
oDataTable = initTable();
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
var oTable = $('#dtbList').DataTable($.extend(true, {},
|
||||||
|
DATATABLES_CONSTANT.DATA_TABLES.SERVER_SIDE_OPTION,
|
||||||
|
|
||||||
|
{
|
||||||
|
ajax: {
|
||||||
|
"url": "{% url 'cmdb:eam-cabinet-list' %}",
|
||||||
|
"data": function (d) {
|
||||||
|
d.number = $("#number").val();
|
||||||
|
d.position = $("#position").val();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
DATATABLES_CONSTANT.DATA_TABLES.COLUMN.CHECKBOX,
|
||||||
|
{
|
||||||
|
data: "id",
|
||||||
|
width: "5%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "number",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "position",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "desc",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "id",
|
||||||
|
width: "10%",
|
||||||
|
bSortable: "false",
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
var ret = "";
|
||||||
|
var ret = "<button title='详情-修改' onclick='doUpdate("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-pencil'></i></button>";
|
||||||
|
ret = ret + "<button title='删除' onclick='doDelete("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-trash'></i></button>";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}));
|
||||||
|
return oTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新数据
|
||||||
|
$("#btnRefresh").click(function () {
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
});
|
||||||
|
//新建数据
|
||||||
|
$("#btnCreate").click(function () {
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '新增',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '400px'],
|
||||||
|
content: "{% url 'cmdb:eam-cabinet-create' %}",
|
||||||
|
end: function () {
|
||||||
|
//关闭时做的事情
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//修改数据
|
||||||
|
function doUpdate(id) {
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '编辑',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '400px'],
|
||||||
|
content: ["{% url 'cmdb:eam-cabinet-update' %}" + '?id=' + id, 'no'],
|
||||||
|
end: function () {
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//checkbox全选
|
||||||
|
$("#checkAll").on("click", function () {
|
||||||
|
if ($(this).prop("checked") === true) {
|
||||||
|
$("input[name='checkList']").prop("checked", $(this).prop("checked"));
|
||||||
|
$('#example tbody tr').addClass('selected');
|
||||||
|
} else {
|
||||||
|
$("input[name='checkList']").prop("checked", false);
|
||||||
|
$('#example tbody tr').removeClass('selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//批量删除
|
||||||
|
$("#btnDelete").click(function () {
|
||||||
|
if ($("input[name='checkList']:checked").length == 0) {
|
||||||
|
layer.msg("请选择要删除的记录");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrId = new Array();
|
||||||
|
$("input[name='checkList']:checked").each(function () {
|
||||||
|
//alert($(this).val());
|
||||||
|
arrId.push($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
sId = arrId.join(',');
|
||||||
|
|
||||||
|
layer.alert('确定删除吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:eam-cabinet-delete' %}",
|
||||||
|
data: {"id": sId, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert("操作成功", {icon: 1});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
} else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert("操作失败", {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//删除单个数据
|
||||||
|
function doDelete(id) {
|
||||||
|
layer.alert('确定删除吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:eam-cabinet-delete' %}",
|
||||||
|
data: {"id": id, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('删除成功', {icon: 1});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
} else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert('删除失败', {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//select2
|
||||||
|
$(function () {
|
||||||
|
//Initialize Select2 Elements
|
||||||
|
$(".select2").select2();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btnSearch").click(function(){
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
99
templates/cmdb/cabinet_form.html
Normal file
99
templates/cmdb/cabinet_form.html
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
{% extends 'base-layer.html' %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
|
||||||
|
<!-- iCheck for checkboxes and radio inputs -->
|
||||||
|
{% endblock %}
|
||||||
|
{% block main %}
|
||||||
|
<div class="box box-danger">
|
||||||
|
<form class="form-horizontal" id="addForm" method="post">
|
||||||
|
<input type="hidden" name='id' value="{{ cabinet.id }}" />
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="box-body">
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
<h4>机柜信息</h4>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">机柜编号</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="number" type="text" value="{{ cabinet.number }}"/>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">机柜位置</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="position" type="text" value="{{ cabinet.position }}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">描述信息</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input class="form-control" id="desc" name="desc" type="text" value="{{ cabinet.desc }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="box-footer ">
|
||||||
|
<div class="row span7 text-center ">
|
||||||
|
<button type="button" id="btnCancel" class="btn btn-default margin-right ">重置</button>
|
||||||
|
<button type="button" id="btnSave" class="btn btn-info margin-right ">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
<script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function getUrl() {
|
||||||
|
if ($("input[name='id']").val()) {
|
||||||
|
var url = "{% url 'cmdb:eam-cabinet-update' %}";
|
||||||
|
} else {
|
||||||
|
var url = "{% url 'cmdb:eam-cabinet-create' %}";
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#btnSave").click(function () {
|
||||||
|
var data = $("#addForm").serialize();
|
||||||
|
$.ajax({
|
||||||
|
type: $("#addForm").attr('method'),
|
||||||
|
url: getUrl(),
|
||||||
|
data: data,
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('数据保存成功!', {icon: 1}, function (index) {
|
||||||
|
parent.layer.closeAll(); //关闭所有弹窗
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layer.alert(msg.error, {icon: 5});
|
||||||
|
//$('errorMessage').html(msg.message)
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*点取消刷新新页面*/
|
||||||
|
$("#btnCancel").click(function () {
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
//Initialize Select2 Elements
|
||||||
|
$(".select2").select2();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<label class="col-sm-2 control-label">描述信息</label>
|
<label class="col-sm-2 control-label">描述信息</label>
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<input class="form-control" id="desc" name="desc" type="text" />
|
<input class="form-control" id="desc" name="desc" type="text" value="{{ code.desc }}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
329
templates/cmdb/device_scan.html
Normal file
329
templates/cmdb/device_scan.html
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
{% extends "base-left.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/datatables/jquery.dataTables.min.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
|
<section class="content">
|
||||||
|
<div id="devlist">
|
||||||
|
<div class="box box-primary" id="liebiao">
|
||||||
|
<div class="box-header">
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnRefresh" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-repeat"></i> 刷新
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group pull-left"> </div>
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnScan" class="btn btn-default" onclick="doScan()">
|
||||||
|
<i class="glyphicon glyphicon-search"></i> 执行扫描
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group pull-left"> </div>
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnInbound" class="btn btn-default" onclick="doInbound()">
|
||||||
|
<i class="glyphicon glyphicon-floppy-disk"></i> 执行入库
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group pull-left"> </div>
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnDelete" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> 删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box-body">
|
||||||
|
<table id="dtbList" class="display" cellspacing="0" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr valign="middle">
|
||||||
|
<th><input type="checkbox" id="checkAll"></th>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>主机名</th>
|
||||||
|
<th>IP地址</th>
|
||||||
|
<th>MAC地址</th>
|
||||||
|
<th>认证类型</th>
|
||||||
|
<th>登陆状态</th>
|
||||||
|
<th>系统类型</th>
|
||||||
|
<th>设备类型</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<small>点击【执行入库】可将扫描结果中,登陆状态为:成功(succeed)的设备数据导入正式设备管理数据库。</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- /.content -->
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
|
||||||
|
<script src="{% static 'plugins/datatables/jquery.dataTables.min.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/datatables/dataTables.const-1.js' %}"></script>
|
||||||
|
<script src="{% static 'js/plugins/layer/layer.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// 菜单选中高亮
|
||||||
|
$(function () {
|
||||||
|
$('#CMDB-PORTAL').addClass('active');
|
||||||
|
$('#CDMB-PORTAL-DEVICE_SCAN').addClass('active');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// datatables 初始化配置
|
||||||
|
var oDataTable = null;
|
||||||
|
$(function () {
|
||||||
|
oDataTable = initTable();
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
var oTable = $('#dtbList').DataTable($.extend(true, {},
|
||||||
|
DATATABLES_CONSTANT.DATA_TABLES.SERVER_SIDE_OPTION,
|
||||||
|
|
||||||
|
{
|
||||||
|
ajax: {
|
||||||
|
"url": "{% url 'cmdb:portal-device_scan-list' %}",
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
DATATABLES_CONSTANT.DATA_TABLES.COLUMN.CHECKBOX,
|
||||||
|
{
|
||||||
|
data: "id",
|
||||||
|
width: "5%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "sys_hostname",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "hostname",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "mac_address",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "auth_type",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "status",
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
if (data == "succeed") {
|
||||||
|
var ret = "<button class='btn btn-info btn-xs'>成功</button>";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (data == "failed") {
|
||||||
|
var ret = "<button class='btn btn-danger btn-xs'>失败</button>";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var ret = "<button class='btn btn-default btn-xs'>未知</button>";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "os_type",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "device_type",
|
||||||
|
//width : "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "id",
|
||||||
|
width: "10%",
|
||||||
|
bSortable: "false",
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
var ret = "";
|
||||||
|
var ret = "<button title='详情' onclick='doDetail("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-list-alt'></i></button>";
|
||||||
|
ret = ret + "<button title='删除' onclick='doDelete("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-trash'></i></button>";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}));
|
||||||
|
return oTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新数据
|
||||||
|
$("#btnRefresh").click(function () {
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function doDetail(id){
|
||||||
|
window.location.href="/cmdb/portal/device_scan/detail/?id="+id;
|
||||||
|
}
|
||||||
|
|
||||||
|
//checkbox全选
|
||||||
|
$("#checkAll").on("click", function () {
|
||||||
|
if ($(this).prop("checked") === true) {
|
||||||
|
$("input[name='checkList']").prop("checked", $(this).prop("checked"));
|
||||||
|
$('#example tbody tr').addClass('selected');
|
||||||
|
} else {
|
||||||
|
$("input[name='checkList']").prop("checked", false);
|
||||||
|
$('#example tbody tr').removeClass('selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//批量删除
|
||||||
|
$("#btnDelete").click(function () {
|
||||||
|
if ($("input[name='checkList']:checked").length == 0) {
|
||||||
|
layer.msg("请选择要删除的记录");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrId = new Array();
|
||||||
|
$("input[name='checkList']:checked").each(function () {
|
||||||
|
//alert($(this).val());
|
||||||
|
arrId.push($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
sId = arrId.join(',');
|
||||||
|
|
||||||
|
layer.alert('确定删除吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:portal-device_scan-delete' %}",
|
||||||
|
data: {"id": sId, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert("操作成功", {icon: 1});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
} else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert("操作失败", {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//删除单个数据
|
||||||
|
function doDelete(id) {
|
||||||
|
layer.alert('确定删除吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:portal-device_scan-delete' %}",
|
||||||
|
data: {"id": id, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('删除成功', {icon: 1});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
} else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert('删除失败', {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//资产扫描
|
||||||
|
function doScan() {
|
||||||
|
layer.alert('确定开始扫描吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: "{% url 'cmdb:portal-device_scan-exec' %}",
|
||||||
|
cache: false,
|
||||||
|
beforeSend:function(){
|
||||||
|
this.layerIndex = layer.load(2, {
|
||||||
|
shade: [0.1,'#fff']
|
||||||
|
});
|
||||||
|
},
|
||||||
|
success: function (msg) {
|
||||||
|
layer.closeAll('loading');
|
||||||
|
if (msg.status == 'success') {
|
||||||
|
layer.alert('扫描任务已下发', {icon: 1});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
else if (msg.status == 'already_queued') {
|
||||||
|
layer.alert('当前已有扫描任务正在执行', {icon: 4});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert('扫描失败', {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doInbound() {
|
||||||
|
layer.alert('确定将扫描结果导入设备管理库吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3
|
||||||
|
, time: 0
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:portal-device_scan-inbound' %}",
|
||||||
|
data: {csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('设备已入库', {icon: 1});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert('设备入库失败', {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
113
templates/cmdb/device_scan_detail.html
Normal file
113
templates/cmdb/device_scan_detail.html
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
{% extends "base-left.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
|
|
||||||
|
<section class="content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">设备详情</h3>
|
||||||
|
|
||||||
|
<div class="box-tools">
|
||||||
|
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i
|
||||||
|
class="fa fa-minus"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-body no-padding">
|
||||||
|
<div class="btn-group pull-right margin">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs margin-r-5" title="返回" id="btnReturn">
|
||||||
|
<i class="fa fa-undo"> 返回</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive mailbox-messages">
|
||||||
|
<table class="table" id="tbWorkList" style="white-space: nowrap;">
|
||||||
|
<tbody>
|
||||||
|
<tr class="info">
|
||||||
|
<td width="10%"><strong>主机名</strong></td>
|
||||||
|
<td class="text-left">{{ device.sys_hostname }}</td>
|
||||||
|
<td width="10%"><strong>SN编号</strong></td>
|
||||||
|
<td class="text-left">{{ device.sn_number }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>SSH用户名</strong></td>
|
||||||
|
<td>{{ device.username }}</td>
|
||||||
|
<td><strong>SSH端口</strong></td>
|
||||||
|
<td>{{ device.port }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="info">
|
||||||
|
<td><strong>认证类型</strong></td>
|
||||||
|
<td>{{ device.auth_type }}</td>
|
||||||
|
<td><strong>登陆状态</strong></td>
|
||||||
|
<td>{{ device.status }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>IP地址</strong></td>
|
||||||
|
<td>{{ device.hostname }}</td>
|
||||||
|
<td><strong>MAC地址</strong></td>
|
||||||
|
<td>{{ device.mac_address }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="info">
|
||||||
|
<td><strong>系统类型</strong></td>
|
||||||
|
<td>{{ device.os_type }}</td>
|
||||||
|
<td><strong>设备类型</strong></td>
|
||||||
|
<td>{{ device.device_type }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>入库时间</strong></td>
|
||||||
|
<td>{{ device.add_time }}</td>
|
||||||
|
<td><strong>变更时间</strong></td>
|
||||||
|
<td>{{ device.modify_time }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="info">
|
||||||
|
<td><strong>错误信息</strong></td>
|
||||||
|
<td colspan="3">{{ device.error_message }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="box-footer margin-b-10">
|
||||||
|
<small>该设备信息为自动扫描入库设备,不提供修改功能,可通过管理页面【执行入库】按钮将登陆状态为:成功(succeed)的设备迁移到正式设备管理库</small>
|
||||||
|
</div>
|
||||||
|
<!-- /.box-footer -->
|
||||||
|
</div>
|
||||||
|
<!-- /.box-body -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
<!-- TO DO List -->
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- /.content -->
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
<script src="{% static 'js/plugins/layer/layer.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/masonry/masonry.js' %}"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
$('#CMDB-PORTAL').addClass('active');
|
||||||
|
$('#CDMB-PORTAL-DEVICE_SCAN').addClass('active');
|
||||||
|
|
||||||
|
});
|
||||||
|
//返回
|
||||||
|
$("#btnReturn").click(function () {
|
||||||
|
history.back();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
347
templates/cmdb/deviceinfo.html
Normal file
347
templates/cmdb/deviceinfo.html
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
{% extends "base-left.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/datatables/jquery.dataTables.min.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
|
<section class="content">
|
||||||
|
<div id="devlist">
|
||||||
|
<div class="box box-primary" id="liebiao">
|
||||||
|
<div class="box-header">
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnRefresh" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-repeat"></i>刷新
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group pull-left"> </div>
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnCreate" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-plus"></i>新增
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="btn-group pull-left"> </div>
|
||||||
|
<div class="btn-group pull-left">
|
||||||
|
<button type="button" id="btnDelete" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i>删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-header">
|
||||||
|
<form class="form-inline" id="queryForm">
|
||||||
|
<div class="form-group searchArea margin-r-5 margin-top-5">
|
||||||
|
<label>主机名:</label>
|
||||||
|
<input type="text" name="sys_hostname" class="form-control inputText" id="sys_hostname">
|
||||||
|
</div>
|
||||||
|
<div class="form-group searchArea margin-r-5 margin-top-5">
|
||||||
|
<label>设备地址:</label>
|
||||||
|
<input type="text" name="hostname" class="form-control inputText" id="hostname">
|
||||||
|
</div>
|
||||||
|
<div class="form-group searchArea margin-r-5 margin-top-5">
|
||||||
|
<label>网络类型:</label>
|
||||||
|
<select class="form-control inputText select2" name="network_type" id="network_type">
|
||||||
|
<option></option>
|
||||||
|
{% for code in all_code %}
|
||||||
|
{% ifequal code.parent.key 'NETWORK_TYPE' %}
|
||||||
|
<option value="{{ code.id }}">{{ code.value }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group searchArea margin-r-5 margin-top-5">
|
||||||
|
<label>服务类型:</label>
|
||||||
|
<select class="form-control inputText select2" name="service_type" , id="service_type">
|
||||||
|
<option></option>
|
||||||
|
{% for code in all_code %}
|
||||||
|
{% if code.parent.key == 'SERVICE_TYPE' %}
|
||||||
|
<option value="{{ code.id }}">{{ code.value }}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group searchArea margin-r-5 margin-top-5">
|
||||||
|
<label>业务类型:</label>
|
||||||
|
<select class="form-control inputText select2" name="operation_type" , id="operation_type">
|
||||||
|
<option></option>
|
||||||
|
{% for code in all_code %}
|
||||||
|
{% if code.parent.key == 'OPERATION_TYPE' %}
|
||||||
|
<option value="{{ code.id }}">{{ code.value }}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="btnSearch" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-search"></i>查询
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box-body">
|
||||||
|
<table id="dtbList" class="display" cellspacing="0" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr valign="middle">
|
||||||
|
<th><input type="checkbox" id="checkAll"></th>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>主机名</th>
|
||||||
|
<th>SN编号</th>
|
||||||
|
<th>系统类型</th>
|
||||||
|
<th>设备类型</th>
|
||||||
|
<th>设备地址</th>
|
||||||
|
<th>MAC地址</th>
|
||||||
|
<th>责任人</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br> <br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- /.content -->
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
|
||||||
|
<script src="{% static 'plugins/datatables/jquery.dataTables.min.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/datatables/dataTables.const-1.js' %}"></script>
|
||||||
|
<script src="{% static 'js/plugins/layer/layer.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// 菜单选中高亮
|
||||||
|
$(function () {
|
||||||
|
$('#CMDB-EAM').addClass('active');
|
||||||
|
$('#CMDB-EAM-DEVICE').addClass('active');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// datatables 初始化配置
|
||||||
|
var oDataTable = null;
|
||||||
|
$(function () {
|
||||||
|
oDataTable = initTable();
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
var oTable = $('#dtbList').DataTable($.extend(true, {},
|
||||||
|
DATATABLES_CONSTANT.DATA_TABLES.SERVER_SIDE_OPTION,
|
||||||
|
|
||||||
|
{
|
||||||
|
ajax: {
|
||||||
|
"url": "{% url 'cmdb:eam-device-list' %}",
|
||||||
|
"data": function (d) {
|
||||||
|
d.sys_hostname = $("#sys_hostname").val();
|
||||||
|
d.hostname = $("#hostname").val();
|
||||||
|
d.network_type = $("#network_type").val();
|
||||||
|
d.service_type = $("#service_type").val();
|
||||||
|
d.operation_type = $("#operation_type").val();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
DATATABLES_CONSTANT.DATA_TABLES.COLUMN.CHECKBOX,
|
||||||
|
|
||||||
|
{
|
||||||
|
data: "id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "sys_hostname",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "sn_number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "os_type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "device_type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "hostname",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "mac_address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "leader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "id",
|
||||||
|
bSortable: "false",
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
var ret = "<button title='详情' onclick='doDetail("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-list-alt'></i></button>";
|
||||||
|
ret = ret + "<button title='修改' onclick='doUpdate("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-pencil'></i></button>";
|
||||||
|
ret = ret + "<button title='认证管理' onclick='doDevice2Connection("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-user'></i></button>";
|
||||||
|
ret = ret + "<button title='删除' onclick='doDelete("
|
||||||
|
+ data + ")'><i class='glyphicon glyphicon-trash'></i></button>";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}));
|
||||||
|
return oTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新数据
|
||||||
|
$("#btnRefresh").click(function () {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
//新建数据
|
||||||
|
$("#btnCreate").click(function () {
|
||||||
|
var div=layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '新增',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '400px'],
|
||||||
|
content: "{% url 'cmdb:eam-device-create' %}",
|
||||||
|
end: function () {
|
||||||
|
//关闭时做的事情
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layer.full(div )
|
||||||
|
});
|
||||||
|
|
||||||
|
//修改数据
|
||||||
|
function doUpdate(id) {
|
||||||
|
var div=layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '编辑',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '400px'],
|
||||||
|
content: ["{% url 'cmdb:eam-device-update' %}" + '?id=' + id, 'no'],
|
||||||
|
end: function () {
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layer.full(div )
|
||||||
|
}
|
||||||
|
|
||||||
|
//checkbox全选
|
||||||
|
$("#checkAll").on("click", function () {
|
||||||
|
if ($(this).prop("checked") === true) {
|
||||||
|
$("input[name='checkList']").prop("checked", $(this).prop("checked"));
|
||||||
|
$('#example tbody tr').addClass('selected');
|
||||||
|
} else {
|
||||||
|
$("input[name='checkList']").prop("checked", false);
|
||||||
|
$('#example tbody tr').removeClass('selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//批量删除
|
||||||
|
$("#btnDelete").click(function () {
|
||||||
|
if ($("input[name='checkList']:checked").length == 0) {
|
||||||
|
layer.msg("请选择要删除的记录");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrId = new Array();
|
||||||
|
$("input[name='checkList']:checked").each(function () {
|
||||||
|
//alert($(this).val());
|
||||||
|
arrId.push($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
sId = arrId.join(',');
|
||||||
|
|
||||||
|
layer.alert('确定删除吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:eam-device-delete' %}",
|
||||||
|
data: {"id": sId, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert("操作成功", {icon: 1});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
} else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert("操作失败", {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//删除单个数据
|
||||||
|
function doDelete(id) {
|
||||||
|
layer.alert('确定删除吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:eam-device-delete' %}",
|
||||||
|
data: {"id": id, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('删除成功', {icon: 1});
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
} else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert('删除失败', {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//select2
|
||||||
|
$(function () {
|
||||||
|
//Initialize Select2 Elements
|
||||||
|
$(".select2").select2();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btnSearch").click(function(){
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function doDevice2Connection(id) {
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '认证管理',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '400px'],
|
||||||
|
content: ["{% url 'cmdb:eam-device-device2connection' %}" + '?id=' + id, 'no'],
|
||||||
|
end: function () {
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doDetail(id){
|
||||||
|
window.location.href="{% url 'cmdb:eam-device-detail' %}?id="+id;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
108
templates/cmdb/deviceinfo2connection.html
Normal file
108
templates/cmdb/deviceinfo2connection.html
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
{% extends 'base-layer.html' %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
|
||||||
|
<!-- iCheck for checkboxes and radio inputs -->
|
||||||
|
{% endblock %}
|
||||||
|
{% block main %}
|
||||||
|
<div class="box box-danger">
|
||||||
|
<form class="form-horizontal" id="addForm" method="post">
|
||||||
|
<input type="hidden" name='id' value="{{ connection_info.id }}" />
|
||||||
|
<input type="hidden" name='hostname' value="{{ device.hostname }}" />
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="box-body">
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
<h4>关联设备:{{ device.sys_hostname }}({{ device.hostname }})</h4>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">用户名</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="username" type="text" value="{{ connection_info.username }}"/>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">认证类型</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control select2" style="width:100%;" name="auth_type">
|
||||||
|
<option value="password" {% ifequal connection_info.auth_type 'password' %}selected="selected"{% endifequal %}>密码</option>
|
||||||
|
<option value="private_key" {% ifequal connection_info.auth_type 'private_key' %}selected="selected"{% endifequal %}>密钥</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">密码</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="password" type="password" value="{{ connection_info.password }}"/>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">密钥</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="private_key" type="text" value="{{ connection_info.private_key }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">端口</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="port" type="text" value="{{ connection_info.port }}"/>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">状态</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="status" type="text" value="{{ connection_info.status }}" readonly/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="box-footer ">
|
||||||
|
<div class="row span7 text-center ">
|
||||||
|
<button type="button" id="btnCancel" class="btn btn-default margin-right ">重置</button>
|
||||||
|
<button type="button" id="btnSave" class="btn btn-info margin-right ">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
<script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
||||||
|
$("#btnSave").click(function () {
|
||||||
|
var data = $("#addForm").serialize();
|
||||||
|
$.ajax({
|
||||||
|
type: $("#addForm").attr('method'),
|
||||||
|
url: "{% url 'cmdb:eam-device-device2connection' %}",
|
||||||
|
data: data,
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('数据保存成功!', {icon: 1}, function (index) {
|
||||||
|
parent.layer.closeAll(); //关闭所有弹窗
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layer.alert(msg.error, {icon: 5});
|
||||||
|
//$('errorMessage').html(msg.message)
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*点取消刷新新页面*/
|
||||||
|
$("#btnCancel").click(function () {
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
//Initialize Select2 Elements
|
||||||
|
$(".select2").select2();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
316
templates/cmdb/deviceinfo_detail.html
Normal file
316
templates/cmdb/deviceinfo_detail.html
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
{% extends "base-left.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load extra_tags %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<section class="content">
|
||||||
|
|
||||||
|
<div class="nav-tabs-custom">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active"><a href="#activity" data-toggle="tab">设备详情</a></li>
|
||||||
|
<li><a href="#history" data-toggle="tab">历史纪录</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="active tab-pane" id="activity">
|
||||||
|
<div class="box-body no-padding">
|
||||||
|
<div class="btn-group pull-right margin">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm margin-r-5"
|
||||||
|
onclick="doUpload({{ device.id }})">
|
||||||
|
<i class="fa fa-cloud-upload"> 上传资料</i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm margin-r-5" title="认证管理" onclick="doDevice2Connection({{ device.id }})">
|
||||||
|
<i class="fa fa-user"> 认证管理</i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm margin-r-5" title="自动更新" onclick="doAutoUpdate({{ device.id }})">
|
||||||
|
<i class="fa fa-circle-o-notch"> 自动更新</i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm margin-r-5" title="编辑" onclick="doUpdate({{ device.id }})">
|
||||||
|
<i class="fa fa-pencil"> 编辑</i>
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnReturn" class="btn btn-primary btn-sm">
|
||||||
|
<i class="fa fa-arrow-left"></i> 返回
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive mailbox-messages">
|
||||||
|
<table class="table" id="tbWorkList" style="white-space: nowrap;">
|
||||||
|
<tbody>
|
||||||
|
<tr class="info">
|
||||||
|
<td width="10%"><strong>主机名</strong></td>
|
||||||
|
<td class="text-left">{{ device.sys_hostname }}</td>
|
||||||
|
<td width="10%"><strong>SN编号</strong></td>
|
||||||
|
<td class="text-left">{{ device.sn_number }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>系统类型</strong></td>
|
||||||
|
<td>{{ device.os_type }}</td>
|
||||||
|
<td><strong>设备类型</strong></td>
|
||||||
|
<td>{{ device.device_type }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="info">
|
||||||
|
<td><strong>设备地址</strong></td>
|
||||||
|
<td>{{ device.hostname }}</td>
|
||||||
|
<td><strong>MAC地址</strong></td>
|
||||||
|
<td>{{ device.mac_address }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>网络类型</strong></td>
|
||||||
|
<td>{% get_con all_code device.network_type 'value' %}</td>
|
||||||
|
<td><strong>服务类型</strong></td>
|
||||||
|
<td>{% get_con all_code device.service_type 'value' %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="info">
|
||||||
|
<td><strong>业务类型</strong></td>
|
||||||
|
<td>{% get_con all_code device.operation_type 'value' %}</td>
|
||||||
|
<td><strong>机柜信息</strong></td>
|
||||||
|
<td>{% get_con all_cabinet device.dev_cabinet 'number' %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>购买日期</strong></td>
|
||||||
|
<td>{{ device.buyDate }}</td>
|
||||||
|
<td><strong>质保日期</strong></td>
|
||||||
|
<td>{{ device.warrantyDate }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="info">
|
||||||
|
<td><strong>所属</strong></td>
|
||||||
|
<td>{% if device.parent %}
|
||||||
|
{{ device.parent.sys_hostname }}({{ device.parent.hostname }})
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td><strong>责任人</strong></td>
|
||||||
|
<td>{% get_con all_user device.leader 'name' %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>入库时间</strong></td>
|
||||||
|
<td>{{ device.add_time }}</td>
|
||||||
|
<td><strong>最后变更</strong></td>
|
||||||
|
<td>{{ device.modify_time }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="info">
|
||||||
|
<td><strong>最后操作人</strong></td>
|
||||||
|
<td>{{ device.changed_by.name }}</td>
|
||||||
|
<td><strong></strong></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>备注信息</strong></td>
|
||||||
|
<td colspan="3">{{ device.desc }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="box-footer">
|
||||||
|
|
||||||
|
<ul class="mailbox-attachments clearfix" id="imageContainer">
|
||||||
|
{% for file in all_file %}
|
||||||
|
<li class="imageItem">
|
||||||
|
<div class="mailbox-attachment-info">
|
||||||
|
<a href="/media/{{ file.file_content }}" target="_blank"><i
|
||||||
|
class="fa fa-file-text"></i>
|
||||||
|
<small>{{ file.file_content|cut:'asset_file/' }}</small>
|
||||||
|
</a>
|
||||||
|
<span class="mailbox-attachment-size">
|
||||||
|
<b>上传人</b>:{{ file.upload_user }}
|
||||||
|
<a href="/media/{{ file.file_content }}" download="{{ file.file_content }}"
|
||||||
|
class="btn btn-primary btn-xs pull-right">
|
||||||
|
<i class="fa fa-cloud-download" title="下载文件"></i>
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-adn btn-xs pull-right margin-r-5"
|
||||||
|
onclick="doDelete({{ file.id }})">
|
||||||
|
<i class="fa fa-trash" title="删除文件"> </i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.tab-pane -->
|
||||||
|
<div class="tab-pane" id="history">
|
||||||
|
<div class="box-body">
|
||||||
|
<ul class="todo-list">
|
||||||
|
{% for log in logs %}
|
||||||
|
<li>
|
||||||
|
<!-- drag handle -->
|
||||||
|
<span class="handle">
|
||||||
|
<small class="text-maroon">
|
||||||
|
<i class="glyphicon glyphicon-time"></i>
|
||||||
|
{{ log.history_date }}
|
||||||
|
|
||||||
|
{{ log.changed_by }}
|
||||||
|
|
||||||
|
{{ log.history_type }}
|
||||||
|
</small>
|
||||||
|
</span>
|
||||||
|
<span class="text-sm">
|
||||||
|
{{ log.changes | compare_result }}
|
||||||
|
</span>
|
||||||
|
<!--
|
||||||
|
<button class="btn btn-xs btn-danger pull-right">还原数据</button>
|
||||||
|
-->
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- /.tab-pane -->
|
||||||
|
</div>
|
||||||
|
<!-- /.tab-content -->
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- /.content -->
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
<script src="{% static 'js/plugins/layer/layer.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/masonry/masonry.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
$('#CMDB-EAM').addClass('active');
|
||||||
|
$('#CMDB-EAM-DEVICE').addClass('active');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btnReturn").click(function(){
|
||||||
|
history.back();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 资产文件瀑布流
|
||||||
|
|
||||||
|
$('#imageContainer').masonry({
|
||||||
|
columnWidth: 10,
|
||||||
|
itemSelector: '.imageItem'
|
||||||
|
});
|
||||||
|
|
||||||
|
//上传资料
|
||||||
|
function doUpload(id) {
|
||||||
|
var div = layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '上传设备文件',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['770px', '400px'],
|
||||||
|
content: ["/cmdb/eam/device/upload/" + '?id=' + id],
|
||||||
|
end: function () {
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layer.full(div)
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除文件
|
||||||
|
function doDelete(id) {
|
||||||
|
layer.alert('确定删除吗?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3
|
||||||
|
, time: 0
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'cmdb:eam-device-file_delete' %}",
|
||||||
|
data: {"id": id, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('删除成功', {icon: 1}, function () {
|
||||||
|
parent.location.reload()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert('删除失败', {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//自动更新设备信息
|
||||||
|
function doAutoUpdate(id) {
|
||||||
|
layer.alert('确定自动更新设备信息?', {
|
||||||
|
title: '提示'
|
||||||
|
, icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
|
||||||
|
, time: 0 //不自动关闭
|
||||||
|
, btn: ['YES', 'NO']
|
||||||
|
, yes: function (index) {
|
||||||
|
layer.close(index);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
data: {"id": id, csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||||
|
url: "{% url 'cmdb:eam-device-auto_update_device_info' %}",
|
||||||
|
cache: false,
|
||||||
|
beforeSend:function(){
|
||||||
|
this.layerIndex = layer.load(2, {
|
||||||
|
shade: [0.1,'#fff']
|
||||||
|
});
|
||||||
|
},
|
||||||
|
success: function (msg) {
|
||||||
|
layer.closeAll('loading');
|
||||||
|
if (msg.status == 'success') {
|
||||||
|
layer.alert('设备信息更新成功!', {icon: 1}, function(){
|
||||||
|
parent.location.reload()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (msg.status == 'con_empty') {
|
||||||
|
layer.alert('请先添加认证信息!', {icon: 4});
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//alert(msg.message);
|
||||||
|
layer.alert('设备信息更新失败!', {icon: 2});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改数据
|
||||||
|
function doUpdate(id) {
|
||||||
|
var div=layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '编辑',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '400px'],
|
||||||
|
content: ["{% url 'cmdb:eam-device-update' %}" + '?id=' + id, 'no'],
|
||||||
|
end: function () {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layer.full(div )
|
||||||
|
}
|
||||||
|
|
||||||
|
function doDevice2Connection(id) {
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '认证管理',
|
||||||
|
shadeClose: false,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['800px', '400px'],
|
||||||
|
content: ["{% url 'cmdb:eam-device-device2connection' %}" + '?id=' + id, 'no'],
|
||||||
|
end: function () {
|
||||||
|
oDataTable.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
221
templates/cmdb/deviceinfo_form.html
Normal file
221
templates/cmdb/deviceinfo_form.html
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
{% extends 'base-layer.html' %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{%static 'plugins/select2/select2.min.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap-datetimepicker.min.css' %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<div class="box box-danger">
|
||||||
|
<form class="form-horizontal" id="addForm" method="post">
|
||||||
|
<input type="hidden" name='id' value="{{ deviceinfo.id }}" />
|
||||||
|
<input type="hidden" name='changed_by' value="{{ request.user.id }}" />
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="box-body">
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
</legend>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">主机名</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="sys_hostname" type="text" value="{{ deviceinfo.sys_hostname }}" />
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">SN编号</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="sn_number" type="text" value="{{ deviceinfo.sn_number }}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">系统类型</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="os_type" type="text" value="{{ deviceinfo.os_type }}" />
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">设备类型</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="device_type" type="text" value="{{ deviceinfo.device_type }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">设备地址</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="hostname" type="text" value="{{ deviceinfo.hostname }}" />
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">MAC地址</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input class="form-control" name="mac_address" type="text" value="{{ deviceinfo.mac_address }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">网络类型</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control select2" style="width:100%;" name="network_type">
|
||||||
|
<option {% ifequal deviceinfo.network_type '' %}selected="selected"{% endifequal %}></option>
|
||||||
|
{% for code in all_code %}
|
||||||
|
{% ifequal code.parent.key 'NETWORK_TYPE' %}
|
||||||
|
<option value="{{ code.id }}" {% ifequal deviceinfo.network_type code.id %}selected="selected"{% endifequal %}>
|
||||||
|
{{ code.value }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">服务类型</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control select2" style="width:100%;" name="service_type">
|
||||||
|
<option {% ifequal deviceinfo.service_type '' %}selected="selected"{% endifequal %}></option>
|
||||||
|
{% for code in all_code %}
|
||||||
|
{% if code.parent.key == 'SERVICE_TYPE' %}
|
||||||
|
<option value="{{ code.id }}" {% ifequal deviceinfo.service_type code.id %}selected="selected"{% endifequal %}>
|
||||||
|
{{ code.value }}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">业务类型</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control select2" style="width:100%;" name="operation_type">
|
||||||
|
<option {% ifequal deviceinfo.operation_type '' %}selected="selected"{% endifequal %}></option>
|
||||||
|
{% for code in all_code %}
|
||||||
|
{% if code.parent.key == 'OPERATION_TYPE' %}
|
||||||
|
<option value="{{ code.id }}" {% ifequal deviceinfo.operation_type code.id %}selected="selected"{% endifequal %}>
|
||||||
|
{{ code.value }}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">机柜信息</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control select2" style="width:100%;" name="dev_cabinet">
|
||||||
|
<option {% ifequal deviceinfo.dev_cabinet '' %}selected="selected"{% endifequal %}></option>
|
||||||
|
{% for cabinet in all_cabinet %}
|
||||||
|
<option value="{{ cabinet.id }}" {% ifequal deviceinfo.dev_cabinet cabinet.id %}selected="selected"{% endifequal %}>
|
||||||
|
{{ cabinet.number }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">购买日期</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input type="text" class="form-control pull-right form_datetime" name="buyDate"
|
||||||
|
value="{{ deviceinfo.buyDate | date:'Y-m-d' }}" readonly/>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">质保日期</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input type="text" class="form-control pull-right form_datetime" name="warrantyDate"
|
||||||
|
value="{{ deviceinfo.warrantyDate | date:'Y-m-d' }}" readonly/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">所属</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control select2" style="width:100%;" name="parent">
|
||||||
|
<option {% ifequal deviceinfo.parent_id '' %}selected="selected"{% endifequal %}></option>
|
||||||
|
{% for device in all_device %}
|
||||||
|
<option value="{{ device.id }}" {% ifequal deviceinfo.parent_id device.id %}selected="selected"{% endifequal %}>
|
||||||
|
{{ device.sys_hostname }}({{ device.hostname }})</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-2 control-label">责任人</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control select2" style="width:100%;" name="leader">
|
||||||
|
<option {% ifequal deviceinfo.leader '' %}selected="selected"{% endifequal %}></option>
|
||||||
|
{% for u in all_user %}
|
||||||
|
<option value="{{ u.id }}" {% ifequal deviceinfo.leader u.id %}selected="selected"{% endifequal %}>
|
||||||
|
{{ u.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label">备注信息</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<textarea class="form-control" name="desc" rows="5" >{{ deviceinfo.desc }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="box-footer ">
|
||||||
|
<div class="row span7 text-center ">
|
||||||
|
<button type="button" id="btnCancel" class="btn btn-default margin-right " >重置</button>
|
||||||
|
<button type="button" id="btnSave" class="btn btn-info margin-right " >保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
<script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
|
||||||
|
<script src="{% static 'bootstrap/js/bootstrap-datetimepicker.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function getUrl() {
|
||||||
|
if ($("input[name='id']").val()) {
|
||||||
|
var url = "{% url 'cmdb:eam-device-update' %}";
|
||||||
|
} else {
|
||||||
|
var url = "{% url 'cmdb:eam-device-create' %}";
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#btnSave").click(function () {
|
||||||
|
var data = $("#addForm").serialize();
|
||||||
|
$.ajax({
|
||||||
|
type: $("#addForm").attr('method'),
|
||||||
|
url: getUrl(),
|
||||||
|
data: data,
|
||||||
|
cache: false,
|
||||||
|
success: function (msg) {
|
||||||
|
if (msg.result) {
|
||||||
|
layer.alert('数据保存成功!', {icon: 1}, function (index) {
|
||||||
|
parent.layer.closeAll(); //关闭所有弹窗
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layer.alert(msg.error, {icon: 5});
|
||||||
|
//$('errorMessage').html(msg.message)
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*点取消刷新新页面*/
|
||||||
|
$("#btnCancel").click(function () {
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/*input 时间输入选择*/
|
||||||
|
$(".form_datetime").datetimepicker({
|
||||||
|
language: 'zh',
|
||||||
|
minView: 'month', //选择范围知道日期,不选择时分
|
||||||
|
//weekStart: 1,
|
||||||
|
//todayBtn: 1,
|
||||||
|
autoclose: 1,
|
||||||
|
todayHighlight: 1,
|
||||||
|
//startView: 2,
|
||||||
|
forceParse: 0,
|
||||||
|
showMeridian: 1,
|
||||||
|
format: 'yyyy-mm-dd'
|
||||||
|
}).on('changeDate', function (ev) {
|
||||||
|
$(this).datetimepicker('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
// select2
|
||||||
|
$(function () {
|
||||||
|
//Initialize Select2 Elements
|
||||||
|
$(".select2").select2();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
65
templates/cmdb/deviceinfo_upload.html
Normal file
65
templates/cmdb/deviceinfo_upload.html
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{% extends 'base-layer.html' %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{%static 'plugins/select2/select2.min.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap-datetimepicker.min.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'plugins/fileinput/fileinput.css' %}">
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block main %}
|
||||||
|
<div class="box box-danger">
|
||||||
|
<form class="form-horizontal" id="addForm" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="box-body">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="file-loading">
|
||||||
|
<input id="file_content" name="file_content" type="file" multiple="multiple"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group has-feedback">
|
||||||
|
<label class="col-sm-2 control-label"></label>
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<p class="text-red">同时最多可上传4个文件,支持文件格式:png", "jpg", "gif", "zip", "rar",大小不得超过10M</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
<script src="{% static 'plugins/fileinput/fileinput.js' %}"></script>
|
||||||
|
<script src="{% static 'plugins/fileinput/zh.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
//上传文件
|
||||||
|
|
||||||
|
$(document).on('ready', function() {
|
||||||
|
$("#file_content").fileinput({
|
||||||
|
language: "zh",
|
||||||
|
showUpload: true,
|
||||||
|
allowedFileExtensions: ["png", "jpg", "gif", "zip", "rar"],
|
||||||
|
uploadUrl: "{% url 'cmdb:eam-device-upload' %}",
|
||||||
|
uploadExtraData: {
|
||||||
|
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
||||||
|
'device': '{{ device.id }}',
|
||||||
|
'upload_user': '{{ request.user.name }}',
|
||||||
|
},
|
||||||
|
maxFileCount: 4,
|
||||||
|
autoReplace: true,
|
||||||
|
maxFileSize: 10240,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user