CMDB2 采集客户端目录架构设计, 高级配置文件, 可插拔式的采集

采集客户端目录结构设计 参考ATM架构

  • bin : 可执行文件 start.py / run.py

  • conf: 配置文件目录 config.py

  • lib : 第三方文件目录

  • src /core : 核心的源代码文件目录 //- log: 记录日志 放在 /var/logs/ 下面

  • test: 测试文件目录

高级配置文件的设置

1、集成用户自定义配置与默认配置

学习Django的配置文件我们配置文件,将类实例化,方法写在__init__中,在其他文件中直接 .settings就能获取到用户自定义的配置和默认的全局配置了,有自定义先用自定义,没有用默认的,先配置全局的配置,再配置自定义的配置

lib/config/conf.py

from conf import config     ## 导入自定义配置
from . import global_settings   ## 导入高级配置

class mySettings():
    # 继承用户自定义的配置和默认配置
    def __init__(self):
        # 全局配置
        for k in dir(global_settings):  # 变为字符串
            if k.isupper():
                v = getattr(global_settings, k)
                setattr(self, k, v)


        # 集成用户自定义的配置
        for k in dir(config):   # 变为字符串
            if k.isupper():
                v = getattr(config, k)
                setattr(self,k,v)

settings = mySettings()

使用高内聚低耦合思想

目标:实现agent与ssh两套方案切换采集

两套方案实现采集主机名的思路:

第一个版本代码:

    if settings.MODE == 'agent':
        import subprocess
        res = subprocess.getoutput('hostname')
    else:
        import paramiko

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

        # 执行命令
        stdin, stdout, stderr = ssh.exec_command('hostname')
        # 获取命令结果
        result = stdout.read()
        print(result)
        # 关闭连接
        ssh.close()

如果上述代码这样写的话,会带来如下问题:

  • 耦合度太高

  • 结构混乱,导致查找问题的时候不方便

  • 业务逻辑代码不能写在启动文件中

问:如何解决上述存在的问题?

答:将每一个功能都封装成一个文件,比如说采集磁盘的信息,可以搞一个disk.py文件,这个文件中所有的代码都是要和采集磁盘相关的,不能有其他 的相关代码。以此类推,采集CPU的信息,也要搞一个cpu.py文件. 这种思想就是高内聚低耦合思想

第二个版本,执行采集:

from src.plugins.basic import Basic
from src.plugins.disk import Disk
#from src.plugins.memory import Memory

if __name__ == '__main__':
  # 需要获取哪一个将哪一个导入,如果不需要则将哪个注释
    Basic().process()
    Disk().process()
    #Memory().process()

但是上述做法不是特别的完美,解决的方案是:将这些采集的插件写到配置文件中统一管理,参考django的中间件, 可插拔式的采集

核心的采集方法:可插拔式样的采集

 conf/config.py

## 用户自定义的配置文件

USER = 'root'
import os
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

MODE = 'agent'  ### ssh
# MODE = 'ssh'

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',
}

src/plugins/__init__.py

from lib.config.conf import settings
import importlib

class PluginsManager():
    def __init__(self):
        self.plugins_dict = settings.PLUGINS_DICT
        self.debug = settings.DEBUG

    # 管理配置文件插件,采集数据  从配置文件中读取配置,循环导入模块,实例化类,执行插件类对应的采集方法
    def execute(self):
        ### 1.从配置文件中读取配置
        reponse = {}
        for k,v in self.plugins_dict.items():
            '''
            k:basic
            v:src.plugins.basic.Basic
            '''
            # 2.循环导入模块
            '''
            moudle_path:'src.plugins.basic'
            class_name:'Basic'  
            '''
            moudle_path,class_name = v.rsplit('.',1)    # 从右面分割1次
            m = importlib.import_module(moudle_path)    # 导入字符串路径
            # print(m)
            # 导入类,实例化类,执行方法
            cls = getattr(m,class_name)
            # 执行类下的获取信息函数,每一个方法都相同,鸭子类型,方便获取
            ret = cls().process(self.command_func,self.debug)
            reponse[k] = ret
        return reponse

    def command_func(self,cmd):
        if settings.MODE == 'agent':
            import subprocess
            res = subprocess.getoutput(cmd)
            return res
        else:
            import paramiko

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

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

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

插件代码,判断方案冗余,解决方案:

1.继承 每一个子类都要继承父类的方法,通过传参的方式执行不一样的命令

2.将函数名当成一个参数传给另一个函数

其他部分代码:

src/plugins/basic.py

# 服务器基础信息,包括服务器主机名,操作系统版本等

class Basic():

    def process(self,command_func,debug):
        if debug:
            output = {
                'os_platform': "linux",
                'os_version': "CentOS release 6.6 (Final)
Kernel 
 on an m",
                'hostname': 'c1.com'
            }
        else:
            output = {
                'os_platform':command_func('uname').strip(),    # 操作系统名
                'os_version':str(command_func('cat /etc/issue'),encoding=('utf8')).strip().split('
')[0],
                'hostname':command_func('hostname').strip(),
            }
        return output

lib/config/global_settings.py

### 全局的配置

EMAIL_PORT = 25

bin/start.py

from src.plugins import PluginsManager

if __name__ == '__main__':
    res = PluginsManager().execute()

    for k, v in res.items():
        print(k,v)
原文地址:https://www.cnblogs.com/ludingchao/p/12578303.html