时间:2021-07-01 10:21:17 帮助过:13人阅读
一、远程服务器资产信息采集方案
实现方案一:agent——server服务端模式
应用场景:多应用于服务器数量多情况下,效率比ssh方式高
客户端:
################### 方式一:Agent,每一台服务器一份 ####################
import subprocess
v1 = subprocess.getoutput(‘ipconfig‘)   # 开启一个子进程
value1= v1[20:30]
v2 = subprocess.getoutput(‘dir‘)   # 开启一个子进程
value2= v2[0:5]
import requests
url = ‘http://127.0.0.1:8001/asset.html‘
response = requests.post(url, data={‘k1‘:value1, ‘k2‘:value2})  # 发送数据
print(response)
服务端:
 url(r‘^asset.html$‘, views.asset),
1
from django.shortcuts import render,HttpResponse
def asset(request):
    if request.method == "POST":
        print(request.POST)
        # 写入到数据
        return HttpResponse(‘1002‘)
    else:
        return HttpResponse(‘姿势不对‘)
实现方案二:通过SSH远程连接服务器,使用Paramiko创建中控机
pip3 install paramiko
应用场景:服务器数量不多的时候,运行效率相对低,但维护简单,不需要每台服务安装客户端
客户端:
# #################### 方式二:Paramiko,中控机放一份 ####################
"""
- 远程连接服务器,执行命令,获取结果
- 将结果发送API
192.168.11.98
"""
import paramiko
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接远程服务器
ssh.connect(hostname=‘192.168.121.128‘, port=22, username=‘lh‘, password=‘152303832‘)
# 执行命令
stdin, stdout, stderr = ssh.exec_command(‘ls‘)
# 获取命令结果
result = stdout.read()
# 关闭连接
ssh.close()
value = result[0:10]
print(value)
url = "http://127.0.0.1:8001/asset.html"
import requests
response = requests.post(url, data={‘k1‘: value, ‘k2‘: value})
print(response.text)
服务端:与agent服务端相同
实现方案三:使用saltStack开源软件(Python开发)
内部原理:采用消息队列实现,而非ssh,
修改root用户密码:sudo passwd root 
切换到root用户:su root 
安装软件要用root权限
分为master和salve两部分:
master部分: 
- yum install salt-master # 安装master 
- interface: 192.168.121.128 # 对配置文件进行配置 /etc/salt/master, ip地址为master所在的服务器地址 
- service salt-master start # 启动服务
salve部分:
yum install salt-minion
配置:master的 ip地址 # 对配置文件进行配置 /etc/salt/minion
service salt-minion start
再对所有savle进行授权操作: 
salt-key -L # 查看连接的salve 
salt-key -A # 将所有的salve进行授权,可以实现master控制所有的被授权过的salve所在的服务器
最后执行命令:在master服务器上执行: salt “*” cmd.run “ifconfig” # 表示所有salve服务器执行ifconfig命令
python代码实现: 
v3 = subprocess.getoutput(‘salt “*” cmd.run “ifconfig” ‘) # 开启一个子进程在本地运行cmd命令
二、采集器部分开发
目录结构:
三、 高级配置文件处理
可执行文件:start.py
import os
os.environ[‘USER_SETTINGS‘] = "config.settings"  # 将用户级别的配置文件路径添加到环境变量中
from lib.conf.config import settings    # 备注:需要将该导入放置在添加环境变量语句后面,否则报错
print(settings.USER)
用户自定义配置文件:settings
"""
用户自定义配置文件
"""
USER = ‘lh‘ # 服务器登陆信息
PWD = ‘152303832‘
内置配置文件:global_settings.py
"""
内置配置文件
"""
EMAIL = ‘152303832@qq.com‘
配置文件整合:config.py
"""
整合用户配置文件和内置配置文件
"""
import os
import importlib
from . import global_settings
class Settings(object):
    def __init__(self):
    ######## 找到默认内置配置文件 ########
        for name in dir(global_settings):   # 获得模块中的所有属性名列表
            if name.isupper():
                value = getattr(global_settings, name)  # 反射获得模块中的属性值
                setattr(self, name, value)  # 为传入的参数对象添加该属性名及属性值
    # ######## 找到自定义配置 ########
    # 根据字符串导入模块
        settings_module = os.environ.get(‘USER_SETTINGS‘) # 获得环境变量(内存)中的自定义配置文件的路径值
        if not settings_module:
            return
        custom_settings = importlib.import_module(settings_module)    # 根据字符串导入应对的模块
        for name in dir(custom_settings):
            if name.isupper():
                value = getattr(custom_settings, name)
                setattr(self, name, value)
settings = Settings()   # 将实例化的对象作为属性值
三、 CMDB可插拔插件制作
settings.py 配置文件
"""
用户自定义的配置文件
"""
USER = ‘lh‘  # 服务器登陆信息
PWD = ‘152303832‘
import os
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# ##############插件所需的配置参数################
MODE = ‘AGENT‘  # 采用agent模式采集服务器信息
# MODE = ‘SALT‘  # 采用salt模式采集服务器信息
# MODE = ‘SSH‘  # 采用SSH模式采集服务器信息
DEBUG = True
SSH_USER = ‘root‘  # 连接远程服务器的用户名
SSH_PWD = ‘root‘  # 连接远程服务器的密码
SSH_KEY = ‘/XX/XX/XX‘  # 通过公钥私钥来连接远程服务器实现免密登陆
SSH_PORT = 22
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",
}
# api接口 url地址
# API = "http://www.oldboyedu.com"
API = "http://127.0.0.1:8000/api/asset.html"
# 用于服务器唯一标识符,防止服务器数量出现叠加错误
CERT_PATH = os.path.join(BASEDIR, ‘config‘, ‘cert‘)
config.py 配合配置文件
"""
整合用户配置文件和内置配置文件
"""
import os
import importlib
from . import global_settings
class Settings(object):
    def __init__(self):
        ######## 找到默认内置配置文件 ########
        for name in dir(global_settings):
            if name.isupper():
                value = getattr(global_settings, name)  # 反射获得模块中的属性值
                setattr(self, name, value)  # 为传入的参数对象添加该属性名及属性值
        # ######## 找到自定义配置 ########
        # 根据字符串导入模块
        settings_module = os.environ.get(‘USER_SETTINGS‘)  # 获得环境变量(内存)中的自定义配置文件的路径值
        if not settings_module:
            return
        custom_settings = importlib.import_module(settings_module)  # 根据字符串导入应对的模块
        for name in dir(custom_settings):
            if name.isupper():
                value = getattr(custom_settings, name)
                setattr(self, name, value)
settings = Settings()  # 将实例化的对象作为属性值
start.py启动文件
import os
import sys
# 程序启动入口文件
os.environ[‘USER_SETTINGS‘] = "config.settings"  # 将用户级别的配置文件路径添加到环境变量中
from lib.conf.config import settings # 备注:需要将该导入放置在添加环境变量语句后面,否则报错
# print(settings.USER)
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
from src import script
if __name__ == ‘__main__‘:
    script.run()
script.py脚本文件:
from lib.conf.config import settings
from .client import Agent
from .client import SSHSALT
def run():
    """
    根据配置文件中的内容选择不同的采集方式
    :param object:
    :return:
    """
    if settings.MODE == ‘AGENT‘:
        obj = Agent()
    else:
        obj = SSHSALT()
    obj.excute()
client.py客户端:
import requests
from lib.conf.config import settings
from src.plugins import PluginManager
import json
from concurrent.futures import ThreadPoolExecutor
class Base(object):
    """
    负责往api发送数据
    """
    def post_asset(self, server_info):
        # 将数据转换成json字符串格式发送
        requests.post(settings.API, json=server_info)  # 数据封装在body中: 会在源码中自动转换 json.dumps(server_info)
        # headers= {‘content-type‘:‘application/json‘}
        # request.body   # 需从body中取出数据
        # json.loads(request.body)
class Agent(Base):
    """
    用agent方式采集数据并提交到api
    """
    def excute(self):
        servier_info = PluginManager().exec_plugin()  # 采集数据
        # 唯一标识符处理
        hostname = servier_info[‘basic‘][‘data‘][‘hostname‘]  # 获得主机名,用来验证唯一标识符
        certname = open(settings.CERT_PATH, ‘r‘, encoding=‘utf-8‘).read().strip()  # 获得服务器上的文件中的主机名
        if not certname:
            with open(settings.CERT_PATH, ‘w‘, encoding=‘utf-8‘) as f:  # 如果文件中不存在该主机名,表示该主机名未初始化,写入文件即可
                f.write(hostname)
        else:
            # 用文件中的主机名覆盖被用户修改过的主机名,防止出现主机重复导致数量叠加错误
            servier_info[‘basic‘][‘data‘][‘hostname‘] = certname
self.post_asset(servier_info) # 子类对象调用父类方法来发送数据
class SSHSALT(Base):
    """
    用SSH方式和SALT方式采集数据和发送
    """
    def get_host(self):  # 该方式先获取未采集过数据的主机列表
        response = requests.get(settings.API)
        result = json.load(response.text)  # "{status:‘True‘,data: [‘c1.com‘,‘c2.com‘]}"
        if result[‘status‘]:
            return None
        return result[‘data‘]
    # 执行服务器信息采集,并将该信息发送给API
    def run(self, host):
        server_info = PluginManager(host).exec_plugin()  # 该两种采集方式都需传入主机host信息
        self.post_asset(server_info)
    # 基于线程池实现并发采集资产
    def excute(self):
        host_list = self.get_host()
        # 开启线程池并发任务,一次使用10个线程同时完成任务即可,多了会占用更多的系统资源
        pool = ThreadPoolExecutor(10)
        for host in host_list:
            pool.submit(self.run, host)  # 提交要执行的任务及对应的参数
_init_.py文件:
from lib.conf.config import settings
import importlib
import traceback
class PluginManager(object):
    def __init__(self, hostname=None):  # 为agent/salt模式预留的主机名参数值
        self.hostname = hostname
        self.plugin_dict = settings.PLUGINS_DICT
        self.mode = settings.MODE  # 采集模式
        self.debug = settings.DEBUG
        if self.mode == ‘SSH‘:
            self.ssh_user = settings.SSH_USER
            self.ssh_port = settings.SSH_PORT
            self.ssh_pwd = settings.SSH_PWD
            self.ssh_key = settings.SSH_KEY
    def exec_plugin(self):
        """
        获取所有插件,并执行插件中的方法获得返回值
        :return:
        """
        response = {}
        for k, v in self.plugin_dict.items():
            #  ‘basic‘: "src.plugins.basic.Basic",
            ret = {‘stauts‘: 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[‘stauts‘] = False
                # traceback.format_exc()获得具体的错误信息   k表示插件名称
                ret[‘data‘] = ‘[%s][%s]采集数据出现错误:%s‘ % (
                self.hostname if self.hostname else ‘AGENT‘, k, traceback.format_exc())
            response[k] = ret
        return response
    ######判断采集方法############
    def command(self, cmd):
        if self.mode == ‘AGENT‘:
            return self.__agent(cmd)
        elif self.mode == ‘SSH‘:
            return self.__ssh(cmd)
        elif self.mode == ‘SALT‘:
            return self.__salt(cmd)
        else:
            raise Exception(‘模式只能是 AGENT/SSH/SALT‘)
    ########## 执行对应的采集方法##########
    def __agent(self, cmd):  # 私有方法,只有当前类的对象可调用
        import subprocess
        output = subprocess.getoutput(cmd)
        return output
    def __ssh(self, cmd):
        import paramiko
        # 通过公钥私钥方式登陆远程服务器
        # private_key = paramiko.RSAKey.from_private_key_file(self.ssh_key)
        # ssh = paramiko.SSHClient()
        # ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        # ssh.connect(hostname=self.hostname, port=self.ssh_port, username=self.ssh_user, pkey=private_key)
        # stdin, stdout, stderr = ssh.exec_command(cmd)
        # result = stdout.read()
        # ssh.close()
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
        ssh.connect(hostname=self.hostname, port=self.ssh_port, username=self.ssh_user, password=self.ssh_pwd)
        stdin, stdout, stderr = ssh.exec_command(cmd)
        result = stdout.read()
        ssh.close()
        return result
    #   通过salt方式获得远程服务器信息
    def __salt(self, cmd):
        salt_cmd = "salt ‘%s‘ cmd.run ‘%s‘" % (self.hostname, cmd,)
        import subprocess
        output = subprocess.getoutput(salt_cmd)
        return output
basic.py采集基础信息:
class Basic:
    """
    获取服务器基本信息(服务器名称...)
    """
    def __init__(self):
        pass
    @classmethod
    def inital(cls):  # 定义类方法,用于扩展,在执行init方法时提前执行的扩展方法
        return cls()
    def process(self, command_func, debug):
        if debug:  # 用于在windows环境下的测试
            output = {
                ‘os_platform‘: "linux",
                ‘os_version‘: "CentOS release 6.6 (Final)\nKernel \r on an \m",
                ‘hostname‘: ‘c1.com‘
            }
        else:  # 在linux系统下执行的命令
            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
board.py采集主板信息:
from lib.conf.config import settings
import os
class Board:
    """
    获取服务器主板信息
    """
    def __init__(self):
        pass
    @classmethod
    def initial(cls):
        return cls()
    def process(self, command_func, debug):
        if debug:
            output = open(os.path.join(settings.BASEDIR, ‘files/board.out‘), ‘r‘, encoding=‘utf-8‘).read()
        else:
            output = command_func(‘sudo dmidecode -t1‘)  # 调用command_func方法来执行对应的采集方式
        return self.parse(output)
    # 解析服务器信息结果
    def parse(self, content):
        result = {}
        key_map = {
            ‘Manufacturer‘: ‘manufacturer‘,
            ‘Product Name‘: ‘model‘,
            ‘Serial Number‘: ‘sn‘,
        }
        for item in content.split(‘\n‘):
            row_data = item.strip().split(‘:‘)
            if len(row_data) == 2:
                if row_data[0] in key_map:
                    result[key_map[row_data[0]]] = row_data[1].strip() if row_data[1] else row_data[1]
        return result
cpu.py 采集CPU信息:
import os
from lib.conf.config import settings
class Cpu:
    """
    获取服务器CPUT信息
    """
    def __init__(self):
        pass
    @classmethod
    def initial(cls):
        return cls()
    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(‘\n\n‘):
            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
disk.py 采集硬盘信息:
import re
import os
from lib.conf.config import settings
class Disk:
    """
    获取服务器硬盘信息
    """
    def __init__(self):
        pass
    @classmethod
    def initial(cls):
        return cls()
    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("sudo MegaCli  -PDList -aALL")
        return self.parse(output)
    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 = ‘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
memory.py 采集内存信息:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
from lib import convert
from lib.conf.config import settings
class Memory(object):
    """
    获取服务器内存信息
    """
    def __init__(self):
        pass
    @classmethod
    def initial(cls):
        return cls()
    def process(self, command_func, debug):
        if debug:
            output = open(os.path.join(settings.BASEDIR, ‘files/memory.out‘), ‘r‘, encoding=‘utf-8‘).read()
        else:
            output = command_func("sudo dmidecode  -q -t 17 2>/dev/null")
        return self.parse(output)
    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 not line.strip():
                    continue
                if len(line.split(‘:‘)):
                    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
采集网卡信息:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import re
from lib.conf.config import settings
class Nic(object):
    """
    获取服务器网卡信息
    """
    def __init__(self):
        pass
    @classmethod
    def initial(cls):
        return cls()
    def process(self, command_func, debug):
        if debug:
            output = open(os.path.join(settings.BASEDIR, ‘files/nic.out‘), ‘r‘, encoding=‘utf-8‘).read()
            interfaces_info = self._interfaces_ip(output)
        else:
            interfaces_info = self.linux_interfaces(command_func)
self.standard(interfaces_info)
return interfaces_info
    def linux_interfaces(self, command_func):
        ‘‘‘
        Obtain interface information for *NIX/BSD variants
        ‘‘‘
        ifaces = dict()
        ip_path = ‘ip‘
        if ip_path:
            cmd1 = command_func(‘sudo {0} link show‘.format(ip_path))
            cmd2 = command_func(‘sudo {0} addr show‘.format(ip_path))
            ifaces = self._interfaces_ip(cmd1 + ‘\n‘ + cmd2)
        return ifaces
    def which(self, exe):
        def _is_executable_file_or_link(exe):
            # check for os.X_OK doesn‘t suffice because directory may executable
            return (os.access(exe, os.X_OK) and
                    (os.path.isfile(exe) or os.path.islink(exe)))
        if exe:
            if _is_executable_file_or_link(exe):
                # executable in cwd or fullpath
                return exe
            # default path based on busybox‘s default
            default_path = ‘/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin‘
            search_path = os.environ.get(‘PATH‘, default_path)
            path_ext = os.environ.get(‘PATHEXT‘, ‘.EXE‘)
            ext_list = path_ext.split(‘;‘)
            search_path = search_path.split(os.pathsep)
            if True:
                # Add any dirs in the default_path which are not in search_path. If
                # there was no PATH variable found in os.environ, then this will be
                # a no-op. This ensures that all dirs in the default_path are
                # searched, which lets salt.utils.which() work well when invoked by
                # salt-call running from cron (which, depending on platform, may
                # have a severely limited PATH).
                search_path.extend(
                    [
                        x for x in default_path.split(os.pathsep)
                        if x not in search_path
                    ]
                )
            for path in search_path:
                full_path = os.path.join(path, exe)
                if _is_executable_file_or_link(full_path):
                    return full_path
return None
    def _number_of_set_bits_to_ipv4_netmask(self, set_bits):  # pylint: disable=C0103
        ‘‘‘
        Returns an IPv4 netmask from the integer representation of that mask.
        Ex. 0xffffff00 -> ‘255.255.255.0‘
        ‘‘‘
        return self.cidr_to_ipv4_netmask(self._number_of_set_bits(set_bits))
    def cidr_to_ipv4_netmask(self, cidr_bits):
        ‘‘‘
        Returns an IPv4 netmask
        ‘‘‘
        try:
            cidr_bits = int(cidr_bits)
            if not 1 <= cidr_bits <= 32:
                return ‘‘
        except ValueError:
            return ‘‘
        netmask = ‘‘
        for idx in range(4):
            if idx:
                netmask += ‘.‘
            if cidr_bits >= 8:
                netmask += ‘255‘
                cidr_bits -= 8
            else:
                netmask += ‘{0:d}‘.format(256 - (2 ** (8 - cidr_bits)))
                cidr_bits = 0
        return netmask
    def _number_of_set_bits(self, x):
        ‘‘‘
        Returns the number of bits that are set in a 32bit int
        ‘‘‘
        # Taken from http://stackoverflow.com/a/4912729. Many thanks!
        x -= (x >> 1) & 0x55555555
        x = ((x >> 2) & 0x33333333) + (x & 0x33333333)
        x = ((x >> 4) + x) & 0x0f0f0f0f
        x += x >> 8
        x += x >> 16
        return x & 0x0000003f
    def _interfaces_ip(self, out):
        ‘‘‘
        Uses ip to return a dictionary of interfaces with various information about
        each (up/down state, ip address, netmask, and hwaddr)
        ‘‘‘
        ret = dict()
        right_keys = [‘name‘, ‘hwaddr‘, ‘up‘, ‘netmask‘, ‘ipaddrs‘]
        def parse_network(value, cols):
            ‘‘‘
            Return a tuple of ip, netmask, broadcast
            based on the current set of cols
            ‘‘‘
            brd = None
            if ‘/‘ in value:  # we have a CIDR in this address
                ip, cidr = value.split(‘/‘)  # pylint: disable=C0103
            else:
                ip = value  # pylint: disable=C0103
                cidr = 32
            if type_ == ‘inet‘:
                mask = self.cidr_to_ipv4_netmask(int(cidr))
                if ‘brd‘ in cols:
                    brd = cols[cols.index(‘brd‘) + 1]
            return (ip, mask, brd)
        groups = re.compile(‘\r?\n\\d‘).split(out)
        for group in groups:
            iface = None
            data = dict()
            for line in group.splitlines():
                if ‘ ‘ not in line:
                    continue
                match = re.match(r‘^\d*:\s+([\w.\-]+)(?:@)?([\w.\-]+)?:\s+<(.+)>‘, line)
                if match:
                    iface, parent, attrs = match.groups()
                    if ‘UP‘ in attrs.split(‘,‘):
                        data[‘up‘] = True
                    else:
                        data[‘up‘] = False
                    if parent and parent in right_keys:
                        data[parent] = parent
                    continue
                cols = line.split()
                if len(cols) >= 2:
                    type_, value = tuple(cols[0:2])
                    iflabel = cols[-1:][0]
                    if type_ in (‘inet‘,):
                        if ‘secondary‘ not in cols:
                            ipaddr, netmask, broadcast = parse_network(value, cols)
                            if type_ == ‘inet‘:
                                if ‘inet‘ not in data:
                                    data[‘inet‘] = list()
                                addr_obj = dict()
                                addr_obj[‘address‘] = ipaddr
                                addr_obj[‘netmask‘] = netmask
                                addr_obj[‘broadcast‘] = broadcast
                                data[‘inet‘].append(addr_obj)
                        else:
                            if ‘secondary‘ not in data:
                                data[‘secondary‘] = list()
                            ip_, mask, brd = parse_network(value, cols)
                            data[‘secondary‘].append({
                                ‘type‘: type_,
                                ‘address‘: ip_,
                                ‘netmask‘: mask,
                                ‘broadcast‘: brd,
                            })
                            del ip_, mask, brd
                    elif type_.startswith(‘link‘):
                        data[‘hwaddr‘] = value
            if iface:
                if iface.startswith(‘pan‘) or iface.startswith(‘lo‘) or iface.startswith(‘v‘):
                    del iface, data
                else:
                    ret[iface] = data
                    del iface, data
        return ret
def standard(self, interfaces_info):
        for key, value in interfaces_info.items():
            ipaddrs = set()
            netmask = set()
            if not ‘inet‘ in value:
                value[‘ipaddrs‘] = ‘‘
                value[‘netmask‘] = ‘‘
            else:
                for item in value[‘inet‘]:
                    ipaddrs.add(item[‘address‘])
                    netmask.add(item[‘netmask‘])
                value[‘ipaddrs‘] = ‘/‘.join(ipaddrs)
                value[‘netmask‘] = ‘/‘.join(netmask)
                del value[‘inet‘]
四、资产入库数据库表设计
from django.db import models
class UserProfile(models.Model):
    """
    用户信息
    """
    name = models.CharField(u‘姓名‘, max_length=32)
    email = models.EmailField(u‘邮箱‘)
    phone = models.CharField(u‘座机‘, max_length=32)
    mobile = models.CharField(u‘手机‘, max_length=32)
    class Meta:
        verbose_name_plural = "用户表"
    def __str__(self):
        return self.name
class AdminInfo(models.Model):
    """
    用户登陆相关信息
    """
    user_info = models.OneToOneField("UserProfile", on_delete=‘‘)
    username = models.CharField(u‘用户名‘, max_length=64)
    password = models.CharField(u‘密码‘, max_length=64)
    class Meta:
        verbose_name_plural = "管理员表"
    def __str__(self):
        return self.user_info.name
class UserGroup(models.Model):
    """
    用户组
    """
    name = models.CharField(max_length=32, unique=True)
    users = models.ManyToManyField(‘UserProfile‘)
    class Meta:
        verbose_name_plural = "用户组表"
    def __str__(self):
        return self.name
class BusinessUnit(models.Model):
    """
    业务线
    """
    name = models.CharField(‘业务线‘, max_length=64, unique=True)
    contact = models.ForeignKey(‘UserGroup‘, verbose_name=‘业务联系人‘, related_name=‘c‘, on_delete=‘‘)
    manager = models.ForeignKey(‘UserGroup‘, verbose_name=‘系统管理员‘, related_name=‘m‘, on_delete=‘‘)
    class Meta:
        verbose_name_plural = "业务线表"
    def __str__(self):
        return self.name
class IDC(models.Model):
    """
    机房信息
    """
    name = models.CharField(‘机房‘, max_length=32)
    floor = models.IntegerField(‘楼层‘, default=1)
    class Meta:
        verbose_name_plural = "机房表"
    def __str__(self):
        return self.name
class Tag(models.Model):
    """
    资产标签
    """
    name = models.CharField(‘标签‘, max_length=32, unique=True)
    class Meta:
        verbose_name_plural = "标签表"
    def __str__(self):
        return self.name
class Asset(models.Model):
    """
    资产信息表,所有资产公共信息(交换机,服务器,防火墙等)
    """
    device_type_choices = (
        (1, ‘服务器‘),
        (2, ‘交换机‘),
        (3, ‘防火墙‘),
    )
    device_status_choices = (
        (1, ‘上架‘),
        (2, ‘在线‘),
        (3, ‘离线‘),
        (4, ‘下架‘),
    )
    device_type_id = models.IntegerField(choices=device_type_choices, default=1)
    device_status_id = models.IntegerField(choices=device_status_choices, default=1)
    cabinet_num = models.CharField(‘机柜号‘, max_length=30, null=True, blank=True)
    cabinet_order = models.CharField(‘机柜中序号‘, max_length=30, null=True, blank=True)
    idc = models.ForeignKey(‘IDC‘, verbose_name=‘IDC机房‘, null=True, blank=True, on_delete=‘‘)
    business_unit = models.ForeignKey(‘BusinessUnit‘, verbose_name=‘属于的业务线‘, null=True, blank=True, on_delete=‘‘)
tag = models.ManyToManyField(‘Tag‘,)
    latest_date = models.DateField(null=True)
    create_at = models.DateTimeField(auto_now_add=True)
    class Meta:
        verbose_name_plural = "资产表"
    def __str__(self):
        return "%s-%s-%s" % (self.idc.name, self.cabinet_num, self.cabinet_order)
class Server(models.Model):
    """
    服务器信息
    """
    asset = models.OneToOneField(‘Asset‘, on_delete=‘‘)
    hostname = models.CharField(max_length=128, unique=True)
    sn = models.CharField(‘SN号‘, max_length=64, db_index=True)
    manufacturer = models.CharField(verbose_name=‘制造商‘, max_length=64, null=True, blank=True)
    model = models.CharField(‘型号‘, max_length=64, null=True, blank=True)
    manage_ip = models.GenericIPAddressField(‘管理IP‘, null=True, blank=True) # ip地址字段,有校验功能
    manage_ip = models.GenericIPAddressField(‘管理IP‘, null=True, blank=True) # ip地址字段,有校验功能
    os_platform = models.CharField(‘系统‘, max_length=16, null=True, blank=True)
    os_version = models.CharField(‘系统版本‘, max_length=16, null=True, blank=True)
    cpu_count = models.IntegerField(‘CPU个数‘, null=True, blank=True)
    cpu_physical_count = models.IntegerField(‘CPU物理个数‘, null=True, blank=True)
    cpu_model = models.CharField(‘CPU型号‘, max_length=128, null=True, blank=True)
create_at = models.DateTimeField(auto_now_add=True, blank=True)
    class Meta:
        verbose_name_plural = "服务器表"
    def __str__(self):
        return self.hostname
class NetworkDevice(models.Model):
    asset = models.OneToOneField(‘Asset‘, on_delete=‘‘)
    management_ip = models.CharField(‘管理IP‘, max_length=64, blank=True, null=True)
    vlan_ip = models.CharField(‘VlanIP‘, max_length=64, blank=True, null=True)
    intranet_ip = models.CharField(‘内网IP‘, max_length=128, blank=True, null=True)
    sn = models.CharField(‘SN号‘, max_length=64, unique=True)
    manufacture = models.CharField(verbose_name=u‘制造商‘, max_length=128, null=True, blank=True)
    model = models.CharField(‘型号‘, max_length=128, null=True, blank=True)
    port_num = models.SmallIntegerField(‘端口个数‘, null=True, blank=True)
    device_detail = models.CharField(‘设置详细配置‘, max_length=255, null=True, blank=True)
    class Meta:
        verbose_name_plural = "网络设备"
class Disk(models.Model):
    """
    硬盘信息
    """
    slot = models.CharField(‘插槽位‘, max_length=8)
    model = models.CharField(‘磁盘型号‘, max_length=32)
    capacity = models.FloatField(‘磁盘容量GB‘)
    pd_type = models.CharField(‘磁盘类型‘, max_length=32)
    server_obj = models.ForeignKey(‘Server‘,related_name=‘disk‘, on_delete=‘‘)
    class Meta:
        verbose_name_plural = "硬盘表"
    def __str__(self):
        return self.slot
class NIC(models.Model):
    """
    网卡信息
    """
    name = models.CharField(‘网卡名称‘, max_length=128)
    hwaddr = models.CharField(‘网卡mac地址‘, max_length=64)
    netmask = models.CharField(max_length=64)
    ipaddrs = models.CharField(‘ip地址‘, max_length=256)
    up = models.BooleanField(default=False)
    server_obj = models.ForeignKey(‘Server‘,related_name=‘nic‘, on_delete=‘‘)
    class Meta:
        verbose_name_plural = "网卡表"
    def __str__(self):
        return self.name
class Memory(models.Model):
    """
    内存信息
    """
    slot = models.CharField(‘插槽位‘, max_length=32)
    manufacturer = models.CharField(‘制造商‘, max_length=32, null=True, blank=True)
    model = models.CharField(‘型号‘, max_length=64)
    capacity = models.FloatField(‘容量‘, null=True, blank=True)
    sn = models.CharField(‘内存SN号‘, max_length=64, null=True, blank=True)
    speed = models.CharField(‘速度‘, max_length=16, null=True, blank=True)
server_obj = models.ForeignKey(‘Server‘,related_name=‘memory‘, on_delete=‘‘)
    class Meta:
        verbose_name_plural = "内存表"
    def __str__(self):
        return self.slot
class AssetRecord(models.Model):
    """
    资产变更记录,creator为空时,表示是资产汇报的数据。
    """
    asset_obj = models.ForeignKey(‘Asset‘, related_name=‘ar‘, on_delete=‘‘)
    content = models.TextField(null=True)# 新增硬盘
    creator = models.ForeignKey(‘UserProfile‘, null=True, blank=True, on_delete=‘‘) #
    create_at = models.DateTimeField(auto_now_add=True)
    class Meta:
        verbose_name_plural = "资产记录表"
    def __str__(self):
        return "%s-%s-%s" % (self.asset_obj.idc.name, self.asset_obj.cabinet_num, self.asset_obj.cabinet_order)
class ErrorLog(models.Model):
    """
    错误日志,如:agent采集数据错误 或 运行错误
    """
    asset_obj = models.ForeignKey(‘Asset‘, null=True, blank=True, on_delete=‘‘)
    title = models.CharField(max_length=16)
    content = models.TextField()
    create_at = models.DateTimeField(auto_now_add=True)
    class Meta:
        verbose_name_plural = "错误日志表"
    def __str__(self):
        return self.title
五、API获取资产并保存入库
urls:
url(r‘api/‘, include(‘api.urls‘)),
1
api模块:
url(r‘^asset.html$‘, views.asset),
1
views:
def asset(request):
    if request.method == ‘POST‘:
        # 新资产信息
        server_info = json.loads(request.body.decode(‘utf-8‘)) # 将发送的数据解码成字符串,再通过json反序列化成字典格式
        hostname = server_info[‘basic‘][‘data‘][‘hostname‘] # 获得采集器中发过来的信息中的主机名
        server_obj = models.Server.objects.filter(hostname = hostname)
        if not server_obj:
            return HttpResponse(‘当前主机名在资产中未录入‘)
        return HttpResponse(‘‘)
七、资产入库处理 (以硬盘为例)
views:
from django.shortcuts import render
import json
from django.shortcuts import HttpResponse
from repository import models
# Create your views here.
def asset(request):
    if request.method == ‘POST‘:
        # 新资产信息
        server_info = json.loads(request.body.decode(‘utf-8‘))  # 将发送的数据解码成字符串,再通过json反序列化成字典格式
        hostname = server_info[‘basic‘][‘data‘][‘hostname‘]  # 获得采集器中发过来的信息中的主机名
        server_obj = models.Server.objects.filter(hostname=hostname).first()  # 根据主机名获得服务器QuuerySet对象
        if not server_obj:
            return HttpResponse(‘当前主机名在资产中未录入‘)
        # 将硬盘信息入库
        # ‘disk‘: {‘stauts‘: True, ‘data‘: {
        #     ‘0‘: {‘slot‘: ‘0‘, ‘pd_type‘: ‘SAS‘, ‘capacity‘: ‘279.396‘,
        #           ‘model‘: ‘SEAGATE ST300MM0006     LS08S0K2B5NV‘},
        #     ‘1‘: {‘slot‘: ‘1‘, ‘pd_type‘: ‘SAS‘, ‘capacity‘: ‘279.396‘,
        #           ‘model‘: ‘SEAGATE ST300MM0006     LS08S0K2B5AH‘},
        #     ‘2‘: {‘slot‘: ‘2‘, ‘pd_type‘: ‘SATA‘, ‘capacity‘: ‘476.939‘,
        #           ‘model‘: ‘S1SZNSAFA01085L     Samsung SSD 850 PRO 512GB               EXM01B6Q‘},
        #     ‘3‘: {‘slot‘: ‘3‘, ‘pd_type‘: ‘SATA‘, ‘capacity‘: ‘476.939‘,
        #           ‘model‘: ‘S1AXNSAF912433K     Samsung SSD 840 PRO Series              DXM06B0Q‘},
        #     ‘4‘: {‘slot‘: ‘4‘, ‘pd_type‘: ‘SATA‘, ‘capacity‘: ‘476.939‘,
        #           ‘model‘: ‘S1AXNSAF303909M     Samsung SSD 840 PRO Series              DXM05B0Q‘},
        #     ‘5‘: {‘slot‘: ‘5‘, ‘pd_type‘: ‘SATA‘, ‘capacity‘: ‘476.939‘,
        #           ‘model‘: ‘S1AXNSAFB00549A     Samsung SSD 840 PRO Series              DXM06B0Q‘}}},
        if not server_info[‘disk‘][‘stauts‘]:  # 采集信息中状态为False时,将错误信息添加到错误日志中
            models.ErrorLog.objects.create(content=server_info[‘disk‘][‘data‘], asset_obj=server_obj.asset,
                                           title=‘【%s】硬盘采集错误信息‘ % hostname)
        new_disk_dict = server_info[‘disk‘][‘data‘]  # 获得服务器中最新的硬盘信息数据
        """
               {
                   5: {‘slot‘:5,capacity:476...}
                   3: {‘slot‘:3,capacity:476...}
               }
               """
        old_disk_list = models.Disk.objects.filter(server_obj=server_obj)  # 获得服务器中之前的所有硬盘数据QuerySer对象列表
        """
                [
                    Disk(‘slot‘:5,capacity:476...)
                    Disk(‘slot‘:4,capacity:476...)
                ]
                """
new_slot_list = list(new_disk_dict.keys()) # 获得最新硬盘数据中的插槽ID ,[0,1,2,3,4,5]
        old_slot_list = []  # 获得之前的硬盘数据中的插槽ID
        for item in old_disk_list:
            old_slot_list.append(item.slot)
        # 采用交集运算后的结果作为硬盘数据的 更新,获得共有的数据进行比较
        update_list = set(new_slot_list).intersection(old_slot_list)  # 采用集合进行交集运算
        #  采用差集运算后的结果作为硬盘数据的 创建(新数据有,老数据没有)
        create_list = set(new_slot_list).difference(old_slot_list)
        # 采用差集运算后的结果作为硬盘数据的 删除(老数据有,新数据没有)
        del_list = set(old_slot_list).difference(new_slot_list)
        ###################从硬盘数据表中删除数据#################3
        if del_list:
            models.Disk.objects.filter(server_obj=server_obj, slot__in=del_list).delete()
            # 记录日志信息
            models.AssetRecord.objects.create(asset_obj=server_obj.asset, content=‘移除硬盘:%s‘ % (‘、‘.join(del_list)))
        ###################从硬盘数据表中增加数据#################
        record_list = []
        for slot in create_list:
            disk_dict = new_disk_dict[
                slot]  # {‘capacity‘: ‘476.939‘, ‘slot‘: ‘4‘, ‘model‘: ‘S1AXNSAF303909M     Samsung SSD 840 PRO Series
            disk_dict[‘server_obj‘] = server_obj  # 同时将服务器对象添加到该字典中一同添加到Disk数据表中
            models.Disk.objects.create(**disk_dict)  # 以字典的形式添加数据到Disk数据表中
            # 组装硬盘变更记录信息
            temp = "新增硬盘:位置{slot},容量{capacity},型号:{model},类型:{pd_type}".format(**disk_dict)
            record_list.append(temp)
        if record_list:
            content = ‘;‘.join(record_list)  # 将所有变更信息拼接成一个字符串
            models.AssetRecord.objects.create(asset_obj=server_obj.asset, content=content)  # 将变更记录添加到记录表中
        ###################从硬盘数据表中修改数据#################
        record_list = []  # 变更记录列表
        row_map = {‘capacity‘: ‘容量‘, ‘pd_type‘: ‘类型‘, ‘model‘: ‘型号‘}
        for slot in update_list:
            new_disk_row = new_disk_dict[slot]  # 获得新采集过来的单条硬盘数据
            old_disk_row = models.Disk.objects.filter(slot=slot, server_obj=server_obj).first()
            for k, v in new_disk_row.items():
                # k: capacity;  slot;   pd_type;    model
                # v: ‘476.939‘  ‘xxies              DXM05B0Q‘   ‘SATA‘
                value = getattr(old_disk_row, k)  # 通过反射获得对象中属性的值
                if v != value:  # 如果两者中的值不相等则表示需要更新
                    setattr(old_disk_row, k, v)  # 将对象中的属性值重新赋值
                    record_list.append(‘槽位%s,%s由%s变更为%s‘ % (slot, row_map[k], value, v))
            old_disk_row.save()  # 保存更新后的硬盘数据
            # 将变更信息保存到变更记录表中
            if record_list:
                content = ";".join(record_list)
                models.AssetRecord.objects.create(asset_obj=server_obj.asset, content=content)
        return HttpResponse(‘‘)
八、牛x的API验证
采集程序客户端的动态令牌生成:
############### 客户端生成并发送动态令牌完成API验证 ###############
import requests
import time
import hashlib
# 生成动态令牌
ctime = time.time()  # 动态时间戳
key = "asdfasdfasdfasdf098712sdfs"  # 假定API发送过来的静态令牌
new_key = ‘%s|%s‘ % (key, ctime)  # 在静态令牌基础加入动态时间,形成动态令牌
print(ctime)
# 动态令牌通过md5进行加密
m = hashlib.md5()  # 初始化md5
m.update(bytes(new_key, encoding=‘utf-8‘))  # 将动态令牌转换成字节,将由md5进行计算
md5_key = m.hexdigest()  # 获得加密后的令牌
print(md5_key)
md5_time_key = ‘%s|%s‘ % (md5_key, ctime) # 将生成动态令牌所需的时间一同发给API,让API进行md5进行加密完成动态令牌的生成,以便完成动态令牌数据的比对
# 将添加了时间的动态令牌添加到请求头中发往API
response = requests.get(‘http://127.0.0.1:8000/api/asset.html‘, headers={‘OpenKey‘: md5_time_key})
print(response.text)  # 获得响应结果
API接收令牌完成身份验证:
from django.shortcuts import render
import json
from django.shortcuts import HttpResponse
from repository import models
import time
from server import settings
import hashlib
api_key_record = {  # 访问记录表,由动态令牌作为key, 生成动态令牌中所需的时间+10s 作为value,表示该值保存10s
    # "1b96b89695f52ec9de8292a5a7945e38|1501472467.4977243":1501472477.4977243
}
def asset(request):
    client_md5_time_key = request.META.get(‘HTTP_OPENKEY‘)  # 获取客户端通过请求头中发送过来的数据
    client_md5_key, client_ctime = client_md5_time_key.split(‘|‘)  # 切分出动态令牌和时间
    client_ctime = float(client_ctime)
    server_time = time.time()  # 获得服务器当前时间
    # 第一关验证:客户端第二次发送动态令牌的时间不能超过10s,完成第一层黑客攻击过滤
    if server_time - client_ctime > 10:
        return HttpResponse(‘第一关通过失败,时间超时‘)
    # 第二关验证:生成动态令牌的时间不匹配,防止黑客获得动态令牌,并通过第一关到达第二关
    temp = ‘%s|%s‘ % (settings.AUTH_KEY, client_ctime)   # 从配置文件中读取出静态令牌
    # 完成服务端的动态令牌生成
    m = hashlib.md5()
    m.update(bytes(temp, encoding=‘utf-8‘))
    server_md5_key = m.hexdigest()
    if server_md5_key != client_md5_key:   # 如果两个动态令牌不相等
        return HttpResponse(‘第二关通过失败,生成服务端动态令牌中的时间与生成动态令牌中的时间不一致‘)
    # 对api_key_record记录表中进行数据更新,用于第三关验证
    for k in list(api_key_record.keys()):
        v = api_key_record[k]
        if server_time > v:  # 如果服务器当前时间大于动态令牌有效时间,则删除该令牌,以便减少记录表容量占比
            del api_key_record[k]
    # 第三关:保持记录表中的唯一性,如果发送过请求,则其它的请求无效
    if client_md5_time_key in api_key_record:
        return HttpResponse(‘第三关通过失败,已经有人访问过了‘)
    else:
        api_key_record[client_md5_time_key] = client_ctime + 10  # 将是第一次的请求写入记录表中
    if request.method == ‘GET‘:
        ys = ‘api验证成功‘
        return HttpResponse(ys)
    elif request.method == ‘POST‘:
    略……
九、对资产信息进行AES加密
client客户端程序utils.py:
from lib.conf.config import settings
from Crypto.Cipher import AES
def encrypt(message):
    """
    AES加密资产数据
    :param message:
    :return:
    """
    key = settings.DATA_KEY
    cipher = AES.new(key, AES.MODE_CBC, key)  # 初始化AES对象
    ba_data = bytearray(message, encoding=‘utf-8‘)  # 字符串编码成字节数组
    v1 = len(ba_data)  # 计算需要加密的数据的长度
    v2 = v1 % 16  # AES只对16的倍数的字节长度进行加密
    if v2 == 0:
        v3 = 16
    else:
        v3 = 16 - v2
    for i in range(v3):  # 需要为不是16倍数的字节数组添加字节,只至满足是16的倍数
        ba_data.append(v3)  # 添加的字节内容采用需补齐的长度值
    final_data = ba_data.decode(‘utf-8‘)  # 字节解码成字符串
    msg = cipher.encrypt(final_data)  # 对字符串进行加密,成为字节
    return msg
def decrypt(msg):
    """
    数据解密
    :param msg:
    :return:
    """
    from Crypto.Cipher import AES
    key = settings.DATA_KEY
    cipher = AES.new(key, AES.MODE_CBC, key)
    result = cipher.decrypt(msg)
    data = result[0:-result[-1]]  # 获取补齐到16位长度前真正的内容,result[-1]表示补齐的长度值
    return str(data, encoding=‘utf-8‘)  # 转换成字符串
def auth():
    """
    API验证服务器身份
    :return:
    """
    import time
    import hashlib
    # 生成动态令牌
    ctime = time.time()  # 动态时间戳
    key = "asdfasdfasdfasdf098712sdfs"  # 假定API发送过来的令牌
    new_key = ‘%s|%s‘ % (key, ctime)  # 在原有的随机字符串上加入动态时间,形成动态令牌
    print(ctime)
    # 动态令牌通过md5进行加密
    m = hashlib.md5()  # 初始化md5
    m.update(bytes(new_key, encoding=‘utf-8‘))  # 将动态令牌转换成字节,将由md5进行计算
    md5_key = m.hexdigest()  # 获得加密后的令牌
    print(md5_key)
    md5_time_key = ‘%s|%s‘ % (md5_key, ctime)  # 将生成动态令牌所需的时间一同发给API,让API进行md5进行加密,以便完成加密数据的比对
    return md5_time_key
client.py :
class Base(object):
    """
    负责往api发送数据
    """
    def post_asset(self, server_info):
        # 将数据转换成json字符串格式发送
        data = encrypt(json.dumps(server_info))    # 将字典格式的数据转换成encrypt所需的字符串格式,然后加密
        response = requests.post(
            url=settings.API,
            data = data,
            headers={‘OpenKey‘:auth(), ‘Content-Type‘:‘application/json‘}   # 
        )
        print(response.text)
server服务端程序中的api模块接收数据:
def decrypt(msg):
    """
    对AES加密数据进行解密
    :param msg: 
    :return: 
    """
    from Crypto.Cipher import AES
    key = b‘dfdsdfsasdfdsdfs‘   # 该Key值需与客户端加密所需的Key保持相同
    cipher = AES.new(key, AES.MODE_CBC, key)
    result = cipher.decrypt(
        msg)  # result = b‘\xe8\xa6\x81\xe5\x8a\xa0\xe5\xaf\x86\xe5\x8a\xa0\xe5\xaf\x86\xe5\x8a\xa0sdfsd\t\t\t\t\t\t\t\t\t‘
    data = result[0:-result[-1]]
    return str(data, encoding=‘utf-8‘)
    def asset(request):
    """
    接收客户端采集的资产信息
    :param request: 
    :return: 
    """
    if ……
    elif request.method == ‘POST‘:
        server_info = decrypt(request.body) # 对客户端发送过来的AES加密数据进行解密
        server_info = json.loads(server_info)   # 对字符串进行反序列化成字典
        print(server_info)
十、CMDB后台管理之CURD插件数据格式化
urls:
    url(r‘^curd.html$‘, views.curd), # 进入到数据展示页面
1
views:
def curd(request):
    """
    进入到curd.html页面
    :param request:
    :return:
    """
    return render(request, ‘curd.html‘)
html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css" />
</head>
<body>
<div style="width: 700px; margin: 0 auto">
    <table class="table table-bordered table-striped">
        <thead id="tbHead">
        <tr>
        </tr>
        </thead>
        <tbody id="tbBody">
</tbody>
</table>
</div>
<script src="/static/jquery-1.12.4.js"></script>
urls:
    url(r‘^curd_json.html$‘, views.curd_json)   # 通过页面加载时启动的js获得数据呈现在页面中
1
views:
def curd_json(request):
    """
    进行数据结构处理
    :param request:
    :return:
    """
    table_config = [  # 配置文件,用于前端页面数据定制显示
        {
            ‘q‘: ‘id‘,  # 用于数据库查询字段名
            ‘title‘: ‘ID‘,  # 用于前端页面中表头字段名的显示
            ‘text‘: {
                ‘tpl‘: ‘{n1}‘,  # 用于生成格式化字符串中的占位符
                ‘kwargs‘: {‘n1‘: ‘@id‘}  # 占位符中具体的id数值,用于生成链接中对单条数据的操作
            }
        },
        {
            ‘q‘: ‘hostname‘,
            ‘title‘: ‘主机名‘,
            ‘text‘: {
                ‘tpl‘: ‘{n1}-{n2}‘,
                ‘kwargs‘: {‘n1‘: ‘@hostname‘, ‘n2‘: ‘@id‘}
            }
        },
        {
            ‘q‘: ‘create_at‘,
             ‘title‘: ‘创建时间‘,
             ‘text‘: {
                 ‘tpl‘: ‘{n1}‘,
                 ‘kwargs‘: {‘n1‘: ‘@create_at‘}
             }
        },
        {
            ‘q‘: ‘asset__cabinet_num‘, ‘title‘: ‘机柜号‘,
            ‘text‘: {
                ‘tpl‘: "BJ-{n1}",
                ‘kwargs‘: {‘n1‘: ‘@asset__cabinet_num‘}
            }
        },
        {
            ‘q‘: ‘asset__business_unit__name‘,
            ‘title‘: ‘业务线名称‘,
            ‘text‘: {
                ‘tpl‘: "{n1}",
                ‘kwargs‘: {‘n1‘: ‘@asset__business_unit__name‘}
            }
        },
        # 页面显示 操作: 删除,编辑,a标签生成
        {
            ‘q‘:None,
            ‘title‘:‘操作‘,
            ‘text‘:{
                ‘tpl‘: "<a href=‘/del?nid={nid}‘>删除</a>",
                ‘kwargs‘:{‘nid‘:‘@id‘},
            }
        },
    ]
    # 组装数据库查询所需的字段
    value_list = []
    for row in table_config:
        if not row[‘q‘]:
            continue
        value_list.append(row[‘q‘])
    from datetime import datetime
    from datetime import date
    class JsonCustomEncoder(json.JSONEncoder):
        """
        json扩展:针对日期格式数据进行自定义转换成字符串处理
        """
        def default(self, value):
            if isinstance(value, datetime):
                return value.strftime(‘%Y-%m-%d %H:%M:%S‘)
            elif isinstance(value, date):
                return value.strftime(‘%Y-%m-%d‘)
            else:
                return json.JSONEncoder.default(self, value)    # 调用父类中的default方法
    server_list = models.Server.objects.values(*value_list)   # 传入列表获得字典格式数据
    ret = {
        ‘server_list‘:list(server_list),   # 将Querylist转换成列表
        ‘table_config‘:table_config,
    }
    return HttpResponse(json.dumps(ret, cls=JsonCustomEncoder))
html:
<script src="/static/jquery-1.12.4.js"></script>
<script>
    $(function () {
        {#通过ajax异步获得初始化数据#}
        initial();
    });
 // 为字符串创建format方法,用于字符串格式化
    String.prototype.format = function (args) {
        return this.replace(/\{(\w+)\}/g, function (s, i) {
            return args[i];
        });
    };
    {#页面加载时发送ajax请求#}
    function initial() {
        $.ajax({
            url: ‘/backend/curd_json.h