需求
在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. c++读取文件夹及子文件夹数据

    这里有两种情况:读取文件夹下所有嵌套的子文件夹里的所有文件  和 读取文件夹下的指定子文件夹(或所有子文件夹里指定的文件名) <ps,里面和file文件有关的结构体类型和方法在 <io.h ...

  2. Spring Boot中使用@Transactional注解配置事务管理

    事务管理是应用系统开发中必不可少的一部分.Spring 为事务管理提供了丰富的功能支持.Spring 事务管理分为编程式和声明式的两种方式.编程式事务指的是通过编码方式实现事务:声明式事务基于 AOP ...

  3. hdu 1392 Surround the Trees (凸包)

    Surround the Trees Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  4. Javascript 中 == 和 === 区别是什么?

    Javascript 中 == 和 === 区别是什么? 作者:Belleve链接:https://www.zhihu.com/question/31442029/answer/77772323来源: ...

  5. 转:DP和HDP

    Dirichlet Process and Hierarchical Dirichlet Process 原文:http://hi.baidu.com/zentopus/item/46a622f5ef ...

  6. BZOJ5249:[九省联考2018]IIIDX——题解

    https://www.luogu.org/problemnew/show/P4364#sub https://www.lydsy.com/JudgeOnline/problem.php?id=524 ...

  7. POJ.2387 Til the Cows Come Home (SPFA)

    POJ.2387 Til the Cows Come Home (SPFA) 题意分析 首先给出T和N,T代表边的数量,N代表图中点的数量 图中边是双向边,并不清楚是否有重边,我按有重边写的. 直接跑 ...

  8. UVA.11464 Even Parity (思维题 开关问题)

    UVA.11464 Even Parity (思维题 开关问题) 题目大意 给出一个n*n的01方格,现在要求将其中的一些0转换为1,使得每个方格的上下左右格子的数字和为偶数(如果存在的话),求使得最 ...

  9. bzoj1024: [SCOI2009]生日快乐(DFS)

    dfs(x,y,n)表示长为x,宽为y,切n块 每次砍的一定是x/n的倍数或者y/n的倍数 #include<bits/stdc++.h> using namespace std; con ...

  10. Java反转字符串的方式?

    1. 将String转换成字符数组,再利用字符数组进行首尾调换. 2. 利用递归的方式,主要是:reverse(str.substring(1)) + str.charAt(0); 3. 虽然Stri ...