yangmv

最近正好在给公司做CMDB资产管理系统,现在做的也差不多了,现在回头吧思路整理下。

CMDB介绍

CMDB --Configuration Management Database 配置管理数据库, CMDB存储与管理企业IT架构中设备的各种配置信息,它与所有服务支持和服务交付流程都紧密相联,支持这些流程的运转、发挥配置信息的价值,同时依赖于相关流程保证数据的准确性。

在实际的项目中,CMDB常常被认为是构建其它ITIL流程的基础而优先考虑,ITIL项目的成败与是否成功建立CMDB有非常大的关系。

 

一、需求分析

  • 存储所有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\']

 

 

 六、部分展示

 

 

  

未完待续。。。。。。。。。。。

  

 

分类:

技术点:

相关文章: