需求
在CMDB系统中,我们需要对资产进行采集和资产入库,包括serverBasic、disk、memory、nic信息等,客户端需要采集这些硬件的信息,服务端则负责资产入库,但是需要采集的硬件并不是固定不变的,我们需要根据实际情况适当的添加或者减少硬件信息的采集,所以在生产环境中,我们把每个硬件信息的采集和入库做成插件,相互独立,可在配置文件中增减或者移除。
 
知识点:
字符串的形式导入插件方式:(字符串的格式也就是插件这个类的路径,这种格式正好也是类的导入格式)
# 根据字符串形式导入模块,并且找到其中的类并执行
import importlib v = "src.plugins.nic.Nic"
module_path,cls_name = v.rsplit('.',maxsplit=1)
m = importlib.import_module(module_path)
cls = getattr(m,cls_name)
obj = cls()
obj.process()
 
 
设计: (参考django中间件的加载方式)
在settings配置文件中做好插件配置,在加载插件的方法中,使用字符串形式导入类的方法,循环加载各个插件。
settings.py

 
plugins/__init__.py下的加载插件方法:
def exec_plugin(self):
server_info = {}
for k,v in self.plugin_items.items():
# 找到v字符串:src.plugins.nic.Nic,
# src.plugins.disk.Disk
info = {'status':True,'data': None,'msg':None}
try:
module_path,cls_name = v.rsplit('.',maxsplit=1)
module = importlib.import_module(module_path)
cls = getattr(module,cls_name) if hasattr(cls,'initial'):
obj = cls.initial()
else:
obj = cls()
ret = obj.process(self.exec_cmd,self.test)
info['data'] = ret
except Exception as e:
info['status'] = False
info['msg'] = traceback.format_exc() server_info[k] = info
return server_info
 
应用实例(及整个业务逻辑):
客户端:
执行流程:run.py >> script.py >> client.py >> plugins
run.py,设置全局变量,主函数启动
import sys
import os BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR) # 设置全局变量(手动配置路径)
os.environ['AUTO_CLIENT_SETTINGS'] = "conf.settings" # # 导入插件管理类
# from src.plugins import PluginManager from src import script if __name__ == '__main__':
script.start()
 
script.py,约束采集信息模式(agent,ssh,salt),兼容三种模式
from lib.config import settings
from .client import AgentClient
from .client import SaltSshClient def start():
# 这个函数用来判断模式,并约束可选模式
if settings.MODE == 'AGENT':
obj = AgentClient()
elif settings.MODE == "SSH" or settings.MODE == 'SALT':
obj = SaltSshClient()
else:
raise Exception('模式仅支持:AGENT/SSH/SALT')
obj.exec()
 
client.py,三种模式的调用插件,API验证
import requests,json,time,hashlib
from src.plugins import PluginManager
from lib.config import settings
from concurrent.futures import ThreadPoolExecutor class BaseClient(object):
def __init__(self):
self.api = settings.API def post_server_info(self,server_dict): # requests.post(self.api,data=server_dict) # 1. k=v&k=v, 2. content-type: application/x-www-form-urlencoded
response = requests.post(self.api,json=server_dict,headers={'auth-api':auth_header_val}) # 1. 字典序列化;2. 带请求头 content-type: application/json def exec(self):
raise NotImplementedError('必须实现exec方法') class AgentClient(BaseClient): # 继承BaseClient类 def exec(self):
obj = PluginManager()
server_dict = obj.exec_plugin()
print('采集到的服务器信息:',server_dict)
self.post_server_info(server_dict) class SaltSshClient(BaseClient): # 继承BaseClient类
def task(self,host):
obj = PluginManager(host)
server_dict = obj.exec_plugin()
self.post_server_info(server_dict) def get_host_list(self):
response = requests.get(self.api,headers={'auth-api':auth_header_val})
print(response.text) # [{"hostname":"c1.com"}]
return json.loads(response.text)
# return ['c1.com',] def exec(self):
pool = ThreadPoolExecutor(10) host_list = self.get_host_list()
for host in host_list:
# 注意格式,如果host_list为字典,用host.hostname取值
pool.submit(self.task,host)
 
plugins
  
__init__.py,初始化插件
import importlib
import requests
from lib.config import settings
import traceback class PluginManager(object):
def __init__(self,hostname=None):
self.hostname = hostname
self.plugin_items = settings.PLUGIN_ITEMS
self.mode = settings.MODE
self.test = settings.TEST
if self.mode == "SSH":
self.ssh_user = settings.SSH_USER
self.ssh_port = settings.SSH_PORT
self.ssh_pwd = settings.SSH_PWD def exec_plugin(self):
server_info = {}
for k,v in self.plugin_items.items():
# 找到v字符串:src.plugins.nic.Nic,
# src.plugins.disk.Disk
info = {'status':True,'data': None,'msg':None}
try:
module_path,cls_name = v.rsplit('.',maxsplit=1)
module = importlib.import_module(module_path)
cls = getattr(module,cls_name) if hasattr(cls,'initial'):
obj = cls.initial()
else:
obj = cls()
ret = obj.process(self.exec_cmd,self.test)
info['data'] = ret
except Exception as e:
info['status'] = False
info['msg'] = traceback.format_exc() server_info[k] = info
return server_info def exec_cmd(self,cmd):
# 调用插件方法时,会把执行命令的方法作为参数传到插件,line:48
if self.mode == "AGENT":
import subprocess
result = subprocess.getoutput(cmd)
elif self.mode == "SSH":
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=self.hostname, port=self.ssh_port, username=self.ssh_user, password=self.ssh_pwd)
stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read()
ssh.close()
elif self.mode == "SALT":
import subprocess
result = subprocess.getoutput('salt "%s" cmd.run "%s"' %(self.hostname,cmd))
else:
raise Exception("模式选择错误:AGENT,SSH,SALT")
return result
 

 
服务端:
执行流程:views.py >> plugins
settings.py
PLUGIN_ITEMS = {
"nic": "api.plugins.nic.Nic",
"disk": "api.plugins.disk.Disk",
"memory": "api.plugins.memory.Memory",
}
 
 
views.py:获取未采集列表,调用写入数据库插件
        manager = PluginManager()
response = manager.exec(server_dict) return HttpResponse(json.dumps(response))
 
 
plugins
  
__init__.py,初始化插件
        for k,v in self.plugin_items.items():
print(k,v)
# try:
module_path,cls_name = v.rsplit('.',maxsplit=1)
md = importlib.import_module(module_path)
cls = getattr(md,cls_name)
obj = cls(server_obj,server_dict[k])
obj.process()
print(k,v)
 

Django【设计】可插拔的插件方式实现的更多相关文章

  1. 在.NET Core中三种实现“可插拔”AOP编程方式(附源码)

    一看标题肯定会联想到使用动态编织的方式实现AOP编程,不过这不是作者本文讨论的重点. 本文讨论另外三种在netcore中可实现的方式,Filter(过滤器,严格意义上它算是AOP方式),Dynamic ...

  2. Django中间件-跨站请求伪造-django请求生命周期-Auth模块-seettings实现可插拔配置(设计思想)

    Django中间件 一.什么是中间件 django中间件就是类似于django的保安;请求来的时候需要先经过中间件,才能到达django后端(url,views,models,templates), ...

  3. 在可插拔settings的基础上加入类似中间件的设计

    在可插拔settings的基础上加入类似中间件的设计 settings可插拔设计可以看之前的文章 https://www.cnblogs.com/zx125/p/11735505.html 设计思路 ...

  4. Django数据库设计中字段为空的方式

    今天在做数据库设计的时候,设计了如下User表,其中我把email和phone字段设置为允许为空: class User(models.Model): username = models.CharFi ...

  5. ARM上的linux如何实现无线网卡的冷插拔和热插拔

    ARM上的linux如何实现无线网卡的冷插拔和热插拔 fulinux 凌云实验室 1. 冷插拔 如果在系统上电之前就将RT2070/RT3070芯片的无线网卡(以下简称wlan)插上,即冷插拔.我们通 ...

  6. WPF 插拔触摸设备触摸失效

    原文:WPF 插拔触摸设备触摸失效 最近使用 WPF 程序,在不停插拔触摸设备会让 WPF 程序触摸失效.通过分析 WPF 源代码可以找到 WPF 触摸失效的原因. 在 Windows 会将所有的 H ...

  7. SphereEx 登陆 ApacheCon Asia|依托 ShardingSphere 可插拔架构体系打造数据应用完整生态

    2021 年 8 月 8 日,ApacheCon 首次亚洲大会于线上正式闭幕.作为久负盛名的开源盛宴,本届 ApacheCon Asia 受到了海内外众多开源领域人士的关注. 作为 Apache 软件 ...

  8. 我心中的核心组件(可插拔的AOP)~第二回 缓存拦截器

    回到目录 AOP面向切面的编程,也称面向方面的编程,我更青睐于前面的叫法,将一个大系统切成多个独立的部分,而这个独立的部分又可以方便的插拔在其它领域的系统之中,这种编程的方式我们叫它面向切面,而这些独 ...

  9. 带卡扣的网卡接口使用小Tips,大家注意插拔网线的手法啊!

    最近入手了一台X401,因为机器本身比较薄,它的网卡接口是有卡扣的,插网线的时候卡扣往下沉,这种设计应该有很多机型都采用了.但是大家有没有发现啊,这种接口的卡扣,时间长了,可能会有点松动.为了保护爱机 ...

随机推荐

  1. 基本数据类型(int,bool,str)

    目录: 1.int        数字类型 2.bool      布尔值 3.str    字符串类型 一.整型(int) 在python3中所有的整数都是int类型.但在python2中如果数据量 ...

  2. 第45天:2017webstrom下载破解汉化

    1.webstrom 11.0.3下载地址1:http://pan.baidu.com/s/1kVQjcwf 密码:uggr 下载地址2:http://pan.baidu.com/s/1kVQjcwf ...

  3. Go语言【第七篇】:Go函数

    Go语言函数 函数是基本的代码块,用于执行某个任务.Go语言最少有个main()函数,可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务.函数声明告诉了编译器函数的名称,返回类型和参数.Go ...

  4. BZOJ3167/BZOJ4824 HEOI2013SAO/CQOI2017老C的键盘(树形dp)

    前者是后者各方面的强化版. 容易想到设f[i][j]表示i子树中第j小的是i的方案数(即只考虑相对关系).比较麻烦的在于转移.考虑逐个合并子树.容易想到枚举根原来的排名和子树根原来的排名,算一发组合数 ...

  5. 关于 [lambda x: x*i for i in range(4)] 理解

    题目: lst = [lambda x: x*i for i in range(4)] res = [m(2) for m in lst] print res 实际输出:[6, 6, 6, 6] 想要 ...

  6. (转)把hadoop源码关联到eclipse工程

    把hadoop源码关联到eclipse工程     转:http://www.superwu.cn/2013/08/04/355 在eclipse中阅读源码非常方便,利于我们平时的学习,下面讲述如何把 ...

  7. 【BZOJ 3811】玛里苟斯 大力观察+期望概率dp+线性基

    大力观察:I.从输出精准位数的约束来观察,一定会有猫腻,然后仔细想一想,就会发现输出的时候小数点后面不是.5就是没有 II.从最后答案小于2^63可以看出当k大于等于3的时候就可以直接搜索了 期望概率 ...

  8. selenium测试-open chrome

    通过selenium来打开浏览器测试之前,需要确认本地已安装相应的webdriver,本例以chrome为例. 1. 查看本地chrome版本,以此确认需要安装的webdriver版本 查看chrom ...

  9. STL之四:list用法详解

    转载于:http://blog.csdn.net/longshengguoji/article/details/8520891 list容器介绍 相对于vector容器的连续线性空间,list是一个双 ...

  10. zabbix调优PPT

    http://www.slideshare.net/xsbr/alexei-vladishev-zabbixperformancetuning# http://zabbixzone.com/zabbi ...