DEVOPS 运维开发系列十:怎样设计一套资产管理平台

为什么需要一个资产管理平台

资产管理平台负责记录基础的物理信息,如:IDC、服务器(资产编号、参数、采购时间、供应商)、配件、网络设备、IP地址、ACL等。提供了多个子功能,如:预算管理、自助装机、故障报修、IP地址管理、ACL管理、LVS管理等。

资产管理平台作为所有物理资源的唯一出入口,通过流程将预算管理、故障管理这些可能导致资产信息变更的环节打通。新采购的服务器录入到资产管理平台,服务器报废也必须经过它。通过资产管理平台,可以很方便地查询各种物理资源的使用情况。比如,一共有多少服务器、有哪些机房、机房的机柜分布情况、每个机柜摆放的服务器位置等信息。

资产管理平台的主要用户是系统运维工程师,他们关注设备的出入、维修等管理工作,交付资源给上层业务;资产管理平台负责底层的物理信息管理,提供API供服务管理平台查询和同步。服务管理平台通过API获取新交付的服务器列表及其详细信息,将它们归属到服务树产品线节点,分配对应的权限。应用运维工程师在服务树上领取空闲服务器,进行一系列的环境初始化、服务部署、监控添加等工作。应用运维工程师在服务管理平台提交报修申请、服务器归还等操作,通过API将信息推送到资产管理平台,由系统运维工程师进行相应处理。

再进一步,一个完整且准确的资产管理平台,往往是实现配置管理、应用管理、服务管理所需要依赖的一套重要的基础设施资源。

以下是我在工作中实践过的一个资产管理平台设计、开发与使用案例,对中小企业的资产管理工作会有一些帮助或启发。
本文将先重点介绍下资产管理平台库表建模需要怎么设计和实现等。

因为本文中涉及的很多网络设备都设计了通过程序自动获取和解析防火墙、交换机设备的配置信息,推荐使用NAPALM、netmiko等Python库,可以方便得得到需要的配置信息,为网络专线、vpn、端口转发等配置的自动识别,提供数据支持。当然,这些Python库也是可以直接用于管理、配置网络设备、接口的。

Step1:变更日志数据表设计

我们做资产管理功能,为什么第一个需要建模的表是"变更日志"?
因为我们需要掌握一项设备资产的全生命周期内发生的每一个变更,比如说去年把设备上架到A机房给X业务使用的,今年因为一些原因换到B机房给Y业务在使用。变更日志表中记录所有这些变化信息,最终为按时间线展示一个设备的生命周期中的各个事件提供数据支持。

该表中的记录均将是由程序自动写入且不做删除,不允许手工直接编辑该表。
表重要属性设计:

  • 实体表表名table_name
  • 实体表记录唯一标识
  • 创建时间create_time
  • 创建人created_by
  • 变更类型type【add/edit/del】
  • 变更内容

表建模代码示例:

class ChangeLog(models.Model):
    entity_table = models.CharField(max_length=64, verbose_name=u"实体表的tablename")
    entity = models.CharField(max_length=128, verbose_name=u"唯一标识一条资源记录")
    type = models.IntegerField(choices=CHANGE_TYPE, blank=True, null=True, verbose_name=u"变更类型")
    datetime = models.DateTimeField(auto_now=True)
    operator = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"操作人")
    content = models.CharField(max_length=1024, blank=True, null=True, verbose_name=u"变更的内容")

Step2:机房管理表设计

表重要属性设计:

  • 名称
  • 合作名义
  • 类型【自用机房/客户机房/运营商机房】
  • 关联的互联网带宽【通过查询有哪些带宽的idc属性等于该idc来获取需要的数据】
  • 关联的业务私网网段【通过查询有哪些network vlan的idc属性等于该idc来获取需要的数据】
  • 机柜【关联的机柜】
  • 开通时间
  • 网络拓朴图
  • 联系人
  • 联系电话
  • 联系邮箱
  • 机房地址
  • 机房照片
  • 查看变更日志
  • 备注

表建模代码示例:

class IDC(models.Model):
    name = models.CharField(max_length=32, verbose_name=u'机房名称')
    notional = models.IntegerField(choices=COMPANY_TYPE, blank=True, null=True, default=0, verbose_name=u"合作名义")
    type = models.IntegerField(choices=IDC_TYPE, blank=True, null=True, default=0, verbose_name=u"类型")
    linkman = models.CharField(max_length=16, blank=True, null=True, default='', verbose_name=u'联系人')
    phone = models.CharField(max_length=32, blank=True, null=True, default='', verbose_name=u'联系电话')
    address = models.CharField(max_length=128, blank=True, null=True, default='', verbose_name=u"机房地址")
    topology_url = models.URLField(max_length=200, blank=True, null=True, verbose_name=u"拓扑图链接")
    photo_url = models.URLField(max_length=200, blank=True, null=True, verbose_name=u"机房照片链接")
    operator = models.CharField(max_length=32, blank=True, default='', null=True, verbose_name=u"运营商")
    date_added = models.DateField(auto_now=True, null=True, verbose_name=u"创建时间")
    deleted = models.BooleanField(default=False, verbose_name=u"是否已删除")
    comment = models.CharField(max_length=128, blank=True, default='', null=True, verbose_name=u"备注")

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u"IDC机房"
        verbose_name_plural = verbose_name

Step3:机柜管理表设计

表重要属性设计:

  • 机柜名称
  • 机柜空间大小
  • 所属机房
  • 本柜设备【显示设备数量,点击数字后进入本机柜中设备资产的列表页】
  • 创建时间
  • 状态【使用中/已下线】
  • 查看变更日志
  • 删除【标识位,在删除操作时仅标识该属性为False,不做真删除】
  • 备注

表建模代码示例:

class Rack(models.Model):
    name = models.CharField(max_length=32, verbose_name=u'机柜名称')
    units = models.IntegerField(blank=True, null=True, default=47, verbose_name=u'机柜大小【U】')
    units_available = models.IntegerField(blank=True, null=True, verbose_name=u'可用空间【U】')
    idc = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'机房')
    create_time = models.DateTimeField(auto_now=True)
    image_url = models.CharField(max_length=256, blank=True, null=True, verbose_name=u"机柜照片的URL地址")
    status = models.IntegerField(choices=RACK_STATUS, blank=True, null=True, default=1, verbose_name=u"机柜状态")
    deleted = models.BooleanField(default=False, verbose_name=u"是否已删除")
    comment = models.CharField(max_length=256, blank=True, default='', null=True, verbose_name=u"备注")

    def __unicode__(self):
        return self.idc.name + "-" + self.name

    class Meta:
        ordering = ['idc', 'name']

Step4:互联网带宽管理属性设计

表重要属性设计:

  • 名称【点击后进入公网IP地址资源的列表页】
  • 运营商
  • 电路编号
  • 带宽
  • 所属机房
  • 网络设备【接入在哪个网络设备上,向资产做关联】
  • 网关
  • 掩码
  • 起始IP
  • 终止IP
  • DNS
  • 开通时间
  • 状态【使用中/已下线】
  • 查看变更日志
  • 备注

增加按带宽做过滤进行展示公网IP资源的功能。

表建模代码示例:

class BindWidth(models.Model):
    name = models.CharField(max_length=64, blank=True, null=True, verbose_name=u"带宽名称")
    svc_provider = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"运营商")
    circuit_id = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"电路编号")
    size = models.IntegerField(blank=True, default=0, verbose_name=u"带宽大小(Mb)")
    idc = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'所属机房')
    asset = models.ForeignKey(Asset, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'关联的网络设备')
    gateway = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"网关地址")
    netmask = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"掩码地址")
    start_ip = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"起始地址")
    end_ip = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"终止地址")
    dns = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"DNS")
    create_time = models.DateTimeField(auto_now=True, verbose_name=u"开通时间")
    status = models.IntegerField(choices=COMMON_STATUS, blank=True, null=True, default=0, verbose_name=u"状态")
    deleted = models.BooleanField(default=False, verbose_name=u"是否已删除")
    comment = models.CharField(max_length=256, blank=True, default='', null=True, verbose_name=u"备注")

    def __unicode__(self):
        return self.name

Step5:公网IP地址资源表设计

该表中数据完全由程序自动填充进来,不需手工维护;在删除bindwidth记录时,要同时把与之相关的公网IP地址也删除

重要属性设计:

  • IP地址【根据带宽定义时的起止IP地址自动计算出】
  • 所属的互联网带宽
  • 端口信息【提供一个按钮,点击后执行查看NAT配置的查询keyword操作】
  • 状态【使用中/空闲,根据防火墙配置信息做自动识别和展示】
  • 备注

表建模代码示例:

class PublicIPAddress(models.Model):
    public_ip = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"公网IP")
    bind_width = models.ForeignKey(BindWidth, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'所属的带宽')
    status = models.BooleanField(default=False, verbose_name=u"状态")
    comment = models.CharField(max_length=256, blank=True, default='', null=True, verbose_name=u"描述")

Step6:VLAN资源表的设计

重要属性设计:

  • 名称【唯一标识】
  • 网段信息
  • 网络性质【生产网络/办公网络/测试网络/客户侧网络】
  • 所属机房
  • 开通时间
  • 状态【使用中/已下线】
  • 查看变更日志
  • 备注

注:

  • 需要增加一个点击vlan名称进入该vlan中活跃状态的host ip的列表页,这些ip要经由自动发现来获取并写入表中,默认为未确认状态。
  • 需要定义一个可以用于VLAN下活跃ip自动发现服务的探针节点的salt名称。使用该探针节点,执行对其所在VLAN的一个C段地址的探活。
  • 需要增加一个自动为host ip与现有应用进行关联链接的功能,点击后可进入相关应用的详情页。

表建模代码示例:

class NetworkVLAN(models.Model):
    name = models.CharField(max_length=64, blank=True, null=True, verbose_name=u"VLAN名称")
    vlan = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"网段信息")
    vlan_type = models.IntegerField(choices=VLAN_TYPE, blank=True, null=True, default=0, verbose_name=u"网络性质")
    idc = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'所属的机房')
    create_time = models.DateTimeField(auto_now=True, verbose_name=u"开通时间")
    deleted = models.BooleanField(default=False, verbose_name=u"是否已删除")
    probe_host = models.ForeignKey(Asset, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'探针节点')
    comment = models.CharField(max_length=256, blank=True, default='', null=True, verbose_name=u"描述")

    def __unicode__(self):
        return self.name + "-" + self.vlan

Step7:VPN资源表的设计

提供一个通过机房和防火墙设备,获取VPN配置信息,解析入库后,提供过滤展示vpn资源信息的功能。

从防火墙的配置信息中识别出配置了哪些vpn链路,链路两端的公网ip地址是什么。唯一定义一条vpn线路的方法是:ip1:ip2+type,ip2:ip1+type 二者存在其一,即认为已定义。使用type标识gre/ipsec类型。

重要属性设计:

  • 本端地址<–>对端地址
  • 本端网络设备
  • 对端网络设备
  • 类型【ipsec/gre】
  • 用途
  • 创建时间
  • 备注

表建模代码示例:

class VPN(models.Model):
    ipaddress_a_and_b = models.CharField(max_length=40, blank=True, null=True, verbose_name=u"IP地址a_and_b")
    asset_a = models.IntegerField(max_length=11, blank=True, null=True, verbose_name=u'A机房网络设备')
    asset_b = models.IntegerField(max_length=11, blank=True, null=True, verbose_name=u'B机房网络设备')
    asset_a_hostname = models.CharField(blank=True, null=True, max_length=128, verbose_name=u"A网络设备名")
    asset_b_hostname = models.CharField(blank=True, null=True, max_length=128, verbose_name=u"B网络设备名")
    type = models.IntegerField(choices=VPN_TYPE, blank=True, null=True, default=1, verbose_name=u"VPN类型")
    usage = models.CharField(max_length=128, blank=True, default='', null=True, verbose_name=u"用途")
    create_time = models.DateTimeField(auto_now=True, verbose_name=u"创建时间")
    comment = models.CharField(max_length=128, blank=True, default='', null=True, verbose_name=u"备注")

    def __unicode__(self):
        return self.ipaddress_a_and_b

    class Meta:
        ordering = ['ipaddress_a_and_b']

Step8:专线资源表的设计

重要属性设计:

  • 使用部门
  • 专线号
  • 所属运营商
  • 带宽
  • 地区【机房A–>机房B】
  • 设备与接口【设备A–>设备B】
  • 公司名义
  • 是否有备份线路
  • 报障信息
  • 变更日志
  • 备注

表建模代码示例:

class ZhuanXian(models.Model):
    department = models.CharField(max_length=64, blank=True, null=True, verbose_name=u"使用部门")
    svc_provider = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"运营商")
    circuit_id = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"电路编号")
    size = models.IntegerField(blank=True, default=0, verbose_name=u"带宽大小(Mb)")
    idc_a = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'所属机房', related_name='idc_a')
    idc_b = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'所属机房', related_name='idc_b')
    asset_a = models.ForeignKey(Asset, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'关联的网络设备', related_name='asset_a')
    asset_b = models.ForeignKey(Asset, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'关联的网络设备', related_name='asset_b')
    company = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"公司名义")
    has_bak = models.IntegerField(choices=TRUE_FALSE, blank=True, null=True, default=0, verbose_name=u"有无备线")
    service_call = models.CharField(max_length=256, blank=True, null=True, verbose_name=u"报障信息")
    comment = models.CharField(max_length=256, blank=True, default='', null=True, verbose_name=u"备注")
    deleted = models.BooleanField(default=False, verbose_name=u"是否已删除")

    def __unicode__(self):
        return self.circuit_id

Step9:主机及虚机资产管理表设计

这里只列出部分属性,完整的属性列表参见下面的表模型代码:

  • 采购时间
  • sn号
  • 资产编号
  • 所属产品线【改为通过设备分组的属性来标识】
  • 所属机房
  • 所属机柜
  • cpu/内存/硬盘的数据展示,展示出配件数量和规格信息来
  • raid卡及raid配置信息
  • 绑定的公网ip
  • 配置的公网端口转发
  • zabbix监控信息【地址+端口】
  • 远程管理【支持/不支持】
  • 状态【上架/上线/下线/下架/报废】
  • 用途
  • 变更日志

表建模代码示例:

class Asset(models.Model):
    """
    asset modle
    """
    ip = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"主机IP")
    other_ip = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"其他IP")
    hostname = models.CharField(unique=True, max_length=128, verbose_name=u"主机名")
    port = models.IntegerField(blank=True, null=True, verbose_name=u"端口号")
    group = models.ManyToManyField(AssetGroup, blank=True, verbose_name=u"设备分组")
    username = models.CharField(max_length=16, blank=True, null=True, verbose_name=u"管理用户名")
    password = models.CharField(max_length=64, blank=True, null=True, verbose_name=u"密码")
    use_default_auth = models.BooleanField(default=True, verbose_name=u"使用默认管理账号")
    idc = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'机房')
    rack = models.ForeignKey(Rack, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'机柜')
    mac = models.CharField(max_length=20, blank=True, null=True, verbose_name=u"MAC地址")
    remote_ip = models.CharField(max_length=16, blank=True, null=True, verbose_name=u'远控卡IP')
    brand = models.CharField(max_length=64, blank=True, null=True, verbose_name=u'硬件厂商型号')
    cpu = models.CharField(max_length=64, blank=True, null=True, verbose_name=u'CPU')
    memory = models.CharField(max_length=128, blank=True, null=True, verbose_name=u'内存')
    disk = models.CharField(max_length=1024, blank=True, null=True, verbose_name=u'硬盘')
    system_type = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"系统类型")
    system_version = models.CharField(max_length=8, blank=True, null=True, verbose_name=u"系统版本号")
    system_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=u"系统平台")
    cabinet = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'机柜号')
    position = models.IntegerField(blank=True, null=True, verbose_name=u'柜中位置')
    number = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'资产编号')
    status = models.IntegerField(choices=ASSET_STATUS, blank=True, null=True, default=1, verbose_name=u"机器状态")
    asset_type = models.IntegerField(choices=ASSET_TYPE, blank=True, null=True, verbose_name=u"设备类型")
    env = models.IntegerField(choices=ASSET_ENV, blank=True, null=True, verbose_name=u"运行环境")
    sn = models.CharField(max_length=128, blank=True, null=True, verbose_name=u"SN编号")
    date_added = models.DateTimeField(auto_now=True, null=True)
    is_active = models.BooleanField(default=True, verbose_name=u"是否激活")
    comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=u"备注")
    policy = models.IntegerField(choices=AVAILABLE_POLICY, blank=True, null=True, verbose_name=u"高可用策略")
    related_asset = models.IntegerField(max_length=11, blank=True, null=True, verbose_name=u'高可用策略关联设备')
    start_time = models.DateTimeField(blank=True, null=True, verbose_name=u"采购时间")
    usage = models.CharField(max_length=128, blank=True, null=True, verbose_name=u"用途")

    def __unicode__(self):
        return self.ip

以上主机资产表的模型设计是在开源跳板机项目jumpserver中的主机表设计为基础,按需进行的扩展。

Step10:网络设备资源管理表的设计

防火墙和交换机的端口列表信息,采用人工自定义和程序协助生成两种方式,以程序自动获取、识别、解析入库为主。

重要属性设计:

  • 设备类型【防火墙/交换机/其他】
  • 设备型号
  • 高可用策略【堆叠/双机热备/双机冷备/单机设备】
  • 高可用的关联设备【仅当设备不是单点设备时选填】
  • 所属产品线
  • 端口,点击后进入端口管理,支持手动和程序自动进行端口信息的维护管理】
    • 编号
    • 接口类型【电口/光口】
    • 链路类型【access/trunk/Hybrid】
    • 配置说明
    • 连接到【外链关联至asset,asset的范围要排除虚机和容器类型】
    • 备注
  • 所属机房
  • 所属机柜
  • 状态【上架/上线/下线/下架/报废】
  • 查看变更日志
  • 备注

一点功能设计思路
为交换机和防火墙设备提供一个端口信息初始化的功能:

  • 在输入了端口命名的前缀信息、接口类型、接口数量后,可以自动向网络设备的端口管理表中插入初始化数据
  • 例如交换机的端口信息初始化:自动生成接口名、所属vlan、vlan地址;
  • 需要为接口提供批量编辑功能;
  • 同时保留单独增加、编辑一个端口的功能;

表建模代码示例:

网络设备作为一种资产,直接使用Asset表进行管理,此外另定义一个Interface表用于管理网络设备接口。


class Interface(models.Model):
    name = models.CharField(max_length=64, blank=True, null=True, verbose_name=u"接口名称")
    type = models.IntegerField(choices=NETDEV_INTERFACE_TYPE, blank=True, null=True, default=0, verbose_name=u"电口/光口")
    line_type = models.IntegerField(choices=NETDEV_LINE_TYPE, blank=True, null=True, default=0, verbose_name=u"链路类型")
    config = models.TextField(max_length=300, blank=True, default='', null=True, verbose_name=u"配置说明")
    asset = models.IntegerField(max_length=11, verbose_name=u'属于哪个网络设备')
    related_asset = models.ForeignKey(Asset, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'连接到哪个设备')
    status = models.IntegerField(choices=COMMON_STATUS, blank=True, null=True, default=2, verbose_name=u"状态")
    deleted = models.BooleanField(default=False, verbose_name=u"是否已删除")
    comment = models.CharField(max_length=128, blank=True, default='', null=True, verbose_name=u"备注")

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ['name']

Step11:NAT配置管理表设计

随着业务发展,对公网地址以及转发端口资源的管理会遇到一些挑战。我们需要准确、全面地掌握已经配置了的是哪些,哪些是可用资源等。
重要属性设计:

  • 公网IP
  • 目标IP
  • 类型【一对一NAT/转发】
  • 关联到带宽
  • 端口信息【一对一NAT:显示放行端口的列表;转发:显示公网port–>私网port的转发端口列表】
  • 状态【确认/未确认】(需要提供个批量功能)
  • 描述(仅需为状态和描述两个属性提供编辑支持)

注:端口转发配置以及静态NAT配置,实现为自动从防火墙配置信息中识别的方式。

表建模代码示例:

class NatRule(models.Model):
    public_ip = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"公网IP")
    target_ip = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"私网目标IP")
    type = models.IntegerField(choices=NAT_TYPE, blank=True, null=True, default=2, verbose_name=u"NAT使用方式")
    port_mapping = models.CharField(max_length=512, blank=True, null=True, verbose_name=u"端口使用信息")
    create_time = models.DateTimeField(auto_now=True)
    confirm = models.BooleanField(default=False, verbose_name=u"是否确认")
    asset = models.IntegerField(max_length=11, verbose_name=u'属于哪个网络设备')
    deleted = models.BooleanField(default=False, verbose_name=u"是否已删除")
    bindwidth = models.IntegerField(blank=True, null=True, verbose_name=u'关联的带宽')
    comment = models.CharField(max_length=256, blank=True, default='', null=True, verbose_name=u"描述")

    def __unicode__(self):
        return self.public_ip + "-" + self.target_ip

    class Meta:
        ordering = ['public_ip', 'target_ip']

Step12:OA工作流程单的设计

再好的功能或愿望,也要有强制性的工作流程控制,才会得到认真的执行,以及持续的优化。
如果已经有成熟的办公信息化平台,可以直接设计并创建出需要的工作流程,否则在自动化运维平台上自行设计实现一个简单的工作流程与任务协同的功能也未尝不可。

设计和实现一个OA上的设备上架申请单、设备下架申请单,把设备上下架流程强制性的固化下来,在流程环节中要求指定人处理资产管理平台中的设备信息维护工作,以及部署基础性监控,接入管控平台等。

猜你喜欢

转载自blog.csdn.net/watermelonbig/article/details/88389866