CMDB-02
内容概要: 1. 采集资产的补充 2. API【资产入库】 内容详细: 1. 采集资产的补充 1. 采集插件补充 - 新增CPU、basic、main_board - 采集插件异常处理+错误堆栈信息 2. 日志 - logging + 单例模式 3. 插件补充 - 基本信息 - 主板信息 修改资产信息格式 = { 'disk': { 'status': True, 'error': None, 'data': { '0': { 'slot': '0', 'pd_type': 'SAS', 'capacity': '279.396', 'model': 'SEAGATE ST300MM0006 LS08S0K2B5NV' }, '1': { 'slot': '1', 'pd_type': 'SAS', 'capacity': '279.396', 'model': 'SEAGATE ST300MM0006 LS08S0K2B5AH' }, '2': { 'slot': '2', 'pd_type': 'SATA', 'capacity': '476.939', 'model': 'S1SZNSAFA01085L Samsung SSD 850 PRO 512GB EXM01B6Q' }, '3': { 'slot': '3', 'pd_type': 'SATA', 'capacity': '476.939', 'model': 'S1AXNSAF912433K Samsung SSD 840 PRO Series DXM06B0Q' }, '4': { 'slot': '4', 'pd_type': 'SATA', 'capacity': '476.939', 'model': 'S1AXNSAF303909M Samsung SSD 840 PRO Series DXM05B0Q' }, '5': { 'slot': '5', 'pd_type': 'SATA', 'capacity': '476.939', 'model': 'S1AXNSAFB00549A Samsung SSD 840 PRO Series DXM06B0Q' } } }, 'memory': { 'status': True, 'error': None, 'data': { 'DIMM #0': { 'capacity': 1024, 'slot': 'DIMM #0', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' }, 'DIMM #1': { 'capacity': 0, 'slot': 'DIMM #1', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' }, 'DIMM #2': { 'capacity': 0, 'slot': 'DIMM #2', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' }, 'DIMM #3': { 'capacity': 0, 'slot': 'DIMM #3', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' }, 'DIMM #4': { 'capacity': 0, 'slot': 'DIMM #4', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' }, 'DIMM #5': { 'capacity': 0, 'slot': 'DIMM #5', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' }, 'DIMM #6': { 'capacity': 0, 'slot': 'DIMM #6', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' }, 'DIMM #7': { 'capacity': 0, 'slot': 'DIMM #7', 'model': 'DRAM', 'speed': '667 MHz', 'manufacturer': 'Not Specified', 'sn': 'Not Specified' } } }, 'network': { 'status': True, 'error': None, 'data': { 'eth0': { 'up': True, 'hwaddr': '00:1c:42:a5:57:7a', 'ipaddrs': '10.211.55.4', 'netmask': '255.255.255.0' } } }, 'basic': { 'status': True, 'error': None, 'data': { 'os_platform': 'linux', 'os_version': '6.5', 'hostname': 'c1.com' } }, 'cpu': { 'status': True, 'error': None, 'data': { 'cpu_count': 24, 'cpu_physical_count': 2, 'cpu_model': ' Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz' } }, 'main_board': { 'status': True, 'error': None, 'data': { 'manufacturer': 'Parallels Software International Inc.', 'model': 'Parallels Virtual Platform', 'sn': 'Parallels-1A 1B CB 3B 64 66 4B 13 86 B0 86 FF 7E 2B 20 30' } }, 'type': 'update' } 3.1 Agent引擎唯一标识问题处理 - 只有在agent模式中会遇到唯一标识问题 2. API【资产入库】 1. 数据库设计 from django.db import models class BusinessUnit(models.Model): """ 业务线 """ name = models.CharField('业务线', max_length=64, unique=True) class Meta: verbose_name_plural = "业务线表" def __str__(self): return self.name class IDC(models.Model): """ 机房信息 """ name = models.CharField('机房', max_length=32) floor = models.IntegerField('楼层', default=1) class Meta: verbose_name_plural = "机房表" def __str__(self): return self.name class Server(models.Model): """ 服务器信息 """ device_status_choices = ( (1, '上架'), (2, '在线'), (3, '离线'), (4, '下架'), ) device_status_id = models.IntegerField(choices=device_status_choices, default=1) idc = models.ForeignKey('IDC', verbose_name='IDC机房', null=True, blank=True) cabinet_num = models.CharField('机柜号', max_length=30, null=True, blank=True) cabinet_order = models.CharField('机柜中序号', max_length=30, null=True, blank=True) business_unit = models.ForeignKey('BusinessUnit', verbose_name='属于的业务线', null=True, blank=True) # 基本信息 + 主板信息 + CPU信息 hostname = models.CharField(max_length=128, unique=True) os_platform = models.CharField('系统', max_length=16, null=True, blank=True) os_version = models.CharField('系统版本', max_length=16, null=True, blank=True) sn = models.CharField('SN号', max_length=64, db_index=True) manufacturer = models.CharField(verbose_name='制造商', max_length=64, null=True, blank=True) model = models.CharField('型号', max_length=64, null=True, blank=True) cpu_count = models.IntegerField('CPU个数', null=True, blank=True) cpu_physical_count = models.IntegerField('CPU物理个数', null=True, blank=True) cpu_model = models.CharField('CPU型号', max_length=128, null=True, blank=True) latest_date = models.DateField(null=True) create_at = models.DateTimeField(auto_now_add=True, blank=True) class Meta: verbose_name_plural = "服务器表" def __str__(self): return self.hostname class Disk(models.Model): """ 硬盘信息 """ slot = models.CharField('插槽位', max_length=8) model = models.CharField('磁盘型号', max_length=32) capacity = models.FloatField('磁盘容量GB') pd_type = models.CharField('磁盘类型', max_length=32) server = models.ForeignKey(verbose_name='服务器', to='Server', related_name='disk_list') class Meta: verbose_name_plural = "硬盘表" def __str__(self): return self.slot class NIC(models.Model): """ 网卡信息 """ name = models.CharField('网卡名称', max_length=128) hwaddr = models.CharField('网卡mac地址', max_length=64) netmask = models.CharField(max_length=64) ipaddrs = models.CharField('ip地址', max_length=256) up = models.BooleanField(default=False) server = models.ForeignKey('Server', related_name='nic_list') class Meta: verbose_name_plural = "网卡表" def __str__(self): return self.name class Memory(models.Model): """ 内存信息 """ slot = models.CharField('插槽位', max_length=32) manufacturer = models.CharField('制造商', max_length=32, null=True, blank=True) model = models.CharField('型号', max_length=64) capacity = models.FloatField('容量', null=True, blank=True) sn = models.CharField('内存SN号', max_length=64, null=True, blank=True) speed = models.CharField('速度', max_length=16, null=True, blank=True) server = models.ForeignKey('Server', related_name='memory_list') class Meta: verbose_name_plural = "内存表" def __str__(self): return self.slot class AssetRecord(models.Model): """ 资产变更记录,creator为空时,表示是资产汇报的数据。 """ server = models.ForeignKey('Server', related_name='servers') content = models.TextField(null=True) # creator = models.ForeignKey('UserProfile', null=True, blank=True) create_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name_plural = "资产记录表" class ErrorLog(models.Model): """ 错误日志,如:agent采集数据错误 或 运行错误 """ server = models.ForeignKey('Server', null=True, blank=True) title = models.CharField(max_length=16) content = models.TextField() create_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name_plural = "错误日志表" def __str__(self): return self.title 2. 录入资产数据(采集器汇报的信息字段和数据库对应,方便创建。使用**字典即可创建) - create 找到disk_info有,disk_queryset 无 - update 找到disk_queryset有,disk_info无 - update-hostname 找到disk_info有,disk_queryset 有 3. API验证 1. 使用密钥+时间生成动态密钥认证 三步判断方法: - 判断key的时间是否超时 - 判断key是否存在 - 判断key是否正确 2. 简版写法 server: from rest_framework.views import Response from django.http import JsonResponse from api import models from api.lib import service from django.views import View from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt auth_key = "32425sdf23423tsdf324" key_dict = {} def get_key(ctime): key = "%s|%s" % (auth_key, ctime) md5 = hashlib.md5() md5.update(key.encode("utf-8")) return md5.hexdigest() @method_decorator(csrf_exempt, name="dispatch") class TestApi(View): def get(self, request): pass def post(self, request): info = { "status": True, "msg": None } # api认证开始 server_ctime = time.time() client_ctime = request.GET.get("ctime") client_key = request.GET.get("key") # 1. 判断key的时间是否超时 if int(server_ctime) - int(float(client_ctime)) > 5: info["status"] = False info["msg"] = "key已经超时" return JsonResponse(info, json_dumps_params={'ensure_ascii': False}) # 2. 判断key存不存在 if client_key in key_dict: info["status"] = False info["msg"] = "key已经被使用" return JsonResponse(info, json_dumps_params={'ensure_ascii': False}) # 3. 判断key是否正确 server_key = get_key(client_ctime) if client_key != server_key: info["status"] = False info["msg"] = "key错误" return JsonResponse(info, json_dumps_params={'ensure_ascii': False}) # 保存使用过的key key_dict[client_key] = client_ctime print(request.POST.get("data")) return JsonResponse(info) client: #!/usr/bin/env python3 import requests import time import hashlib key = "32425sdf23423tsdf324" ctime = time.time() key = "%s|%s" % (key, ctime) md5 = hashlib.md5() md5.update(key.encode("utf-8")) key = md5.hexdigest() response = requests.post( url="http://127.0.0.1:8000/api/test/", params={"key": key, "ctime": ctime}, data={"data": "aaaa"} ) print(response.text) 3. 使用RestAPI server: import time import hashlib from rest_framework.views import APIView from rest_framework.views import Response from django.http import JsonResponse from api import models from api.lib import service from django.views import View from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt class TestApi(APIView): def dispatch(self, request, *args, **kwargs): info = { "status": True, "msg": None } # api认证开始 server_ctime = time.time() client_ctime = request.GET.get("ctime") client_key = request.GET.get("key") # 1. 判断key的时间是否超时 if int(server_ctime) - int(float(client_ctime)) > 5: info["status"] = False info["msg"] = "key已经超时" return Response(info) # 2. 判断key存不存在 if client_key in key_dict: info["status"] = False info["msg"] = "key已经被使用" return Response(info) # 3. 判断key是否正确 server_key = get_key(client_ctime) if client_key != server_key: info["status"] = False info["msg"] = "key错误" return Response(info) # 保存使用过的key key_dict[client_key] = client_ctime return super().dispatch(request, *args, **kwargs) def get(self, request): host_list = ["c1.com", "c2.com"] return Response(host_list) def post(self, request): print(request.POST.get("data")) return Response("POST访问成功") client: #!/usr/bin/env python3 import requests import time import hashlib key = "32425sdf23423tsdf324" ctime = time.time() key = "%s|%s" % (key, ctime) md5 = hashlib.md5() md5.update(key.encode("utf-8")) key = md5.hexdigest() response = requests.post( url="http://127.0.0.1:8000/api/test/", params={"key": key, "ctime": ctime}, data={"data": "aaaa"} ) print(response.text) 4. rsa加密 5. 资产变更记录 1. 问 - 给你一个数据库类,请帮我找到他的所有字段; 示例: class OrmView(APIView): def get(self,request): for filed in models.Disk._meta.fields: print(filed.name,filed.verbose_name) return Response('...') - 给你一个类和一个名称,找到他的verbose_name cls = models.Disk name = 'slot' obj = cls._meta.get_field(name) print(obj.verbose_name) - 给你一个字典 cls = models.Disk info = { 'slot': '0', 'pd_type': 'SAS', 'capacity': '279.396', 'model': 'SEAGATE ST300MM0006 LS08S0K2B5NV' } 得到一个字符串:"插槽位:0;磁盘类型:SAS;型号:SEAGATE ST300MM0006 LS08S0K2B5NV;磁盘容量GB:279.396" - 以上知识点+反射实现:资产变更记录。 注意:硬盘 2. 记录硬盘更新记录代码示例: def disk(request, server): """ 数据库录入硬盘信息 :param request: :param server: :return: """ # 2.1 取到数据库中的硬盘信息 disk_queryset = models.Disk.objects.filter(server=server) # 2.2 取到采集传递过来的硬盘信息 disk_info = request.data["disk"]["data"] # 2.3 根据槽位转成集合,判断哪些槽位进行了更新,删除,增加 disk_queryset_set = {i.slot for i in disk_queryset} disk_info_set = set(disk_info) # 2.3.1 需要增加的 add_slot_list = disk_info_set - disk_queryset_set # 2.3.2 需要删除的 del_slot_list = disk_queryset_set - disk_info_set # 2.3.3 需要更新的 update_slot_list = disk_queryset_set & disk_info_set # 2.4 增加硬盘 for slot in add_slot_list: row_dict = disk_info[slot] record_list = [] for name, new_value in row_dict.items(): verbose_name = models.Disk._meta.get_field(name) tpl = "%s: %s" % (verbose_name, new_value) record_list.append(tpl) if record_list: msg = "【新增硬盘】 槽位%s新增硬盘,硬盘信息:%s" % (slot, ";".join(record_list)) models.AssetRecord.objects.create(server=server, content=msg) row_dict["server"] = server # models.Disk.objects.create(**row_dict) # 2.5 删除硬盘 models.Disk.objects.filter(server=server, slot__in=del_slot_list).delete() if del_slot_list: msg = "【硬盘删除】 移除槽位%s的硬盘" % (";".join(del_slot_list),) models.AssetRecord.objects.create(server=server, content=msg) # 2.6 更新硬盘 for slot in update_slot_list: # models.Disk.objects.filter(server=server, slot=slot).update(**disk_info[slot]) # 取到硬盘对象 obj = models.Disk.objects.filter(server=server, slot=slot).first() # 取到采集传递过来的硬盘槽位信息 row_dict = disk_info[slot] record_list = [] for name, new_value in row_dict.items(): old_value = str(getattr(obj, name)) if old_value != new_value: setattr(obj, name, new_value) verbose_name = models.Disk._meta.get_field(name).verbose_name msg = "【硬盘变更】槽位%s: %s由%s更新为%s" % (slot, verbose_name, old_value, new_value) record_list.append(msg) obj.save() if record_list: models.AssetRecord.objects.create(server=server, content=";".join(record_list)) 作业:资产变更记录 - 基本信息 - 网卡 - 内存