• python---CMDB配置管理数据库


    前戏:项目目的

    是一个运维自动化管理项目:

      为了减少人工干预,降低人员成本
      ---资产管理
      --操作管理

    避免人员直接操作服务器,使用后台去统一操作

    一:实现方式

    (一)Agent基于shell命令实现(在服务器去上安装Agent,在服务器本机定时自动去获取信息,发送到数据库,然后后台获取数据进行处理)

    注意:一般我们不会直接将数据直接传递到数据库,会将数据传递到API接口先进行处理,过滤,然后才会发送到数据库。

    注意:数据是由服务器agent主动发送至API

     实现方案:

    本地执行cmd命令。
    方法一:os.system("命令")    不可以返回数据
    方法二:subprocess模块,使用进程执行命令,可以获取到数据Popen("命令"),进程.stdout.read()<py2>或者直接getoutput("命令")<py3>
        def agent(self,cmd):
            import subprocess
            try:
                ret = subprocess.getoutput(cmd)
            except AttributeError:
                sub = subprocess.Popen(args=cmd,shell=True,stdout=subprocess.PIPE)
                sub.wait()
                ret = sub.stdout.read()
            return ret
    python实现agent

    优点:信息采集快,由服务器自己采集信息传递到API

    缺点:每台服务器都必须安装Agent


     (二)SSH方法:使用paramiko模块,通过中控机服务器统一去获取指定服务器的信息。

    paramiko模块了解

    def ssh(self,cmd):
            import paramiko
            #1.创建SSH对象
            ssh = paramiko.SSHClient()
            #2.加上这句话不用担心选yes的问题,会自动选上
            #3.用ssh连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yes
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(hostname="远程主机名",port="远程端口",username="用户名",password="密码")
            #执行命令,获取结果到标准输入出错误流中
            stdin,stdout,stderr = ssh.exec_command(cmd)
            #4.获取命令结果
            result = stdout.read()
            #5.关闭连接
            ssh.close()
    paramiko实现远程命令执行(方法一:使用用户名,密码)
        def ssh(self,cmd):
            import paramiko
            #1.创建SSH对象
            ssh = paramiko.SSHClient()
            #2.加上这句话不用担心选yes的问题,会自动选上
            #用ssh连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yes
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            #3.获取私钥
            private_key = paramiko.RSAKey.from_private_key_file("文件:其中保存了私钥,用于解密")
            #4.通过私钥去连接远程服务器(前提是自己的公钥已经在对方的authorized_keys文件中,paramiko已实现)
            ssh.connect(hostname="远程主机名",port="远程端口",username="用户名",pkey="私钥private_key")
            #5.执行命令,获取结果到标准输入出错误流中
            stdin,stdout,stderr = ssh.exec_command(cmd)
            #6.获取命令结果
            result = stdout.read()
            #7.关闭连接
            ssh.close()
            return result
    paramiko使用私钥去登录远程服务器执行命令

    优点:不需要为服务器安装agent等软件

    缺点:速度慢,适用于服务器少得时候

     (三)saltstack:使用master对slave进行操作,基于列队实现(使用广)

    # 1. 安装saltstack
    #       rpm --import https://repo.saltstack.com/yum/redhat/6/x86_64/latest/SALTSTACK-GPG-KEY.pub
    #
    #
    """
            Master: yum install salt-master
           Master准备:
                a. 配置文件,监听本机IP
                    vim /etc/salt/master
                    interface: 本机IP地址
                b. 启动master
                    /etc/init.d/salt-master start
    
    
            Slave:  yum install salt-minion
            Slave准备:
                a. 配置文件,连接那个master
                    vim /etc/salt/minion
                    master: 远程master地址
                b. 启动slave
                    /etc/init.d/salt-minion start
    
    2. 创建关系
        查看
        Master:salt-key -L
            Accepted Keys:
            Denied Keys:
            Unaccepted Keys:
                c1.com
                c2.com
                c3.com
            Rejected Keys:
        接受
        Master:salt-key -a c1.com
            Accepted Keys:
                c1.com
                c2.com
            Denied Keys:
            Unaccepted Keys:
                c3.com
            Rejected Keys:
    
    
    3. 执行命令
        master:
            salt 'c1.com' cmd.run  'ifconfig'
    
        import salt.client
        local = salt.client.LocalClient()
        result = local.cmd('c2.salt.com', 'cmd.run', ['ifconfig'])
    
    """
    saltstack安装
        def salt(self,cmd):
            import subprocess
            result = subprocess.getoutput("Salt '主机名' cmd.run '"+cmd+"'")
    
            return result
    中控机执行命令:subprocess执行salt
        def salt(self,cmd):
    
            import salt.client
            local = salt.client.LocalClient()
            result = local.cmd(self.hostname,'cmd.run',[cmd])
            return result[self.hostname]
    中控机执行命令:salt.client

    优点:快,开发成本低

    缺点:依赖saltstack

    (四)使用puppet(使用ruby写的,python不易扩展)

    优点:自动汇报

    缺点:ruby实现

     二:代码实现(客户端)

     

    bin目录(含启动文件)

    from src.script import client
    import os,sys
    
    BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASEDIR)
    
    if __name__ == "__main__":
        client()
    start.py启动文件

    conf目录(含配置文件)

    import os
    
    BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    MODE = "Agent"  #SSH Salt
    
    PLUGINS = {
        'basic':'src.plugins.basic.BasicPlugin',
        'disk':'src.plugins.disk.DiskPlugin',
        'mem': 'src.plugins.mem.MemPlugin',
        # 'nic': 'src.plugins.nic.NicPlugin',
    }
    
    # 如果采用SSH方式,则需要配置SSH的KEY和USER
    SSH_PRIVATE_KEY = "/home/auto/.ssh/id_rsa"
    SSH_USER = "root"
    SSH_PORT = 22
    
    # 用于API认证的KEY
    KEY = '299095cc-1330-11e5-b06a-a45e60bec08b'
    # 用于API认证的请求头
    AUTH_KEY_NAME = 'auth-key'
    
    ASSET_API = "http://127.0.0.1:8000/API/asset"
    
    
    # Agent模式保存服务器唯一ID的文件
    CERT_FILE_PATH = os.path.join(BASEDIR, 'conf', 'cert')
    
    TEST_MODE = True
    setting.py
    ...
    cert含有主机名信息

    lib目录(含有链接库)

    class BaseResponse(object):
        def __init__(self):
            self.status = True
            self.message = None
            self.data = None
            self.error = None
    response.py定义一种数据格式用于在客户端传递
    import json as default_json
    from .response import BaseResponse
    
    def conv(obj):
        return obj.__dict__
    
    
    class Json(object):
        @staticmethod
        def dumps(response):
            return default_json.dumps(response,default=conv)
    serialize.py序列化上面的数据,将其变为可以在网络上传递的数据

    log目录(记录日志)

    client.py定义多种类,不同的方法与API交互
    from conf import setting
    from .client import *
    
    def client():
        if setting.MODE == 'Agent':  #"Agent"  #SSH Salt
            cli = AutoAgent()
        elif setting.MODE == "SSH":
            cli = AutoSSH()
        elif setting.MODE == "Salt":
            cli = AutoSalt()
        else:
            raise Exception("配置信息出错")
        cli.process()
    script.py脚本,根据配置文件去选择实例化那种类,执行process方法获取数据

    二级目录:plugins目录

    from conf import setting
    from lib.response import BaseResponse
    
    
    def pack(hostname=None):
        response = {}
    
        for k,v in setting.PLUGINS.items():
            file_name,cls_name = v.rsplit('.',maxsplit=1)
            pk = __import__(file_name,fromlist=True)
            if hasattr(pk,cls_name):
                obj = getattr(pk,cls_name)()
                response[k] = obj.execute(hostname)
    
        return response
    __init__.py定义pack方法去依次获取数据
    from conf import setting
    
    class BasePlugin(object):
        def __init__(self):
            mode_list = ['Agent','Salt','SSH']
            self.mode = setting.MODE
            if self.mode not in mode_list:
                raise Exception("请选择正确的管理模式")
    
        def shell_cmd(self,cmd):
            if self.mode == "SSH":
                ret = self.ssh(cmd)
            elif self.mode == "Salt":
                ret = self.salt(cmd)
            else:
                ret = self.agent(cmd)
            return ret
    
        def execute(self,hostname=None):
            self.hostname = hostname
            #windows:systeminfo详细信息 ver版本号 linux:cat /etc/issue | grep Linux
            sys_ver = self.shell_cmd("ver")
            pat = "command not found"
            if pat in sys_ver: #是linux...还是继续判断吧,应该不需要了
                sys_ver = self.shell_cmd("head -n 1 /etc/issue")
    
            if "Linux" in sys_ver:
                return self.linux()
            elif "Windows" in sys_ver:
                return self.windows()
            else:
                raise Exception("只支持linux和windows平台")
    
        def linux(self):
            raise Exception("请重载linux函数")
    
        def windows(self):
            raise Exception("请实现windows方法")
    
        def write(self,output):
            import os
            with open(os.path.join(setting.BASEDIR,'file',"test.txt"),"w") as fp:
                fp.write(output)
    
    
        def agent(self,cmd):
            import subprocess
            try:
                ret = subprocess.getoutput(cmd)
            except AttributeError:
                sub = subprocess.Popen(args=cmd,shell=True,stdout=subprocess.PIPE)
                sub.wait()
                ret = sub.stdout.read()
            return ret
    
        def salt(self,cmd):
            import subprocess
            result = subprocess.getoutput("Salt '主机名' cmd.run '"+cmd+"'")
    
            return result
            # import salt.client
            # local = salt.client.LocalClient()
            # result = local.cmd(self.hostname,'cmd.run',[cmd])
            # return result[self.hostname]
    
        def ssh(self,cmd):
            import paramiko
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            private_key = paramiko.RSAKey.from_private_key_file("文件:其中保存了私钥,用于解密")
            ssh.connect(hostname="远程主机名",port="远程端口",username="用户名",pkey="私钥private_key")
            stdin,stdout,stderr = ssh.exec_command(cmd)
            result = stdout.read()
            ssh.close()
            return result
    base.py定义基类,实现部分方法
    from .base import BasePlugin
    import re
    from conf import setting
    import traceback
    from lib.response import BaseResponse
    
    class BasicPlugin(BasePlugin):
        def __init__(self):
            super(BasicPlugin, self).__init__()
            self.Basic_data = {}  # 存放我们获取的数据
    
        def windows(self):
            response = BaseResponse()
            try:
                # 获取主机名
                output = self.shell_cmd("hostname")
                self.Basic_data['hostname'] = output
    
                output = self.shell_cmd("ver")
                #获取操作平台
                ret = re.search("(.*)s*[",output)
                if ret:
                    self.Basic_data['os_platform'] = ret.group(1)
    
                # 获取系统版本
                ret = re.search("[(.*)]",output)
                if ret:
                    self.Basic_data['os_version'] = ret.group(1)
    
                response.data = self.Basic_data
            except Exception as e:
                msg = "%s window memory plugins error: %s"
                response.status = False
                response.error = msg % (self.hostname, traceback.format_exc())  # traceback.format_exc()返回前面错误的信息
            return response
    
        def linux(self):
            # 获取返回的字符串
            output = self.shell_cmd("查看硬盘")
            # 进行正则匹配,放入Mem_data中
            return self.Basic_data
    basic.py获取主板信息
    from .base import BasePlugin
    from conf import setting
    import re,traceback
    from lib.response import BaseResponse
    
    class DiskPlugin(BasePlugin):
        def __init__(self):
            super(DiskPlugin, self).__init__()
            self.Disk_data = {'Slot': 'slot', 'Raw Size': 'capacity', 'Inquiry': 'model', 'PD Type': 'pd_type'}
    
        def windows(self):
            response = BaseResponse()
            try:
                if setting.TEST_MODE == True:
                    import os
                    with open(os.path.join(setting.BASEDIR,'file','disk.out'),"r") as fp:
                        output = fp.read()
                else:
                    # 获取返回的字符串
                    output = self.shell_cmd("wmic logicaldisk")
    
                response.data = self.parse(output)
            except Exception as e:
                msg = "%s window disk plugin error: %s"
                response.status = False
                response.error = msg % (self.hostname,traceback.format_exc())   #traceback.format_exc()返回前面错误的信息
            return response
    
        def linux(self):
            # 获取返回的字符串
            output = self.shell_cmd("查看硬盘")
            # 进行正则匹配,放入Mem_data中
            return self.Disk_data
    
        def parse(self,content):
            response = {}
            result = [] #模拟多台   使用4个
    分割
            for row_line in content.split("
    
    
    
    "):
                result.append(row_line)
            for item in result:
                temp_dict = {}
                for row in item.split('
    '):
                    if not row.strip():
                        continue
                    if len(row.split(":")) != 2:
                        continue
                    key,val = row.split(":")
                    name = self.patter_match(key)
                    if name:
                        if key == 'Raw Size':
                            val = re.search("(d+.d+)",val.strip())
                            if not val:
                                val = 0
                            else:
                                val = val.group(1)
                        temp_dict[name] = val
                if temp_dict:
                    response[temp_dict['slot']] = temp_dict
            return response
    
        def patter_match(self,key):
            for k,v in self.Disk_data.items():
                if key.startswith(k):
                    return v
            return False
    disk.py获取硬盘信息
    from .base import BasePlugin
    from conf import setting
    import traceback
    from lib.response import BaseResponse
    
    class MemPlugin(BasePlugin):
        def __init__(self):
            super(MemPlugin, self).__init__()
            self.Mem_data = {
                'Size': 'capacity',
                'Locator': 'slot',
                'Type': 'model',
                'Speed': 'speed',
                'Manufacturer': 'manufacturer',
                'Serial Number': 'sn',
            }
    
        def windows(self):
            response = BaseResponse()
            try:
                if setting.TEST_MODE == True:
                    import os
                    with open(os.path.join(setting.BASEDIR, 'file', 'memory.out'), "r") as fp:
                        output = fp.read()
                else:
                    # 获取返回的字符串
                    output = self.shell_cmd("wmic memorychip")
    
                response.data = self.parse(output)
            except Exception as e:
                msg = "%s window memory plugins error: %s"
                response.status = False
                response.error = msg % (self.hostname, traceback.format_exc())  # traceback.format_exc()返回前面错误的信息
            return response
    
        def linux(self):
            # 获取返回的字符串
            output = self.shell_cmd("查看内存")
            # 进行正则匹配,放入Mem_data中
            return self.Mem_data
    
    
        def parse(self,content):
            response = {}
            result = [] #模拟多台   使用4个
    分割
            for row_line in content.split("Memory Device"):
                result.append(row_line)
            for item in result:
                for row in item.split('
    	'):
                    if not row.strip():
                        continue
                    if len(row.split(":")) != 2:
                        continue
                    key,val = row.split(":")
                    name = self.patter_match(key)
                    if name:
                        response[name] = val
            return response
    
        def patter_match(self,key):
            for k,v in self.Mem_data.items():
                if key.startswith(k):
                    return v
            return False
    mem.py获取内存信息

    file文件目录(含有测试的文件,文件包含各种命令下的数据)

    Enclosure Device ID: 32
    Slot Number: 0
    Drive's postion: DiskGroup: 0, Span: 0, Arm: 0
    Enclosure position: 0
    Device Id: 0
    WWN: 5000C5007272C288
    Sequence Number: 2
    Media Error Count: 0
    Other Error Count: 0
    Predictive Failure Count: 0
    Last Predictive Failure Event Seq Number: 0
    PD Type: SAS
    Raw Size: 279.396 GB [0x22ecb25c Sectors]
    Non Coerced Size: 278.896 GB [0x22dcb25c Sectors]
    Coerced Size: 278.875 GB [0x22dc0000 Sectors]
    Firmware state: Online, Spun Up
    Device Firmware Level: LS08
    Shield Counter: 0
    Successful diagnostics completion on :  N/A
    SAS Address(0): 0x5000c5007272c289
    SAS Address(1): 0x0
    Connected Port Number: 0(path0) 
    Inquiry Data: SEAGATE ST300MM0006     LS08S0K2B5NV            
    FDE Enable: Disable
    Secured: Unsecured
    Locked: Unlocked
    Needs EKM Attention: No
    Foreign State: None 
    Device Speed: 6.0Gb/s 
    Link Speed: 6.0Gb/s 
    Media Type: Hard Disk Device
    Drive Temperature :29C (84.20 F)
    PI Eligibility:  No 
    Drive is formatted for PI information:  No
    PI: No PI
    Drive's write cache : Disabled
    Port-0 :
    Port status: Active
    Port's Linkspeed: 6.0Gb/s 
    Port-1 :
    Port status: Active
    Port's Linkspeed: Unknown 
    Drive has flagged a S.M.A.R.T alert : No
    
    
    
    Enclosure Device ID: 32
    Slot Number: 1
    Drive's postion: DiskGroup: 0, Span: 0, Arm: 1
    Enclosure position: 0
    Device Id: 1
    WWN: 5000C5007272DE74
    Sequence Number: 2
    Media Error Count: 0
    Other Error Count: 0
    Predictive Failure Count: 0
    Last Predictive Failure Event Seq Number: 0
    PD Type: SAS
    Raw Size: 279.396 GB [0x22ecb25c Sectors]
    Non Coerced Size: 278.896 GB [0x22dcb25c Sectors]
    Coerced Size: 278.875 GB [0x22dc0000 Sectors]
    Firmware state: Online, Spun Up
    Device Firmware Level: LS08
    Shield Counter: 0
    Successful diagnostics completion on :  N/A
    SAS Address(0): 0x5000c5007272de75
    SAS Address(1): 0x0
    Connected Port Number: 0(path0) 
    Inquiry Data: SEAGATE ST300MM0006     LS08S0K2B5AH            
    FDE Enable: Disable
    Secured: Unsecured
    Locked: Unlocked
    Needs EKM Attention: No
    Foreign State: None 
    Device Speed: 6.0Gb/s 
    Link Speed: 6.0Gb/s 
    Media Type: Hard Disk Device
    Drive Temperature :29C (84.20 F)
    PI Eligibility:  No 
    Drive is formatted for PI information:  No
    PI: No PI
    Drive's write cache : Disabled
    Port-0 :
    Port status: Active
    Port's Linkspeed: 6.0Gb/s 
    Port-1 :
    Port status: Active
    Port's Linkspeed: Unknown 
    Drive has flagged a S.M.A.R.T alert : No
    
    
    
    Enclosure Device ID: 32
    Slot Number: 2
    Drive's postion: DiskGroup: 1, Span: 0, Arm: 0
    Enclosure position: 0
    Device Id: 2
    WWN: 50025388A075B731
    Sequence Number: 2
    Media Error Count: 0
    Other Error Count: 1158
    Predictive Failure Count: 0
    Last Predictive Failure Event Seq Number: 0
    PD Type: SATA
    Raw Size: 476.939 GB [0x3b9e12b0 Sectors]
    Non Coerced Size: 476.439 GB [0x3b8e12b0 Sectors]
    Coerced Size: 476.375 GB [0x3b8c0000 Sectors]
    Firmware state: Online, Spun Up
    Device Firmware Level: 1B6Q
    Shield Counter: 0
    Successful diagnostics completion on :  N/A
    SAS Address(0): 0x500056b37789abee
    Connected Port Number: 0(path0) 
    Inquiry Data: S1SZNSAFA01085L     Samsung SSD 850 PRO 512GB               EXM01B6Q
    FDE Enable: Disable
    Secured: Unsecured
    Locked: Unlocked
    Needs EKM Attention: No
    Foreign State: None 
    Device Speed: 6.0Gb/s 
    Link Speed: 6.0Gb/s 
    Media Type: Solid State Device
    Drive:  Not Certified
    Drive Temperature :25C (77.00 F)
    PI Eligibility:  No 
    Drive is formatted for PI information:  No
    PI: No PI
    Drive's write cache : Disabled
    Drive's NCQ setting : Disabled
    Port-0 :
    Port status: Active
    Port's Linkspeed: 6.0Gb/s 
    Drive has flagged a S.M.A.R.T alert : No
    
    
    
    Enclosure Device ID: 32
    Slot Number: 3
    Drive's postion: DiskGroup: 1, Span: 0, Arm: 1
    Enclosure position: 0
    Device Id: 3
    WWN: 50025385A02A074F
    Sequence Number: 2
    Media Error Count: 0
    Other Error Count: 0
    Predictive Failure Count: 0
    Last Predictive Failure Event Seq Number: 0
    PD Type: SATA
    Raw Size: 476.939 GB [0x3b9e12b0 Sectors]
    Non Coerced Size: 476.439 GB [0x3b8e12b0 Sectors]
    Coerced Size: 476.375 GB [0x3b8c0000 Sectors]
    Firmware state: Online, Spun Up
    Device Firmware Level: 6B0Q
    Shield Counter: 0
    Successful diagnostics completion on :  N/A
    SAS Address(0): 0x500056b37789abef
    Connected Port Number: 0(path0) 
    Inquiry Data: S1AXNSAF912433K     Samsung SSD 840 PRO Series              DXM06B0Q
    FDE Enable: Disable
    Secured: Unsecured
    Locked: Unlocked
    Needs EKM Attention: No
    Foreign State: None 
    Device Speed: 6.0Gb/s 
    Link Speed: 6.0Gb/s 
    Media Type: Solid State Device
    Drive:  Not Certified
    Drive Temperature :28C (82.40 F)
    PI Eligibility:  No 
    Drive is formatted for PI information:  No
    PI: No PI
    Drive's write cache : Disabled
    Drive's NCQ setting : Disabled
    Port-0 :
    Port status: Active
    Port's Linkspeed: 6.0Gb/s 
    Drive has flagged a S.M.A.R.T alert : No
    
    
    
    Enclosure Device ID: 32
    Slot Number: 4
    Drive's postion: DiskGroup: 1, Span: 1, Arm: 0
    Enclosure position: 0
    Device Id: 4
    WWN: 50025385A01FD838
    Sequence Number: 2
    Media Error Count: 0
    Other Error Count: 0
    Predictive Failure Count: 0
    Last Predictive Failure Event Seq Number: 0
    PD Type: SATA
    Raw Size: 476.939 GB [0x3b9e12b0 Sectors]
    Non Coerced Size: 476.439 GB [0x3b8e12b0 Sectors]
    Coerced Size: 476.375 GB [0x3b8c0000 Sectors]
    Firmware state: Online, Spun Up
    Device Firmware Level: 5B0Q
    Shield Counter: 0
    Successful diagnostics completion on :  N/A
    SAS Address(0): 0x500056b37789abf0
    Connected Port Number: 0(path0) 
    Inquiry Data: S1AXNSAF303909M     Samsung SSD 840 PRO Series              DXM05B0Q
    FDE Enable: Disable
    Secured: Unsecured
    Locked: Unlocked
    Needs EKM Attention: No
    Foreign State: None 
    Device Speed: 6.0Gb/s 
    Link Speed: 6.0Gb/s 
    Media Type: Solid State Device
    Drive:  Not Certified
    Drive Temperature :27C (80.60 F)
    PI Eligibility:  No 
    Drive is formatted for PI information:  No
    PI: No PI
    Drive's write cache : Disabled
    Drive's NCQ setting : Disabled
    Port-0 :
    Port status: Active
    Port's Linkspeed: 6.0Gb/s 
    Drive has flagged a S.M.A.R.T alert : No
    
    
    
    Enclosure Device ID: 32
    Slot Number: 5
    Drive's postion: DiskGroup: 1, Span: 1, Arm: 1
    Enclosure position: 0
    Device Id: 5
    WWN: 50025385A02AB5C9
    Sequence Number: 2
    Media Error Count: 0
    Other Error Count: 0
    Predictive Failure Count: 0
    Last Predictive Failure Event Seq Number: 0
    PD Type: SATA
    Raw Size: 476.939 GB [0x3b9e12b0 Sectors]
    Non Coerced Size: 476.439 GB [0x3b8e12b0 Sectors]
    Coerced Size: 476.375 GB [0x3b8c0000 Sectors]
    Firmware state: Online, Spun Up
    Device Firmware Level: 6B0Q
    Shield Counter: 0
    Successful diagnostics completion on :  N/A
    SAS Address(0): 0x500056b37789abf1
    Connected Port Number: 0(path0) 
    Inquiry Data: S1AXNSAFB00549A     Samsung SSD 840 PRO Series              DXM06B0Q
    FDE Enable: Disable
    Secured: Unsecured
    Locked: Unlocked
    Needs EKM Attention: No
    Foreign State: None 
    Device Speed: 6.0Gb/s 
    Link Speed: 6.0Gb/s 
    Media Type: Solid State Device
    Drive:  Not Certified
    Drive Temperature :28C (82.40 F)
    PI Eligibility:  No 
    Drive is formatted for PI information:  No
    PI: No PI
    Drive's write cache : Disabled
    Drive's NCQ setting : Disabled
    Port-0 :
    Port status: Active
    Port's Linkspeed: 6.0Gb/s 
    Drive has flagged a S.M.A.R.T alert : No
    
    
    
    
    Exit Code: 0x00
    disk.out
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: 1024 MB
        Form Factor: DIMM
        Set: None
        Locator: DIMM #0
        Bank Locator: BANK #0
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: No Module Installed
        Form Factor: DIMM
        Set: None
        Locator: DIMM #1
        Bank Locator: BANK #1
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: No Module Installed
        Form Factor: DIMM
        Set: None
        Locator: DIMM #2
        Bank Locator: BANK #2
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: No Module Installed
        Form Factor: DIMM
        Set: None
        Locator: DIMM #3
        Bank Locator: BANK #3
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: No Module Installed
        Form Factor: DIMM
        Set: None
        Locator: DIMM #4
        Bank Locator: BANK #4
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: No Module Installed
        Form Factor: DIMM
        Set: None
        Locator: DIMM #5
        Bank Locator: BANK #5
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: No Module Installed
        Form Factor: DIMM
        Set: None
        Locator: DIMM #6
        Bank Locator: BANK #6
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    
    Memory Device
        Total Width: 32 bits
        Data Width: 32 bits
        Size: No Module Installed
        Form Factor: DIMM
        Set: None
        Locator: DIMM #7
        Bank Locator: BANK #7
        Type: DRAM
        Type Detail: EDO
        Speed: 667 MHz
        Manufacturer: Not Specified
        Serial Number: Not Specified
        Asset Tag: Not Specified
        Part Number: Not Specified
        Rank: Unknown
    memory.out

    .....

     三:代码实现(API接口Django实现)

    API目录

    from django.views import View
    from django.views.decorators.csrf import csrf_exempt
    from django.utils.decorators import method_decorator
    from django.http import HttpResponse,JsonResponse
    from repository import models
    from API import config
    from utils import auth
    import importlib
    import json
    
    
    class AssetView(View):
        @method_decorator(csrf_exempt)
        def dispatch(self, request, *args, **kwargs):
            return super(AssetView, self).dispatch(request, *args, **kwargs)
    
        @method_decorator(auth.api_auth)
        def get(self,request,*args,**kwargs):
            pass
    
        @method_decorator(auth.api_auth)
        def post(self,request,*args,**kwargs):
            '''
            获取传递的数据,进行添加,修改
            :param request:
            :param args:
            :param kwargs:
            :return:1000成功,1001,接口授权失败,1002数据库中资产不存在
            '''
            #获取所有信息
            # print(request.body.decode('utf-8')) #带转义符的字符串
            server_info = json.loads(request.body.decode("utf-8"))
            # print(server_info)  #字符串
            server_info = json.loads(server_info)
            #获取主机名
            hostname = server_info['basic']['data']['hostname']
            ret = {"code":1000,"message":'[%s]更新完成'%hostname}
    
            #根据主机名去数据库中获取相关信息
            server_obj = models.Server.objects.filter(hostname=hostname).select_related('asset').first()
            if not server_obj:
                ret['code'] = 1002
                ret['message'] = '[%s]资产不存在'
                return JsonResponse(ret)
    
            for k,v in config.PLUGINS_DICT.items():
                module_path,cls_name = v.rsplit('.',1)
                cls = getattr(importlib.import_module(module_path),cls_name)
                response = cls.process(server_obj,server_info,None)
                if not response.status:
                    ret['code'] = 1003
                    ret['message'] = "[%s]资产更新异常" % hostname
                if hasattr(cls,'update_last_time'):
                    cls.update_last_time(server_obj,None)
    
            return JsonResponse(ret)
    views.py实现数据处理MBV实现获取数据和发送数据

    二级目录service

    from repository import models
    import traceback,datetime
    from utils.response import BaseResponse
    from utils.agorithm import *
    
    class HandleBasic(object):
        @staticmethod
        def process(server_obj,server_info,user_obj):
            response = BaseResponse()
    
            try:
                log_list = []
                main_board = server_info['basic']['data']
                if main_board['os_platform'] != server_obj.os_platform:
                    log_list.append("系统由%s变更为%s"%(server_obj.os_platform,main_board['os_platform']))
                    server_obj.os_platform = main_board['os_platform']
    
                if main_board['os_version'] != server_obj.os_version:
                    log_list.append("系统由%s变更为%s"%(server_obj.os_version,main_board['os_version']))
                    server_obj.os_version = main_board['os_version']
    
                server_obj.save()
                if log_list:
                    models.AssetRecord.objects.create(asset_obj=server_obj.asset,creator=user_obj,content=';'.join(log_list))
    
            except Exception as e:
                response.status = False
                models.ErrorLog.objects.create(asset_obj=server_obj.asset,title="basic-run",
                                               content=traceback.format_exc())
    
            return response
    
        @staticmethod
        def update_last_time(server_obj,user_obj):
            response = BaseResponse()
            try:
                current_date = datetime.date.today()
                server_obj.asset.latest_date = current_date
                server_obj.asset.save()
                models.AssetRecord.objects.create(asset_obj=server_obj.asset,creator=user_obj,content="资产汇报")
            except Exception as e:
                response.status = False
                models.ErrorLog.objects.create(asset_obj=server_obj.asset,title="basic-run",
                                               content=traceback.format_exc())
    
            return response
    
    class HandleDisk(object):
        @staticmethod
        def process(server_obj,server_info,user_obj):
            response = BaseResponse()
            try:
                disk_info = server_info['disk']
                if not disk_info['status']:
                    response.status = False
                    models.ErrorLog.objects.create(
                        asset_obj=server_obj.asset,
                        title="disk-plugins",
                        content=disk_info['error']
                    )
                    return response
                client_disk_dict = disk_info['data']
    
                #获取服务器下的所有硬盘设备
                disk_obj_list = models.Disk.objects.filter(server_obj=server_obj)
                #获取插槽
                disk_slots = map(lambda x:x, (item.slot for item in disk_obj_list))
    
                update_list = get_intersection(set(client_disk_dict.keys()),set(disk_slots))
                add_list = get_exclude(client_disk_dict.keys(),update_list)
                del_list = get_exclude(disk_slots,update_list)
    
                HandleDisk._add_disk(add_list,client_disk_dict,server_obj,user_obj)
                HandleDisk._update_disk(update_list,disk_obj_list,client_disk_dict,server_obj,user_obj)
                HandleDisk._del_disk(del_list,disk_obj_list,server_obj,user_obj)
    
            except Exception as e:
                response.status = False
                models.ErrorLog.objects.create(
                    asset_obj=server_obj.asset,
                    title="disk-plugins",
                    content=traceback.format_exc()
                )
    
            return response
    
        @staticmethod
        def _add_disk(add_list,client_disk_dict,server_obj,user_obj):
            for item in add_list:
                cur_disk_dict = client_disk_dict[item]
                log_str = "[新增硬盘]插槽位{slot};容量为{capacity};硬盘类型为{pd_type};型号为{model}".format(**cur_disk_dict)
                cur_disk_dict['server_obj'] = server_obj
                models.Disk.objects.create(**cur_disk_dict)
                models.AssetRecord.objects.create(
                    asset_obj=server_obj.asset,creator=user_obj,content=log_str
                )
    
        @staticmethod
        def _del_disk(del_lsit,disk_objs,server_obj,user_obj):
            for item in disk_objs:
                if item.slot in del_lsit:
                    log_str = "[移除硬盘]插槽位{slot};容量为{capacity};硬盘类型为{pd_type};型号为{model}".format(item.__dict__)
                    item.delete()
                    models.AssetRecord.objects.create(
                        asset_obj = server_obj.asset,
                        creator = user_obj,
                        content = log_str
                    )
    
        @staticmethod
        def _update_disk(update_list,disk_objs,client_disk_dict,server_obj,user_obj):
            for item in disk_objs:
                if item.slot in update_list:
                    log_list = []
    
                    new_model = client_disk_dict[item.slot]['model']
                    if item.model != new_model:
                        log_list.append("[更新硬盘]插槽为%s:型号由%s变更为%s"%(item.slot,item.model,new_model))
                        item.model = new_model
    
                    new_capacity = client_disk_dict[item.slot]['capacity']
                    new_capacity = float(new_capacity)
    
                    if new_capacity != item.capacity:
                        log_list.append("[更新硬盘]插槽为%s:容量由%sGB变更为%sGB"%(item.slot,item.capacity,new_capacity))
                        item.capacity = new_capacity
    
                    new_pd_type = client_disk_dict[item.slot]['pd_type']
                    if item.pd_type != new_pd_type:
                        log_list.append("[更新硬盘]插槽为%s:类型由%s变更为%s"%(item.slot,item.pd_type,new_pd_type))
                        item.pd_type = new_pd_type
    
                    item.save()
                    if log_list:
                        models.AssetRecord.objects.create(
                            asset=server_obj.asset,
                            creator=user_obj,
                            content=';'.join(log_list)
                        )
    asset.py资产管理

    utils工具目录

    import hashlib,time,redis
    from CMDBAPI import settings
    from django.http import JsonResponse
    
    def api_auth_method(request):
        auth_key = settings.KEY
        auth_name = settings.AUTH_KEY_NAME
        auth_data = request.META['HTTP_'+auth_name.upper()]
    
        auth_val,auth_time = str(auth_data).split('|')
        #1:时间上的验证
        if float(auth_time) + 10 < time.time():
            return False
        #2:数据是否过期,先从redis获取数据{auth_val:auth_time}
    
        rd = redis.Redis(host="localhost",port=6379)
    
        r_time = rd.get(auth_val)
        if r_time:
            return False
    
        #3:验证数据是否被修改
        ha = hashlib.md5(auth_key.encode("utf-8"))
        ha.update(("%s|%s" % (auth_key, auth_time)).encode("utf-8"))
        encryption = ha.hexdigest()
        if encryption != auth_val:
            return False
    
        rd.set(auth_val,auth_time)
    
        return True
    
    
    def api_auth(func):
        def inner(request,*args,**kwargs):
            if not api_auth_method(request):
                return JsonResponse({"code":1001,"message":"API授权失败"},json_dumps_params={'ensure_ascii': False})
            return func(request,*args,**kwargs)
        return inner
    auth.py权限认证,对传递的数据进行过滤,其中使用redis
    def get_intersection(*args):
        '''
        获取集合的并集
        :param args:  set集合
        :return:  并集列表
        '''
        base = args[0]
        result = base.intersection(*args)
        return list(result)
    
    def get_exclude(total,part):
        '''
        获取差集
        :param total:
        :param part:
        :return:
        '''
        result = []
        for item in total:
            if item in part:
                pass
            else:
                result.append(item)
        return result
    agorithm.py处理数据,获取差并集来代表数据更新删除,添加

    四:数据库实现

    class UserProfile(models.Model):
        '''
        用户信息
        '''
        name = models.CharField("姓名",max_length=32)
        email = models.EmailField("邮箱")
        phone = models.CharField("座机",max_length=32)
        mobile = models.CharField("手机",max_length=32)
    
        class Meta:
            verbose_name_plural = "用户表"
    
        def __str__(self):
            return self.name
    
    class AdminInfo(models.Model):
        '''
        用户登录管理信息
        与用户信息表时一对一关系
        '''
        user_info = models.OneToOneField("UserProfile")
    
        username = models.CharField("用户名",max_length=64)
        password = models.CharField("密码",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")  #多个人,所以按组来分配
        manager = models.ForeignKey("UserGroup",verbose_name="系统管理员",related_name="m")
    
        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):
        '''
        资产标签:以前是做什么的,web服务器....,一个资产可以有多个标签,一个标签可以有端个资产
        '''
        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=32,null=True,blank=True)
        cabinet_order = models.CharField("机柜中的序号",max_length=32,null=True,blank=True)
    
        idc = models.ForeignKey("IDC",verbose_name="IDC机房",null=True,blank=True)
        business_unit = models.ForeignKey("BusinessUnit",verbose_name="属于的业务线",null=True,blank=True)
    
        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')
    
        hostname = models.CharField(max_length=128,unique=True)
        sn = models.CharField('SN号',max_length=64,db_index=True)
        manafacturer = models.CharField("制造商",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)
    
        os_platform = models.CharField("系统",max_length=32,null=True,blank=True)
        os_version = models.CharField("系统版本",max_length=32,null=True,blank=True)
    
        cpu_count = models.IntegerField("CPU个数",null=True,blank=True)
        cpu_physical = 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")
        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')
    
        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)
        idaddrs = models.CharField("IP地址",max_length=256)
        up = models.BooleanField(default=False)
        server_obj = models.ForeignKey("Server",related_name="nic")
    
        class Meta:
            verbose_name_plural = "网卡表"
    
        def __str__(self):
            return self.name
    
    class Memory(models.Model):
        '''
        内存信息表
        '''
        slot = models.CharField("插槽位",max_length=32)
        manafacturer = 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")
    
        class Meta:
            verbose_name_plural = "内存表"
    
        def __str__(self):
            return self.slot
    
    class AssetRecord(models.Model):
        '''
        资产变更记录
        '''
        asset_obj = models.ForeignKey("Asset",related_name='ar')
        content = models.TextField(null=True)
        creator = models.ForeignKey("UserProfile",null=True,blank=True) #creator为空,代表是资产汇报的数据
        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):
        '''
        错误日志
        '''
        asset_obj = models.ForeignKey("Asset",null=True,blank=True)
        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
    数据库表结构

     五:后台管理(实现动态编辑数据)

    from django.shortcuts import render,HttpResponse
    from django.views.decorators.csrf import csrf_exempt
    from django.utils.decorators import method_decorator
    from django.db import transaction
    from django.views import View
    from repository import models
    import json,traceback
    # Create your views here.
    
    class Assert(View):
        def get(self,request,*args,**kwargs):
    
            return render(request,"asset.html")
    
    class AssertJson(View):
        @method_decorator(csrf_exempt)
        def dispatch(self, request, *args, **kwargs):
            return super(AssertJson, self).dispatch(request,*args,**kwargs)
    
        def get(self,request,*args,**kwargs):
            config_list = [
                {
                    'field': None,
                    'display': True,
                    'title': "选项",
                    'text': {'content': "<input type='checkbox'/>", 'kwargs': {}},
                    'attrs': {}
                },
                {
                    'field': "id",
                    'display': False,
                    'title': "ID",
                    'text': {},
                    'attrs': {}
                },
                {
                    'field':"cabinet_num",
                    'display':True,
                    'title':"机柜号",
                    'text': {"content":"{n}","kwargs":{'n':"@cabinet_num"}},
                    'attrs': {'name':'cabinet_num','origin':'@cabinet_num','edit-able': True, 'edit-type': 'input'}
                },
                {
                    'field': "cabinet_order",
                    'display': True,
                    'title': "机柜位置",
                    'text':{"content":"{n}","kwargs":{'n':"@cabinet_order"}},
                    'attrs': {'name':'cabinet_order','origin':'@cabinet_order','edit-able': True, 'edit-type': 'input'}
                },
                {
                    'field': "device_type_id",
                    'display': True,
                    'title': "资产类型",
                    'text': {"content": "{n}", "kwargs": {'n': "@@device_type_choices"}},
                    'attrs': {'name':'device_type_id','origin':'@device_type_id','edit-able': True, 'edit-type': 'select','global-name':'device_type_choices'}
                },
                {
                    'field': "device_status_id",
                    'display': True,
                    'title': "设备状态",
                    'text': {"content": "{n}", "kwargs": {'n': "@@device_status_choices"}},
                    'attrs': {'name':'device_status_id','origin':'@device_status_id','edit-able': True, 'edit-type': 'select','global-name':'device_status_choices'}
                },
                {
                    'field': "idc__id",
                    'display': False,
                    'title': "IDC",
                    'text': {},
                    'attrs': {}
                },
                {
                    'field': "idc__name",
                    'display': True,
                    'title': "IDC机房",
                    'text': {"content": "{n}", "kwargs": {'n': "@idc__name"}},
                    'attrs': {'name': 'idc_id', 'origin': '@idc__id', 'edit-able': True,'edit-type': 'select', 'global-name': 'idc_choices'}
                },
                {
                    'field': None,
                    'display': True,
                    'title': "操作",
                    "text":{"content":"<a href='/asset.html/{m}'>{n}</a>","kwargs":{'n':"点击查看","m":"@id"}},
                    'attrs': {}
                },
            ]
    
            #获取数据库中的数据
            req_list = []
            for item in config_list:
                if item['field']:
                    req_list.append(item['field'])
            content = models.Asset.objects.all().values(*req_list)
            content = list(content)
    
            #获取类型
            global_data = {
                'device_type_choices':models.Asset.device_type_choices,
                'device_status_choices':models.Asset.device_status_choices,
                'idc_choices':list(models.IDC.objects.values_list('id','name')),
            }
    
            response = {
                'table_config':config_list,
                'content':content,
                'global_data':global_data,
            }
            return HttpResponse(json.dumps(response))
    
        def put(self,request,*args,**kwargs):
            ret = {
                'status':True,
                'message':None,
            }
            data = json.loads(request.body.decode("utf-8"))
            try:
                with transaction.atomic():
                    AssetObj = models.Asset.objects
                    for item in data:
                        AssetObj.filter(id=item['id']).update(**item)
            except Exception as e:
                ret['status'] = False
                ret['message'] = traceback.format_exc()
            return HttpResponse(json.dumps(ret))
    Django后台View视图代码:注意配置数据的格式
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/statics/plugins/bootstrap-3.3.7-dist/css/bootstrap.min.css">
    </head>
    <body>
        <div style=" 1000px;margin: 0 auto;">
            <h1>资源列表</h1>
            <div class="btn-group" role="group">
                <button id="idCheckAll" type="button" class="btn btn-default">全选</button>
                <button id="idReverseAll" type="button" class="btn btn-default">反选</button>
                <button id="idCancelAll" type="button" class="btn btn-default">取消</button>
                <button id="idEditMode" type="button" class="btn btn-default">进入编辑模式</button>
                <button type="button" class="btn btn-default">批量删除</button>
                <button id="idSave" type="button" class="btn btn-default">保存</button>
                <button type="button" class="btn btn-default">添加</button>
            </div>
            <table class="table table-bordered">
                <thead id="table_th"></thead>
                <tbody id="table_tb"></tbody>
            </table>
        </div>
    
    </body>
    </html>
    
    <script src="/statics/js/jquery.js"></script>
    <script src="/statics/js/nbplugin.js"></script>
    <script>
        $(function(){
            $.NB("/Backend/asset-join.html");
        })
    </script>
    Html前端代码
    (function(){
        var requestUrl = null;
    
        /*自定义字符串格式化函数*/
        String.prototype.format= function(kwargs){
            var ret=this.replace(/{(w+)}/g,function(g,g1){
                return kwargs[g1];
            })
            return ret
        }
    
        function bindSave(){
            $("#idSave").click(function(){
                var postList = [];
                var flag = false;    //判断是否有数据被修改
                //先找到修改过的行
                $("#table_tb").find("tr[has-edit='true']").each(function(){
                   var temp = {};
                   var id = $(this).attr("row-id");
                   $(this).find("td[edit-able='true']").each(function(){
                       var origin = $(this).attr("origin");
                       var newVal = $(this).attr("new-val");
                       if(newVal && newVal != origin){
                           var name = $(this).attr("name");
                           temp[name] = newVal;
                           flag = true
                       }
                   });
    
                   //如果被修改过,添加到postList中
                   if(flag){
                       temp['id'] = id;
                       postList.push(temp);
                   }
    
                });
    
                if(flag){
                    $.ajax({
                        'url':requestUrl,
                        'type':'PUT',   //put表示更新数据
                        'data':JSON.stringify(postList),
                        'dataType':"json",
                        success:function(response){
                            if(response.status){
                                alert("更新成功");
                                init();
                            }else{
                                alert("更新失败:"+response.message);
                            }
                        }
                    })
                }
            })
        }
    
        function bindReverse(){
            $("#idReverseAll").click(function(){
                var editing = $("#idEditMode").hasClass("btn-warning");
                $("#table_tb").find(":checkbox").each(function(){
                    if(editing){
                        //若是点击了编辑模式,则是需要进入或退出编辑模式
                        if($(this).prop("checked")){
                            $tr = $(this).parents("tr");
                            trOutEditMode($tr);
                            $(this).prop("checked",false);
                        }else{
                            $tr = $(this).parents("tr");
                            trIntoEditMode($tr);
                            $(this).prop("checked",true);
                        }
                    }else{
                        //只需要修改复选框按钮状态
                        $(this).prop("checked",!$(this).prop("checked"));
                    }
                })
            })
        }
    
        function bindCancelAll(){
            $("#idCancelAll").click(function(){
                var editing = $("#idEditMode").hasClass("btn-warning");
                $("#table_tb").find(":checkbox").each(function(){
                    if(editing){
                        if($(this).prop("checked")){
                            $tr = $(this).parents("tr");
                            trOutEditMode($tr);
                            $(this).prop("checked",false);
                        }
                    }else{
                        if($(this).prop("checked")){
                            $(this).prop("checked",false);
                        }
                    }
                });
            });
        }
    
        function bindCheckAll() {
            $("#idCheckAll").click(function(){
                var editing = $("#idEditMode").hasClass("btn-warning");
                if(editing){
                    $("#table_tb").find(":checkbox").each(function(){
                        if($(this).prop("checked")){
                            //无操作
                        }else{
                            $tr = $(this).parents("tr");
                            trIntoEditMode($tr);
                            $(this).prop("checked",true);
                        }
                    })
                }else{
                    $("#table_tb").find(":checkbox").each(function(){
                        if(!$(this).prop("checked")){
                            $(this).prop("checked",true);
                        }
                    })
                }
            })
        }
    
        function bindCheckBox(){
            $("#table_tb").on('click',':checkbox',function(){
                if($("#idEditMode").hasClass("btn-warning")){
                    var ck = $(this).prop("checked");
                    $currentTr = $(this).parents("tr");
                    if(ck){
                        //进入编辑模式
                        trIntoEditMode($currentTr);
                    }else{
                        //退出编辑模式
                        trOutEditMode($currentTr);
                    }
                }
            })
        }
    
        function trIntoEditMode($tr){
            $tr.addClass("success");    //添加样式
            $tr.attr("has-edit","true");    //代表进入了编辑模式,传递数据的时候会去看他
            $tr.children().each(function(){
                var edit_enable = $(this).attr("edit-able");
                var edit_type = $(this).attr("edit-type");
                if(edit_enable == "true"){
                    if(edit_type == "select"){
                        var global_name = $(this).attr("global-name");
                        var origin = $(this).attr("origin");
    
                        var tag = document.createElement('select');
                        tag.className = "form-control";
                        $.each(window[global_name],function(k,v){
                            var optag = document.createElement("option");
                            optag.setAttribute("value",v[0]);
                            optag.innerHTML = v[1];
                            tag.append(optag);
                        });
    
                        $(tag).val(origin);
    
                        $(this).html(tag);
                    }else if(edit_type == "input"){
                        var innerText = $(this).text();
                        var tag = document.createElement("input");
                        tag.className = "form-control";
                        tag.value = innerText;
                        $(this).html(tag)
                    }
                }
            })
        }
    
        function trOutEditMode($tr) {
            $tr.removeClass("success");    //添加样式
    
            $tr.children().each(function(){
                var edit_enable = $(this).attr("edit-able");
                var edit_type = $(this).attr("edit-type");
    
                if(edit_enable == "true"){
                    if(edit_type == "select"){
                        var $select = $(this).children().first();
                        var newId = $select.val();
                        var newText = $select[0].selectedOptions[0].text;
    
                        $(this).attr("new-val",newId);
                        $(this).html(newText);
                    }else if(edit_type == "input"){
                        var $input = $(this).children().first();
                        var inputValue = $input.val();
                        $(this).html(inputValue);
                        $(this).attr("new-val",inputValue);
                    }
                }
            })
        }
    
        function bindEditMode(){
            $("#idEditMode").click(function(){
                var editstatus = $(this).hasClass("btn-warning");
                if(!editstatus){
                    /*进入编辑模式*/
                    $(this).addClass("btn-warning");
                    $(this).text("退出编辑模式");
    
                    $("#table_tb").find(":checked").each(function(){
                        var $tr = $(this).parents("tr");
                        trIntoEditMode($tr);
                    })
                }else{
                    /*退出编辑模式*/
                    $(this).removeClass("btn-warning");
                    $(this).text("进入编辑模式");
    
                    $("#table_tb").find(":checked").each(function(){
                        var $tr = $(this).parents("tr");
                        trOutEditMode($tr);
                    })
                }
            })
        }
    
        function init(){
            $("#table_th").empty();
            $("#table_tb").empty();
            $.ajax({
                url:requestUrl,
                type:"get",
                dataType:"json",
                success:function(response){
                    initGlobal(response.global_data);
                    initHeader(response.table_config);
                    intiContent(response.table_config,response.content);
                }
            })
        }
    
        function initGlobal(gloable_data) {
            $.each(gloable_data,function(k,v){
                window[k] = v
            })
        }
    
        function initHeader(table_config){
            var tr = document.createElement("tr");
            $.each(table_config,function(k,v){
                if(v.display){
                    var th = document.createElement("th");
                    th.innerHTML = v.title;
                    tr.append(th);
                }
            })
            $("#table_th").append(tr);
        }
    
        function intiContent(table_config,content){
            $.each(content,function(k,item){
                var tr = document.createElement("tr");
                $.each(table_config,function(k1,tit){
                    if(tit.display){
                        var td = document.createElement("td");
                        /*生成文本信息*/
                        var kwargs = {}; /*将格式化的信息放在该对象中保存,之后我们生成文本信息的时候直接从这里面去取*/
                        $.each(tit.text.kwargs,function(k2,v2) {
                            if(v2.substring(0,2) == "@@"){
                                var index = item[tit.field];
                                var global_name = v2.substring(2,v2.length);
                                kwargs[k2] = getValueFromGlobalByID(global_name,index)
                            }else if(v2[0] == "@"){
                                kwargs[k2] = item[v2.substring(1,v2.length)]
                            }else{
                                kwargs[k2] = v2
                            }
                        });
    
                        /*设置属性*/
                        $.each(tit.attrs,function(k3,v3){
                            if(v3[0] == "@"){
                                td.setAttribute(k3,item[v3.substring(1,v3.length)])
                            }else{
                                td.setAttribute(k3,v3)
                            }
                        })
    
                        /*内联代码*/
                        td.innerHTML = tit.text.content.format(kwargs);
                        tr.append(td)
                    }
                })
    
                $(tr).attr("row-id",item['id'])
                $("#table_tb").append(tr)
            })
        }
    
        function getValueFromGlobalByID(global_name,index){
            var ret = null;
            $.each(window[global_name],function(k,v){
                if(v[0] == index){
                    ret = v[1]
                    return false
                }
            })
            return ret
        }
    
        jQuery.extend({
            'NB':function (url){
                requestUrl = url;
                init();
                bindEditMode();
                bindCheckBox();
                bindCheckAll();
                bindCancelAll();
                bindReverse();
                bindSave();
            },
        })
    })()
    自定义js插件:实现动态获取,编辑数据

    六:CMDB项目总结

    1. 四种采集资产的方式(掌握3种)
        唯一标识的处理(选用主机名<人为定制规则>,因为其他的也是不具有唯一性,SN码在虚拟机上是一致的)
    2. API
        API验证(时间验证,同一时间请求过滤,加密密匙)3次验证,(同加密cookie+时间限制+访问记录)
        数据库表设计
    3. 后台管理
        js自定制插件(前端+后端配置),减少CURD操作
  • 相关阅读:
    JQuery实现1024小游戏
    Windows Server2008 R2安装wampserver缺少api-ms-win-crt-runtime-l1-1-0.dll解决方案
    ASP.NET MVC 邮件发送的功能(微软邮箱发送)。
    浅谈撞库防御策略
    极验高并发验证服务背后的技术实现
    2015年国内数据安全事件盘点
    转载——验证码的昨天、今天和明天
    转载——最近百度云盘不提供搜索,闲来无事,玩玩python爬虫,爬一下百度云盘的资源
    SQL 查询语句
    SQL Server 目录
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9060367.html
Copyright © 2020-2023  润新知