Python之CMDB资产管理系统
最近正好在给公司做CMDB资产管理系统,现在做的也差不多了,现在回头吧思路整理下。
CMDB介绍
CMDB --Configuration Management Database 配置管理数据库, CMDB存储与管理企业IT架构中设备的各种配置信息,它与所有服务支持和服务交付流程都紧密相联,支持这些流程的运转、发挥配置信息的价值,同时依赖于相关流程保证数据的准确性。
一、需求分析
-
存储所有IT资产信息
-
数据可手动添加
-
硬件信息可自动收集
-
硬件信息可自动变更
-
可对其它系统灵活开放API
-
API接口安全认证
-
资产类型:
-
服务器(物理机/虚拟机)
-
网络设备(路由器/交换机/AP)
-
机房设备(机柜/UPS)
-
软件资产(操作系统license)
-
资产属性:
-
品牌、型号、位置、用途、IP
- 供应商、厂商、合同、购买日期
二、架构设计
功能模块
-
资产搜集: 通过salt搜集各minion资产信息并统一汇报至CMDB
-
资产审核: 资产首次汇报需要人工审核
-
资产查询: 可多条件复杂查询
-
对外API: 方便其他系统调用资产接口信息,如运维自动化平台
-
自动变更: 资产变更更新及变更记录
-
自动监控: (计划)
-
告警自愈: (暂无)
什么是对外API:
对外API接口就是提供一个可以对外部系统访问的URL,如 http://api.***.com/
外部系统可以通过GET/POST等方法来获取想要的数据。如炫踪运维自动化平台需要展示各机房有多少台主机,这时向API传递指定的参数,API即可反馈给相应的结果。
什么时候自动监控:
当cmdb有新主机上线,则自动添加zabbix监控,并根据主机类型关联相应监控模板,如web服务器则关联web监控模板;主机下线,则自动禁用zabbix监控。
什么是告警自愈:
当系统发现机器的CPU有异常的时候,需要对 CPU高负载进行故障干预和恢复,这种情况下我们怎么做?我们可以取到告警的信息,告警里会告诉我这台机器的IP地址和告警的值; 通过IP,可以从CMDB中查一下这个机器属于哪个业务,再根据业务信息可以查询到同业务下还有那些机器; 然后我们通过同业务的IP地址把其它机器的当前CPU值都查询出来,得出的平均值再去和告警的CPU值来对比; 最后判断出是否需要系统干预。如果需要修复,系统会根据告警的IP地址到CMDB中去查询相应的恢复策略,再进行处理。通过这种灵活和完整的验证处理闭环,我们就可以构建出各种可靠的自动故障恢复策略。
三、资产收集/汇报
采集硬件信息,一般有两种模式:主动采集,和被动采集;
- 编写agent,客户端定时执行=>汇报给服务端API接口 (被动)
- salt的grains采集功能主动采集(主动)
- pupppet的report、ansible、zabbix等
之前采用的是第一种方法,写了一个客户端脚本支持linux和windows和linux,然后每天定时汇报给服务端的API接口。agent用salt或者ansible批量推过去就可以了。
后来因考虑到agent版本更新等维护成本高,索性改用第二种方法,用salt自定义了一个grains并分发至所有minion,granis来搜集minion的资产信息,然后调用salt-api定时搜集所有minion返回的granis信息即可。
方法1如图 :
方法二,如图:
(后台通过salt-api来获取即可)
def task_update_asset(sapi): '''定时更新所有salt资产信息''' try: jid = sapi.grains_item('*','sysinfo','glob') all_data = sapi.get_jid_data(jid) #拉取最新资产信息 while not all_data: all_data = sapi.get_jid_data(jid) else: # print all_data callback = [] tag = 0 if all_data.get('data'): all_data = all_data.get('data') for line in all_data: data = all_data[line].values()[0] if not data: continue start_report = report_asset.report(json.loads(data)) #开始汇报 callback.append({'salt_name':line,'result':start_report}) tag += 1 callback.append({u'执行总数':tag}) return json.dumps(callback) except Exception,e: print e return HttpResponse('The task Faild,please check!')
采集到的数据,可以用在线json解析一下查看
、
ok,这样客户端数据就拿到了,发送给Server的API接口来接收就行了。
四、资产汇报流程
资产汇报流程图
以下是资产变更展示
五、表结构设计
表结构代码如下:
#!/usr/bin/env python #encoding:utf-8 from django.db import models class Status(models.Model): name = models.CharField(max_length=64) code = models.CharField(max_length=64) memo = models.TextField(u'备注', null=True, blank=True) def __unicode__(self): return self.name class Meta: verbose_name_plural = "状态" class DeviceType(models.Model): '''设备类型''' name = models.CharField(max_length=128) code = models.CharField(max_length=64) memo = models.CharField(max_length=256,null=True,blank=True) create_at = models.DateTimeField(blank=True, auto_now_add=True) update_at = models.DateTimeField(blank=True, auto_now=True) def __unicode__(self): return self.name class Meta: verbose_name_plural = "设备类型" class Asset(models.Model): '''资产总表''' device_type = models.ForeignKey('DeviceType') device_status = models.ForeignKey('Status',default=1,null=True, blank=True) cabinet_num = models.CharField(u'机柜号', max_length=30, null=True, blank=True) cabinet_order = models.CharField(u'机柜中序号', max_length=30, null=True, blank=True) memo = models.TextField(u'备注', null=True, blank=True) create_at = models.DateTimeField(blank=True, auto_now_add=True) update_at = models.DateTimeField(blank=True, auto_now=True) idc = models.ForeignKey('IDC', verbose_name=u'IDC机房', null=True, blank=True) contract = models.ForeignKey('Contract', verbose_name=u'合同', null=True, blank=True) trade_date = models.DateField(u'购买时间',null=True, blank=True) expire_date = models.DateField(u'过保修期',null=True, blank=True) price = models.FloatField(u'价格',null=True, blank=True) business_unit = models.ForeignKey('BusinessUnit', verbose_name=u'属于的业务线', null=True, blank=True) manage_user = models.ForeignKey('UserProfile', verbose_name=u'管理员', related_name='+', null=True, blank=True) tag = models.ManyToManyField('Tag', null=True, blank=True) latest_date = models.DateField(null=True, blank=True) class Meta: verbose_name = '资产总表' verbose_name_plural = "资产总表" def __unicode__(self): return self.server.sn class Server_Type(models.Model): '''服务器类型''' name = models.CharField(max_length=128) memo = models.CharField(max_length=256,null=True,blank=True) def __unicode__(self): return self.name class Meta: verbose_name_plural = "服务器类型" class Server(models.Model): '''服务器信息''' asset = models.OneToOneField('Asset') sub_asset_type = models.ForeignKey('Server_Type') hostname = models.CharField(max_length=128, blank=True, null=True) salt_name = models.CharField(max_length=128, blank=True, null=True) hosted_on = models.ForeignKey('self',related_name='hosted_on_server',blank=True,null=True,verbose_name=u'宿主机') #虚拟机关联宿主机 service_sn = models.CharField(u'快速服务编码', max_length=128, blank=True,null=True) sn = models.CharField(u'SN号', max_length=64, blank=True, null=True,unique=True) manufactory = models.CharField(verbose_name=u'制造商', max_length=128, null=True, blank=True) model = models.CharField(u'型号', max_length=128, null=True, blank=True) manage_ip = models.GenericIPAddressField(u'管理IP',null=True, blank=True) business_ip = models.GenericIPAddressField(u'业务IP',null=True, blank=True) os_platform = models.CharField(u'系统类型', max_length=64, null=True, blank=True) os_distribution = models.CharField(u'OS厂商',max_length=64,blank=True,null=True) os_version = models.CharField(u'系统版本', max_length=64, null=True, blank=True) cpu_count = models.IntegerField(null=True, blank=True) cpu_physical_count = models.IntegerField(null=True, blank=True) cpu_model = models.CharField(max_length=128, null=True, blank=True) create_at = models.DateTimeField(blank=True, auto_now_add=True) update_at = models.DateTimeField(blank=True, auto_now=True) class Meta: verbose_name = '服务器' verbose_name_plural = "服务器" index_together = ["sn", "asset"] def __unicode__(self): return '<id:%s>'%(self.id) class NetworkDevice(models.Model): '''网络设备''' asset = models.OneToOneField('Asset') sub_assset_type_choices = ( (0,'路由器'), (1,'交换机'), (2,'无线AP'), (3,'VPN设备'), ) sub_asset_type = models.SmallIntegerField(choices=sub_assset_type_choices,verbose_name="设备类型",default=0) management_ip = models.CharField(u'管理IP',max_length=64,blank=True,null=True) vlan_ip = models.GenericIPAddressField(u'VlanIP',blank=True,null=True) intranet_ip = models.GenericIPAddressField(u'内网IP',blank=True,null=True) sn = models.CharField(u'SN号',max_length=128,unique=True) manufactory = models.CharField(verbose_name=u'制造商',max_length=128,null=True, blank=True) model = models.CharField(u'型号',max_length=128,null=True, blank=True ) port_num = models.SmallIntegerField(u'端口个数',null=True, blank=True ) device_detail = models.TextField(u'设置详细配置',null=True, blank=True ) class Meta: verbose_name = '网络设备' verbose_name_plural = "网络设备" class Memory(models.Model): slot = models.CharField(u'插槽位',max_length=32,blank=True) manufactory = models.CharField(u'制造商', max_length=32,null=True,blank=True) model = models.CharField(u'型号', max_length=64,blank=True) capacity = models.FloatField(u'容量MB',blank=True) sn = models.CharField(max_length=256,null=True,blank=True,default='') memo = models.TextField(u'备注', null=True,blank=True) create_at = models.DateTimeField(blank=True, auto_now_add=True) update_at = models.DateTimeField(blank=True, auto_now=True) server_info = models.ForeignKey('server') class Meta: verbose_name = '内存部件' verbose_name_plural = "内存部件" def __unicode__(self): return '%s: %sGB '%( self.slot, self.capacity) class NIC(models.Model): name = models.CharField(u'网卡名称',max_length=128,blank=True) model = models.CharField(u'网卡型号', max_length=128, blank=True,null=True) hwaddr = models.CharField(u'网卡mac地址', max_length=64) up = models.BooleanField(default=False, blank=True) netmask = models.CharField(max_length=64,blank=True) ipaddrs = models.CharField(u'ip地址',max_length=256,null=True) memo = models.TextField(u'备注', blank=True) create_at = models.DateTimeField(blank=True, auto_now_add=True) update_at = models.DateTimeField(blank=True, auto_now=True) server_info = models.ForeignKey('server') speed = models.CharField(max_length=64,null=True,blank=True,default='') class Meta: verbose_name = '网卡部件' verbose_name_plural = "网卡部件" def __unicode__(self): return u'网卡%s --> MAC:%s;IP%s;up:%s;netmask:%s' %(self.name,self.hwaddr,self.ipaddrs,self.up,self.netmask) class Disk(models.Model): name = models.CharField(u'磁盘名',max_length=32,blank=True,null=True) slot = models.CharField(u'插槽位',max_length=32,blank=True,null=True) sn = models.CharField(u'SN号', max_length=128, blank=True,null=True) model = models.CharField(u'磁盘型号', max_length=128,blank=True,null=True) capacity = models.FloatField(u'磁盘容量GB',blank=True,null=True) disk_iface_choice = ( ('SATA', 'SATA'), ('SAS', 'SAS'), ('SCSI', 'SCSI'), ('SSD', 'SSD'), ) pd_type = models.CharField(u'磁盘类型',choices=disk_iface_choice,max_length=64,blank=True,null=True) memo = models.TextField(u'备注', blank=True) create_at = models.DateTimeField(blank=True, auto_now_add=True) update_at = models.DateTimeField(blank=True, auto_now=True) server_info = models.ForeignKey('server') def __unicode__(self): return 'slot:%s size:%s' % (self.slot,self.capacity) class Meta: verbose_name_plural = "硬盘" class IDC(models.Model): region = models.CharField(u'区域',max_length=64) name = models.CharField(u'机房名称',max_length=32) floor = models.IntegerField(u'楼层',default=1) display = models.CharField(max_length=128) memo = models.CharField(u'备注',max_length=64) def __unicode__(self): return 'region:%s idc:%s floor:%s' %(self.region,self.name,self.floor) class Meta: verbose_name = '机房' verbose_name_plural = "机房" class Contract(models.Model): sn = models.CharField(u'合同号', max_length=64,unique=True) name = models.CharField(u'合同名称', max_length=64 ) cost = models.IntegerField(u'合同金额') start_date = models.DateTimeField(null=True, blank=True) end_date = models.DateTimeField(null=True, blank=True) license_num = models.IntegerField(u'license数量',null=True, blank=True) memo = models.TextField(u'备注',null=True, blank=True) create_at = models.DateTimeField(blank=True, auto_now_add=True) update_at = models.DateTimeField(blank=True, auto_now=True) class Meta: verbose_name = '合同' verbose_name_plural = "合同" def __unicode__(self): return self.name class BusinessUnit(models.Model): name = models.CharField(u'业务线', max_length=64, unique=True) contact = models.ForeignKey('UserProfile') user_group = models.ForeignKey('UserGroup', null=True, blank=True) memo = models.CharField(u'备注', max_length=64, blank=True) def __unicode__(self): return self.name class Meta: verbose_name = '业务线' verbose_name_plural = "业务线" class HandleLog(models.Model): asset_info = models.ForeignKey('Asset') content = models.TextField(null=True, blank=True) creator = models.ForeignKey('UserProfile') create_at = models.DateTimeField(auto_now_add=True) def __unicode__(self): return self.content class Meta: verbose_name_plural = "资产变更日志" class ErrorLog(models.Model): name = models.CharField(max_length=256) class NewAssetApprovalZone(models.Model): '''没有注册的资产,允许自动汇报,但是不入库。放到待批准表里临时存储''' sn = models.CharField(u'资产SN号',max_length=128, unique=True) #资产不能重复汇报,一个资产批准前只能汇报一次 asset_type_choices = ( ('server', u'服务器'), ('switch', u'交换机'), ('router', u'路由器'), ('firewall', u'防火墙'), ('wireless', u'无线AP'), ) device_type = models.CharField(choices=asset_type_choices,max_length=64,blank=True,null=True) manufactory = models.CharField(max_length=64,blank=True,null=True) #厂商 model = models.CharField(max_length=128,blank=True,null=True) ram_size = models.IntegerField(blank=True,null=True) cpu_model = models.CharField(max_length=128,blank=True,null=True) cpu_count = models.IntegerField(blank=True,null=True) #cpu_core_count = models.IntegerField(blank=True,null=True) cpu_physical_count = models.IntegerField(blank=True,null=True) #os_type = models.CharField(max_length=64,blank=True,null=True) os_platform = models.CharField(u'系统类型',max_length=64,blank=True,null=True) os_distribution = models.CharField(u'OS厂商',max_length=64,blank=True,null=True) #os_release = models.CharField(max_length=64,blank=True,null=True) os_version = models.CharField(u'系统名称',max_length=64,blank=True,null=True) data = models.TextField(u'资产数据') #这里才是真正详细的数据,批准后存入正式表里面 date = models.DateTimeField(u'汇报日期',auto_now_add=True) approved = models.BooleanField(u'已批准',default=False) approved_by = models.ForeignKey('UserProfile',verbose_name=u'批准人',blank=True,null=True) approved_date = models.DateTimeField(u'批准日期',blank=True,null=True) def __str__(self): return self.sn class Meta: verbose_name = '新上线待批准资产' verbose_name_plural = "新上线待批准资产" ordering = ['-id']
六、部分展示
未完待续。。。。。。。。。。。