1.什么是cmdb

  配置管理数据库 ,存储基础设备的各种信息配置等

  CMDB可以存储并自动发现整个IT网络上的各种信息,比如一个IT网络上有多少台服务器、多少存储、设备的品牌、资产编号、维护人员、所属部门、服务器上运营什么操作系统、操作系统的版本、操作系统上有哪些应用、每个应用的版本等等,不仅如此,CMDB还有一个非常重要的功能——存储不同资源之间的依赖关系,如果网络上某个节点出现问题(比如某个服务器down了),通过CMDB,可以判断因此受到影响的业务

  CMDB由三个部分实现 : api系统(django)   +  资产采集系统   +  后台管理系统

2.知识点分解

  1)django实现api ,python普通项目实现client ,完成二者间的通信

    api使用Django提供的rest_framwork模块

      APIView避免csrf对post的限制

      request.data中存放了客户端post请求的数据

      Response方法将数据进行json转换

    client使用requests和json模块

      requests.get对api发起get请求获取到的数据可以使用content拿出Byte类型

      requests.post对api发起post提交数据必须提交json编码后的类型

###api
##路由
urlpatterns = [
url(r'^asset/', views.Asset.as_view()),
]
##视图函数
from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response class Asset(APIView):
def get(self, request):
server_info = ['master1', 'master2']
return Response(server_info) def post(self, request):
print(request.data)
return HttpResponse('200ok')
###client端
import requests
import json client_info = {'hostname': 'c1.com'} r1 = requests.get(
url='http://127.0.0.1:8000/api/asset/'
) r1_data = json.loads(r1.content.decode('utf-8')) print(r1_data)
  
r2 = requests.post(                      ###post提交数据一定要加请求头部标记json格式
url='http://127.0.0.1:8000/api/asset/',
data=json.dumps(client_info).encode('utf-8'),
headers={
'content-type': 'application/json'
}
)

  2)通过字符串加载文件中的类 ,实现开放封闭必备小点

    我们如何在配置中定义使用哪个文件中的类呢 ,使用字典 ,以点分割 目录.文件.类名

    如 : agent模式对应了一个字符串它对应了 /src/engine/agent.py文件中的AgentHandler类

###settings.py
ENGINE = 'agent' ENGINE_DICT = {
'agent': 'src.engine.agent.AgentHandler',
'ssh': 'src.engine.ssh.SSHHandler',
} ####设计一个函数完成从文件中获取类
import importlib
def import_string(class_string):
"""
根据配置中字符串获取文件中的类
:param 'src.engine.agent.AgentHandler',
module, engine = src.engine.agent 和 AgentHandler
importlib.import_module()就是import src.engine.agent
engine_class = 反射获取到AgentHandler类
"""
module, engine = class_string.rsplit('.', maxsplit=1)
module_file = importlib.import_module(module)
engine_class = getattr(module_file, engine)
return engine_class

3.CMDB资产采集系统简单实现要点

  采集模式engine
    agent模式 ,每台主机安装client ,执行命令将采集的数据上报api
    中控机模式(ssh ansible) ,使用一台机器远程所有的主机 ,执行命令完成资产采集 ,再将数据上报api
    调用issa层的api接口直接获取基础设备信息

  程序设计思想

    开放封闭原则 ,代码封闭 ,配置开放(定义当前的使用的engine与plugin),支持扩展

  程序设计关注点

    资产采集

      engine采集模式可扩展 (抽象接口)

      plugin命令插件可扩展 (抽象接口)

    唯一标识
    错误处理
    日志

4.CMDB资产采集系统简单实现代码

  1)API端后续完善 ,先使用标题2-1的api

  2)新建项目 ,完善常规目录

  3)程序入口/bin/client

    执行run()

import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from src.script import run if __name__ == '__main__':
run()

  4)资产采集上报入口/src/script

    定义run()

    根据配置settings实例化engine的对象 ,并执行对象的handler方法

from conf import settings
from lib.import_class import import_string #标题2-2的根据配置字符串取类 def run():
"""资产采集入口"""
engine_path = settings.ENGINE_DICT.get(settings.ENGINE)
engine_class = import_string(engine_path)
obj = engine_class()
obj.handler()

  5)采集模式engine实现handler()方法

    定义基类约束BaseHandler() 要求每个engine必须有handler()完成采集与上报, cmd()完成调用命令窗口

    定义基类简化ssh这一类的插件SSHandSaltHandler(BaseHandler)  ,这一类的handler()方法都是从api获取主机列表 ,远程采集设备信息 ,再提交api ,所以会出现同时对很多的主机进行连接 ,这里使用线程池

import requests
from concurrent.futures import ThreadPoolExecutor
from ..plugins import get_server_info
import json class BaseHandler():
"""
定义engine的基类 ,每个engine都要有这两个方法
""" def handler(self):
raise NotImplementedError('handler() must be Implemented') def cmd(self, command, hostname=None):
raise NotImplementedError('cmd() must be Implemented') class SShandSaltHandler(BaseHandler):
"""
简化ssh这一类engine的代码
""" def handler(self):
# 1.获取主机列表
r1 = requests.get(
url='http://127.0.0.1:8000/api/asset/',
)
host_list = r1.json() # 2.创建线程池
pool = ThreadPoolExecutor(20) # 3.提交任务给线程池
for hostname in host_list:
pool.submit(self.task, hostname) def task(self, hostname):
"""线程池任务"""
info = get_server_info(self, hostname)
r1 = requests.post(
url='http://127.0.0.1:8000/api/asset/',
data=json.dumps(info).encode('gbk'),
headers={
'content-type': 'application/json'
}
)

    engine--agent模式实现handler()

      cmd()方法使用subprocess模块完成本地命令调用

from src.engine.base import BaseHandler
from ..plugins import get_server_info
import requests
import json class AgentHandler(BaseHandler):
"""定义cmd窗口 ,操控资产采集 + 上报""" def cmd(self, command, hostname=None):
import subprocess
return subprocess.getoutput(command) def handler(self):
info = get_server_info(self)
r1 = requests.post(
url='http://127.0.0.1:8000/api/asset/',
data=json.dumps(info).encode('gbk'),
headers={
'content-type': 'application/json'
}
)

    engine--ssh模式实现handler()

      cmd()方法使用paramiko模块完成远程命令调用 (秘钥需要配置在settings中)

from src.engine.base import SShandSaltHandler
from conf import settings class SSHandler(SShandSaltHandler):
"""仅定义cmd即可 ,采集与上报在父类中""" def cmd(self, command, hostname=None):
import paramiko
private_key = paramiko.RSAKey.from_private_key_file(settings.SSH_PRIVATE_KEY)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=hostname, port=settings.SSH_PORT, username=settings.SSH_USER, pkey=private_key)
stdin, stdout, stderr = ssh.exec_command(command)
result = stdout.read()
ssh.close()
return result

  6)handler方法中的数据采集使用了get_server_info()方法

    script.py与__init__有异曲同工之处 ! 依据配置应用不同插件

from conf import settings
from lib.import_class import import_string def get_server_info(handler, hostname=None):
"""采集信息入口"""
info = {}
for name, path in settings.PLUGINS_DICT.items():
"""
{'disk':'src.plugins.disk.Disk'}
"""
plugin_class = import_string(path)
obj = plugin_class()
info = obj.process(handler, hostname)
return info

  7)命令插件plugins实现process()方法

    定义基类约束BasePlugin

      增加debug属性判断是否为调试模式 ,增加项目根路径

      每个命令插件都要分为window与linux的具体系统判断执行什么命令

from conf import settings

class BasePlugin:
def __init__(self):
self.debug = settings.DEBUG
self.base_dir = settings.BASE_DIR def get_os(self, handler, hostname=None): ## 调试
# os = handler.cmd('命令',hostname)
return 'linux' def win(self, handler, hostname=None):
raise NotImplementedError('win() must be Implemented') def linux(self, handler, hostname=None):
raise NotImplementedError('linux() must be Implemented') def process(self, handler, hostname=None):
os = self.get_os(handler, hostname)
if os == 'win':
ret = self.win(handler, hostname)
else:
ret = self.linux(handler, hostname) return ret

    查看硬盘的命令插件

      其中process--->调用win或者linux--->执行handler对象的cmd()方法 (这个对象最开始就被当做参数传过来了)

      其中还会有debug判断 ,如果是调试就直接从文件获取数据了

      其中parse方法是将采集的数据格式化 ,需要根据api所需要的格式进行格式化

from .base import BasePlugin
import os
import re class Disk(BasePlugin):
def win(self, handler, hostname=None):
ret = handler.cmd('dir', hostname)[:60] return 'Disk' def linux(self, handler, hostname=None):
if self.debug:
with open(os.path.join(self.base_dir, 'files', 'disk.out')) as f:
ret = f.read()
else:
ret = handler.cmd('ifconfig', hostname)[:60]
return self.parse(ret) def parse(self, content):
"""
解析shell命令返回结果
:param content: shell 命令结果
:return:解析后的结果
"""
response = {}
result = []
for row_line in content.split("\n\n\n\n"):
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 = ''
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

    查看内存的命令插件

from .base import BasePlugin
import os
from lib import convert class Memory(BasePlugin):
def win(self, handler, hostname=None):
"""
windowns下执行命令
:param handler:
:param hostname:
:return:
"""
ret = handler.cmd('dir', hostname)[:60] return 'Disk' def linux(self, handler, hostname=None):
if self.debug:
with open(os.path.join(self.base_dir, 'files', 'memory.out')) as f:
ret = f.read()
else:
ret = handler.cmd('lsblk', hostname)[:60] return ret def parse(self, content):
"""
解析shell命令返回结果
:param content: shell 命令结果
:return:解析后的结果
"""
ram_dict = {}
key_map = {
'Size': 'capacity',
'Locator': 'slot',
'Type': 'model',
'Speed': 'speed',
'Manufacturer': 'manufacturer',
'Serial Number': 'sn', }
devices = content.split('Memory Device')
for item in devices:
item = item.strip()
if not item:
continue
if item.startswith('#'):
continue
segment = {}
lines = item.split('\n\t')
for line in lines:
if len(line.split(':')) > 1:
key, value = line.split(':')
else:
key = line.split(':')[0]
value = ""
if key in key_map:
if key == 'Size':
segment[key_map['Size']] = convert.convert_mb_to_gb(value, 0)
else:
segment[key_map[key.strip()]] = value.strip()
ram_dict[segment['slot']] = segment return ram_dict

5.简单一些想法完成开放封闭

  首先属于同类功能 ,这类的功能是需要不停增加的 ,或者说可以选用的都通过配置实现 ,使用抽象类约束

    配置 :指定操作者  ,操作者使用的工具

    生成操作者(script)

    handler操作者: 可以操作任意工具

    生成操作者使用的工具(__init__)  

    plugin工具: 网卡查看工具  硬盘查看工具查看

cmdb项目-1的更多相关文章

  1. Python Django CMDB项目实战之-3创建form表单,并在前端页面上展示

    基于之前的项目代码 Python Django CMDB项目实战之-1如何开启一个Django-并设置base页.index页.文章页面 Python Django CMDB项目实战之-2创建APP. ...

  2. Python Django CMDB项目实战之-2创建APP、建模(models.py)、数据库同步、高级URL、前端页面展示数据库中数据

    基于之前的项目代码来编写 Python Django CMDB项目实战之-1如何开启一个Django-并设置base页index页文章页面 现在我们修改一个文章列表是从数据库中获取数据, 下面我们就需 ...

  3. Python Django CMDB项目实战之-1如何开启一个Django-并设置base页、index页、文章页面

    1.环境 win10 python 2.7.14 django 1.8.2 需要用到的依赖包:MySQLdb(数据库的接口包).PIL/pillow(处理图片的包) 安装命令: pip install ...

  4. CMDB项目要点之技术点(面试题)

    1.单例模式 日志对象用单例模式 django admin中注册类是,用到单例模式 为什么要用单例模式 同一个对象操作 维护全局变量 + 对全局变量做一些操作 # __new__ import thr ...

  5. Python之路【第二十二篇】CMDB项目

    浅谈ITIL TIL即IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库)由英国政府部门CCTA(Central ...

  6. CMDB项目开发

    CMDB介绍 CMDB --Configuration Management Database 配置管理数据库, CMDB存储与管理企业IT架构中设备的各种配置信息,它与所有服务支持和服务交付流程都紧 ...

  7. CMDB项目实战

    01-CMDB项目介绍 02-CMDB开发背景 03-CMDB开发目的 04-CMDB资产采集方式之agent 05-CMDB资产采集方式之ssh 06-CMDB资产采集方式之saltstack 07 ...

  8. cmdb项目-3

    1. cmdb资产审计 2.stark组件使用 快速完成网站的一个组件,使用方式与django的admin系统类似 ,仅仅将model注册 ,就可以生成对model增删改查的页面 ,当然这里还包括了模 ...

  9. CMDB项目要点总结之中控机

    1.基于paramiko对远程主机执行命令操作 秘钥形式 private_key = paramiko.RSAKey.from_private_key_file('c:/Users/用户名/.ssh/ ...

随机推荐

  1. Wonder第一期3D引擎和编辑器线下培训班报名开始啦(免费学习)

    Wonder第一次举办 针对3D底层技术的 线下培训班,免费学习,请大家多多支持-感谢- 培训地点 成都 开课时间 报名满5人开课. 报名方式 加QQ群:732861508 备注请写:报名培训 老师介 ...

  2. MIT-6.824 操作系统 汇总

    MIT-6.828-JOS-环境搭建 ELF文件格式 lab1:C, Assembly, Tools, and Bootstrapping lab2:Memory management lab3:Us ...

  3. 后台管理tab栏滑动解决方案

    后台管理系统中比较常见的布局是左边菜单栏,右边tab切换栏,但是一般的tab组件不包含tab页过多的切换问题的,所以需要个性化实现,本文的实现方案是滑动鼠标滚轮绑定tab达到切换的效果,先上一个动态图 ...

  4. FCC---CSS Flexbox: Apply the flex-direction Property to Create a Column in the Tweet Embed

    The tweet embed header and footer used the flex-direction property earlier with a row value. Similar ...

  5. Android DSelectorBryant 单选滚动选择器

    单选滚动选择器.diy丰富.有阻尼效果.简单美观.触摸or点击模式 (Rolling Selector, Diy Rich, Damping Effect, Simple and Beautiful, ...

  6. gohook 一个支持运行时替换 golang 函数的库实现

    运行时替换函数对 golang 这类静态语言来说并不是件容易的事情,语言层面的不支持导致只能从机器码层面做些奇怪 hack,往往艰难,但如能成功,那挣脱牢笼带来的成就感,想想就让人兴奋. gohook ...

  7. 源码分析RocketMQ ACL实现机制

    目录 1.BrokerController#initialAcl 2.PlainAccessValidator 2.1 类图 2.2 构造方法 2.3 parse方法 2.4 validate 方法 ...

  8. FCC---CSS Flexbox: Apply the flex-direction Property to Create Rows in the Tweet Embed

    The header and footer in the tweet embed example have child items that could be arranged as rows usi ...

  9. java使用htmlunit工具抓取js中加载的数据

    htmlunit 是一款开源的java 页面分析工具,读取页面后,可以有效的使用htmlunit分析页面上的内容.项目可以模拟浏览器运行,被誉为java浏览器的开源实现.这个没有界面的浏览器,运行速度 ...

  10. Android项目实战之高仿网易云音乐项目介绍

    这一节我们来讲解这个项目所用到的一些技术,以及一些实现的效果图,让大家对该项目有一个整体的认识,推荐大家收藏该文章,因为我们发布文章后会在该文章里面加入链接,这样大家找着就很方便. 目录 第1章 前期 ...