- 本书赞誉
- 前言
- 第一部分 基础篇
- 第1章 系统基础信息模块详解
- 第2章 业务服务监控详解
- 第3章 定制业务质量报表详解
- 第4章 Python 与系统安全
- 第二部分 高级篇
- 第5章 系统批量运维管理器 pexpect 详解
- 第6章 系统批量运维管理器 paramiko 详解
- 第7章 系统批量运维管理器Fabric详解
- 第8章 从零开发一个轻量级 WebServer
- 第9章 集中化管理平台 Ansible 详解
- 第10章 集中化管理平台 Saltstack 详解
- 第11章 统一网络控制器 Func 详解
- 第12章 Python 大数据应用详解
- 第三部分 案例篇
- 第13章 从零开始打造 B/S 自动化运维平台
- 第14章 打造 Linux 系统安全审计功能
- 第15章 构建分布式质量监控平台
- 第16章 构建桌面版 C/S 自动化运维平台
13.5 系统功能模块设计
13.5.1 前端数据加载模块
OMServer平台的Web前端采用prototype.js作为默认Ajax框架,通过get方式向定义好的Django视图发起请求,功能视图通过HttpResponse方法直接输出结果,前端会将输出的结果做页面渲染。图13-6为应用ID(app_categId)等于1的HttpResponse输出结果,前端会将这个结果串进行分割,然后填充页面元素,后端返回主机信息。
图13-6 后端返回主机信息
前端各区域对应的数据库表及视图方法见图13-7。
图13-7 前端各区域对应后台方法及数据库表
局部方法代码如下:
【/data/www/OMserverweb/autoadmin/views.py】
""" =Return server IP list =返回服务器列表方法 """ def server_list(request): ip="" ip_hostname="" if not 'app_categId' in request.GET: app_categId="" else: app_categId=request.GET['app_categId'] #获取用户选择的应用分类ID #ServerList为server_list表模型对象,实现过滤获取的应用分类ID相匹配的主机列表 ServerListObj = ServerList.objects.filter(server_app_id=app_categId) for e in ServerListObj: ip+=","+e.server_lip ip_hostname+=","+e.server_lip+"*"+e.server_name server_list_string=ip[1:]+"|"+ip_hostname[1:] # 输出格式:192.168.1.10,192.168.1.20|192.168.1.10*sn2012-07-010,\ #192.168.1.20*sn2013-08-020,其中“|”分隔符前部分为IP地址,作为HTML <option> 下拉框显示项, #分隔符后部分为<option>的value,以“*”号作为分隔符,目的是为后端提供主机名及IP两种 目标地址支持 return HttpResponse(server_list_string) """ =Return module list =返回功能模块列表方法 """ def module_list(request): module_id="-1" module_name=u"请选择功能模块..." # ModuleList为module_list表模型对象,实现读取所有模块列表,以模块id做排序 ModuleObj = ModuleList.objects.order_by('id') for e in ModuleObj: module_id+=","+str(e.id) module_name+=","+e.module_name module_list_string=module_name+"|"+module_id #输出格式:“请选择功能模块...,查看系统日志,查看最新登录,查看系统版本|-1,1001,1002,1003” #其中“|”号分隔模块名称与模块ID,Web前端获取数据后通过JavaScript做拆分与组装 return HttpResponse(module_list_string)
13.5.2 数据传输模块设计
传输模块采用rpyc分布式计算框架,利用分布式特点可以实现多台主控设备的支持,具备一定横向扩展及容灾能力。rpyc分为两种角色,一种为Server端,另一种为Client端,与传统的Socket工作方式一样,区别是rpyc实现了更高级的封装,支持同步与异步操作、回调和远程服务以及透明的对象代理,可以轻松在Server与Client之间传递Python的任意对象,在性能方面也非常高效。下面介绍的是Django的module_run视图方法,实现接收功能模块的提交参数、加密、发送、接收功能模块运行结果等,局部方法代码如下:
【/data/www/OMserverweb/autoadmin/views.py】
""" = Run module = 运行模块视图方法(向rpyc服务器端发起任何请求) """ def module_run(request): import rpyc put_string="" if not 'ModuleID' in request.GET: #接收模块ID、操作主机、模块扩展参数等(更多源码已省略) Module_Id="" else: Module_Id=request.GET['ModuleID'] put_string+=Module_Id+"@@" …… try: conn=rpyc.connect('192.168.1.20',11511) #连接rpyc主控端主机,端口:11511 #调用rpyc Server的login方法实现账号、密码校验,屏蔽恶意的连接 conn.root.login('OMuser','KJS23o4ij09gHF734iuhsdfhkGYSihoiwhj38u4h') except Exception,e: logger.error('connect rpyc server error:'+str(e)) return HttpResponse('connect rpyc server error:'+str(e)) #对请求数据串使用tencode方法进行加密,密钥使用Django 中settings.SECRET_KEY的值 put_string=tencode(put_string,settings.SECRET_KEY) #调用rpyc Server的Runcommands方法实现功能模块的任务下发,返回的结果使用tdecode进行解密 OPresult=tdecode(conn.root.Runcommands(put_string),settings.SECRET_KEY) return HttpResponse(OPresult) #输出结果供前端渲染
关于rpyc服务器端的实现原理,首先接收rpyc客户端传递过来的信息,通过解密方法还原出模块ID、操作对象、模块扩展参数等信息,再通过exec方法导入相应的功能模块(要事先完成编写,否则会提示找不到指定功能模块),调用功能模块的相关方法,实现操作任务向业务集群服务器下发与执行,最后将任务执行结果串进行格式化、加密后返回给Web层。完整实现代码如下:
【/home/test/OMServer/OMservermain.py】
# -*- coding: utf-8 -*- import time import os,sys import re from cPickle import dumps from rpyc import Service from rpyc.utils.server import ThreadedServer import logging from libraries import * from config import * #定义服务器端模块存放路径 sysdir=os.path.abspath(os.path.dirname(__file__)) sys.path.append(os.sep.join((sysdir,'modules/'+AUTO_PLATFORM))) class ManagerService(Service): #定义login认证方法,对外开放调用的方法,rpyc要求加上“ exposed_”前缀,调用时使用 # login即可 def exposed_login(self,user,passwd): if user=="OMuser" and passwd=="KJS23o4ij09gHF734iuhsdfhkGYSihoiwhj38u4h": self.Checkout_pass=True #认证结果标记变量,值为“True”则认证通过,反之 #认证失败 else: self.Checkout_pass=False def exposed_Runcommands(self,get_string): logging.basicConfig(level=logging.DEBUG, #启用系统日志记录 format='%(asctime)s [%(levelname)s] %(message)s', filename=sys.path[0]+'/logs/omsys.log', filemode='a') #判断是否通过认证 try: if self.Checkout_pass!=True: return tencode("User verify failed!",SECRET_KEY) except: return tencode("Invalid Login!",SECRET_KEY) #获取rpyc Client的请求串get_string,通过tdecode方法解密后再进行分隔,分隔符为“@@” self.get_string_array=tdecode(get_string,SECRET_KEY).split('@@') self.ModuleId=self.get_string_array[0] #获取功能模块ID self.Hosts=self.get_string_array[1] #获取操作目标主机 sys_param_array=[] #获取功能模块的扩展参数并追加到列表 for i in range(2,len(self.get_string_array)-1): sys_param_array.append(self.get_string_array[i]) #加载模块ID应对的模块名,格式为“Mid_”+模块ID,如“Mid_1001.py” mid="Mid_"+self.ModuleId importstring = "from "+mid+" import Modulehandle" try: exec importstring except: return tencode(u"module\""+mid+u"\"does not exist, Please add it",SECRET_KEY) #调用模块相关方法,下发执行任务 Runobj=Modulehandle(self.ModuleId,self.Hosts,sys_param_array) Runmessages=Runobj.run #根据不同主控端组件格式化输出,支持Func、Ansible、Saltstack if AUTO_PLATFORM=="func": if type(Runmessages) == dict: returnString = func_transform(Runmessages,self.Hosts) else: returnString = str(Runmessages).strip elif AUTO_PLATFORM=="ansible": if type(Runmessages) == dict: returnString = ansible_transform(Runmessages,self.Hosts) else: returnString = str(Runmessages).strip elif AUTO_PLATFORM=="saltstack": if type(Runmessages) == dict: returnString = saltstack_transform(Runmessages,self.Hosts) else: returnString = str(Runmessages).strip #对返回给rpyc Client的数据串进行加密 return tencode(returnString,SECRET_KEY) s=ThreadedServer(ManagerService,port=11511,auto_register=False) s.start #启动rpyc服务监听、接收、响应请求
数据传输的安全性关系到整个运营平台的生命线,因此严格做好入侵安全防范至关重要。OMServer平台采用base64.b64encode、base64.b64decode加上密钥混淆算法(RC4)实现数据的加密与解密。OMServer平台遵循一个原则,数据在传输之前调用tencode方法进行加密,在数据接收完毕后调用dencode方法进行解密。解密的密钥采用项目settings.py中的SECRET_KEY变量值。同时在rpyc服务器端添加login方法,实现逻辑层的安全防护。
【/home/test/OMServer/libraries.py】
# -*- coding: utf-8 -*- #!/usr/bin/env python import random, base64 from hashlib import sha1 #RC4加密算法 def crypt(data, key): x = 0 box = range(256) for i in range(256): x = (x + box[i] + ord(key[i % len(key)])) % 256 box[i], box[x] = box[x], box[i] x = y = 0 out = [] for char in data: x = (x + 1) % 256 y = (y + box[x]) % 256 box[x], box[y] = box[y], box[x] out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256])) return ''.join(out) #使用RC4算法加密编码后的数据,data为加密的数据,key为密钥 def tencode(data, key, encode=base64.b64encode, salt_length=16): """RC4 encryption with random salt and final encoding""" salt = '' for n in range(salt_length): salt += chr(random.randrange(256)) data = salt + crypt(data, sha1(key + salt).digest) if encode: data = encode(data) return data #使用RC4算法解密编码后的数据,data为加密的数据,key为密钥 def tdecode(data, key, decode=base64.b64decode, salt_length=16): if decode: data = decode(data) salt = data[:salt_length] return crypt(data[salt_length:], sha1(key + salt).digest)
13.5.3 平台功能模块扩展
OMServer平台模块的扩展需要完成两件事情,一是在前端添加模块基本信息,二是在服务器端编写对应的任务模块,下面对具体内容进行详细说明。
(1)添加前端模块
添加前端模块包括指定模块名称、功能说明、模块扩展(HTML表单作为模块参数)等,具体操作是点击首页的【添加模块】按钮,跳转到“添加模块”表单页面,其中最关键的是“模块扩展”输入框,支持所有HTML表单元素,后台通过name属性引用其值(value)。OMServer目前支持最多两个扩展参数,name属性要求使用“sys_param_1”、“sys_param_2”作为其定义值,当然,扩展更多参数的改造成本也非常低。在本示例中添加“重启进程服务”模块,具体操作如图13-8所示。
提交后将返回新增模块的ID,该模块ID同时会作为服务器端任务模块的后缀名,如图13-9所示,记下模块ID“1007”,前端模块添加完毕。
图13-8 添加前端模块
图13-9 提交前端模块添加
(2)添加服务器端任务模块
服务器端模块的作用是负责具体远程操作任务的功能封装,支持3种Python自动化操作组件,包括Saltstack、Ansible、Func。不同组件的API语法及返回数据结构都不一样,因此OMServer在设计时就将不同组件的模块进行隔离,具体模块目录结构如图13-10所示,在模块目录(modules)下组件名作为二级目录名,二级目录下为具体的任务模块,文件名称由“Mid_”+模块ID组成,与前端生成的模块ID进行关联。
图13-10 服务器端模块目录结构
关于任务模块的编写,不同组件的实现规范和方法都不一样,在编写任务模块之前需要更新配置文件config.py的两个选项,其中“AUTO_PLATFORM”为指定组件环境,可选项为“ansible”、“saltstack”、“func”,“SECRET_KEY”为指定加密、解密的密钥,与项目settings.py中的SECRET_KEY变量保持一致。另外modules/(ansible|saltstack|func)/Public_lib.py文件的作用是导入、定义各组件的API模块包及全局参数,同时也增加代码的复用性。
【/home/test/OMServer/config.py】
# -*- coding: utf-8 -*- #!/usr/bin/env python AUTO_PLATFORM = "saltstack" #指定组件环境,支持Saltstack、Ansible、Func #密钥,与项目中setting.py的SECRET_KEY变量保持一致 SECRET_KEY = "ctmj#&;8hrgow_^sj$ejt@9fzsmh_o)-=(byt5jmg=e3#foya6u"
服务器端任务模块由Modulehandle类及其两个方法组成,其中__init__方法作用为初始化模块基本信息,包括操作主机列表、模块ID、模块扩展参数等;run方法实现组件API的调用以及返回执行结果。针对“重启进程服务”这个任务模块,下面分别介绍3个组件的不同实现方法。
1)编写Ansible组件ID为“1007”模块。
根据Ansible组件模块的开发原理,通过调用command模块实现远程命令执行,使用copy模块实现文件远程同步,详细源码如下:
【/home/test/OMServer/modules/ansible/Mid_1007.py】
# -*- coding: utf-8 -*- from Public_lib import * #重启应用模块进程服务# class Modulehandle: def __init__(self,moduleid,hosts,sys_param_row): #初始化方法无须改动 self.hosts = "" self.Runresult = "" self.moduleid = moduleid #模块ID self.sys_param_array= sys_param_row #模块扩展参数列表 self.hosts=target_host(hosts,"IP") #格式化主机信息,参数“IP”为IP地址,“SN”为主机名 #任务下发、执行方法 def run(self): try: #根据模块扩展参数定义执行的不同命令集 commonname=str(self.sys_param_array[0]) if commonname=="resin": self.command="/etc/init.d/resin restart" elif commonname=="nginx": self.command="/etc/init.d/nginx restart" elif commonname=="haproxy": self.command="/etc/init.d/haproxy restart" elif commonname=="apache": self.command="/etc/init.d/httpd restart" elif commonname=="mysql": self.command="/etc/init.d/mysql restart" elif commonname=="lighttpd": self.command="/etc/init.d/lighttpd restart" #调用Ansible提供的API(command模块),执行远程命令 self.Runresult = ansible.runner.Runner( pattern=self.hosts, forks=forks, module_name="command", module_args=self.command,).run if len(self.Runresult['dark']) == 0 and len(self.Runresult['contacted']) == 0: return "No hosts found,请确认主机已经添加ansible环境!" except Exception,e: return str(e) return self.Runresult #返回执行结果
2)编写Saltstack组件ID为“1007”模块。
根据Saltstack组件模块的开发原理,通过调用cmd方法配置“cmd.run”与“cp.get_file”参数实现远程命令执行及文件远程同步,详细源码(部分)如下:
【/home/test/OMServer/modules/saltstack/Mid_1007.py】
def run(self): try: client = salt.client.LocalClient …… #调用Saltstack提供的API(cmd.run模块),执行远程命令 self.Runresult = client.cmd(self.hosts,'cmd.run',[self.command],\ expr_form='list') if len(self.Runresult) == 0: return "No hosts found,请确认主机已经添加saltstack环境!" except Exception,e: return str(e) return self.Runresult #返回执行结果
3)编写Func组件ID为“1007”模块。
根据Func组件模块的开发原理,通过调用client.command.run方法实现远程命令执行,使用client.copyfile.copyfile方法实现文件远程同步,详细源码(部分)如下:
【/home/test/OMServer/modules/func/Mid_1007.py】
def run(self): try: client = fc.Overlord(self.hosts) …… #调用Func提供的API(command.run模块),执行远程命令 commonname=str(self.sys_param_array[0]) self.Runresult=client.command.run(self.command) except Exception,e: return str(e) return self.Runresult #返回执行结果
任务模块编写完成后,启动服务端服务,运行以下命令:
# cd /home/test/OMServer # python OMservermain.py &
最后,打开浏览器访问http://omserver.domain.com,效果见图13-11。
图13-11 远程操作功能截图
参考提示 RC4加密算法参考文章http://www.snip2code.com/Snippet/27937/Blockout-encryption-decryption-methods-p。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论