hulao 2020-02-12
首先应该能想到的代码:
if self.mode == 'agent': import subprocess res = subprocess.getoutput(cmd) return res elif self.mode == 'ssh': import paramiko # 创建SSH对象 ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko. AutoAddPolicy()) # 连接服务器 ssh.connect(hostname=self.hostname, port=22, username='root', password='root') # 执行命令 stdin, stdout, stderr = ssh.exec_command(cmd) # 获取命令结果 result = stdout.read() # 关闭连接 ssh.close() return result elif self.mode == 'salt': import salt.client local = salt.client.LocalClient() result = local.cmd(self.hostname, 'cmd.run', [cmd]) return result else: raise Exception('当前模式只支持 agent/ssh/salt !!!')
存在的问题很明显:
1、复用性差,需要将其封装成方法,然后在进行调用
2、高内聚低耦合原则(这一块代码是负责干啥的,其所有的代码都应该和这个功能是相关的)
举个栗子:
在函数中是: def getUserInfo(): #此函数体内,所有的代码,都应该和获取用户信息的这个目标功能相关联 ## 非 得要写处理订单相关的功能 在类中,也是一样的概念: class UserInfo(): def getUserInfo(): pass def login(): pass def logOut(): pass
采用高内聚低耦合的原则,迭代上述代码:
思路:
1.将硬盘或者cpu等代码封装成一个文件,此文件中涉及到的代码和这个文件的具体功能是相关的
采取仿Django的可插拔式采集:
a. conf.py 中的配置插件:
import os BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) USER = 'root' MODE = 'agent' ## ssh /salt DEBUG = True ### True 代表调试模式 False 代表上线正式采集 PLUGINS_DICT = { 'basic' : 'src.plugins.basic.Basic', 'board' : 'src.plugins.board.Board', 'cpu' : 'src.plugins.cpu.Cpu', 'disk' : 'src.plugins.disk.Disk', 'memory': 'src.plugins.memory.Memory', 'nic' : 'src.plugins.nic.Nic', }
b. src下的plugins目录 创建__init__.py , 此文件中有一个类:
#### 用来管理采集的插件的 import traceback #这个模块是用来查看详情错误信息的,不然用了异常捕获,获取的信息只有一点点 from lib.config.settings import settings #配置文件(高级配置和自定义都有) import importlib #关键模块,用来获取每个文件里面的类,然后调用Process方法。 class Plugins_Dict(): def __init__(self, hostname=None): self.mode = settings.MODE self.plugins_dict = settings.PLUGINS_DICT self.debug = settings.DEBUG self.hostname = hostname #### 从配置文件中读取插件配置,并执行相关的函数获取数据 def execute(self): ### 1.从配置文件中读取插件配置 res = {} for k, v in self.plugins_dict.items(): ### 'basic' : 'src.plugins.basic.Basic', ### k: basic ### v: 'src.plugins.basic.Basic' response = {'status':None, 'data':None} #### 2. 导入模块, 获取类, 并实例化 try: # 这里解压缩,一个是src.plugins.basic,一个是Basic module_path, class_name = v.rsplit('.', 1) ### 3. 加载模块,给一个路径,就能把名称空间加载过来 m = importlib.import_module(module_path) #### 4. 加载类, 通过反射,就可以在这个上面加载过来的名称空间 中拿到类。也就是栗子里的Basic类 cls = getattr(m, class_name) #### 5. 执行每一个插件类下面的process方法,并且传入两个参 数,第一个是函数对象,第二个是debug。因为这里的每个process代表的只是设备,你需要获取信息的设备,如网卡,磁盘等等,以什么样的方式采集信息才是重点,也就是这个函数对象所做的。debug就是判断是否是调试。 ret = cls().process(self.command_func, self.debug ) response['status'] = 10000 response['data'] = ret except Exception as e: response['status'] = 10001 #本来被捕获的异常打印出来只有一点点错误信息,无法精准判断错误位置和错误类型,用这个模块就可以知道了。 response['data'] = traceback.format_exc() res[k] = response return res #这个函数就是用来传进process方法里的。之前也写过这个方法了。放在了这个__init__文件里面。 def command_func(self, cmd): if self.mode == 'agent': import subprocess res = subprocess.getoutput(cmd) return res elif self.mode == 'ssh': import paramiko # 创建SSH对象 ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 连接服务器 ssh.connect(hostname=self.hostname, port=22, username='root', password='root') # 执行命令 stdin, stdout, stderr = ssh.exec_command(cmd) # 获取命令结果 result = stdout.read() # 关闭连接 ssh.close() return result elif self.mode == 'salt': import salt.client local = salt.client.LocalClient() result = local.cmd(self.hostname, 'cmd.run', [cmd]) return result else: raise Exception('当前模式只支持 agent/ssh/salt !!!')
这些都是插件,就是一个个设备,随便拿一个看一下,就拿cpu来看
import os from lib.config.settings import settings class Cpu(object): def __init__(self): pass @classmethod def initial(cls): return cls() def process(self, command_func, debug): #如果是debug模式,就返回一些已经有了的准备好了的信息。 if debug: output = open(os.path.join(settings.BASEDIR, 'files /cpuinfo.out'), 'r',encoding='utf8').read() else: output = command_func("cat /proc/cpuinfo") return self.parse(output) #这个函数就是用来解析拿到的结果的。 def parse(self, content): """ 解析shell命令返回结果 :param content: shell 命令结果 :return:解析后的结果 """ response = {'cpu_count': 0, 'cpu_physical_count': 0, 'cpu_model': ''} cpu_physical_set = set() content = content.strip() for item in content.split('\n\n'): for row_line in item.split('\n'): key, value = row_line.split(':') key = key.strip() if key == 'processor': response['cpu_count'] += 1 elif key == 'physical id': cpu_physical_set.add(value) elif key == 'model name': if not response['cpu_model']: response['cpu_model'] = value response['cpu_physical_count'] = len(cpu_physical_set) return response
这是一个Cpu类, 在__init__里面,通过反射拿到了这个类,并实例化,再执行了process方法。