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模块,通过中控机服务器统一去获取指定服务器的信息。
def ssh(self,cmd):
import paramiko
#.创建SSH对象
ssh = paramiko.SSHClient()
#.加上这句话不用担心选yes的问题,会自动选上
#.用ssh连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yes
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname="远程主机名",port="远程端口",username="用户名",password="密码")
#执行命令,获取结果到标准输入\出\错误流中
stdin,stdout,stderr = ssh.exec_command(cmd)
#.获取命令结果
result = stdout.read()
#.关闭连接
ssh.close()
paramiko实现远程命令执行(方法一:使用用户名,密码)
def ssh(self,cmd):
import paramiko
#.创建SSH对象
ssh = paramiko.SSHClient()
#.加上这句话不用担心选yes的问题,会自动选上
#用ssh连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yes
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#.获取私钥
private_key = paramiko.RSAKey.from_private_key_file("文件:其中保存了私钥,用于解密")
#.通过私钥去连接远程服务器(前提是自己的公钥已经在对方的authorized_keys文件中,paramiko已实现)
ssh.connect(hostname="远程主机名",port="远程端口",username="用户名",pkey="私钥private_key")
#.执行命令,获取结果到标准输入\出\错误流中
stdin,stdout,stderr = ssh.exec_command(cmd)
#.获取命令结果
result = stdout.read()
#.关闭连接
ssh.close()
return result
paramiko使用私钥去登录远程服务器执行命令
优点:不需要为服务器安装agent等软件
缺点:速度慢,适用于服务器少得时候
(三)saltstack:使用master对slave进行操作,基于列队实现(使用广)
# . 安装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 . 创建关系
查看
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: . 执行命令
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 = # 用于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目录(记录日志)
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=)
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() # 获取系统版本
ret = re.search("\[(.*)\]",output)
if ret:
self.Basic_data['os_version'] = ret.group() 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个\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():
continue
if len(row.split(":")) != :
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 =
else:
val = val.group()
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个\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():
continue
if len(row.split(":")) != :
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:
Slot Number:
Drive's postion: DiskGroup: 0, Span: 0, Arm: 0
Enclosure position:
Device Id:
WWN: 5000C5007272C288
Sequence Number:
Media Error Count:
Other Error Count:
Predictive Failure Count:
Last Predictive Failure Event Seq Number:
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:
Successful diagnostics completion on : N/A
SAS Address(): 0x5000c5007272c289
SAS Address(): 0x0
Connected Port Number: (path0)
Inquiry Data: SEAGATE ST300MM0006 LS08S0K2B5NV
FDE Enable: Disable
Secured: Unsecured
Locked: Unlocked
Needs EKM Attention: No
Foreign State: None
Device Speed: .0Gb/s
Link Speed: .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- :
Port status: Active
Port's Linkspeed: 6.0Gb/s
Port- :
Port status: Active
Port's Linkspeed: Unknown
Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID:
Slot Number:
Drive's postion: DiskGroup: 0, Span: 0, Arm: 1
Enclosure position:
Device Id:
WWN: 5000C5007272DE74
Sequence Number:
Media Error Count:
Other Error Count:
Predictive Failure Count:
Last Predictive Failure Event Seq Number:
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:
Successful diagnostics completion on : N/A
SAS Address(): 0x5000c5007272de75
SAS Address(): 0x0
Connected Port Number: (path0)
Inquiry Data: SEAGATE ST300MM0006 LS08S0K2B5AH
FDE Enable: Disable
Secured: Unsecured
Locked: Unlocked
Needs EKM Attention: No
Foreign State: None
Device Speed: .0Gb/s
Link Speed: .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- :
Port status: Active
Port's Linkspeed: 6.0Gb/s
Port- :
Port status: Active
Port's Linkspeed: Unknown
Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID:
Slot Number:
Drive's postion: DiskGroup: 1, Span: 0, Arm: 0
Enclosure position:
Device Id:
WWN: 50025388A075B731
Sequence Number:
Media Error Count:
Other Error Count:
Predictive Failure Count:
Last Predictive Failure Event Seq Number:
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:
Successful diagnostics completion on : N/A
SAS Address(): 0x500056b37789abee
Connected Port Number: (path0)
Inquiry Data: S1SZNSAFA01085L Samsung SSD PRO 512GB EXM01B6Q
FDE Enable: Disable
Secured: Unsecured
Locked: Unlocked
Needs EKM Attention: No
Foreign State: None
Device Speed: .0Gb/s
Link Speed: .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- :
Port status: Active
Port's Linkspeed: 6.0Gb/s
Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID:
Slot Number:
Drive's postion: DiskGroup: 1, Span: 0, Arm: 1
Enclosure position:
Device Id:
WWN: 50025385A02A074F
Sequence Number:
Media Error Count:
Other Error Count:
Predictive Failure Count:
Last Predictive Failure Event Seq Number:
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:
Successful diagnostics completion on : N/A
SAS Address(): 0x500056b37789abef
Connected Port Number: (path0)
Inquiry Data: S1AXNSAF912433K Samsung SSD PRO Series DXM06B0Q
FDE Enable: Disable
Secured: Unsecured
Locked: Unlocked
Needs EKM Attention: No
Foreign State: None
Device Speed: .0Gb/s
Link Speed: .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- :
Port status: Active
Port's Linkspeed: 6.0Gb/s
Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID:
Slot Number:
Drive's postion: DiskGroup: 1, Span: 1, Arm: 0
Enclosure position:
Device Id:
WWN: 50025385A01FD838
Sequence Number:
Media Error Count:
Other Error Count:
Predictive Failure Count:
Last Predictive Failure Event Seq Number:
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:
Successful diagnostics completion on : N/A
SAS Address(): 0x500056b37789abf0
Connected Port Number: (path0)
Inquiry Data: S1AXNSAF303909M Samsung SSD PRO Series DXM05B0Q
FDE Enable: Disable
Secured: Unsecured
Locked: Unlocked
Needs EKM Attention: No
Foreign State: None
Device Speed: .0Gb/s
Link Speed: .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- :
Port status: Active
Port's Linkspeed: 6.0Gb/s
Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID:
Slot Number:
Drive's postion: DiskGroup: 1, Span: 1, Arm: 1
Enclosure position:
Device Id:
WWN: 50025385A02AB5C9
Sequence Number:
Media Error Count:
Other Error Count:
Predictive Failure Count:
Last Predictive Failure Event Seq Number:
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:
Successful diagnostics completion on : N/A
SAS Address(): 0x500056b37789abf1
Connected Port Number: (path0)
Inquiry Data: S1AXNSAFB00549A Samsung SSD PRO Series DXM06B0Q
FDE Enable: Disable
Secured: Unsecured
Locked: Unlocked
Needs EKM Attention: No
Foreign State: None
Device Speed: .0Gb/s
Link Speed: .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- :
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: bits
Data Width: bits
Size: MB
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: MHz
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown Memory Device
Total Width: bits
Data Width: bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: MHz
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown Memory Device
Total Width: bits
Data Width: bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: MHz
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown Memory Device
Total Width: bits
Data Width: bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: MHz
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown Memory Device
Total Width: bits
Data Width: bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: MHz
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown Memory Device
Total Width: bits
Data Width: bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: MHz
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown Memory Device
Total Width: bits
Data Width: bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: MHz
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown Memory Device
Total Width: bits
Data Width: bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: DIMM #
Bank Locator: BANK #
Type: DRAM
Type Detail: EDO
Speed: 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成功,,接口授权失败,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":,"message":'[%s]更新完成'%hostname} #根据主机名去数据库中获取相关信息
server_obj = models.Server.objects.filter(hostname=hostname).select_related('asset').first()
if not server_obj:
ret['code'] =
ret['message'] = '[%s]资产不存在'
return JsonResponse(ret) for k,v in config.PLUGINS_DICT.items():
module_path,cls_name = v.rsplit('.',)
cls = getattr(importlib.import_module(module_path),cls_name)
response = cls.process(server_obj,server_info,None)
if not response.status:
ret['code'] =
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('|')
#:时间上的验证
if float(auth_time) + < time.time():
return False
#:数据是否过期,先从redis获取数据{auth_val:auth_time} rd = redis.Redis(host="localhost",port=) r_time = rd.get(auth_val)
if r_time:
return False #:验证数据是否被修改
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":,"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[]
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=)
email = models.EmailField("邮箱")
phone = models.CharField("座机",max_length=)
mobile = models.CharField("手机",max_length=) 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=)
password = models.CharField("密码",max_length=) class Meta:
verbose_name_plural = "管理员表" def __str__(self):
return self.user_info.name class UserGroup(models.Model):
'''
用户组:业务是按照用户组分配,而不是某个人
每个人可以有多个组
每个组有多个人
''' name = models.CharField(max_length=,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=,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=)
floor = models.IntegerField("楼层",default=) class Meta:
verbose_name_plural = "机房表" def __str__(self):
return self.name class Tag(models.Model):
'''
资产标签:以前是做什么的,web服务器....,一个资产可以有多个标签,一个标签可以有端个资产
'''
name = models.CharField("标签",max_length=,unique=True) class Meta:
verbose_name_plural = "标签表" def __str__(self):
return self.name class Asset(models.Model):
'''
资产信息表,所有资产公共信息(交换机,服务器,防火墙等)
'''
device_type_choices = (
(,"服务器"),
(,"交换机"),
(,"防火墙"),
) device_status_choices = (
(,"上架"),
(,"在线"),
(,"离线"),
(,"下架")
) device_type_id = models.IntegerField(choices=device_type_choices,default=)
device_status_id = models.IntegerField(choices=device_status_choices,default=) cabinet_num = models.CharField("机柜号",max_length=,null=True,blank=True)
cabinet_order = models.CharField("机柜中的序号",max_length=,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=,unique=True)
sn = models.CharField('SN号',max_length=,db_index=True)
manafacturer = models.CharField("制造商",max_length=,null=True,blank=True)
model = models.CharField("型号",max_length=,null=True,blank=True) manage_ip = models.GenericIPAddressField("管理IP",null=True,blank=True) os_platform = models.CharField("系统",max_length=,null=True,blank=True)
os_version = models.CharField("系统版本",max_length=,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=,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=,blank=True,null=True)
vlan_ip = models.CharField("VlanIP",max_length=,blank=True,null=True)
intranet_ip = models.CharField('内网IP', max_length=, blank=True, null=True)
sn = models.CharField('SN号', max_length=, unique=True)
manufacture = models.CharField(verbose_name=u'制造商', max_length=, null=True, blank=True)
model = models.CharField('型号', max_length=, null=True, blank=True)
port_num = models.SmallIntegerField('端口个数', null=True, blank=True)
device_detail = models.CharField('设置详细配置', max_length=, null=True, blank=True) class Meta:
verbose_name_plural = "网络设备" class Disk(models.Model):
'''
硬盘信息
'''
slot = models.CharField("插槽位",max_length=)
model = models.CharField("硬盘类型",max_length=)
capacity = models.FloatField("磁盘容量GB")
pd_type = models.CharField("磁盘类型",max_length=)
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=)
hwaddr = models.CharField("网卡mac地址",max_length=)
netmask = models.CharField(max_length=)
idaddrs = models.CharField("IP地址",max_length=)
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=)
manafacturer = models.CharField("制造商",max_length=,null=True,blank=True)
model = models.CharField("型号",max_length=)
capacity = models.FloatField("容量",null=True,blank=True)
sn = models.CharField("内存SN号",max_length=,null=True,blank=True)
speed = models.CharField("速度",max_length=,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=)
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="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[]);
optag.innerHTML = v[];
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[].selectedOptions[].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(,) == "@@"){
var index = item[tit.field];
var global_name = v2.substring(,v2.length);
kwargs[k2] = getValueFromGlobalByID(global_name,index)
}else if(v2[] == "@"){
kwargs[k2] = item[v2.substring(,v2.length)]
}else{
kwargs[k2] = v2
}
}); /*设置属性*/
$.each(tit.attrs,function(k3,v3){
if(v3[] == "@"){
td.setAttribute(k3,item[v3.substring(,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[] == index){
ret = v[]
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操作
python---CMDB配置管理数据库的更多相关文章
- CMDB, 配置管理数据库, ITIL - ManageEngine ServiceDesk Plus
Download Bitnami Review Board Stack click here CMDB, 配置管理数据库, ITIL - ManageEngine ServiceDesk Plus
- CMDB 配置管理数据库
- Python CMDB开发
Python CMDB开发 运维自动化路线: cmdb的开发需要包含三部分功能: 采集硬件数据 API 页面管理 执行流程:服务器的客户端采集硬件数据,然后将硬件信息发送到API,API负责将获取 ...
- python操作mysql数据库增删改查的dbutils实例
python操作mysql数据库增删改查的dbutils实例 # 数据库配置文件 # cat gconf.py #encoding=utf-8 import json # json里面的字典不能用单引 ...
- 《Python操作SQLite3数据库》快速上手教程
为什么使用SQLite数据库? 对于非常简单的应用而言,使用文件作为持久化存储通常就足够了,但是大多数复杂的数据驱动的应用需要全功能的关系型数据库.SQLite的目标则是介于两者之间的中小系统.它有以 ...
- Python操作Mysql数据库时SQL语句的格式问题
一.概述 近日使用Python对Mysql数据库进行操作,遇到SQL语句死活出问题的情况.由于最初没有将异常打印出来,一直不知道原因.随后,将异常打印出来之后,通过异常信息,对代码进行修改.最终,成功 ...
- python——连接Oracle数据库
前言 Python自带的模块中有很多操纵文件的.我们可以把文件的数据读出来,经过处理还可以将数据写入文件中.但是对于数据的管理和分析来说,数据库还是专业一些.如果Python能和数据库结合在一起,那么 ...
- python使用mysql数据库
一,安装mysql 如果是windows 用户,mysql 的安装非常简单,直接下载安装文件,双击安装文件一步一步进行操作即可. Linux 下的安装可能会更加简单,除了下载安装包进行安装外,一般的l ...
- python操作mysql数据库的相关操作实例
python操作mysql数据库的相关操作实例 # -*- coding: utf-8 -*- #python operate mysql database import MySQLdb #数据库名称 ...
- [python]用Python进行SQLite数据库操作
用Python进行SQLite数据库操作 1.导入Python SQLITE数据库模块 Python2.5之后,内置了SQLite3,成为了内置模块,这给我们省了安装的功夫,只需导入即可~ ]: u ...
随机推荐
- 对网络助手的NABCD分析心得
Sunny--Code团队::刘中睿,杜晓松,郑成 我们小组这次做的软件名字叫为校园网络助手.在大学学习的同学都知道学校里面有着内网与外网两种,并且有着流量限制,所以我们设计出来了这项软件,它主要有着 ...
- IO异常 的处理 test
package com.throwsss; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFou ...
- 灵悟礼品网上专卖店Sprint计划
一.现状 小组成员初步了解了所做项目的大致内容,需要时间一步一步分析和规划. 二.部分需求索引卡 第一个阶段没有具体功能的实现,只是先把所要做的项目思路理清,并把相应的数据库建立好. 三.任务认领 产 ...
- KEIL C51程序中如何嵌入汇编
模块内接口:使用如下标志符:#pragma asm汇编语句#pragma endasm注意:如果在c51程序中使用了汇编语言,注意在Keil编译器中需要激活Properties中的“Generate ...
- elasticsearch文档-字段的mapping
mapping == Mapping是指定义如何将document映射到搜索引擎的过程,比如一个字段是否可以查询以及如何分词等,一个索引可以存储含有不同"mapping types" ...
- 一键轻松查看apk包名和Main Activity
环境 Windows系统(我的是Win10 64位) Python3(我的是3.6.1) 已安装Git 安装 pip install git+https://github.com/codeskyblu ...
- (暂时弃坑)(半成品)ACM数论之旅18---反演定理 第二回 Mobius反演(莫比乌斯反演)((づ ̄3 ̄)づ天才第一步,雀。。。。)
莫比乌斯反演也是反演定理的一种 既然我们已经学了二项式反演定理 那莫比乌斯反演定理与二项式反演定理一样,不求甚解,只求会用 莫比乌斯反演长下面这个样子(=・ω・=) d|n,表示n能够整除d,也就是d ...
- Java代码中谁拿到了锁?
我们都知道当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁.这些基础也许大家都知道,但是很多人还是搞不清哪个对象才是锁?如果你能正确回答以下问题,那么才算你彻底搞明白了哪个 ...
- VS2012 Nuget 安装 AutoMapper时报错的解决方法
VS2012 在.net 4.0下安装AutoMapper时,会报以下错误: “AutoMapper”已拥有为“Standard.Library”定义的依赖项. 'AutoMapper' alread ...
- HNOI/AHOI2018题解
作为一名高二老年选手来补一下我省去年的省选题. D1T1:寻宝游戏 按顺序给出\(n\)个\(m\)位的二进制数\(a_i\),再在最前方添一个\(0\), 给出\(q\)次询问,每次询问给出一个同样 ...