安福子偷懒必备技能的自动化主机检查脚本
一、简介

去现场之前,老板说客户想运行主机查看脚本,但是服务器很多,上百台,询问有没有办法一键发送,然后执行脚本运行,运行完后回收数据,我听这个需求,这不是只要有手就行吗? 然后我就同意了,并说我会尝试一下。 我以为写个py脚本就很简单了。 确认了需求后远程主机在线测评,我就直接开始了。 一开始我写的是使用ssh服务来批量进行。
二、python实现
首先确定流程,一般流程是这四个:
连接服务器上传脚本执行脚本回收数据
最重要的是先连接起来,不然你想的再好也没用。 如果要连接的话,我用的是py的paramiko,功能很强大。 Paramiko 是一个用 py 编写的模块。 远程连接Linux服务器并查看日志状态,可以使用远程服务器的批量配置、文件上传、文件下载等。 另外我还用pandas。 毕竟如果要批量,就需要读取表数据,从表中获取各个主机的信息。 paramiko的连接有几个坑。 首先是连接。 连接方式有很多种。 首先分为两类,一类是SSH,一类是FTP,每一类有两种连接。 有两种方式,一种是基于密码的,另一种是基于密钥的。 先说一下SSH连接:
ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("IP",22,"user", "password")
在这种情况下,无法上传和下载文件。 如果我们要上传文件、回收数据,就没有办法使用这个方法了。 带上小弟。 所以这里我使用的是Transport,这样连接之后可以实现很多功能。 可以说是一个小腻子。
关键代码
import paramiko import pandas as pd import time #获取表格数据 df = pd.read_excel('server_information.xlsx') data=df.values #获取单列长度,之后可以作为循环次数的依据 L = len(df) #------------------------------------------------------------------------------------------------------------ #这里做了个分割,是因为下面这块我把for去掉了,上面的表格是需要循环的时候用的,下面针对单个服务器进行说明 IP = data[i][0] port = data[i][1] name=data[i][2] password=data[i][3] # 实例化一个transport对象 trans = paramiko.Transport((IP, port)) # 建立连接 trans.connect(username=name, password=password) # 将sshclient的对象的transport指定为以上的trans ssh = paramiko.SSHClient() ssh._transport = trans sftp = paramiko.SFTPClient.from_transport(trans) #下面是执行了获取IP的命令,因为回收的数据命名格式有根据本机IP来,所以这里先获取下IP # stdout 为正确输出,stderr为错误输出,同时是有1个变量有值 stdin, stdout, stderr = ssh.exec_command('ip a|grep inet|grep brd') # inet 10.0.20.12/22 brd 10.0.23.255 scope global eth0 # 打印执行结果 i_IP = stdout.read().decode('utf-8') Intranet_IP_str= i_IP.split() Intranet_IP = Intranet_IP_str[1].split('/') # 10.0.20.12 22
这两种连接方式我都是使用账号和密码来连接和登录的。 也可以使用密钥。 详细可以去百度下载使用。 事实上,它们几乎是一样的。 连接后,您认为已经完成了。 你确实可以上传和下载文件,但仍然存在陷阱。 这里令人愤慨的一点是,连接后你无法进入其他文件夹。 也就是说,你始终在根目录下面。
找了好久,看了exec_command的说明才发现,每次执行完后,都会跳回原来的目录。 因为功能的原因,他会自己返回到原来的目录,这个比较困难。 如何转到我想要执行文件的文件夹? 我尝试了它按照路径执行文件,但是尝试了几次之后发现不行,它只支持执行这个目录下的文件。
搜索了一段时间,发现可以cd到其他文件夹,但是需要写其他东西。 然后,为了偷懒,我开始尝试一次执行多个命令。 毕竟两个命令搞不定,所以我就用一个命令搞定了。 但拼接并不是我们用的那种&&。 该函数的独特之处在于它带有执行多个命令的函数。 使用 ”;” 在一个命令之后将命令分开并将它们视为有两个命令,例如
stdin, stdout, stderr = ssh.exec_command('cd tmp;ls')
它会先cd到tmp目录,然后执行ls命令。 此时文件无法上传下载,也无法跳转目录。 两个坑基本填完了,完整的数据贴在这里。 该表的格式为IP、端口、用户名、密码。
import paramiko import pandas as pd import time df = pd.read_excel('server_information.xlsx') data=df.values L = len(df) print("检测到当前主机数:") print(L) for i in range(0,L): IP = data[i][0] port = data[i][1] name=data[i][2] password=data[i][3] # 实例化一个transport对象 trans = paramiko.Transport((IP, port)) # 建立连接 trans.connect(username=name, password=password) # 将sshclient的对象的transport指定为以上的trans ssh = paramiko.SSHClient() ssh._transport = trans sftp = paramiko.SFTPClient.from_transport(trans) stdin, stdout, stderr = ssh.exec_command('ip a|grep inet|grep brd') # inet 10.0.20.12/22 brd 10.0.23.255 scope global eth0 i_IP = stdout.read().decode('utf-8') Intranet_IP_str= i_IP.split() Intranet_IP = Intranet_IP_str[1].split('/') # 10.0.20.12 22 #localpath—本地文件地址,remotepath——服务器存放地址 sftp.put(localpath='/home/a.sh', remotepath='/tmp/a.sh') stdin, stdout, stderr = ssh.exec_command('cd tmp;./a.sh') Host_information = stdout.read().decode('utf-8') time.sleep(10) sftp.get(localpath=Intranet_IP[0]+'.xml', remotepath='/tmp/'+Intranet_IP[0]+'.xml') #sftp.get—下载文件,sftp.put—上传文件 print(IP+":已完成")
当我兴高采烈地把过去的事情提交给客户的时候,不出意外就会有意外。 客户说他想要一个shell脚本,啊,这个可以啊! 这只是外壳。 这次我会先询问客户开放的端口和服务,以及我需要做什么。 我们先了解清楚,然后经过一番讨价还价,客户说他这边有一个平台,可以批量上传运行文件后,就叫我实现回收,那这个就不好办了。
三、shell脚本实现
回到脚本,已知客户需要的是一个shell脚本,但是不知道怎么做,那么该怎么办呢? 我还能做什么,我现在正在学习,然后我就看了一下shell编程。 看完基础语法慈云数据自营海外云服务器,高稳定高性价比,支持弹性配置,我想我还能再做一遍。 首先,我想到了使用ftp服务。 毕竟这样就可以满足所有需求了。 然后我建立了一个ftp服务。 搞完之后发现shell还支持连接其他服务器的功能,而且写法有很多种。 写起来并不难。
#!/bin/bash #用户名 user= #密码 password= #本地存在这个文件的目录 local_url=/tmp #上传的目的目录 server_url=/home #服务器IP ip= #端口 port=22 cd ${local_url}; #定位在50分钟内生成的xml文件,这个可以根据自己的需求来改需要回收的数据类型 files=`find ${local_url} -mmin -50 -name '*.xml'` for file in ${files} do echo ${file} #建立ftp连接 lftp -u ${user},${password} sftp://${ip}:${port} <
至此,我以为大功告成了,就高兴地送了过去,看准了点,三个踮起脚尖,先喝茶,点了一杯奶茶。 如果没有意外,就会有意外。 客户说他没有lftp的服务,问我是否可以使用SCP服务。 之前用的SCP服务,喝到的奶茶突然不好吃了,没关系! 不就是个SCP吗,开始吧! 也就是改变服务。
#!/bin/bash #用户名 user= #本地存在这个文件的目录 local_url=/tmp #上传的目的目录 server_url=/home #服务器IP ip= #端口 port=22 cd ${local_url} files=`find ${local_url} -mmin -50 -name '*.xml'` for file in ${files} do echo ${file} scp -P ${port} ${file} ${user}@${ip}:${server_url} <
实现了这个功能,但是与FTP不同的是,FTP可以在命令中添加密码,而SCP需要自己输入密码。
lftp -u ${user},${password} sftp://${ip}:${port} <
客户看到还有一个问题,啊,你可以让他自己输入密码吗,我不支持输入密码。 没关系,用expect写个监控关键字就可以了。 没想到就是因为我的一句话,让我秃了两天。
#!/usr/bin/expect #ip=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print }'|tr -d "addr:"` spawn scp -P 22 /home/ax/a.xml 用户名@服务器IP:/tmp set timeout 20 #监测下面的命令行中有没有password这个关键字 expect "password" exec sleep 1 #监测到了就发送密码 send "密码\r" interact
这只是功能的一部分。 上面说了,运行主机后的数据文件是带有本地IP的,所以我们需要找到我们的本地IP,这就是关键点。 如果我想使用
ip=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print }'|tr -d "addr:"`
如果用这个命令来查找IP,我需要使用bash解释器中的东西远程主机在线测评,而我使用expect时需要使用/usr/bin/expect解释器,而这两个不能在上面直接引用。 也就是说,一个shell脚本不能直接使用两个解释器。
#!/usr/bin/expect #!/bin/bash
我这两天一直在寻找解决方案,什么都试过了,包括文件、命令行参数等等,但都无济于事。 我什至午休的时候睡在椅子上,梦想着其他的解决方案,然后醒来,当我打算告诉客户使用ftp时,我无意中看到了一个代码块,里面有一个词抓住了我的心。注意,嵌入式! ! ! ! ! ! 当时我就觉得有什么有趣的地方,好像我要找的路就是他,于是我仔细查看了一下嵌入类型是什么,果然! ! ! ! 我只需要将需要使用解释器的代码部分嵌入到另一个解释器的代码中,就这样。
#!/bin/bash local_ip=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print }'|tr -d "addr:"` #用户名 user= #密码 password= #本地存在这个文件的目录,格式例如/home/ax/ local_url= #上传的目的目录,格式:/tmp server_url= #端口 port=22 #服务器IP server_ip= #这里一定要加<
这样,过滤文件的方法就已经自动输入密码了。 其实如果你想实现那些自动上传脚本,也可以执行这些。 只要两个解释器命令可以在同一个脚本中执行,其他一切都可以。 解决起来很简单,设计一个逻辑思路就可以了,不过这是免费妓女的服务,需求少了那就需求少了。 不过客户用完后可以直接调用,但是他还是想用第一个版本的py脚本~
还没有评论,来说两句吧...