zoukankan      html  css  js  c++  java
  • (1)CMDB项目1

     1 CMDB概念

      CMDB :  Configuraion Management Database配置管理数据库,CMDB存储与管理企业IT架构中设备的各种配置信息,它与所有服务支持和服务交付流程都紧密相连,支持这些流程的运转、发挥配置信息的价值,同时依赖于相关流程保证数据的准确性。

      在实际项目中,CMDB常常被认为是构建其他ITIL流程的基础而有限考虑,ITIL项目的成败与是否成功建立CMDB有非常大的关系。70%~80%的IT相关问题与环境的变更有着更直接的关系。实施变更的难点和重点并不是工具,而是流程。即通过一个自动化的,可重复的流程管理变更,使得当变更发生的时候,有一个标准化的流程去执行,能够预测到这个变更对整个系统管理产生的影响,并对这些影响进行评估和控制。而变更管理流程自动化的实现关键就是CMDB。

      CMDB工具中至少包括这几种关键的功能:整合、调和、同步、映射和可视化。

      整合:是指能够充分利用来自其他数据源的信息,对CMDB中包含的记录源属性进行存取,将多个数据源合并至一个视图中,生成连同来自CMDB和其他数据源信息在内的报告。

      调和:调和能力是指通过对来字每个数据源的匹配字段进行对比,保证CMDB中的记录在多个数据源中没有重复现象,维持CMDB中每个配置项目数据源的完整性;自动调整流程使得初始实施、数据库管理员的手动运作和现场维护支持工作将至最低。

      同步:是指确保CMDB中的信息能够反映联合数据源的更新情况,在联合数据源更新频率的基础上确定CMDB更新日程,按照经过批准的变更来更新CMDB,找出未被批准的变更。

      应用映射与可视化:说明应用间的关系并反应应用和其他组件之间的依存关系,了解变更造成的影响并帮助诊断问题。

      CMDB是运维自动化项目,它可以减少人工干预,降低人员成本。

      功能:自动装机、实时监控、自动化部署软件,建立在他们的基础上是资产信息变更记录(资产管控自动进行汇报)

    2  资产采集

    2.1 资产采集分析  

       CMDB资产管理项目的代码可以分为:资产采集部分,api部分,后台管理部分。

      资产采集部分:

           ---  采集资产:在各个服务器(在CMDB中是客户端)执行采集数据信息命令,使用正则或字符串匹配方式获取想要数据。

          如果此服务器是中控机,需要有执行命令的各个主机名,和执行的命令。

          如果服务器为agent,则只需要执行命令。 

        ---  兼容性(各种资产采集的架构:agent,SSH或者salt都可以兼容)

        ---  汇报数据     

         需要复习知识点:

     1 1 高内聚,低耦合
     2 2 反射: getattr(obj,'xxx')
     3 3 导入模块:
     4 import  re
     5 import importlib
     6 importlib.import_module('re')
     7 django中是如何导入模块的:
     8 m = importlib.import_module('django.middleware.clickjacking')
     9 cls = getattr(m,'XFrameOptionsMiddleware')
    10 cls()
    11 4 面向对象:
    12 (1)
    13 class Foo:
    14     def  __init__(self,xx):
    15             pass
    16     @classmethod
    17     def   instance(cls):
    18             return cls()
    19     def process(self):
    20            pass
    21 if hasattr(Foo,'instance'):
    22      obj=Foo.instance()
    23 else:
    24      obj=Foo()
    25 obj.process()
    26 (2)
    27 class A:
    28     def f1(self):
    29         self.f2()
    30     def  f2(self):
    31         self('A.f2')
    32 class B:
    33     def f2(self):
    34         print('B.f2')
    35 obj = B()
    36 obj.f1()
    需要复习知识点

    2.2 资产采集的四种方案

    2.2.1 Agent方式

      使用场景:主机数量多

           使用架构: 数据库------api程序----------各个主机

           使用: 

           agent自动采集是在各个主机上运行程序,采集数据。需要使用subprocess和requests模块

    1 v1= subprocess.getoutput('ifconfig')
    2 #并发送
    3 url="http://127.0.0.1:8000/asset.html"
    4 response = request.post(url,data={'k1':value1,'k2':value2}) 
    5 print(response.text)

       当agent采集数据发送到api程序时,api主要的任务是:1 url写路由  2 接收自动采集的数据的格式    3 返回值

     2.2.2  SSH方式

    使用场景:主机数量少,ssh连接即使慢也没事
    使用架构:数据库 ----api程序----中控机----各个主机
    中控机需要远程获取各个主机的信息:python中ssh登录各个主机,执行命令,得到数据
    第三方的批量软件: fabric ansible 也是使用ssh原理

    需要使用Paramiko模块:

     1 import  paramiko 
     2 # 创建SSH对象
     3 ssh = paramiko.SSHClient()
     4 # 允许连接不在know_hosts文件中的主机
     5 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
     6 # 连接服务器
     7 ssh.connect(hostname='192.168.11.98',port=22,username='wupeiqi',password='123')
     8 #执行命令
     9 stdin,stdout,stderr=ssh.exec_command('ls')
    10 #获取命令结果
    11 result= stdout.read()
    12 #关闭连接
    13 ssh.close()

    2.2.3 saltstack方式 

    saltstack: master主服务器 salve客户端
    数据库 ----api程序----saltstack服务的master端----各个slave主机

    v = subprocess.getoutput(salt "*" cmd.run "ls")

    根据正则表达式提交到数据库

    saltstack应用

    1 安装和配置

    1 """ salt服务器安装和配置
    2 1. 安装salt-master
    3     yum install salt-master
    4 2. 修改配置文件:/etc/salt/master
    5     interface: 0.0.0.0    # 表示Master的IP 
    6 3. 启动
    7     service salt-master start
    8 
    9 """
     1 """
     2 salt客户端安装和配置
     3 1. 安装salt-minion
     4     yum install salt-minion
     5 
     6 2. 修改配置文件 /etc/salt/minion
     7     master: 10.211.55.4           # master的地址
     8  9     master:
    10         - 10.211.55.4
    11         - 10.211.55.5
    12     random_master: True
    13 
    14     id: c2.salt.com                    # 客户端在salt-master中显示的唯一ID
    15 3. 启动
    16     service salt-minion start
    17 """

    2 授权

    1 """
    2 salt-key -L                    # 查看已授权和未授权的slave
    3 salt-key -a  salve_id      # 接受指定id的salve
    4 salt-key -r  salve_id      # 拒绝指定id的salve
    5 salt-key -d  salve_id      # 删除指定id的salve
    6 """

    3  执行命令

    在master服务器上对salve进行远程操作

    (1)使用salt自己命令

    1 salt 'c2.salt.com' cmd.run  'ifconfig'

    (2)使用salt.client模块

    1 import salt.client
    2 local = salt.client.LocalClient()
    3 result = local.cmd('c2.salt.com', 'cmd.run', ['ifconfig'])

    2.2.4 puppet方式

    使用场景:公司现在在使用puppet
    使用架构:数据库 ----api程序----puppet服务的master端----各个slave主机

    2.3  创建资产采集代码的目录

    bin    # 可执行文件
    config  # 配置文件
          -settings.py
    lib     #  公共的模块
       - conf 
             -config.py 
             - global_setting.py
    src     # 业务代码目录
     #  log     # 日志  一般不放这里,写在系统的某个目录

     

    2.4 资产采集代码中的配置文件

    有两个配置文件:用户自定义配置文件 , 默认配置文件。   配置文件中的变量名一般都是大写。

    使用一个配置文件将两个配置文件结合起来

    此例django的默认配置文件: from django.conf import global_settings
    此例django中from django.conf import settings 会把默认配置和自定义配置合起来,使用settings.使用所有的配置

    lib/conf/config.py 配置文件代码:

     1 import os
     2 import importlib
     3 from . import global_settings
     4 class Settings(object):
     5     def __init__(self):
     6         # ######## 找到默认配置 ########  先找默认,后找自定义配置
     7         for name in dir(global_settings):
     8             if name.isupper():
     9                 value = getattr(global_settings,name)
    10                 setattr(self,name,value)
    11 
    12         # ######## 找到自定义配置 ########
    13         # os.environ是全局环境变量,是字典类型
    14         # 根据字符串导入模块   
    15         settings_module = os.environ.get('USER_SETTINGS')  
    16         if not settings_module:  #如果没有自定义配置文件,直接使用默认文件
    17             return
    18         m = importlib.import_module(settings_module)
    19         for name in dir(m):     # dir() 可以得到此变量的所有属性
    20             if name.isupper():     #  配置文件中的变量名一般大写
    21                 value = getattr(m,name)   # 拿到自定义配置
    22                 setattr(self,name,value)     # 将自定义配置的键和值设置在当前配置文件中
    23 settings = Settings()
    一个文件结合两个配置文件

    使用

    1 import os
    2 os.environ['USER_SETTINGS'] = "config.settings"  #os.environ 系统环境变量 ,只在当前运行程序生效
    3 import sys  # 将代码模块路径放到sys中
    4 BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    5 sys.path.append(BASEDIR)
    6 from lib.conf.config import settings  #只需要导入settings,默认和自定义配置文件都会导入
    7 print(settings.USER)
    8 print(settings.EMAIL)
    结合后配置文件使用

    一般软件的配置文件都是这样做,有两个配置文件,没有自定义就是用默认配置文件。 

    2.5 可插拨式资产采集插件

    公司采集资产有差别,因此插件最好是可插拨式

    在src目录下创建plugins文件夹下basic,board,cpu,disk,menery,nic的py文件,默认采集basic,board,cpu,disk,menery,nic信息

    2.5.1 定义插件类

    # src/plugins/nic.py中的Nic类
    class Nic(object):
        def process(self):
            return 'Nic'
    # src/plugins/board.py中的Board类
    class Board(object):
        def process(self):
            return 'Board'
    #.....
    定义插件类

    2.5.2  插件类写到配置文件中

    #condif/settings.py自定义配置文件写入
    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",
    }
    插件类信息写到配置文件中

    2.5.3 可插拨式采集资产插件的使用方法

     当导入src/plugins里面模块时时,首先会执行src/plugins/__init__.py,src/plugins/__init__.py导入配置文件,获取插件的目录和类名,并导入。根据类名,执行其方法采集资产。

      

    不同架构执行命令: 例如salt和agent方式不同,在代码上涉及判断
    (1)-- 插件上使用继承解决
     继承的类里面command函数需要判断配置文件中的采集数据方式,根据不同的方式执行命令

    # src/plugins/base.py 在command方法中写判断采集数据类型
    class BasePlugin(object):
        def  command(self,cmd):
        if "salt":
             pass
        elif "SSH":
            pass
        # ....
    # src/plugins/cpu.py   各个插件继承BasePlugin类,使用其command方法
    from .base import BasePlugin
    class Cpu(BasePlugin):
         def process(self):
        self.command('xxx')
        return "123321123"
    继承方式解决插件执行不同架构的命令

    (2)-- 插件上多传一个命令参数
    在src/plugins/__init__.py中定义command函数,并将command函数在执行插件时作为参数传过去
    插拔式插件需要有command函数的形参,不用继承,还可以使用__init__.py的所有属性
    command函数需要判断配置文件中的采集数据方式,根据不同的方式执行命令

    2.5.4 插件部分代码运行逻辑

    请求会先执行src/plugins/__init__.py中所有代码
    首先PluginManager的__init__函数,在配置文件中注册的所有插件拿出,
    PluginManager的exec_plugin函数执行各个插件下的process方法,参数为command函数,接收各个process函数返回值,并返回最终值
    各个插件的process方法会拿各个插件自己的命令执行command函数,接收command函数的返回值,并返回
    command根据采集数据的模式不同执行命令,并返回值

    发给api的信息,最好的类型时字典:
    {
    cpu: 'xxx',
    disk:'...'
    }

    2.5.5 资产查询插件中的钩子

    如果你想在构造方法(就是__init__)创建的时候想让插件模块自定义一些操作,可以使用@classmethod和initial函数来做。

    #  src/plugins/__init__.py中PluginManager类的exec_plugin函数
    def exec_plugins(self):
            # 获取所有的插件,并执行插件并返回值
            response = {}
            for k,v in self.plugin_dict.items():
                # 'basic': "src.plugins.basic.Basic"
                # result= "根据v获取类,并执行其方法采集资产"
                module_path,class_name=v.rsplit('.',1)
                m = importlib.import_module(module_path)
                cls = getattr(m,class_name)    # 找到类
                if hasattr(cls,'initial'):
                    obj = cls.initial()
                else:
                    obj = cls()
                result = obj.process(self.command)
                response[k]= result
            return response
    插件中使用钩子
    #  src/plugins/cpu.py中Cpu类的initial函数
    
    class Cpu(object):
        def __init__(self):
            pass
    
        @classmethod
        def initial(cls):
            return cls()
    
        def process(self,command_func):
            return 'Cpu'
    插件中的钩子

    2.5.6  插件对获取的数据进行处理

     src/plugins/cpu.py下的类Cpu中parse方法

     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('
    
    '):
                for row_line in item.split('
    '):
                    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
    对获取数据进行整理

    2.5.7 插件获取到错误堆栈信息时的处理

    trackback模块
    traceback.format_exc() 错误信息堆栈

      def exec_plugin(self):
            """
            获取所有的插件,并执行获取插件返回值
            :return:
            """
            response = {}
            for k,v in self.plugin_dict.items():
                #  'basic': "src.plugins.basic.Basic",
                ret = {'status':True,'data':None}
                try:
                    module_path, class_name = v.rsplit('.', 1)
                    m = importlib.import_module(module_path)
                    cls = getattr(m,class_name)
                    if hasattr(cls,'initial'):
                        obj = cls.initial()
                    else:
                        obj = cls()
                    result = obj.process(self.command,self.debug) # result = "根据v获取类,并执行其方法采集资产"
                    ret['data'] = result
                except Exception as e:
                    ret['status'] = False
                    ret['data'] = "[%s][%s] 采集数据出现错误 : %s" %(self.hostname if self.hostname else "AGENT",k,traceback.format_exc())
                    #  traceback.format_exc() 错误信息堆栈
                response[k] = ret
            return response
        '''
        {
           'disk':{'status':True,'data':'xxx'},
           'nic':{'status':False,'data':'xxxxxx'}
        }
    
        '''
    trackback处理错误信息

    2.5.8 向api发送数据

    支持3种模式:
      agent直接发送就行
      中控机向后台发送,ssh或者salt应该先从后台获取未采集数据的主机列表,再循环主机列表从而获取各个客户端的主机信息,最后将信息数据返回给后台服务器。

    #向api发送数据    和采集资产整合起来
    import requests
    from lib.conf.config import settings
    from src.plugins import PluginManager
    import json
    
    class Base(object):
        def post_asse(self,server_info):
            requests.post(settings.API,json=server_info)
            # server_info 是字典里面有字典,不能直接传过去,需要json转化
            # body:json.dumps(server_info)
            # headers = {'content-type':'application/json'}
            # json.loads(request.body)
    class Agent(Base):
        def execute(self):
            server_info = PluginManager().exec_plugin()
            self.post_asse(server_info)
    
    class SSHSALT(Base):
        def get_host(self):
            # 获取未采集的主机列表
            response=requests.get(settings.API)
            result = json.loads(requests.text) #  “{status:'True',data:['c1.com','c2.com']}”
    
            if result['status']:
                return
            return result['data']
    
        def execute(self):
            host_list = self.get_host()
            for host in host_list:
                server_info = PluginManager(host).exec_plugins()
                self.post_asse(server_info)
    向api发送数据

    插件只是用来采集资产,并且自己判断是哪种模式
    client.py 是组织插件的功能和拿到数据发送给api的业务

    问题:

    发送到api时还可以加密
    还有并发的问题,使用线程池

    2.5.9  唯一标识

    创造唯一标识时,之前必须要做标准化 。 比如认为主板SN  物理机的话可以作为唯一标识,虚拟机的话就不是唯一标识了。

    标准化:主机名不重复;对于agent方式,主机名是唯一标识,可以将主机名写在客户端服务器的某个文件中,采集数据时可以进行主机名比较,如果不一致就拿文件中的主机名。

    标准化:

      - 主机名不重复

      - 流程:

      - 资产录入,机房,机柜,机柜位置

      - 装机时,需要将服务信息录入CMDBc1.com (手动录入或者cobber装机软件录入)

      - 资产采集:c1.com

    标准化创造唯一标识步骤:

    1)装系统,初始化软件(CMDB),运行CMDB

        - 通过命令获取主机名

        - 写入本地指定文件

    2 将资产信息发送到API

    3) 获取资产信息:

        - 本地文件主机名   != 命令获取的主机名(按照文件中的主机名)

        - 本地文件主机名   == 命令获取的主机名

    最终流程:

    标准化:主机名不重复;流程标准化(装机同时,主机名在cmdb中设置)

    服务器资产采集(Agent):

      a. 第一次:文件不存在,或内容为空;

           采集资产:

            - 主机名写入文件

            - 发送API

      b. N次:采集资产,主机名:文件中获取

    agent方式唯一标识代码:

    class Agent(Base):
        def execute(self):
            server_info = PluginManager().exec_plugin()
            hostname = server_info['basic']['data']['hostname']
            certname = open(settings.CERT_PATH,'r',encoding='utf-8').read()
            if not certname.strip():
                with open(settings.CERT_PATH,'w',encoding='utf-8') as f:
                    f.write(hostname)
            else:
                server_info['basic']['data']['hostname'] = certname
    
            self.post_asse(server_info)
    agent方式中唯一标识代码

     SSHSalt方式:中控机从后台获取未采集主机名列表:【c1.com 】,再直接去找客户端,本身拿到的主机名就是唯一标识

    2.5.10 基于线程池实现并发采集资产

    Sshsalt的采集资产方式才可以有线程池。  提高并发:线程、进程

    Python2

      线程池:无

      进程池:有

    Python3

      线程池:有

      进程池:有

    线程池简单例子:

    import time
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    
    def task(i):
        time.sleep(1)
        print(i)
    p = ThreadPoolExecutor(10)
    for row in range(100):
        p.submit(task,row)
    线程池

    进程池简单例子:

    import time
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    
    def task(i):
        time.sleep(1)
        print(i)
    
    p = ProcessPoolExecutor(10)
    for row in range(100):
        p.submit(task,row)
    线程池

    线程池在CMDB中采集数据时的应用:

    # src/client.py
    class SSHSALT(Base):
        def get_host(self):
            # 获取未采集的主机列表
            response=requests.get(settings.API)
            result = json.loads(requests.text) #  “{status:'True',data:['c1.com','c2.com']}”
    
            if result['status']:
                return
            return result['data']
    
        def run(self,host):
            server_info = PluginManager(host).exec_plugins()
            self.post_asse(server_info)
    
        def execute(self):
            host_list = self.get_host()
            from concurrent.futures import ThreadPoolExecutor
            pool = ThreadPoolExecutor(10)
            for host in host_list:
                pool.submit(self.run,host)
    基于线程池采集数据

    2.5.11 代码

     略

  • 相关阅读:
    Android 主题theme说明 摘记
    Android开发 去掉标题栏方法 摘记
    安卓项目五子棋代码详解(二)
    关于 ake sure class name exists, is public, and has an empty constructor that is public
    百度地图3.0实现图文并茂的覆盖物
    android onSaveInstanceState()及其配对方法。
    关于集成科大讯飞语音识别的 一个问题总结
    android 关于 webview 控制其它view的显示 以及更改view数据失败的问题总结
    C# 解析 json Newtonsoft果然强大,代码写的真好
    c#数据类型 与sql的对应关系 以及 取值范围
  • 原文地址:https://www.cnblogs.com/wenyanjie/p/7262856.html
Copyright © 2011-2022 走看看