Python|Python MySSH 实现剧本执行器

通过封装Paramiko这个SSH模块,我们可以实现远程批量管理Linux主机,在上一篇文章中我们封装过一个MySSH类,这个类可以执行命令上传下载文件等,我们在这个类的基础上,实现一个简单的任务执行功能。

  • 执行方式
  • 程序会在Json文件中解析参数,并将参数与所对应的主机进行关联,对不同的主机组执行不同的命令,实现批量脚本执行。

实现批量命令执行: 首先利用封装好的MySSH类为基础,实现一个批量命令执行器,该工具通过命令行参数传递执行不同的操作。
import os,json,sys from MySSH import MySSHdef ping(group): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group) print("-" * 120) print("{0:15} \t {1:6} \t {2:5}".format("IP地址", "用户名", "状态")) print("-" * 120)for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() ref = ssh.GetPing() if ref == True: print("{0:15} \t {1:6} \t {2:5}".format(x[0],x[1],"已连接")) else: print("{0:15} \t {1:6} \t {2:5}".format(x[0], x[1], "未连接")) print("\n")def run(group,command): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group) for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() ref = ssh.BatchCMD(command) print("\n") print("-" * 120) print("执行IP: {0:15} ".format(x[0])) print("-" * 120) print(ref)def memory(group): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group) print("-" * 120) print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "总内存", "剩余内存", "利用率(百分比)")) print("-" * 120)for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() ref = ssh.GetAllMemSpace() if ref != None: print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0],ref["Total"],ref["Free"],ref["Percentage"]))def disk(group): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group)for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() print("-" * 120) print("IP地址: {0:15} \t".format(x[0])) print("-" * 120)ref = ssh.GetAllDiskSpace() if len(ref) !=0: for k,v in ref.items(): print("磁盘路径: {0:30} \t 磁盘利用率: {1:8}".format(k,v))def cpu(group): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group) print("-" * 120) print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "用户态", "内核态", "空闲率(百分比)")) print("-" * 120)for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() ref = ssh.GetCPUPercentage() if len(ref)!=0: print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], ref["us"], ref["sys"], ref["idea"]))def load_avg(group): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group) print("-" * 120) print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "一分钟负载", "五分钟负载", "十五分钟负载")) print("-" * 120)for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() ref = ssh.GetLoadAVG() if len(ref)!=0: print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], ref["1avg"], ref["5avg"], ref["15avg"]))def checkproc(group,proc): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group) print("-" * 120) print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "PID号","CPU占用率", "内存占用率")) print("-" * 120)for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() ref = ssh.CheckProcessName(proc) if len(ref): print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], ref["PID"], ref["CPU"], ref["Mem"])) else: print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], 0, 0, 0))def put_group(group,src,dst): with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) ptr = config_load.get(group) print("-" * 120) print("{0:15} \t {1:7} \t {2:7} \t ".format("IP地址", "源文件","传输到")) print("-" * 120)for x in ptr: ssh = MySSH(x[0],x[1],x[2],22) ssh.Init() ref = ssh.PutLocalFile(src,dst) if ref: print("{0:15} \t {1:7} \t {2:7} \t ".format(x[0], src,dst))if __name__ == '__main__': while True: try: cmd = str(input("[LyShark Shell] # ")).split() cmd_len = len(cmd) if (cmd == ""): continue elif (cmd[0] == "exit"): exit(1)# ping --group=aix elif (cmd[0] == "ping"): if (cmd_len - 1 >= 1): arg = cmd[1].split("=")[1] ping(arg)# run --group=aix --cmd=ls elif (cmd[0] == "run"): if (cmd_len - 1 >= 2): arg1 = cmd[1].split("=")[1] arg2 = cmd[2].split("=")[1] run(arg1,arg2)# memory --group=aix elif (cmd[0] =="memory"): if (cmd_len - 1 >= 1): arg1 = cmd[1].split("=")[1] memory(arg1)# disk --group=aix elif (cmd[0] =="disk"): if (cmd_len - 1 >= 1): arg1 = cmd[1].split("=")[1] disk(arg1)# cpu --group=aix elif (cmd[0] =="cpu"): if (cmd_len - 1 >= 1): arg1 = cmd[1].split("=")[1] cpu(arg1)# load --group=aix elif (cmd[0] =="load"): if (cmd_len - 1 >= 1): arg1 = cmd[1].split("=")[1] load_avg(arg1)# checkproc --group=aix --process=bash elif (cmd[0] =="checkproc"): if (cmd_len - 1 >= 1): arg1 = cmd[1].split("=")[1] arg2 = cmd[2].split("=")[1] checkproc(arg1,arg2)# put_group --group=aix --src=https://www.it610.com/article/aaa.txt --dst=/tmp/aaa.txt elif (cmd[0] =="put_group"): if (cmd_len - 1 >= 3): arg1 = cmd[1].split("=")[1] arg2 = cmd[2].split("=")[1] arg3 = cmd[3].split("=")[1] put_group(arg1,arg2,arg3) else: print("[-] error version 1.0") except Exception: continue

解析文件config.json配置如下所示,每个组中包括一定数量的机器。
{ "aix": [ ["127.0.0.1","root","1233"], ["127.0.0.1","root","123456"]], "suse": [ ["127.0.0.1","root","123123123"] ], }

程序运行后会进入交互Shell环境,我们可以根据需要执行不同的key获取数据。
Python|Python MySSH 实现剧本执行器
文章图片

此外脚本还支持如下参数.
  • 运行命令: run --group=aix --cmd=ls
  • 内存检查: memory --group=aix
  • 磁盘检查: disk --group=aix
  • CPU检查: cpu --group=aix
  • 负载检查: load --group=aix
  • 进程检查: checkproc --group=aix --process=bash
  • 文件上传: put_group --group=aix --src=https://www.it610.com/article/aaa.txt --dst=/tmp/aaa.txt
剧本执行器,这部分内容为扩展部分,我们定义两个函数,函数DisplayAllRule用来获取特定目录下的特定剧本,而RunRule函数则用于解析这个剧本并执行剧本中的命令集合。
import MySSH import os,json,sys# 获取特定目录下所有的剧本 def DisplayAllRule(): print("{0:15} \t {1:10} \t {2:10} \t {3:15} \t {4:5} \t {5:30}". format("名称","应用平台","端口","主机组","命令条数","描述信息"))for switch in all_files: # 首先判断文件结尾是否为Json if( switch.endswith(".json") == True): all_switch_dir = rootdir + switch try: # 判断文件内部是否符合JSON规范 with open(all_switch_dir , "r" ,encoding="utf-8") as read_file: # 判断是否存在指定字段来识别规范 load = json.loads(read_file.read()) if load.get("framework") != None and load.get("task_sequence") != None: print("{0:15} \t {1:10} \t {2:10} \t {3:15} \t {4:5} \t\t {5:30}". format(switch,load.get("framework"),load.get("default_port"),load.get("Group"),len(load.get("task_sequence")),load.get("describe"))) except ValueError: pass# 执行命令行 def RunRule(rule_name): # 先打开配置恩建并读取到数据 with open(config_dir , "r" ,encoding="utf-8") as read_config_ptr: config_load = json.loads(read_config_ptr.read()) # 接着读取选中剧本 with open(rule_dir,"r",encoding="utf-8") as read_rule_ptr: rule_load = json.loads(read_rule_ptr.read()) # 先找到组名称,并以组名称查询组内主机数 ref = config_load.get(rule_load.get("Group")) if ref != None: # 加载剧本中的任务命令 task_sequence = rule_load.get("task_sequence")# 循环执行针对组内主机 for addr in ref: print("-" * 130 , "\n针对地址执行: {}\n".format(addr[0]),"-" * 130) # 每个主机需要执行的命令 for cmd in task_sequence: ssh = MySSH.MySSH(addr[0],addr[1],addr[2],int(rule_load.get("default_port"))) ssh.Init() if cmd[0] == "PUT" and len(cmd) >= 3: if ssh.PutLocalFile(cmd[1],cmd[2]): print("命令序列: {0} {1}".format("PUT",cmd[2])) else: break else: ret = ssh.BatchCMD_NotRef(cmd[0]) print("命令序列: {0}".format(cmd[0])) else: print("[-] 主机组不存在,无法继续执行.") exit(0)if __name__ == "__main__": arg = sys.argv if arg[1] == "display": DisplayAllRule() elif arg[1] == "run": RunRule(arg[2])

文件规划put_file目录用于存放需要上传的文件,rule目录用来存放执行剧本内容,我们先来看一个编译安装Apache服务器的剧本写法。
{ "framework": "Linux", "default_port": "22", "describe": "编译安装Apache组件", "Group": "MyWebServer", "task_sequence": [ ["iptables -F"], ["setenforce 0"], ["yum -y install gcc make pcre-devel openssl-devel expat-devel bzip2"],["PUT","./put_file/httpd-2.4.46.tar.gz","/tmp/apache.tar.gz"], ["PUT","./put_file/apr-1.7.0.tar.bz2","/tmp/apr-1.7.0.tar.bz2"], ["PUT","./put_file/apr-util-1.6.1.tar.bz2","/tmp/apr-util-1.6.1.tar.bz2"],["tar -xzf /tmp/apache.tar.gz -C /tmp/"], ["tar -xf /tmp/apr-1.7.0.tar.bz2 -C /tmp/"], ["tar -xf /tmp/apr-util-1.6.1.tar.bz2 -C /tmp/"],["mv /tmp/apr-util-1.6.1 /tmp/httpd-2.4.46/srclib/apr-util"], ["mv /tmp/apr-1.7.0 /tmp/httpd-2.4.46/srclib/apr"],["/tmp/httpd-2.4.46/configure --prefix=/tmp/httpd --with-zlib -with-included-apr"], ["make && make install"],["echo 'hello lyshark' > /tmp/httpd/htdocs/index.html"], ["/tmp/httpd/bin/httpd"] ] }

【Python|Python MySSH 实现剧本执行器】如上剧本中的Group字段则是需要执行编译安装的所属组,该组内存放执行地址,来看一下组的规划。
{ "MyWebServer": [ ["192.168.191.4","root","1233"], ["192.168.191.5","root","1233"], ["192.168.191.6","root","1233"] ] }

我们首先可以执行main.py display命令,获取当前设备中的所有剧本信息。
Python|Python MySSH 实现剧本执行器
文章图片

在需要执行时输入main.py run test.json尾部加上剧本名字即可。
Python|Python MySSH 实现剧本执行器
文章图片

    推荐阅读