rongge95500

一、 前言

1.1 简介

CMDB(Configuration Management Database)配置管理数据库,是所有运维工具的数据基础。70%~80%的IT相关问题与环境的变更有着直接的关系。实施变更管理的难点和重点并不是工具,而是流程。即通过一个自动化的、可重复的流程管理变更,使得当变更发生的时候,有一个标准化的流程去执行,能够预测到这个变更对整个系统管理产生的影响,并对这些影响进行评估和控制。而变更管理流程自动化的实现关键就是CMDB

1.2 功能

应该知道包含这几种功能:整合、调和、同步、映射和可视化

  • 用户管理,记录测试,开发,运维人员的用户表
  • 业务线管理,需要记录业务的详情
  • 项目管理,指定此项目用属于哪条业务线,以及项目详情
  • 应用管理,指定此应用的开发人员,属于哪个项目,和代码地址,部署目录,部署集群,依赖的应用,软件等信息
  • 主机管理,包括云主机,物理机,主机属于哪个集群,运行着哪些软件,主机管理员,连接哪些网络设备,云主机的资源池,存储等相关信息
  • 主机变更管理,主机的一些信息变更,例如管理员,所属集群等信息更改,连接的网络变更等
  • 网络设备管理,主要记录网络设备的详细信息,及网络设备连接的上级设备
  • IP管理,IP属于哪个主机,哪个网段, 是否被占用等

二、 四种实现方式

2.1 Agent方式

  • 流程分析
    • 在每台服务器上部署一个agent脚本(python代码实现的,用的是它的subprocess模块,定时触发执行subprocess.getoutput(cmd)方法) ,将agent执行的结果通过requests模块返回给API程序,处理之后储存到数据库,然后通过前端界面展示出来

优缺点分析:

  优点: 速度快,使用于服务器多的大型公司

  缺点: 需要为每一台服务器都部署一个Agent程序,机器过多时耗费的人力成本大

2.2 ssh方式(基于paramiko模块

  • 流程分析
    • agent方式不同的是,不用再每台服务器上部署agent脚本,而是通过ssh服务实现的。方法就是将python代码放置于一台单独的服务器上,即中控机。中控机通过paramiko模块以ssh方式远程链接登录到每台服务器上执行linux命令来获取所需要的信息并将数据返回到中控机并转交给API,然后由API程序将数据处理之后储存到数据库,再通过前端界面展示

 

优缺点分析:

  优点: 不需要为每一台服务器都部署一个Agent程序,适用于服务器较少的情况

  缺点: 需要登录,执行速度慢,需要部署一台中控机

import paramiko

#创建ssh对象
ssh = paramiko.SSHClient()

# 连接服务器
ssh.connect(hostname=\'localhost\', port=22, username=\'root\', password=\'123\')

# 执行命令
stdin, stdout, stderr= ssh.exec_command(\'ifconfig\')

# 拿到命令的执行结果
res = stdout.read()

# 关闭连接
ssh.close()
机器较少时可以使用此方法

2.3 saltstack方式

  • 流程分析
    • 与第二种类似,同样需要一个中控机,同时它还依赖于第三方软件saltstacksaltstack有两个身份,即masterminion,我们可以在中控机上安装salt-master来控制所有的salt-minion服务器,中控机salt-master通过(salt \'minion主机名\' cmd.run \'命令\')向服务器minion发送命令,minion会将执行的结果放入队列中,再由master从队列中取出数据,转交给API处理后储存到数据库中,通过前端界面展示

 

优缺点分析:

  优点: 速度快,开发成本低

  缺点: 需要依赖于第三方工具,需要部署中控机

  • saltstack安装与配置
1. 安装
    master端
        # 安装salt-master
            yum install salt-master
        # 修改配置文件:/etc/salt/master
            interface: 0.0.0.0    # 表示Master的IP
        # 启动
            service salt-master start

    slave端:
        # 安装salt-minion
            yum install salt-minion
        # 修改配置文件 /etc/salt/minion
            master: 10.211.55.4   # master的地址
            或
            master:
                - 10.211.55.4
                - 10.211.55.5
            random_master: True
            id: c2.salt.com     # 客户端在salt-master中显示的唯一ID
        # 启动
            service salt-minion start
2. 授权
    """
    salt-key -L                    # 查看已授权和未授权的slave
    salt-key -a  salve_id      # 接受指定id的salve
    salt-key -r  salve_id      # 拒绝指定id的salve
    salt-key -d  salve_id      # 删除指定id的salve
    """

3. 执行
    # 在master服务器上对salve进行远程操作
    salt \'c2.salt.com\' cmd.run  \'ifconfig\'
    
    
    # 基于API的方式
    import salt.client
    local = salt.client.LocalClient()
    result = local.cmd(\'c2.salt.com\', \'cmd.run\', [\'ifconfig\'])

2.4 Puppet方式(了解)

  • 通过RPC消息队列将执行的结果返回给用户!年代久远,作为了解,不做叙述

三、 表结构设计

首先,创建一个Django项目,由于我们的数据表既与后台管理有关又与API有关,所以我们在设计表结构时又新创建了两个APP(backend,warehouse),这样设计的好处就是相互独立又方便三者间的关联。

然后我们在warehouse的models中创建数据库模型

from django.db import models


# Create your models here.
class UserInfo(models.Model):
    \'\'\'
    用户信息
    \'\'\'
    name = models.CharField(verbose_name=\'姓名\', max_length=32)
    age = models.CharField(max_length=11, verbose_name=\'年龄\')
    gender = models.CharField(max_length=11, verbose_name=\'性别\')
    email = models.EmailField(verbose_name=\'邮箱\')
    phone = models.CharField(verbose_name=\'手机\', max_length=32)

    class Meta:
        verbose_name_plural = \'用户表\'

    def __str__(self):
        return self.name


class UserGroup(models.Model):
    \'\'\'
    用户组
    \'\'\'
    name = models.CharField(max_length=32, verbose_name=\'组名\')
    users = models.ManyToManyField(\'UserInfo\', verbose_name=\'组成员\')

    class Meta:
        verbose_name_plural = \'用户组表\'

    def __str__(self):
        return self.name


class BusinessUnit(models.Model):
    \'\'\'
    业务线
    \'\'\'
    name = models.CharField(max_length=32)
    contact = models.ForeignKey(\'UserGroup\', verbose_name=\'业务联系人\')
    manager = models.ForeignKey(\'UserGroup\', verbose_name=\'管理人员\', related_name=\'aaa\')

    class Meta:
        verbose_name_plural = \'业务线表\'

    def __str__(self):
        return self.name


class Idc(models.Model):
    \'\'\'
    机房信息
    \'\'\'
    name = models.CharField(max_length=32, verbose_name=\'机房名\')
    floor = models.IntegerField(default=10, verbose_name=\'楼层\')

    class Meta:
        verbose_name_plural = \'机房信息表\'

    def __str__(self):
        return self.name


class Tag(models.Model):
    \'\'\'
    资产标签
    \'\'\'
    name = models.CharField(\'标签\', max_length=32, unique=True)

    class Meta:
        verbose_name_plural = "标签表"

    def __str__(self):
        return self.name


class Asset(models.Model):
    \'\'\'
    资产信息表
    \'\'\'
    type_choices = (
        (1, \'服务器\'),
        (2, \'交换机\'),
        (3, \'防火墙\')
    )

    status_choices = (
        (1, \'上架\'),
        (2, \'在线\'),
        (3, \'离线\'),
        (4, \'下架\')
    )

    device_type_id = models.IntegerField(choices=type_choices, default=1)
    device_status_id = models.IntegerField(choices=status_choices, default=1)

    cabinet_num = models.CharField(max_length=32, verbose_name=\'机柜号\', null=True, blank=True)
    cabinet_order = models.CharField(max_length=32, verbose_name=\'机柜序号\', null=True, blank=True)

    idc = models.ForeignKey(\'Idc\', verbose_name=\'IDC机房\', null=True, blank=True)
    business_unit = models.ForeignKey(\'BusinessUnit\', verbose_name=\'属于的业务线\', null=True, blank=True)

    tag = models.ManyToManyField(\'Tag\')

    latest_date = models.DateField(null=True)
    create_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "资产表"

    def __str__(self):
        return "%s-%s-%s" % (self.idc.name, self.cabinet_num, self.cabinet_order)


class NetworkDevice(models.Model):
    \'\'\'
    网络设备
    \'\'\'
    asset = models.OneToOneField(\'Asset\')
    management_ip = models.CharField(max_length=64, verbose_name=\'管理ip\', null=True, blank=True)
    vlan_ip = models.CharField(max_length=64, verbose_name=\'VLANIP\', null=True, blank=True)
    intranet_ip = models.CharField(max_length=128, verbose_name=\'内网IP\', null=True, blank=True)
    sn = models.CharField(max_length=64, verbose_name=\'sn号\', null=True, blank=True)
    manufacture = models.CharField(max_length=128, verbose_name=\'制造商\', null=True, blank=True)
    model = models.CharField(max_length=128, verbose_name=\'型号\', null=True, blank=True)
    port_num = models.SmallIntegerField(verbose_name=\'端口数量\', null=True, blank=True)
    device_detail = models.CharField(max_length=255, verbose_name=\'设置详细配置\', null=True, blank=True)

    class Meta:
        verbose_name_plural = "网络设备表"


class Server(models.Model):
    \'\'\'
    服务器
    \'\'\'
    asset = models.OneToOneField(\'Asset\')
    hostname = models.CharField(max_length=32, unique=True)
    sn = models.CharField(max_length=64, db_index=True)
    manufacturer = models.CharField(max_length=64, verbose_name=\'制造商\', null=True, blank=True)
    model = models.CharField(max_length=64, verbose_name=\'型号\', blank=True, null=True)
    manage_ip = models.GenericIPAddressField(verbose_name=\'管理IP\', null=True, blank=True)

    os_platform = models.CharField(max_length=32, verbose_name=\'系统\', null=True, blank=True)
    os_version = models.CharField(max_length=32, verbose_name=\'系统版本\', null=True, blank=True)

    cpu_count = models.IntegerField(verbose_name=\'CPU数量\', null=True, blank=True)
    cpu_physical = models.IntegerField(verbose_name=\'CPU物理数量\', null=True, blank=True)
    cpu_model = models.CharField(max_length=128, verbose_name=\'CPU型号\', null=True, blank=True)

    create_time = models.DateTimeField(auto_now_add=True, blank=True)

    class Meta:
        verbose_name_plural = "服务器表"

    def __str__(self):
        return self.hostname


class Disk(models.Model):
    \'\'\'
    硬盘信息
    \'\'\'
    slot = models.CharField(max_length=12, verbose_name=\'插槽位\')
    model = models.CharField(max_length=32, verbose_name=\'磁盘型号\')
    capacity = models.CharField(max_length=32, verbose_name=\'磁盘容量GB\')
    pd_type = models.CharField(max_length=32, verbose_name=\'磁盘类型\')
    server = models.ForeignKey(\'Server\', related_name=\'disk\')

    class Meta:
        verbose_name_plural = "硬盘表"

    def __str__(self):
        return self.slot


class Nic(models.Model):
    \'\'\'
    网卡信息
    \'\'\'
    name = models.CharField(max_length=32, verbose_name=\'网卡名称\')
    hwaddr = models.CharField(max_length=64, verbose_name=\'网卡MAC地址\')
    netmask = models.CharField(max_length=64)
    ipaddrs = models.CharField(max_length=256, verbose_name=\'IP地址\')
    up = models.BooleanField(default=False)

    server = models.ForeignKey(\'Server\', related_name=\'nic\')

    class Meta:
        verbose_name_plural = "网卡表"

    def __str__(self):
        return self.name


class Memory(models.Model):
    \'\'\'
    内存信息
    \'\'\'
    slot = models.CharField(max_length=12, verbose_name=\'插槽位\')
    model = models.CharField(max_length=64, verbose_name=\'内存型号\')
    manufacturer = models.CharField(max_length=32, verbose_name=\'制造商\', null=True, blank=True)
    capacity = models.FloatField(verbose_name=\'容量\', null=True, blank=True)
    sn = models.CharField(max_length=64, verbose_name=\'sn号\', null=True, blank=True)
    speed = models.CharField(max_length=16, verbose_name=\'速度\', null=True, blank=True)

    class Meta:
        verbose_name_plural = "内存表"

    def __str__(self):
        return self.slot


class AssetRecord(models.Model):
    \'\'\'
    资产变更记录表
    \'\'\'
    asset = models.ForeignKey(\'Asset\')
    content = models.TextField(null=True)
    creator = models.ForeignKey(\'UserInfo\', null=True, blank=True)
    create_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "资产记录表"

    def __str__(self):
        return \'<{}> <{}> <{}>\'.format(self.asset.idc.name, self.asset.cabinet_num, self.asset.cabinet_order)


class ErrorLog(models.Model):
    \'\'\'
    错误日志记录
    \'\'\'
    asset = models.ForeignKey(\'Asset\', null=True, blank=True)
    title = models.CharField(max_length=64)
    content = models.TextField()
    create_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "错误日志表"

    def __str__(self):
        return self.title
models.py

 

分类:

技术点:

相关文章: