diff --git a/apps/cmdb/forms.py b/apps/cmdb/forms.py index 4f44c9b..0e62e9b 100644 --- a/apps/cmdb/forms.py +++ b/apps/cmdb/forms.py @@ -4,7 +4,7 @@ from django import forms -from .models import Code +from .models import Code, DeviceInfo, ConnectionInfo, DeviceFile class CodeCreateForm(forms.ModelForm): @@ -43,4 +43,63 @@ class CodeUpdateForm(CodeCreateForm): raise forms.ValidationError(msg) if matching_code.filter(value=value).exists(): msg = 'value:{} 已经存在'.format(value) - raise forms.ValidationError(msg) \ No newline at end of file + 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__' \ No newline at end of file diff --git a/apps/cmdb/models.py b/apps/cmdb/models.py index 1151a50..bb516c5 100644 --- a/apps/cmdb/models.py +++ b/apps/cmdb/models.py @@ -20,7 +20,7 @@ class AbstractMode(models.Model): class Code(AbstractMode): key = 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: verbose_name = '字典' @@ -53,11 +53,11 @@ class ConnectionAbstract(models.Model): class DeviceAbstract(models.Model): - sys_hostname = models.CharField(max_length=50, blank=True, default='', verbose_name='主机名') - mac_address = models.CharField(max_length=50, blank=True, default='', verbose_name='MAC地址') - sn_number = models.CharField(max_length=50, blank=True, default='', verbose_name='SN号码') - os_type = models.CharField(max_length=50, blank=True, default='', verbose_name='系统类型') - device_type = 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=150, blank=True, default='', verbose_name='MAC地址') + sn_number = models.CharField(max_length=150, blank=True, default='', verbose_name='SN号码') + os_type = models.CharField(max_length=150, blank=True, default='', verbose_name='系统类型') + device_type = models.CharField(max_length=150, blank=True, default='', verbose_name='设备类型') class Meta: abstract = True @@ -100,7 +100,7 @@ class DeviceInfo(AbstractMode, DeviceAbstract, TimeAbstract): warrantyDate = models.DateField(default=datetime.now, 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']) + history = HistoricalRecords(excluded_fields=['add_time', 'modify_time', 'parent']) class Meta: verbose_name = '设备信息' diff --git a/apps/cmdb/signals.py b/apps/cmdb/signals.py index d66e53a..a681322 100644 --- a/apps/cmdb/signals.py +++ b/apps/cmdb/signals.py @@ -1,13 +1,46 @@ import os from django.dispatch import receiver -from django.db.models.signals import post_delete +from django.db.models.signals import post_delete, post_save -from .models import DeviceFile +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) \ No newline at end of file + 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() \ No newline at end of file diff --git a/apps/cmdb/templatetags/__init__.py b/apps/cmdb/templatetags/__init__.py new file mode 100644 index 0000000..e483ac4 --- /dev/null +++ b/apps/cmdb/templatetags/__init__.py @@ -0,0 +1,3 @@ +# @Time : 2019/2/17 21:28 +# @Author : RobbieHan +# @File : __init__.py.py \ No newline at end of file diff --git a/apps/cmdb/templatetags/extra_tags.py b/apps/cmdb/templatetags/extra_tags.py new file mode 100644 index 0000000..f4568c9 --- /dev/null +++ b/apps/cmdb/templatetags/extra_tags.py @@ -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 + } \ No newline at end of file diff --git a/apps/cmdb/urls.py b/apps/cmdb/urls.py index f58f0a7..5856c99 100644 --- a/apps/cmdb/urls.py +++ b/apps/cmdb/urls.py @@ -27,4 +27,15 @@ urlpatterns = [ 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'), ] diff --git a/apps/cmdb/views_eam.py b/apps/cmdb/views_eam.py index 68d9018..4264164 100644 --- a/apps/cmdb/views_eam.py +++ b/apps/cmdb/views_eam.py @@ -1,9 +1,21 @@ -from django.views.generic import TemplateView +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 +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): @@ -36,3 +48,188 @@ class CabinetListView(SandboxListView): 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 = '
  • .*?