CMDB_Agent版本
- 2020 年 3 月 26 日
- 筆記
目录
CMDB_Agent版本
CMDB概念
CMDB: Configure Manage DataBase 中文:配置管理数据库。 主要的作用是:收集服务器的基础信息(包括:服务器的主机名,ip,操作系统版本,磁盘,CPU等信息),将来提供给子系统(代码发布,工单系统等)数据
CMDB_Agent介绍
其本质上就是在各个服务器上执行subprocess.getoutput()命令,然后将每台机器上执行的结果,返回给主机API,然后主机API收到这些数据之后,放入到数据库中,最终通过web界面展现给用户 优点:速度快 缺点:需要为每台服务器步数一个Agent的程序
agent方案
将待采集的服务器看成一个agent,然后再服务器上使用python的subprocess模块执行linux相关的命令,然后分析得到的结果,将分析得到的结果通过requests模块发送给API,API获取到数据之后,进行二次比对数据,最后将比对的结果存入到数据库中,最后django起一个webserver从数据库中将数据获取出来,供用户查看
ssh类方案
在中控机服务器上安装一个模块叫paramiko模块,通过这个模块登录到带采集的服务器上,然后执行相关的linux命令,最后返回执行的结果,将分析得到的结果通过requests模块发送给API,API获取到数据之后,进行二次比对数据,最后将比对的结果存入到数据库中,最后django起一个webserver从数据库中将数据获取出来,供用户查看
相比较
agent方案 优点:不需要额外的增加中控机。 缺点:每新增一台服务器,就需要额外部署agent脚本。使用场景是:服务器多的情况 (1000台以上) ssh方案 优点:不需要额外的部署脚本。 缺点:速度比较慢。使用场景是:服务器少 (1000台往下)
架构目录
bin-start.py 启动文件
from src.plugins import PluginsManager if __name__ == '__main__': res = PluginsManager().execute() for k, v in res.items(): print(k, v)
conf-config.py 自定义配置文件
模仿Django的setting,常用的配置写在这里面。不常用的写在global_settings.py中。
加载顺寻:先加载全局的。再加载局部的
USER = 'root' MODE = 'agent' DEBUG = True # True:代表是开发测试阶段 False:代表是上现阶段 import os BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 模仿Django中间件,插拔式 PLUGINS_DICT = { 'basic': 'src.plugins.basic.Basic', 'cpu': 'src.plugins.cpu.Cpu', #'disk': 'src.plugins.disk.Disk', 'memory': 'src.plugins.memory.Memory', }
files 开发测试的文件
DEBUT=True时为测试阶段,用files的测试数据
lib-config-global_settings.py 全局配置的文件
pass
lib-config-conf.py 读取配置的文件
全局配置放在前面先加载,自定义配置的放在后面后加载。自定义配置了就用自定义的(覆盖),没有配置久用全局的
from conf import config from . import global_settings class mySettings(): def __init__(self): # print('aa:', dir(global_settings)) # print('bb:', dir(config)) # 全局配置 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()
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 # 1.采集数据 def execute(self): response = {} for k, v in self.plugins_dict.items(): ''' k: basic v: src.plugins.basic.Basic ''' # 2.循环导入(字符串路径) moudle_path, class_name = v.rsplit('.', 1) # ['src.plugins.basic','Basic'] # 用importlib.import_module()导入字符串路径 m = importlib.import_module(moudle_path) # 3.导入类 cls = getattr(m, class_name) # 循环执行鸭子类型的process方法,command_func函数的内存地址传过去,把debug传过去 ret = cls().process(self.command_func, self.debug) response[k] = ret return response # 真正的连接,执行命令,返回结果的函数。命令变成参数 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='123456') # 执行命令 stdin, stdout, stderr = ssh.exec_command(cmd) # 获取命令结果 result = stdout.read() # 关闭连接 ssh.close() return result
src-plugins-basic.py 查看硬件信息
from conf import config class Basic(object): def process(self, command_func, debug): if debug: output = { 'os_platform': "linux", 'os_version': "CentOS release 6.6 (Final)nKernel r on an m", 'hostname': 'c1.com' } else: output = { 'os_platform': command_func("uname").strip(), 'os_version': command_func("cat /etc/issue").strip().split('n')[0], 'hostname': command_func("hostname").strip(), } return output
src-plugins-cpu.py 查看cpu属性
import os from lib.config.conf import settings class Cpu(): def __init__(self): pass def process(self, command_func, debug): if debug: output = open(os.path.join(settings.BASEDIR, 'files/cpuinfo.out'), 'r', encoding='utf-8').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('nn'): 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
src-plugins-disk.py 查看磁盘信息
# 采集磁盘信息 from lib.config.conf import settings import os import re class Disk(object): def __init__(self): pass def process(self, command_func, debug): if debug: output = open(os.path.join(settings.BASEDIR, 'files/disk.out'), 'r', encoding='utf-8').read() else: output = command_func('MegaCli -PDList -aALL') # radi 卡 磁盘阵列 return self.parse(output) # 调用过滤的函数 # 过滤函数,对字符串的处理过滤 def parse(self, content): """ 解析shell命令返回结果 :param content: shell 命令结果 :return:解析后的结果 """ response = {} result = [] for row_line in content.split("nnnn"): result.append(row_line) for item in result: temp_dict = {} for row in item.split('n'): if not row.strip(): continue if len(row.split(':')) != 2: continue key, value = row.split(':') name = self.mega_patter_match(key) if name: if key == 'Raw Size': raw_size = re.search('(d+.d+)', value.strip()) if raw_size: temp_dict[name] = raw_size.group() else: raw_size = '0' else: temp_dict[name] = value.strip() if temp_dict: response[temp_dict['slot']] = temp_dict return response @staticmethod def mega_patter_match(needle): grep_pattern = {'Slot': 'slot', 'Raw Size': 'capacity', 'Inquiry': 'model', 'PD Type': 'pd_type'} for key, value in grep_pattern.items(): if needle.startswith(key): return value return False