python---CMDB配置管理数据库
Posted 山上有风景
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
优点:信息采集快,由服务器自己采集信息传递到API
缺点:每台服务器都必须安装Agent
(二)SSH方法:使用paramiko模块,通过中控机服务器统一去获取指定服务器的信息。
def ssh(self,cmd): import paramiko #1.创建SSH对象 ssh = paramiko.SSHClient() #2.加上这句话不用担心选yes的问题,会自动选上 #3.用ssh连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yes ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname="远程主机名",port="远程端口",username="用户名",password="密码") #执行命令,获取结果到标准输入\\出\\错误流中 stdin,stdout,stderr = ssh.exec_command(cmd) #4.获取命令结果 result = stdout.read() #5.关闭连接 ssh.close()
def ssh(self,cmd): import paramiko #1.创建SSH对象 ssh = paramiko.SSHClient() #2.加上这句话不用担心选yes的问题,会自动选上 #用ssh连接远程主机时,第一次连接时会提示是否继续进行远程连接,选择yes ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #3.获取私钥 private_key = paramiko.RSAKey.from_private_key_file("文件:其中保存了私钥,用于解密") #4.通过私钥去连接远程服务器(前提是自己的公钥已经在对方的authorized_keys文件中,paramiko已实现) ssh.connect(hostname="远程主机名",port="远程端口",username="用户名",pkey="私钥private_key") #5.执行命令,获取结果到标准输入\\出\\错误流中 stdin,stdout,stderr = ssh.exec_command(cmd) #6.获取命令结果 result = stdout.read() #7.关闭连接 ssh.close() return result
优点:不需要为服务器安装agent等软件
缺点:速度慢,适用于服务器少得时候
(三)saltstack:使用master对slave进行操作,基于列队实现(使用广)
# 1. 安装saltstack # rpm --import https://repo.saltstack.com/yum/redhat/6/x86_64/latest/SALTSTACK-GPG-KEY.pub # # """ Master: yum install salt-master Master准备: a. 配置文件,监听本机IP vim /etc/salt/master interface: 本机IP地址 b. 启动master /etc/init.d/salt-master start Slave: yum install salt-minion Slave准备: a. 配置文件,连接那个master vim /etc/salt/minion master: 远程master地址 b. 启动slave /etc/init.d/salt-minion start 2. 创建关系 查看 Master:salt-key -L Accepted Keys: Denied Keys: Unaccepted Keys: c1.com c2.com c3.com Rejected Keys: 接受 Master:salt-key -a c1.com Accepted Keys: c1.com c2.com Denied Keys: Unaccepted Keys: c3.com Rejected Keys: 3. 执行命令 master: salt \'c1.com\' cmd.run \'ifconfig\' import salt.client local = salt.client.LocalClient() result = local.cmd(\'c2.salt.com\', \'cmd.run\', [\'ifconfig\']) """
def salt(self,cmd): import subprocess result = subprocess.getoutput("Salt \'主机名\' cmd.run \'"+cmd+"\'") return result
def salt(self,cmd): import salt.client local = salt.client.LocalClient() result = local.cmd(self.hostname,\'cmd.run\',[cmd]) return result[self.hostname]
优点:快,开发成本低
缺点:依赖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()
conf目录(含配置文件)
import os BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) MODE = "Agent" #SSH Salt PLUGINS = { \'basic\':\'src.plugins.basic.BasicPlugin\', \'disk\':\'src.plugins.disk.DiskPlugin\', \'mem\': \'src.plugins.mem.MemPlugin\', # \'nic\': \'src.plugins.nic.NicPlugin\', } # 如果采用SSH方式,则需要配置SSH的KEY和USER SSH_PRIVATE_KEY = "/home/auto/.ssh/id_rsa" SSH_USER = "root" SSH_PORT = 22 # 用于API认证的KEY KEY = \'299095cc-1330-11e5-b06a-a45e60bec08b\' # 用于API认证的请求头 AUTH_KEY_NAME = \'auth-key\' ASSET_API = "http://127.0.0.1:8000/API/asset" # Agent模式保存服务器唯一ID的文件 CERT_FILE_PATH = os.path.join(BASEDIR, \'conf\', \'cert\') TEST_MODE = True
...
lib目录(含有链接库)
class BaseResponse(object): def __init__(self): self.status = True self.message = None self.data = None self.error = None
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)
log目录(记录日志)
client.py定义多种类,不同的方法与API交互
from conf import setting from .client import * def client(): if setting.MODE == \'Agent\': #"Agent" #SSH Salt cli = AutoAgent() elif setting.MODE == "SSH": cli = AutoSSH() elif setting.MODE == "Salt": cli = AutoSalt() else: raise Exception("配置信息出错") cli.process()
二级目录:plugins目录
from conf import setting from lib.response import BaseResponse def pack(hostname=None): response = {} for k,v in setting.PLUGINS.items(): file_name,cls_name = v.rsplit(\'.\',maxsplit=1) pk = __import__(file_name,fromlist=True) if hasattr(pk,cls_name): obj = getattr(pk,cls_name)() response[k] = obj.execute(hostname) return response
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
from .base import BasePlugin import re from conf import setting import traceback from lib.response import BaseResponse class BasicPlugin(BasePlugin): def __init__(self): super(BasicPlugin, self).__init__() self.Basic_data = {} # 存放我们获取的数据 def windows(self): response = BaseResponse() try: # 获取主机名 output = self.shell_cmd("hostname") self.Basic_data[\'hostname\'] = output output = self.shell_cmd("ver") #获取操作平台 ret = re.search("(.*)\\s*\\[",output) if ret: self.Basic_data[\'os_platform\'] = ret.group(1) # 获取系统版本 ret = re.search("\\[(.*)\\]",output) if ret: self.Basic_data[\'os_version\'] = ret.group(1) response.data = self.Basic_data except Exception as e: msg = "%s window memory plugins error: %s" response.status = False response.error = msg % (self.hostname, traceback.format_exc()) # traceback.format_exc()返回前面错误的信息 return response def linux(self): # 获取返回的字符串 output = self.shell_cmd("查看硬盘") # 进行正则匹配,放入Mem_data中 return self.Basic_data
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(":")) != 2: continue key,val = row.split(":") name = self.patter_match(key) if name: if key == \'Raw Size\': val = re.search("(\\d+\\.\\d+)",val.strip()) if not val: val = 0 else: val = val.group(1) temp_dict[name] = val if temp_dict: response[temp_dict[\'slot\']] = temp_dict return response def patter_match(self,key): for k,v in self.Disk_data.items(): if key.startswith(k): return v return False
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(":")) != 2: continue key,val = row.split(":") name = self.patter_match(key) if name: response[name] = val return response def patter_match(self,key): for k,v in self.Mem_data.items(): if key.startswith(k): return v return False
file文件目录(含有测试的文件,文件包含各种命令下的数据)
Enclosure Device ID: 32 Slot Number: 0 Drive\'s postion: DiskGroup: 0, Span: 0, Arm: 0 Enclosure position: 0 Device Id: 0 WWN: 5000C5007272C288 Sequence Number: 2 Media Error Count: 0 Other Error Count: 0 Predictive Failure Count: 0 Last Predictive Failure Event Seq Number: 0 PD Type: SAS Raw Size: 279.396 GB [0x22ecb25c Sectors] Non Coerced Size: 278.896 GB [0x22dcb25c Sectors] Coerced Size: 278.875 GB [0x22dc0000 Sectors] Firmware state: Online, Spun Up Device Firmware Level: LS08 Shield Counter: 0 Successful diagnostics completion on : N/A SAS Address(python--CMDB项目SpringCloud系列四:Eureka 服务发现框架(定义 Eureka 服务端Eureka 服务信息Eureka 发现管理Eureka 安全配置Eureka-HA(高可用) 机制Eur(代码片段