最近正好在給公司做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']
六、部分展示
未完待續。。。。。。。。。。。