CMDB开发

本节内容:

1:CMDB是一个什么项目

2:运维自动化的分类

3:CMDB资产采集的4种方式

4:程序可插拔机制

5:对发送数据的中控机或者agent进行一个api的验证

6:数据库的实现

 

一:CMDB是一个什么项目

CMDB:[运维自动化的一个项目]
    -运维自动化基础
    -主要就是资产管理

 运维自动化项目目的:减少人工干预,降低人员成本。

二:运维自动化的分类

运维自动化工具大概四类;
    安装批量系统: linux kgstart、i z卡
    装环境: 初始化环境,也有一个工具 
    批量打包项目部署:ssh/crt/种子(如迅雷种子)
    监控服务的实时状态: 
    
以上的四类都要依赖CMDB:
    你安装系统,是不是要指定往哪一台机器安装系统?
    装环境 一样需要执行往哪一台?
    批量?哪几台
    监控?监控谁?

常见的运维项目就是如下了:

我们要实现的CMDB:主要就是针对资产管理的方面。

三:CMDB资产采集的4种方式

要做资产管理 那么就必须先获取资产的信息。 

自动获取。

方式1:Agent方式

agent方式:要求在每个的服务器上安装agent程序,
在agent中链接上API,然后再有API去向数据库添加数据。 

API:也是一个django的程序 
agent程序:可以通过subpeocess 模块执行命令,再采用requests模块get或post发送数据。 
API:获取数据,执行添加数据(比如网卡等等信息)

 方式2:SSH的方式

SHH方式:
服务端没有了agent程序,而是在服务器和API的中间加了一个中控服务端[用于采集服务端信息]

中控服务端通过paramiko模块主动去链接服务器,然后通过subprocess模块执行命令,返回命令结果;再发送到API:

去链接服务器可能我们会使用别的模块:如ansible 或 fabic 模块,但是他们都是对paramiko模块的封装。
注意:最新的ansible不再使用paramiko模块封装了,而是自己实现了新的方式。

 方式3:saltstack方式

saltstack方式: 
基于第三方软件(去python写的)去实现,在中控服务端安装一个saltstack-master;服务器安装saltstack-slave。

执行过程:
saltstack-master指定主机发送命令("c1.com","ifconfig"),将命令发送到队列(RPC)中去;
服务器取命令,执行完又将结果发送到一个新的队列中去;
saltstack-master获取数据,向API发送数据。

 

 方式4:puppet方式

puppet方式:
这是相对比较古老的一种方式,上面的几种都是由中控服务端去主动获取数据;
而puppet方式是由puppet-master和puppet-salve组成;puppet-salve每隔30分钟发送数据给puppet-master; 

puppet-master内维护了一个报表:获取这个报表发送数据给API

puppet:是用ruby写的。所以我们在通过报表采集资产的时候,需要使用ruby语言。

4:程序可插拔机制

关于程序的可插拔机制。

开发一套程序时,要使其有充分的扩展性。接下来可以写一些伪代码...

假设项目名为AutoClient, 目录结构如下:

AutoClient/
|-- bin/
|   |-- auto_client.py
|-- config/
|   |-- settings.py
|-- lib/
| |--
|-- log/ | |-- error.log | |-- run.log |-- src/ | |-- plugins/
| |-- __init__.py
| |-- base.py
| |-- cpu.py
| |-- disk.py
| |-- ...
| |-- package.py
| |-- scripts.py |-- README

例如,采集资产信息有上述描述的三种形式(agent,ssh,salt,),而将要做的一件事就是要让程序兼容这三种形式

在配置文件中写一点东西、配置文件就先写这些(settings.py)

# 采集资产的方式,选项有:agent(默认), salt, ssh
MODE = 'agent'

# 采集硬件数据的插件
PLUGINS_DICT = {
    'cpu': 'src.plugins.cpu.CpuPlugin',
    'disk': 'src.plugins.disk.DiskPlugin',
    'main_board': 'src.plugins.main_board.MainBoardPlugin',
    'memory': 'src.plugins.memory.MemoryPlugin',
    'nic': 'src.plugins.nic.NicPlugin',
}

继而写一些公用的(base.py)

# 导入settings文件
from AutoCMDB.conf import settings


class BasePlugin(object):
    def __init__(self):
        """
        验证配置文件
        """
        mode_list = ['agent', 'ssh', 'salt']
        if settings.MODE in mode_list:
            self.mode = settings.MODE
        else:
            raise Exception('配置文件发生错误')

    def ssh(self, cmd):
        pass

    def agent(self, cmd):
        pass

    def salt(self, cmd):
        pass

    def shell_cmd(self, cmd):
        """
        执行判断模式
        :param cmd:
        :return:
        """
        if self.mode == 'agent':
            result = self.agent(cmd)
        elif self.mode == 'ssh':
            result = self.ssh(cmd)
        else:
            result = self.salt(cmd)
        return result

    def execute(self):
        """
        执行判断平台
        :return: 目标 拿到资产采集信息
        """
        result = self.shell_cmd('查看平台命令')
        if result == 'win':
            return self.window()
        elif result == 'linux':
            return self.linux()
        else:
            raise Exception('只支持windows')

    def window(self):
        raise Exception('...')

    def linux(self):
        raise Exception('...')


class DiskPlugin(BasePlugin):
    def window(self):
        # 正则表达式匹配想要的信息
        return '硬盘'

    def linux(self):
        # 正则表达式匹配想要的信息
        #result = shell_cmd("命令")  ##直接调用shell_cmd函数去执行命令
        return '硬盘'

obj = DiskPlugin()
result = obj.execute()
上述代码看似很多,但是这样写有什么好处或者说有什么用意

这样写程序的可插拔性就很强了,例如采集硬盘信息只需在DiskPlugin的window或者linux写代码,此时不用再去判断什么模式,只需要将命令传输即可

其次,做内存、CPU、网卡信息按照硬盘那样去写。而后将各个单独存入相应的文件,这更使程序简洁明了。

接下来呢,想要同时获取CPU、内存、网卡的资产信息

那么配置文件的插件PLUGINS_DICT可以派上用场了。依托于它,可根据反射去做(package.py)
# 导入settings
from AutoCMDB.conf import settings
# 动态导入模块
import importlib

response = {}

for k, v in settings.PLUGINS_DICT.items():
    moudle_path, cls_name = v.rsplit('.', 1)
    if hasattr(importlib.import_module(moudle_path), cls_name):
        func = getattr(importlib.import_module(moudle_path), cls_name)
        response[k] = func().execute()

# 这样response便封装了所有资产信息

5:对发送数据的中控机或者agent进行一个api的验证

为什么需要进行验证api?因为我不可能说随便一个人都给可以发资产的信息,让我更新把?

方式1:通过auth-key进行验证

服务端和发送端各保存一组用于验证的字符串,当post请求来的时候进行比较两个字符串,一样则认证通过。

############发送请求的agent或中控机服务########
import requests

auth_key = "dadsdasdad21"
data_cic = {"nic":{"status":True,"error":"222"}}

result = requests.post(
    url=" http://127.0.0.1:8090/api/asset/",
    json=data_cic,
    headers={"auth-key":auth_key}  ##请求头的key不能是下划线!
)

print(result.text)
##授权失败


###########进行认证的API#################
from django.shortcuts import render,HttpResponse
from django.views.decorators.csrf import csrf_exempt,csrf_protect
import json

auth_key = "dadsdasdad"

@csrf_exempt   ##装饰器,就代表这个函数不再使用csrf进行验证
def asset(request):
    if request.method == "POST":
        custom_auth_key = request.META.get("HTTP_AUTH_KEY") ##请求头的信息在META中,以HTTP_开头,加上发送过来的小写
        if custom_auth_key != auth_key:
            return HttpResponse("授权失败")
        ##认证通过获取请求体的内容
        data = json.loads(str(request.body,encoding="utf8"))
        print(data)
    return HttpResponse("...")

方式2:进行对auth_key的MD5加密

############发送请求的agent或中控机服务########


import requests
import hashlib

auth_key = "dadsdasdad12"  ###auth_key
data_cic = {"nic":{"status":True,"error":"222"}}

md5 = hashlib.md5()
md5.update(auth_key.encode("utf-8")) ##要字节码,在python2没有这个限制
md5_result = md5.hexdigest()  ##加密的auth_key

result = requests.post(
    url=" http://127.0.0.1:8090/api/asset/",
    json=data_cic,
    headers={"auth-key":md5_result}
)
print(result.text)
##授权失败


###########进行认证的API#################
from django.shortcuts import render,HttpResponse
from django.views.decorators.csrf import csrf_exempt,csrf_protect
import json
import hashlib

auth_key = "dadsdasdad"
md5 = hashlib.md5()
md5.update(auth_key.encode("utf-8"))
md5_result = md5.hexdigest()

@csrf_exempt   ##装饰器,就代表这个函数不再使用csrf进行验证
def asset(request):
    if request.method == "POST":
        custom_auth_key = request.META.get("HTTP_AUTH_KEY")
        if custom_auth_key != md5_result:
            return HttpResponse("授权失败")
        ##认证通过获取请求体的内容
        data = json.loads(str(request.body,encoding="utf8"))
        print(data)
    return HttpResponse("...")

方式3:进行对auth_key的MD5加密和加上动态时间

发送requests客户端:

############发送请求的agent或中控机服务########

import requests
import hashlib
import time


auth_key = "dadsdasdad1"
current_time = str(time.time()) 
auth_result= "%s|%s"%(auth_key,current_time)

md5 = hashlib.md5()
md5.update(auth_result.encode("utf-8"))  ##要字节码,在python2没有这个限制
md5_result = md5.hexdigest()
##这是因为服务器和时候和发送的时候肯定是不一样的,所以我们需要携带上客户端的时间
##认证字符串结果= MD5(认证字符+当前时间)|当前时间
md5_result = "%s|%s"%(md5_result,current_time)
print(md5_result)   ##3b2ef845fcbd32a8609db0cb33eecb6c|1526024865.6403847


data_cic = {"nic":{"status":True,"error":"222"}}
result = requests.post(
    url=" http://127.0.0.1:8090/api/asset/",
    json=data_cic,    ##放在请求体中
    headers={"auth-key":md5_result}  ##放在请求头中
)

print(result.text)
##授权失败
View Code

API服务端:

###########进行认证的API#################
from django.shortcuts import render,HttpResponse

from django.views.decorators.csrf import csrf_exempt,csrf_protect
import json
import hashlib
import time

auth_key = "dadsdasdad"
md5 = hashlib.md5()

##这是第三层的验证
auth_list = []

@csrf_exempt   ##装饰器,就代表这个函数不再使用csrf进行验证
def asset(request):
    if request.method == "POST":
        ##获取客户端开来的验证字符串 和客户端时间
        ##81cb319de0d593d81dd7b06a4ae9d091 1526025063.857722
        custom_auth_key,custom_time = request.META.get("HTTP_AUTH_KEY").split("|")
        print("custom_auth_key:",custom_auth_key,custom_time)
        ##进行一个时间的判断,假如客户端发来时间超过了5秒钟,就认为是hack窃取到,url并发送过来
        ##进行第一个api的验证
        if time.time() - float(custom_time) > 5:
            print(1111)
            return HttpResponse("授权失败")

        ##服务端的auth_key加上custom的时间判断两个的验证字符串是不是一样
        ##进行第二个api的验证
        auth_keys="%s|%s"%(auth_key,custom_time)
        md5.update(auth_keys.encode("utf-8"))
        md5_result = md5.hexdigest()
        print("md5_result:",md5_result)
        if  custom_auth_key != md5_result:
            return HttpResponse("授权失败")

        ##进行第三次的api验证,如果已经存在了客户端验证信息,就说明这次是无效的!
        if request.META.get("HTTP_AUTH_KEY") in auth_list:
            print(3333)
            return HttpResponse("授权失败")

        ##认证通过获取请求体的内容
        data = json.loads(str(request.body,encoding="utf8"))
        print(data)
        
        ##如果上面三个通过了就,进行添加进已认证的客户端列表中
        auth_list.append(request.META.get("HTTP_AUTH_KEY"))
    return HttpResponse("...")
View Code

 6:数据库的实现

 

posted @ 2018-05-09 02:24  你是我的神奇  阅读(667)  评论(0编辑  收藏  举报