zoukankan      html  css  js  c++  java
  • 20200311 CMDB的表设计

    昨日内容

    1.CMDB架构
    	- agent
    		待采集的服务器里执行采集的指令,利用requests模块发送至api接口,并保存到数据库,利用Django的web服务启动服务,用户获取
    	- ssh
    		利用统一的一个中控机进行获取服务器信息,并转发至api
    		
    2.CMDB方案分为几部分,(或者有几个人做的)你负责那一部分
    	三部分: 	
    		采集部分 (1个人)
    		API数据表设计和数据分析入库  (1个人)
    		web输出,数据展示  (共用1人)
    		
    3.采集部分怎么完成的
    	- 高级配置文件,参考Django的高级配置文件完成的
    		自定义整合类,利用getattr与getattr分别将设置文件中的属性遍历并保存至类名称空间中(注意自定制的在高级配置下).实例化类得到对象,这样在获取settings设置时,对象.属性自动获取配置.
    		
    	- 采用了高内聚低耦合的原则来完成采集代码
    		例如:采集cpu信息的时候,会单独的写一个cpu.py文件,这个文件中的所有逻辑代码,都是与采集cpu的信息相关的,这样的好处就是:将来查询问题是,方便查询
    	- 可插拔式的采集,参考Django的中间件(利用注释)
    		利用类方法,从配置文件中读取配置,获取配置的获取信息的所有路径,利用getattr获取模块类,循环执行即可.(采集类方法的名称需一致)
    		
    4.遇到的问题是什么,怎么解决的
    	Linux命令的不熟悉,问运维,去Google
    	沟通方面的,沟通能力的提升
    	
    5.做这个项目中,你觉得你收获了什么
        大家注意的是,CMDB项目是一个ToB的项目,就是给公司用的项目,和办公系统(金蝶用友, erp),路飞学成就是一个Toc项目, 是给广大用户用的。ToB的用户是比ToC的用户少很多的, ToC的技术要求比ToB的技术要求大很多,但是ToB的项目架构基本上每一个公司都差不多 
        
        运维开发工程师, 更偏向开发,开发占比80%,运维占20%。 面试官是一个运维的,开发能力可能还不如你,shell
        
        运维开发优势:
            自动化运维的项目,在每一个公司中都差不多,项目经验是不断积累的,所以你下次跳槽的 时候,就可以将这些开发好的系统,90%的功能都可以搬过来复用
    

    CMDB设计

    1.完善客户端采集功能

    错误异常处理

    采集出错的错误信息也是需要进行上报的
    

    使用traceback模块实现获取详细的错误信息

    import traceback 
    def test(): 
        try:
            int('asdc') 
        except Exception as e: 
            print(traceback.format_exc()) 
            
        print('hello') 
        
    test()
    

    traceback

    该模块提供了一个标准接口,用于提取,格式化和打印Python程序的堆栈跟踪。它在打印堆栈跟踪时完全模仿了Python解释器的行为。当您想要在程序控制下打印堆栈跟踪时,这非常有用,例如在解释器周围的“包装器”中。

    • traceback.print_tb(tb [,limit [,file ] ] )
      打印以限制回溯对象tb中的堆栈跟踪条目。如果 省略limit或者None打印所有条目。如果省略文件或None输出转到sys.stderr; 否则它应该是一个打开的文件或类似文件的对象来接收输出。

    • traceback.print_exception(etype,value,tb [,limit [,file ] ] )

      ​ 打印异常信息,最多限制堆栈跟踪条目从traceback tb到文件。这与print_tb()以下方式不同:(1)如果tb不是None,则打印标题; (2)在堆栈跟踪后打印异常etype和值 ; (3)如果etype是且值具有适当的格式,则打印出发生语法错误的行,其中插入符号表示错误的大致位置。Traceback (most recent call last):SyntaxError

    • traceback.print_exc([ limit [,file ] ] )
      这是一个简写。(实际上,它用于以线程安全的方式检索相同的信息,而不是使用已弃用的变量。)print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)sys.exc_info()

    • traceback.format_exc([ 限制] )
      这就像print_exc(limit)但返回一个字符串而不是打印到文件。

    参考博客: https://blog.csdn.net/qq_31446377/article/details/89703542

    使用

        def __init__(self):
            # 初始化,获取setting中的配置
            self.plugins_dict = setting.PLUGINS_DICT    # 规定获取服务器信息
            self.settings = setting.MODE    # 获取服务器信息的方式
            self.debug = setting.DEBUG      # 开发上线模式
    
    
        # 从配置文件中读取采集的插件配置,执行插件类中对应的方法
        def execute(self):
            # 1.循环获取配置中的value值
            response = {}
            for k, v in self.plugins_dict.items():
                ret = {'status':None, 'data':None}
                '''
                自定义的配置,k是想要获取的信息名,v是具体执行文件路径
                '''
                # 错误异常的捕获
                try:
                    # 2.分析采集类的路径
                    '''
                    获得方法的路径信息与具体类名
                    '''
                    module_path, class_name = v.rsplit('.', 1)
                    # 3.获取导入模块的路径 import_module: 导入字符串的模块路径
                    module = importlib.import_module(module_path)
                    # 4.从模块中获取导入类
                    cls = getattr(module,class_name)
                    # 实例化类,执行类对应的具体采集方法  (将类方法command_func传入获取方法中,减少代码冗余)
                    res = cls().process(self.command_func, self.debug)
                    # 执行注册文件中的每一个方法,并保存信息到字典中
                    ret['status'] = 10000
                    ret['data'] = res
                except Exception as e:
                    ret['status'] = 10001
                    # 使用traceback模块就不需要添加e
                    ret['data'] = '错误信息是:%s' % (traceback.format_exc())
    
                response[k] = ret
            return response
    

    ssh类方案的完善

    方法需要采集服务系信息并发送给API,所以整合代码

    start.py

    from src.script import run
    
    if __name__ == '__main__':
        run()
    

    script.py

    from lib.conf.config import setting
    from src.client import Agent, Ssh
    
    def run():
        # 判断设置执行的方法是什么,实例化相应的类
        # if setting.MODE == 'agent':
        #     Agent().collectAndPost()
        # else:
        #     Ssh().collectAndPost()
        if setting.MODE == 'agent':
            obj = Agent()
        else:
            obj = Ssh()
        obj.collectAndPost()
    

    client.py

    #-*- coding: utf-8 -*-
    #!/usr/bin/env python3
    
    ' 获取信息,发送给api '
    
    __author__ = 'Fwzzz'
    
    from lib.conf.config import setting
    from src.plugins import PluginsManager
    import requests
    import json
    
    
    # agent方法类
    class Agent():
        def collectAndPost(self):
            # 获取服务器信息
            ret = PluginsManager().execute()
            # 启动执行execute()方法获取服务器的数据 (定义返回的是字典)
            for k, v in ret.items():
                print(k,v)
    
            # requests.post(setting.API_URL, data=json.dumps(ret))  # 转换为json格式数据
            requests.post(setting.API_URL, json=ret)  # 可以直接使用json=ret转换
    
    
    # ssh方法类
    class Ssh():
        # 需要将分配好的主机名写入数据库 (数据库中提前录好主机名,然后ssh类获取)
        def get_hostname(self):
            # 获取后端API查询出的主机名
            hostnames = requests.get(setting.API_URl)
    
            return hostnames   # 后端发送的是列表形式主机名
    
        # 执行获取,发送任务函数
        def task(self,hostname):
            ret = PluginsManager(hostname=hostname).execute()
            # 发送给后端api入库信息
            requests.post(setting.API_URL, json=ret)
    
        # 线程池函数
        def collectAndPost(self):
            hostnames = self.get_hostname()
            # 线程池运行
            from concurrent.futures import ThreadPoolExecutor
            pool = ThreadPoolExecutor(10)
            # 获取主机名,一个个的服务器去登录采集信息
            for hostname in hostnames:
                pool.submit(self.task,hostname)
    
    
    

    注意:

    1.需要将分配好的主机名写入到数据库中

    2.采集类 plugin_manager 初始化的时候,需要传入主机名

    提高ssh类方案的采集效率

    使用线程池或者进程池来去提高
    	线程与进程的区别:
    		线程池: py2中没有,py3有
    		进程池: pt2,3都有
    		
            
    # python3中线程池的使用方式:
    
    import time
    from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor 
    
    p = ThreadPoolExecutor(10) 
    
    def test(i): 
        time.sleep(2) 
        print(i) 
        
        for i in range(100): 
            p.submit(test, i)
    

    2. api数据分析,数据入库

    先有数据表,需要数据模型model的设计

    img

    • model类应该写在哪里?

      • 写在第三方的一个app中,repository中
    • model类中的字段怎么设计

      • 把握一个原则,字段的设计一定是根据客户端提交过来的字段来进行设计的
    • 业务线

      • 腾讯: 
        	三条业务线,包括:微信,QQ, 王者荣耀
        	
        	QQ跑在多台服务器上,如果,一台服务器上只能跑QQ的话, 这种关系叫:一对多关系 
        	QQ跑在多台服务器上, 如果,一台服务器上还可以跑其他的业务,微信,这种关系叫:多对多的关系
        

    表关系

    img

    img

    server端代码

    创建专用的repository APP,用于管理models类字段

    from django.db import models
    
    # Create your models here.
    class UserProfile(models.Model):
        """
        用户信息
        """
        name = models.CharField(u'姓名', max_length=32)
        email = models.EmailField(u'邮箱')
        phone = models.CharField(u'座机', max_length=32)
        mobile = models.CharField(u'手机', max_length=32)
        password = models.CharField(u'密码', max_length=64)
    
        class Meta:
            verbose_name_plural = "用户表"
    
        def __str__(self):
            return self.name
    
    
    class UserGroup(models.Model):
        """
        用户组
        """
        name = models.CharField(max_length=32, unique=True)
        users = models.ManyToManyField('UserProfile')
    
        class Meta:
            verbose_name_plural = "用户组表"
    
        def __str__(self):
            return self.name
    
    
    
    
    class IDC(models.Model):
        """
        机房信息
        """
        name = models.CharField('机房', max_length=32)
        floor = models.IntegerField('楼层', default=1)
    
        class Meta:
            verbose_name_plural = "机房表"
    
        def __str__(self):
            return self.name
    
    class BusinessUnit(models.Model):
        """
        业务线
        """
        name = models.CharField('业务线', max_length=64, unique=True)
        contact = models.ForeignKey('UserGroup', verbose_name='业务联系人', related_name='c', on_delete=models.CASCADE)
        manager = models.ForeignKey('UserGroup', verbose_name='系统管理员', related_name='m', on_delete=models.CASCADE)
    
        class Meta:
            verbose_name_plural = "业务线表"
    
        def __str__(self):
            return self.name
    
    
    
    class Server(models.Model):
        """
        服务器信息
        """
        device_type_choices = (
            (1, '服务器'),
            (2, '交换机'),
            (3, '防火墙'),
        )
        device_status_choices = (
            (1, '上架'),
            (2, '在线'),
            (3, '离线'),
            (4, '下架'),
        )
    
        device_type_id = models.IntegerField('服务器类型',choices=device_type_choices, default=1)
        device_status_id = models.IntegerField('服务器状态',choices=device_status_choices, default=1)
    
        cabinet_num = models.CharField('机柜号', max_length=30, null=True, blank=True)
        cabinet_order = models.CharField('机柜中序号', max_length=30, null=True, blank=True)
    
        idc = models.ForeignKey('IDC', verbose_name='IDC机房', null=True, blank=True, on_delete=models.CASCADE)
        business_unit = models.ForeignKey('BusinessUnit', verbose_name='属于的业务线', null=True, blank=True, on_delete=models.CASCADE)
    
    
        hostname = models.CharField('主机名',max_length=128, unique=True)
        sn = models.CharField('SN号', max_length=64, db_index=True)
        manufacturer = models.CharField(verbose_name='制造商', max_length=64, null=True, blank=True)
        model = models.CharField('型号', max_length=64, null=True, blank=True)
    
        manage_ip = models.GenericIPAddressField('管理IP', null=True, blank=True)
    
        os_platform = models.CharField('系统', max_length=16, null=True, blank=True)
        os_version = models.CharField('系统版本', max_length=16, null=True, blank=True)
    
        cpu_count = models.IntegerField('CPU个数', null=True, blank=True)
        cpu_physical_count = models.IntegerField('CPU物理个数', null=True, blank=True)
        cpu_model = models.CharField('CPU型号', max_length=128, null=True, blank=True)
    
        create_at = 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=8)
        model = models.CharField('磁盘型号', max_length=32)
        capacity = models.CharField('磁盘容量GB', max_length=32)
        pd_type = models.CharField('磁盘类型', max_length=32)
    
        server_obj = models.ForeignKey('Server',related_name='disk', on_delete=models.CASCADE)
    
        class Meta:
            verbose_name_plural = "硬盘表"
    
        def __str__(self):
            return self.slot
    
    
    class NIC(models.Model):
        """
        网卡信息
        """
        name = models.CharField('网卡名称', max_length=128)
        hwaddr = models.CharField('网卡mac地址', max_length=64)
        netmask = models.CharField(max_length=64)
        ipaddrs = models.CharField('ip地址', max_length=256)
        up = models.BooleanField(default=False)
    
        server_obj = models.ForeignKey('Server',related_name='nic', on_delete=models.CASCADE)
    
    
        class Meta:
            verbose_name_plural = "网卡表"
    
        def __str__(self):
            return self.name
    
    
    class Memory(models.Model):
        """
        内存信息
        """
        slot = models.CharField('插槽位', max_length=32)
        manufacturer = models.CharField('制造商', max_length=32, null=True, blank=True)
        model = models.CharField('型号', max_length=64)
        capacity = models.FloatField('容量', null=True, blank=True)
        sn = models.CharField('内存SN号', max_length=64, null=True, blank=True)
        speed = models.CharField('速度', max_length=16, null=True, blank=True)
    
        server_obj = models.ForeignKey('Server',related_name='memory', on_delete=models.CASCADE)
    
    
        class Meta:
            verbose_name_plural = "内存表"
    
        def __str__(self):
            return self.slot
    
    
    class ErrorLog(models.Model):
        """
        错误日志,如:agent采集数据错误 或 运行错误
        """
        asset_obj = models.ForeignKey('Server', null=True, blank=True, on_delete=models.CASCADE)
        title = models.CharField(max_length=16)
        content = models.TextField()
    
        create_at = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            verbose_name_plural = "错误日志表"
    
        def __str__(self):
            return self.title
    

    注册app

    admin中注册后台表

    from django.contrib import admin
    
    # Register your models here.
    from repository import models
    
    admin.site.register(models.Server)
    admin.site.register(models.Disk)
    admin.site.register(models.UserProfile)
    admin.site.register(models.UserGroup)
    admin.site.register(models.Memory)
    admin.site.register(models.ErrorLog)
    admin.site.register(models.BusinessUnit)
    admin.site.register(models.NIC)
    admin.site.register(models.IDC)
    
  • 相关阅读:
    类和对象基础
    《Python》常用内置模块
    《Python》内置方法进阶和常用模块
    《Python》反射、内置方法(__str__,__repr__)
    《Python》 property、classmethod、staticmethod、isinstance、issubclass
    《Python》 面向对象三大特性之多态、封装
    面向对象三大特性之继承
    面向对象初识(组合)
    面向对象之入门-《初识》
    前端基础之jQuery操作标签
  • 原文地址:https://www.cnblogs.com/fwzzz/p/12734017.html
Copyright © 2011-2022 走看看