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