前戏:项目目的

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

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

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

一:实现方式

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

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

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

实现方案:

本地执行cmd命令。
方法一:os.system("命令")    不可以返回数据
方法二:subprocess模块,使用进程执行命令,可以获取到数据Popen("命令"),进程.stdout.read()<py2>或者直接getoutput("命令")<py3>

    def agent(self,cmd):import subprocesstry: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连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yesssh.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连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yesssh.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-masterMaster准备:a. 配置文件,监听本机IPvim /etc/salt/masterinterface: 本机IP地址b. 启动master/etc/init.d/salt-master startSlave:  yum install salt-minionSlave准备:a. 配置文件,连接那个mastervim /etc/salt/minionmaster: 远程master地址b. 启动slave/etc/init.d/salt-minion start2. 创建关系查看Master:salt-key -LAccepted Keys:Denied Keys:Unaccepted Keys:c1.comc2.comc3.comRejected Keys:接受Master:salt-key -a c1.comAccepted Keys:c1.comc2.comDenied Keys:Unaccepted Keys:c3.comRejected Keys:3. 执行命令master:salt 'c1.com' cmd.run  'ifconfig'import salt.clientlocal = salt.client.LocalClient()result = local.cmd('c2.salt.com', 'cmd.run', ['ifconfig'])"""

saltstack安装

    def salt(self,cmd):import subprocessresult = subprocess.getoutput("Salt '主机名' cmd.run '"+cmd+"'")return result

中控机执行命令:subprocess执行salt

    def salt(self,cmd):import salt.clientlocal = 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,sysBASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)if __name__ == "__main__":client()

start.py启动文件

conf目录(含配置文件)

import osBASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))MODE = "Agent"  #SSH SaltPLUGINS = {'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 = Trueself.message = Noneself.data = Noneself.error = None

response.py定义一种数据格式用于在客户端传递

import json as default_json
from .response import BaseResponsedef conv(obj):return obj.__dict__class Json(object):@staticmethoddef 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 Saltcli = 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 BaseResponsedef 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 settingclass BasePlugin(object):def __init__(self):mode_list = ['Agent','Salt','SSH']self.mode = setting.MODEif 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 retdef execute(self,hostname=None):self.hostname = hostname#windows:systeminfo详细信息 ver版本号 linux:cat /etc/issue | grep Linuxsys_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 oswith open(os.path.join(setting.BASEDIR,'file',"test.txt"),"w") as fp:fp.write(output)def agent(self,cmd):import subprocesstry:ret = subprocess.getoutput(cmd)except AttributeError:sub = subprocess.Popen(args=cmd,shell=True,stdout=subprocess.PIPE)sub.wait()ret = sub.stdout.read()return retdef salt(self,cmd):import subprocessresult = 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 paramikossh = 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 BaseResponseclass 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'] = outputoutput = 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_dataexcept Exception as e:msg = "%s window memory plugins error: %s"response.status = Falseresponse.error = msg % (self.hostname, traceback.format_exc())  # traceback.format_exc()返回前面错误的信息return responsedef 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 BaseResponseclass 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 oswith 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 = Falseresponse.error = msg % (self.hostname,traceback.format_exc())   #traceback.format_exc()返回前面错误的信息return responsedef linux(self):# 获取返回的字符串output = self.shell_cmd("查看硬盘")# 进行正则匹配,放入Mem_data中return self.Disk_datadef parse(self,content):response = {}result = [] #模拟多台   使用4个\n分割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():continueif len(row.split(":")) != 2:continuekey,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 = 0else:val = val.group(1)temp_dict[name] = valif temp_dict:response[temp_dict['slot']] = temp_dictreturn responsedef patter_match(self,key):for k,v in self.Disk_data.items():if key.startswith(k):return vreturn False

disk.py获取硬盘信息

from .base import BasePlugin
from conf import setting
import traceback
from lib.response import BaseResponseclass 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 oswith 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 = Falseresponse.error = msg % (self.hostname, traceback.format_exc())  # traceback.format_exc()返回前面错误的信息return responsedef linux(self):# 获取返回的字符串output = self.shell_cmd("查看内存")# 进行正则匹配,放入Mem_data中return self.Mem_datadef parse(self,content):response = {}result = [] #模拟多台   使用4个\n分割for row_line in content.split("Memory Device"):result.append(row_line)for item in result:for row in item.split('\n\t'):if not row.strip():continueif len(row.split(":")) != 2:continuekey,val = row.split(":")name = self.patter_match(key)if name:response[name] = valreturn responsedef patter_match(self,key):for k,v in self.Mem_data.items():if key.startswith(k):return vreturn 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 : NoEnclosure 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 : NoEnclosure 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 : NoEnclosure 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 : NoEnclosure 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 : NoEnclosure 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 : NoExit Code: 0x00

disk.out

Memory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: 1024 MBForm Factor: DIMMSet: NoneLocator: DIMM #0Bank Locator: BANK #0Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: UnknownMemory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: No Module InstalledForm Factor: DIMMSet: NoneLocator: DIMM #1Bank Locator: BANK #1Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: UnknownMemory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: No Module InstalledForm Factor: DIMMSet: NoneLocator: DIMM #2Bank Locator: BANK #2Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: UnknownMemory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: No Module InstalledForm Factor: DIMMSet: NoneLocator: DIMM #3Bank Locator: BANK #3Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: UnknownMemory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: No Module InstalledForm Factor: DIMMSet: NoneLocator: DIMM #4Bank Locator: BANK #4Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: UnknownMemory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: No Module InstalledForm Factor: DIMMSet: NoneLocator: DIMM #5Bank Locator: BANK #5Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: UnknownMemory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: No Module InstalledForm Factor: DIMMSet: NoneLocator: DIMM #6Bank Locator: BANK #6Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: UnknownMemory DeviceTotal Width: 32 bitsData Width: 32 bitsSize: No Module InstalledForm Factor: DIMMSet: NoneLocator: DIMM #7Bank Locator: BANK #7Type: DRAMType Detail: EDOSpeed: 667 MHzManufacturer: Not SpecifiedSerial Number: Not SpecifiedAsset Tag: Not SpecifiedPart Number: Not SpecifiedRank: 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 jsonclass 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'] = 1002ret['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'] = 1003ret['message'] = "[%s]资产更新异常" % hostnameif 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):@staticmethoddef 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 = Falsemodels.ErrorLog.objects.create(asset_obj=server_obj.asset,title="basic-run",content=traceback.format_exc())return response@staticmethoddef update_last_time(server_obj,user_obj):response = BaseResponse()try:current_date = datetime.date.today()server_obj.asset.latest_date = current_dateserver_obj.asset.save()models.AssetRecord.objects.create(asset_obj=server_obj.asset,creator=user_obj,content="资产汇报")except Exception as e:response.status = Falsemodels.ErrorLog.objects.create(asset_obj=server_obj.asset,title="basic-run",content=traceback.format_exc())return responseclass HandleDisk(object):@staticmethoddef process(server_obj,server_info,user_obj):response = BaseResponse()try:disk_info = server_info['disk']if not disk_info['status']:response.status = Falsemodels.ErrorLog.objects.create(asset_obj=server_obj.asset,title="disk-plugins",content=disk_info['error'])return responseclient_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 = Falsemodels.ErrorLog.objects.create(asset_obj=server_obj.asset,title="disk-plugins",content=traceback.format_exc())return response@staticmethoddef _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_objmodels.Disk.objects.create(**cur_disk_dict)models.AssetRecord.objects.create(asset_obj=server_obj.asset,creator=user_obj,content=log_str)@staticmethoddef _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)@staticmethoddef _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_modelnew_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_capacitynew_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_typeitem.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 JsonResponsedef api_auth_method(request):auth_key = settings.KEYauth_name = settings.AUTH_KEY_NAMEauth_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 Falserd.set(auth_val,auth_time)return Truedef 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:passelse: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.nameclass 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.nameclass 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.nameclass 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.nameclass IDC(models.Model):'''
    机房信息:楼层和机房'''
    name = models.CharField("机房",max_length=32)floor = models.IntegerField("楼层",default=1)class Meta:verbose_name_plural = "机房表"def __str__(self):return self.nameclass Tag(models.Model):'''
    资产标签:以前是做什么的,web服务器....,一个资产可以有多个标签,一个标签可以有端个资产'''
    name = models.CharField("标签",max_length=32,unique=True)class Meta:verbose_name_plural = "标签表"def __str__(self):return self.nameclass 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.hostnameclass 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.slotclass 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.nameclass 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.slotclass 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.objectsfor item in data:AssetObj.filter(id=item['id']).update(**item)except Exception as e:ret['status'] = Falseret['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="width: 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. APIAPI验证(时间验证,同一时间请求过滤,加密密匙)3次验证,(同加密cookie+时间限制+访问记录)数据库表设计
3. 后台管理js自定制插件(前端+后端配置),减少CURD操作

转载于:https://www.cnblogs.com/ssyfj/p/9060367.html

python---CMDB配置管理数据库相关推荐

  1. i-doIT 0.9.9-7发布 CMDB配置管理数据库

    i-doIT是一个基于ITIL技术的CMDB(配置管理数据库).它能够记载IT系统及其变化,对变化定义了应急方案,以及显示重要信息,并有助于确保一个稳定和高效的IT网络运作.由于其模块化的架构,它可以 ...

  2. CMDB(配置管理数据库)基础知识详解

    公众号回复:干货,领取价值58元/套IT管理体系文档 公众号回复:ITIL教材,领取最新ITIL4中文教材 "CMDB"这个词在ITSM相关文档和IT管理领域经常遇到,但鲜有人能解 ...

  3. 规划一个配置管理数据库(CMDB)

    规划一个配置管理数据库(CMDB) 下面简要介绍使用TechExcel ServiceWise来规划一个CMDB的步骤: 1确定需要记录和管理的CI. 会不会是硬件而已?硬件和软件?硬件,软件,文档, ...

  4. CMDB配置管理的实施步骤

    CMDB的应用,是一个庞大芜杂.旷日持久的过程,它涉及到CMDB顾问咨询.产品工具的选择.实施服务.日常维护等多个活动和阶段. 配置管理实施是IT服务管理的关键所在,ServiceHot CMDB配置 ...

  5. python操作mysql数据库实现增删改查

    Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口. Python 数据库接口支持非常多的数据库,你可以选择适合你项目的数据库: ...

  6. python如何编写数据库_如何在几分钟内用Python编写一个简单的玩具数据库

    python如何编写数据库 MySQL, PostgreSQL, Oracle, Redis, and many more, you just name it - databases are a re ...

  7. Python 操作 MongoDB 数据库!

    作者 |黄伟呢 来源 |数据分析与统计学之美 MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的. 先来看看MySQL与MongoDB 概念区别 ...

  8. Python与MySQL数据库的交互实战

    作者 | Huang supreme 编辑 | 郭芮 图源 | 视觉中国 安装PyMySQL库 如果你想要使用python操作MySQL数据库,就必须先要安装pymysql库,这个库的安装很简单,直接 ...

  9. Python中使用数据库SQLite

    参考原文 廖雪峰Python教程 使用SQLite SQLite是一种嵌入式数据库,它的数据库就是一个文件.由于SQLite本身是用C写的,而且体积很小,所以经常被集成到各种应用程序中,甚至在IOS和 ...

  10. Python使用MySQL数据库(新)

    一,安装mysql 如果是windows 用户,mysql 的安装非常简单,直接下载安装文件,双击安装文件一步一步进行操作即可. Linux 下的安装可能会更加简单,除了下载安装包进行安装外,一般的l ...

最新文章

  1. iOS显示gif图片的几种方法
  2. R语言merge函数左连接dataframe数据(Left (outer) join in R)、左连接必须将参数all设置(all.x = TRUE)、默认merge函数通过公共列名合并数据集
  3. 《转》八大算法详细讲解
  4. java蓝桥暑假班_Java实现 蓝桥杯VIP 算法提高 班级排名
  5. (69)FPGA面试题-使用不同的代码实现2:1 MUX ?使用if语句
  6. ASP.NET本质论阅读----应用程序对象
  7. 智能实验室-全能优化(Guardio) 4.0.0.691 beta 11
  8. 需求分析的文档模板的书写方式
  9. POI 模板生成word PDF——牛X神器
  10. 如何用matlab求向量在基下的坐标,请问什么是有关向量的基底、基向量、基坐标?...
  11. 树莓派3代ROS系统镜像文件下载链接
  12. 计算机课怎么加水印,简单给文档添加水印
  13. [JS] canvas 详解
  14. 微信小视频会上传到服务器么,微信新出的视频号功能怎么样发布短视频?能上传一分钟以上吗?...
  15. 科大奥瑞物理实验——光纤传感器实验
  16. 中国电子学会2022年06月份青少年软件编程Python等级考试试卷一级真题(含答案)
  17. 主成分分析、因子分析及其有关的数学基础
  18. 同步任务和异步任务执行过程
  19. 制作 macOS Catalina 正式版安装镜像 .cdr 或 .iso 文件
  20. 曲线弧长和旋转体侧面积的计算公式

热门文章

  1. rk3399安装linux的USB没法用,[RK3399] Type-C改为普通USB
  2. YSZI的微信公众号导航软件按装方法
  3. Flash拖拽元件的元件+元件的元件随鼠标移动:目的让元件的元件随着鼠标移动
  4. 电脑没有声音的几种解决方案
  5. C语言实现不带头结点的单链表逆置的三种方法
  6. 利用kiftd实现局域网文件共享,支持不同操作系统
  7. Linux USB鼠标驱动入门以及处理流程
  8. python代码实现冒泡排序
  9. 编程中的冰山理论——从 RPM 改变文件大小说起
  10. 手机SD卡损坏的修复方法