cmdb资产管理2
新增资产
现在api服务端已经能获取到我们要做的操作了。接下来应该是补充获取操作后对应的程序编写
我们要做的是把post请求发过来的数据保存到数据库。我们创建repository 名字的app,并设计models创建表来存储数据。后面可以从数据库获取信息并展示出来
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, on_delete=models.CASCADE) 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, on_delete=models.CASCADE) # 基本信息 + 主板信息 + 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=108) capacity = models.FloatField('磁盘容量GB') pd_type = models.CharField('磁盘类型', max_length=32) server = models.ForeignKey(verbose_name='服务器', to='Server', related_name='disk_list', on_delete=models.CASCADE) 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', on_delete=models.CASCADE) 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', on_delete=models.CASCADE) class Meta: verbose_name_plural = "内存表" def __str__(self): return self.slot class AssetRecord(models.Model): """ 资产变更记录 """ server = models.ForeignKey('Server', related_name='servers', on_delete=models.CASCADE) content = models.TextField(null=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, on_delete=models.CASCADE) 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
我们可以将业务线先设置成这个。然后执行命令生成表
我们先将cert删除,并且将这里写入文件的注释掉,这样每次判断都是新增主机了
服务端接收的数据如下图,将它存入数据库
我们要将新增的这些资产信息存入数据库
先看新增server,1处有默认,2处可以为空,我们先不管
我们先看这三种硬件信息
而这三种硬件信息和我们传过来的数据一致,字段名字也一样
我们将数据字典取出来,然后update到同一个字典中,合成一个。
获得如下的字典
{ 'os_platform': 'linux', 'os_version': '6.5', 'hostname': 'c1.com', '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', 'cpu_count': 24, 'cpu_physical_count': 2, 'cpu_model': ' Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz' }
然后执行创建orm对象的命令,将这个字典打散存入数据库
再看看新增硬盘,硬盘是一个槽位是一个硬盘,前面新增一台server,已经有了这台server对象了,所以硬盘所属外键server=server。for循环外层字典的值,然后将每个内层字典打散插入到Disk数据表中。
这样就保存了6个槽位的硬盘
这里for循环插入数据,插入一次连接一次数据库,我们可以进行优化一下
for循环创建Disk对象并追加到列表。bulk_create方法根据对象列表一次批量创建数据表记录
新增内存和新增硬盘一样
出问题了,因为数据结构内存和网卡没有修改,没有保存进去
修改后点击执行客户端,客户端采集信息发送给api然后保存到数据库
而新增网卡有点区别,它以name为键但是数据里没有name,
所以创建要将name加进去,循环并取出它的键加到数据库中
更新资产
我们创建cert,将主机名和我们固定采集的主机名一致,这样走的就是更新资产信息了
api服务端走的是更新资产信息,服务端要写的更新程序如下
下面看一下更新主机表的程序
下面我将主机表修改一下,执行客户端程序后又更新回来了
现在我们将数据库修改如下:
因为每次提交这里都是提交0-5的,所以新增,更新,删除就会如上
新增,删除和更新如下。新增是采集的减数据库的(采集的多出来的),删除是数据库的减采集的(数据库多出来的),更新是采集的和数据库都有的(用&集合运算)。这样获取到槽位信息,根据槽位信息对这条记录做相应的操作。
下面根据获取到要做这三个操作的硬盘槽位,对它们做对应的操作
当客户端执行采集并汇报后,服务端显然做了对应的硬盘更新
观察内存和网卡的程序是相似的,只是名字有点区别,可以用notepad++替换,然后将有区别的地方更改正确,这样就完成内存和网卡的更新操作
# 更新内存 memory_info = info['memory']['data'] # 新提交的数据 memory_slot_set = set(memory_info) memory_slot__db_set = {i.slot for i in models.Memory.objects.filter(server=server)} # 新增 删除 更新 add_slot_set = memory_slot_set - memory_slot__db_set # 新增的槽位 del_slot_set = memory_slot__db_set - memory_slot_set # 删除的槽位 update_slot_set = memory_slot__db_set & memory_slot_set # 更新的槽位 # 新增内存 add_memory_lit = [] for slot in add_slot_set: memory = memory_info.get(slot) add_memory_lit.append(models.Memory(**memory, server=server)) if add_memory_lit: models.Memory.objects.bulk_create(add_memory_lit) # 删除内存 if del_slot_set: models.Memory.objects.filter(server=server, slot__in=del_slot_set).delete() # 更新内存 for slot in update_slot_set: memory = memory_info.get(slot) models.Memory.objects.filter(server=server, slot=slot).update(**memory)
nic_info = info['nic']['data'] # 新提交的数据 nic_name_set = set(nic_info) nic_name__db_set = {i.name for i in models.NIC.objects.filter(server=server)} # 新增 删除 更新 add_name_set = nic_name_set - nic_name__db_set # 新增的槽位 del_name_set = nic_name__db_set - nic_name_set # 删除的槽位 update_name_set = nic_name__db_set & nic_name_set # 更新的槽位 # 新增网卡 add_nic_lit = [] for name in add_name_set: nic = nic_info.get(name) nic['name'] = name add_nic_lit.append(models.NIC(**nic, server=server)) if add_nic_lit: models.NIC.objects.bulk_create(add_nic_lit) # 删除网卡 if del_name_set: models.NIC.objects.filter(server=server, name__in=del_name_set).delete() # 更新网卡 for name in update_name_set: nic = nic_info.get(name) nic['name'] = name models.NIC.objects.filter(server=server, name=name).update(**nic)
更新资产+主机名
我们在客户端将cert文件内容改变,让它和会采集到的信息不一致,这样程序就会走更新主机名和资产信息
{ 'disk': { 'status': True, 'error': '', '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': '', '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' } } }, 'nic': { 'status': True, 'error': '', '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': '', 'data': { 'os_platform': 'linux', 'os_version': '6.5', 'hostname': 'c1.com' } }, 'cpu': { 'status': True, 'error': '', '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': '', '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' } }, 'action': 'update_host', 'old_hostname': 'c2.com' }
而提交过来的数据里面,还有老主机名,也就是文件里保存的主机名
因此服务端能从发送过来的info数据获取到主机名,从而从数据库查到这台服务器的信息,因为更新资产信息部分和之前写的单纯地更新资产信息部分是一样的。
这部分我们不用重复写了,所以直接在app里创建service模块写成函数进行调用。
from repository import models def process_basic(info): server_info = {} basic = info['basic']['data'] main_board = info['main_board']['data'] cpu = info['cpu']['data'] server_info.update(basic) server_info.update(main_board) server_info.update(cpu) hostname = info['basic']['data']['hostname'] # 新的hostname old_hostname = info.get('old_hostname') # 老的hostname server_list = models.Server.objects.filter(hostname=old_hostname if old_hostname else hostname) server_list.update(**server_info) server = models.Server.objects.filter(hostname=hostname).first() return server def process_disk(info, server): disk_info = info['disk']['data'] # 新提交的数据 disk_slot_set = set(disk_info) disk_slot__db_set = {i.slot for i in models.Disk.objects.filter(server=server)} # 新增 删除 更新 add_slot_set = disk_slot_set - disk_slot__db_set # 新增的槽位 添加硬盘 del_slot_set = disk_slot__db_set - disk_slot_set # 删除的槽位 减少硬盘 update_slot_set = disk_slot__db_set & disk_slot_set # 更新的槽位 更换硬盘 # 新增硬盘 add_disk_lit = [] for slot in add_slot_set: disk = disk_info.get(slot) add_disk_lit.append(models.Disk(**disk, server=server)) if add_disk_lit: models.Disk.objects.bulk_create(add_disk_lit) # 删除硬盘 if del_slot_set: models.Disk.objects.filter(server=server, slot__in=del_slot_set).delete() # 更新硬盘 for slot in update_slot_set: disk = disk_info.get(slot) models.Disk.objects.filter(server=server, slot=slot).update(**disk) def process_memory(info, server): # 更新内存 memory_info = info['memory']['data'] # 新提交的数据 memory_slot_set = set(memory_info) memory_slot__db_set = {i.slot for i in models.Memory.objects.filter(server=server)} # 新增 删除 更新 add_slot_set = memory_slot_set - memory_slot__db_set # 新增的槽位 del_slot_set = memory_slot__db_set - memory_slot_set # 删除的槽位 update_slot_set = memory_slot__db_set & memory_slot_set # 更新的槽位 # 新增内存 add_memory_lit = [] for slot in add_slot_set: memory = memory_info.get(slot) add_memory_lit.append(models.Memory(**memory, server=server)) if add_memory_lit: models.Memory.objects.bulk_create(add_memory_lit) # 删除内存 if del_slot_set: models.Memory.objects.filter(server=server, slot__in=del_slot_set).delete() # 更新内存 for slot in update_slot_set: memory = memory_info.get(slot) models.Memory.objects.filter(server=server, slot=slot).update(**memory) def process_nic(info, server): nic_info = info['nic']['data'] # 新提交的数据 nic_name_set = set(nic_info) nic_name__db_set = {i.name for i in models.NIC.objects.filter(server=server)} # 新增 删除 更新 add_name_set = nic_name_set - nic_name__db_set # 新增的槽位 del_name_set = nic_name__db_set - nic_name_set # 删除的槽位 update_name_set = nic_name__db_set & nic_name_set # 更新的槽位 # 新增网卡 add_nic_lit = [] for name in add_name_set: nic = nic_info.get(name) nic['name'] = name add_nic_lit.append(models.NIC(**nic, server=server)) if add_nic_lit: models.NIC.objects.bulk_create(add_nic_lit) # 删除网卡 if del_name_set: models.NIC.objects.filter(server=server, name__in=del_name_set).delete() # 更新网卡 for name in update_name_set: nic = nic_info.get(name) nic['name'] = name models.NIC.objects.filter(server=server, name=name).update(**nic)
然后我们导入这四个类并调用。这样就实现了更新资产信息了。
至于客户端主机名的修改,只需要将客户端这里的注释去掉就可以了
api验证