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 ...
随机推荐
- Task 3 求最大数组和
题目:返回一个整数数组中最大子数组的和. (要求:输入一个整形数组,数组里有正数也有负数. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 求所有子数组的和的最大值.要求时间复杂度为 ...
- 超实用 2 ArrayList链表之 员工工资管理系统
package ArrayList的小程序; import java.io.*; import java.util.*; public class kkk { /** * 作者:Mr.fan * 功能 ...
- 对JAVA RMI的认识
RMI的定义 RPC (Remote Procedure Call):远程方法调用,用于一个进程调用另一个进程中的过程,从而提供了过程的分布能力. RMI(Remote Method Invocati ...
- OSG学习:计算纹理坐标
在很多时候,直接指定纹理坐标是非常不方便的,如曲面纹理坐标,只有少数的曲面(如圆锥.圆柱等)可以在不产生扭曲的情况下映射到平面上,其他的曲面在映射到表面时都会产生一定程度的扭曲.一般而言,曲面表面的曲 ...
- Sprint2-2.0
1.开始一个新的冲刺: 起止:2016.6.1~2016.6.14 按照以下过程进行 ProductBacklog:继续向下细化 Sprint 计划会议:确定此次冲刺要完成的目标 Sprint Bac ...
- Internet History, Technology and Security (Week 2)
Week 2 History: The First Internet - NSFNet Welcome to week 2! This week, we'll be covering the hist ...
- 统计Github项目信息
项目总述 项目Github传送门 主要任务是从之前同项目的组员建的关系型数据库里提取出我们需要的GitHub的数据,并把结果保存到文件,以便之后插入到数据库. 从已经建立好的关系型数据库上多线程地读取 ...
- 【转载】Vue项目中的文件/文件夹命名规范
文件或文件夹的命名遵循以下原则: index.js 或者 index.vue,统一使用小写字母开头的(kebab-case)命名规范 属于组件或类的,统一使用大写字母开头的(PascalCase)命名 ...
- epel [Errno 14] problem making ssl connection
问题描述: 执行yum命令时,报错[Errno 14] problem making ssl connection 问题分析: ssl证书问题 问题解决: sed -i 's/^#baseurl/ba ...
- python mysql开发日志
开始做python 的数据库访问了,暂时选定了mysql数据库.原本想使用ORM,后来考虑到项目的情况是:表结构不复杂,但是数据库非常大.还是自己来操作sql,不过PYTHON的那些数据库ORM的代码 ...