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



未完待續。。。。。。。。。。。

