cmdb 可插拔式

Posted chanyuli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cmdb 可插拔式相关的知识,希望对你有一定的参考价值。

实现三套方案,采集IP信息

首先应该能想到的代码:

        if self.mode == \'agent\':
            import subprocess
            res = subprocess.getoutput(cmd)
            return res

        elif self.mode == \'ssh\':
            import paramiko
            # 创建SSH对象
            ssh = paramiko.SSHClient()
            # 允许连接不在know_hosts文件中的主机
            ssh.set_missing_host_key_policy(paramiko.
                                            AutoAddPolicy())
            # 连接服务器
            ssh.connect(hostname=self.hostname, port=22,                                     username=\'root\', password=\'root\')

            # 执行命令
            stdin, stdout, stderr = ssh.exec_command(cmd)
            # 获取命令结果
            result = stdout.read()

            # 关闭连接
            ssh.close()
            return result

        elif self.mode == \'salt\':
            import salt.client

            local = salt.client.LocalClient()
            result = local.cmd(self.hostname, \'cmd.run\', [cmd])
            return result
        else:
            raise  Exception(\'当前模式只支持 agent/ssh/salt !!!\')

存在的问题很明显:

1、复用性差,需要将其封装成方法,然后在进行调用

2、高内聚低耦合原则(这一块代码是负责干啥的,其所有的代码都应该和这个功能是相关的)

举个栗子:

在函数中是:
	def getUserInfo():
        #此函数体内,所有的代码,都应该和获取用户信息的这个目标功能相关联 ## 非			得要写处理订单相关的功能
        
在类中,也是一样的概念:
	class UserInfo():
    	def getUserInfo(): 
            pass 
        def login(): 
            pass 
        def logOut(): 
            pass

采用高内聚低耦合的原则,迭代上述代码:

思路:

1.将硬盘或者cpu等代码封装成一个文件,此文件中涉及到的代码和这个文件的具体功能是相关的

采取仿Django的可插拔式采集:

a. conf.py 中的配置插件:

import os

BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

USER = \'root\'


MODE = \'agent\' ## ssh /salt

DEBUG = True  ### True 代表调试模式  False 代表上线正式采集

PLUGINS_DICT = {
    \'basic\' : \'src.plugins.basic.Basic\',
    \'board\' : \'src.plugins.board.Board\',
    \'cpu\'   : \'src.plugins.cpu.Cpu\',
    \'disk\'  : \'src.plugins.disk.Disk\',
    \'memory\': \'src.plugins.memory.Memory\',
    \'nic\'   : \'src.plugins.nic.Nic\',
}

b. src下的plugins目录 创建__init__.py , 此文件中有一个类:

#### 用来管理采集的插件的
import traceback #这个模块是用来查看详情错误信息的,不然用了异常捕获,获取的信息只有一点点
from lib.config.settings import settings #配置文件(高级配置和自定义都有)

import importlib #关键模块,用来获取每个文件里面的类,然后调用Process方法。

class Plugins_Dict():

    def __init__(self, hostname=None):
        self.mode = settings.MODE
        self.plugins_dict = settings.PLUGINS_DICT
        self.debug = settings.DEBUG
        self.hostname = hostname


    #### 从配置文件中读取插件配置,并执行相关的函数获取数据
    def execute(self):
        ### 1.从配置文件中读取插件配置
        res = {}
        for k, v in self.plugins_dict.items():
            ### \'basic\' : \'src.plugins.basic.Basic\',
            ### k: basic
            ### v: \'src.plugins.basic.Basic\'
            response = {\'status\':None, \'data\':None}
            #### 2. 导入模块, 获取类, 并实例化
            try:
                # 这里解压缩,一个是src.plugins.basic,一个是Basic
                module_path, class_name = v.rsplit(\'.\', 1)
                ### 3. 加载模块,给一个路径,就能把名称空间加载过来
                m = importlib.import_module(module_path)
                #### 4. 加载类, 通过反射,就可以在这个上面加载过来的名称空间					中拿到类。也就是栗子里的Basic类
                cls = getattr(m, class_name)
                #### 5. 执行每一个插件类下面的process方法,并且传入两个参				数,第一个是函数对象,第二个是debug。因为这里的每个process代表的只是设备,你需要获取信息的设备,如网卡,磁盘等等,以什么样的方式采集信息才是重点,也就是这个函数对象所做的。debug就是判断是否是调试。
                ret = cls().process(self.command_func, self.debug )

                response[\'status\'] = 10000
                response[\'data\'] = ret

            except Exception as e:
                response[\'status\'] = 10001
                #本来被捕获的异常打印出来只有一点点错误信息,无法精准判断错误位置和错误类型,用这个模块就可以知道了。
                response[\'data\'] = traceback.format_exc()

            res[k] = response

        return res

	#这个函数就是用来传进process方法里的。之前也写过这个方法了。放在了这个__init__文件里面。
    def command_func(self, cmd):

        if self.mode == \'agent\':
            import subprocess
            res = subprocess.getoutput(cmd)
            return res

        elif self.mode == \'ssh\':
            import paramiko
            # 创建SSH对象
            ssh = paramiko.SSHClient()
            # 允许连接不在know_hosts文件中的主机
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            # 连接服务器
            ssh.connect(hostname=self.hostname, port=22, username=\'root\', password=\'root\')

            # 执行命令
            stdin, stdout, stderr = ssh.exec_command(cmd)
            # 获取命令结果
            result = stdout.read()

            # 关闭连接
            ssh.close()
            return result

        elif self.mode == \'salt\':
            import salt.client

            local = salt.client.LocalClient()
            result = local.cmd(self.hostname, \'cmd.run\', [cmd])
            return result
        else:
            raise  Exception(\'当前模式只支持 agent/ssh/salt !!!\')


img

这些都是插件,就是一个个设备,随便拿一个看一下,就拿cpu来看

import os
from lib.config.settings import settings
class Cpu(object):
    def __init__(self):
        pass

    @classmethod
    def initial(cls):
        return cls()

    def process(self, command_func, debug):
        #如果是debug模式,就返回一些已经有了的准备好了的信息。
        if debug:
            output = open(os.path.join(settings.BASEDIR, \'files
                         /cpuinfo.out\'), \'r\',encoding=\'utf8\').read()
        else:
            output = command_func("cat /proc/cpuinfo")
        return self.parse(output)
	
    #这个函数就是用来解析拿到的结果的。
    def parse(self, content):
        """
        解析shell命令返回结果
        :param content: shell 命令结果
        :return:解析后的结果
        """
        response = {\'cpu_count\': 0, \'cpu_physical_count\': 0, \'cpu_model\': \'\'}

        cpu_physical_set = set()

        content = content.strip()
        for item in content.split(\'\\n\\n\'):
            for row_line in item.split(\'\\n\'):
                key, value = row_line.split(\':\')
                key = key.strip()
                if key == \'processor\':
                    response[\'cpu_count\'] += 1
                elif key == \'physical id\':
                    cpu_physical_set.add(value)
                elif key == \'model name\':
                    if not response[\'cpu_model\']:
                        response[\'cpu_model\'] = value
        response[\'cpu_physical_count\'] = len(cpu_physical_set)

        return response

这是一个Cpu类, 在__init__里面,通过反射拿到了这个类,并实例化,再执行了process方法。

以上是关于cmdb 可插拔式的主要内容,如果未能解决你的问题,请参考以下文章

带你手写基于 Spring 的可插拔式 RPC 框架注册中心

21 参考中间件,实现可插拔式设计

20200310 CMDB基础设计

带你手写基于 Spring 的可插拔式 RPC 框架介绍

# "可插拔式"组件设计,领略组件开发的奥秘

带你手写基于 Spring 的可插拔式 RPC 框架通信协议模块