一、批量运维管理器pexpect详解
1.1 模块功能介绍
pexpect可以理解成Linux下的expect的Python封装,
通过ppexpect我们可以实现对ssh、ftp、passwd、telnet等命令行进行自动交互.而无需人工干涉来达到自动化的目的。
比如我们可以模拟一个FTP登录时的所有交互,包括输入主机地址、用户名、密码、上传文件等,待出现异常我们还可以进行尝试自动处理。
1.2 pexpect的安装
pexpect作为Python的一个普通模块,支持pip、easy_install或源码安装方式,具体安装
命令如下(根据用户环境,自行选择pip或easy_install):
[root@python3 nmap_mode]
pycharm安装: C:\Users\LENOVO\AppData\Local\Programs\Python\Python311\python.exe -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pexpect
1.3 pexpect的核心组件
下面介绍pexpect的几个核心组件包括spawn类、run函数及派生类pxssh等的定义及使用方法。
1.3.1 pexpect–spawn类
def __init__(self, command, args=[], timeout=30, maxread=2000,
searchwindowsize=None, logfile=None, cwd=None, env=None,
ignore_sighup=False, echo=True, preexec_fn=None,
encoding=None, codec_errors='strict', dimensions=None,
use_poll=False):
"""
参数timeout为等待结果的超时时间;参数maxread为pexpect从终端控制台一次读取的
最大字节数,searchwindowsize参数为匹配缓冲区字符串的位立置,默认是从开始位置匹配。
"""
child = pexpect.spawn('/usr/bin/ftp')
child = pexpect.spawn('/usr/bin/ssh user@example.com')
child = pexpect.spawn('ls -latr /tmp')
child = pexpect.spawn('/usr/bin/ftp', [])
child = pexpect.spawn('/usr/bin/ssh', ['user@example.com'])
child = pexpect.spawn('ls', ['-latr', '/tmp'])
child = pexpect.spawn('/bin/bash -c "ls -l : grep LOG > logs.txt"')
child.expect(pexpect.EOF)
"""
当使用spawn()方法启动了一个程序并返回程序控制句柄后,就可以使用expect()方法来等待指定的关键字了。关键字可以是字符串、正则表达式、EOF、TIMEOUT或者以上类型组成的列表,用来匹配子程序返回的结果。如果只提供字符串等非列表,则匹配成功后返回0,如果提供列表,则返回匹配成功的列表元素的索引,匹配失败会抛出异常。
"""
shell_cmd= 'ls -l : grep LOG > logs.txt'
child= pexpect.spawn('/bin/bash',['-c', shell_cmd])
child.expect(pexpect.EOF)
child=pexpect.spawn('some_command')
fout=file('mylog.txt','w')
child.logfile=fout
child=pexpect.spawn('some_command')
child.logfile=sys.stdout
1.3.2 swpan–案例ssh
import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.183.131')
fout = open("mylog.txt",'wb')
child.logfile = fout
child.expect('password:')
child.sendline("1")
child.expect("#")
child.sendline('ls /root')
child.expect("#")
fout.close()
import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.183.131')
fout = open("mylog.txt",'wb')
child.logfile = fout
child.expect('password:')
child.sendline("1")
child.expect("#")
child.sendline('ls /root')
child.expect(pexpect.EOF)
fout.close()
1.3.2.1 swpan-except方法
(1)expect方法
expect定义了一个子程序输出的匹配规则。
方法定义:{expect(pattern,timeout=-1,searchwindowsize=-1)}
"""
其中,参数pattern表示字符串、pexpect.EOF(指向缓冲区尾部,无匹配项)、pexpect.TIMEOUT(匹配等待超时)、正则表达式或者前面四种类型维成的列表(List)
当pattern为一个列表时,且不止一个表列元素被匹配,则返回的结果是子程序输出最先出现的那个元素,或者是列表最左边的元素(最小索引ID)
"""
import pexpect
child = pexpect.spawn("echo 'foobar'")
print(child.expect(['bar','foo','foobar']))
"""
参数timeout指定等待匹配结果的超时时间,单位为秒。当超时被触发时,expect将匹配到pexpect.TIMEOUT;
参数searchwindowsize为匹配缓冲区字符串的位置,默认是从开始位置匹配。
当pexpect.EOF、pexpect.TIMEOUT作为expect的列表参数时,匹配时将返回所处列表中的索引ID,例如:
"""
import pexpect
child = pexpect.spawn("echo 'bad good'")
index = child.expect(['good','bad',pexpect.EOF,pexpect.TIMEOUT])
if index == 0:
print(0)
elif index == 1:
print(1)
elif index == 2:
print(2)
elif index == 3:
print(3)
以上代码等价于:
import pexpect
child = pexpect.spawn("echo 'asd 123 asda good'")
try:
index = child.expect(['x',pexpect.EOF,pexpect.TIMEOUT])
if index == 0:
print(f"{index}:{index}")
elif index == 1:
print(f"{index}:{index}")
except EOFError as e:
print(e)
except TimeoutError as e:
print(e)
"expect方法有两个非常棒的成员:before与after。before成员保存了最近匹配成功之前的内容,after成员保存了最近匹配成功之后的内容。"
例如:
import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.183.131')
fout = open("new_mylog.txt",'wb')
child.logfile = fout
child.expect(["password:"])
child.sendline("1")
print("before:"+str(child.before))
print("after:"+str(child.after))
+++++result:
[root@python3 pexpect_mode]
before:b"root@192.168.183.131's "
after:b'password:'
1.3.2.2 read相关方法
send(self,s) 发送命令,不回车
sendline(self,s='') 发送命令,回车
sendcontrol(self,char) 发送控制字符,如child.sendcontrol('c')等价于"ctrl+c"
sendeof() 发送eof
1.3.3 run函数
run是使用pexpect进行封装的调用外部命令的函数,类似于os.system或os.popen
方法,不同的是,使用run()可以同时获得命令的输出结果及命令的退出状态
函数定义:
pexpect.run(command, timeout=-1,withexitstatus=False,events=None, extra_args=None,logfile=None,cwd=None, env=None)。
--普通swapn方法
import pexpect
child = pexpect.spawn('/usr/bin/scp -r /root/python3/devops/pexpect_mode/mylog.txt root@192.168.183.131:/root')
fout = open("new_mylog.txt",'wb')
child.logfile = fout
child.expect(["password:"])
child.sendline("1")
child.expect(pexpect.EOF)
fout.close()
print("\033[33m命令执行完毕\033[0m")
print("before:"+str(child.before))
print("after:"+str(child.after))
--使用run函数方法:
from pexpect import *
fout = open("new_mylog.txt",'wb')
run('ssh root@192.168.183.131',events={'password':"1"},logfile=fout)
1.3.4 pxssh类
pxssh是pexpect的派生类,针对在ssh会话操作上再做一层封装,提共与基类更加直接的操作方法。
pxssh类定义:
class pexpect.pxssh.pxssh(timeout=30,maxread=2000, searchwindowsize=None,logfile=None,cwd=None, env=None)
"""
pxssh常用的三个方法如下:
login()建立ssh连接;
logout()断开连接;
prompt()等待系统提示符,用于等待命令执行结束。
"""
---案例simple代码:
from pexpect import pxssh
import getpass
ipaddr = '192.168.183.131'
username = 'root'
password = '1'
try:
s = pxssh.pxssh()
s.login(ipaddr,username,password)
s.sendline('uptime')
s.prompt()
print(s.before)
s.sendline('ls -l /root')
s.prompt()
print(s.before)
s.sendline('df -h')
s.prompt()
print(s.before)
s.logout()
except pxssh.ExceptionPxssh as e:
print("pxssh failed on login")
print(str(e))
1.4 pexpect项目代码
1.4.1 实现自动化ftp操作
"""
我们常用FTP协议实现自动化、集中式的文件备份,要求做到账号登录、文件上传与下
载、退出等实现自动化操作,本示例使用pexpect模块的spawnu(方法执行FTP命令,通过
expect()方法定义匹配的输出规则,sendline()方法执行相关FTP3交互命令等,详细源码如下:
"""
from __future__ import unicode_literals
"Python提供了__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性"
import pexpect
fout = open("run_ftp.txt",'wb')
child = pexpect.spawn('lftp 192.168.183.131 -u zhangsan',timeout=5)
child.expect('(?i)password')
child.sendline('123456')
child.expect('(?i)lftp.*>')
child.sendline('pwd')
child.expect('(?i)lftp.*>')
child.sendline('ls')
child.logfile = fout
child.interact()
---单纯下载文件!!
from __future__ import unicode_literals
"Python提供了__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性"
import pexpect
import time
import datetime
bak_time = datetime.datetime.now().strftime('%Y-%m-%d_%H')
fout = open("run_ftp.txt",'wb')
child = pexpect.spawn('lftp 192.168.183.131 -u zhangsan',timeout=5)
child.expect('(?i)password')
child.sendline('123456')
time.sleep(1)
child.logfile = fout
child.expect('(?i)lftp.*>')
time.sleep(1)
child.sendline("get /tmp/%s.sql" % bak_time)
child.expect('(?i)lftp.*>')
child.sendline('exit')
child.logfile = fout
child.expect(pexpect.EOF)
print("\033[33m获取文件完毕!\033[0m")
child.close()
1.4.2 远程文件自动打包并下载
import pexpect
import os
import datetime
import smtplib
from email.mime.text import MIMEText
from email.header import Header
bak_time = datetime.datetime.now().strftime('%Y-%m-%d_%H')
ip = '192.168.183.131'
user = 'root'
password = '1'
def back_up():
print("\033[32m自动备份和推送开始!!\033[0m")
child = pexpect.spawn('/usr/bin/ssh', [user + '@' + ip], timeout=5)
fout = open('sql_back.log', 'wb')
child.logfile = fout
target_file = f'/tmp/{bak_time}.sql'
try:
child.expect('(?i)password')
child.sendline(password)
child.expect("#")
child.sendline(f'tar -zcPf {bak_time}.sql.tar.gz ' + target_file)
child.expect('#')
child.sendline('exit')
child.expect(pexpect.EOF)
print("\033[34m备份完成!!\033[0m")
fout.close()
except EOFError as e:
print(f"\033[31m备份异常{e}\033[0m")
except Exception as e:
print(f"\033[31m备份异常:{e}\033[0m")
except TimeoutError as e:
print(f"\033[31m备份异常{e}\033[0m]")
return True
def copy():
print("\033[32m自动获取备份文件开始!\033[0m")
child = pexpect.spawn(f'/usr/bin/scp -r -P22 root@192.168.183.131:/root/{bak_time}.sql.tar.gz /root/',timeout=5)
fout = open('sql_back.log','ab+')
child.logfile = fout
try:
child.expect('(?i)password')
child.sendline('1')
child.expect(pexpect.EOF)
print("\033[36m文件获取完毕!\033[0m")
fout.close()
except EOFError as e:
print(f"\033[31mscp异常{e}\033[0m")
except Exception as e:
print(f"\033[31mscp异常{e}\033[0m")
except TimeoutError as e:
print(f"\033[31mscp异常{e}\033[0m]")
return True
def smtp():
file = f'/root/{bak_time}.sql.tar.gz'
mail_host = "smtp.163.com"
mail_user = "lisi@163.com"
mail_pass = "********"
sender = "lisi@163.com"
receviers = ['lisi@163.com','zhangsan@163.com']
message = MIMEText(f'mysql备份完成:\nip:{ip}\nfile:{file}', 'plain', 'utf-8')
message['From'] = Header("mysql")
message['To'] = Header("mysql")
subject = f'mysql备份!!!'
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25)
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(sender, receviers, message.as_string())
print("\033[36m邮件发送成功!\033[0m")
except smtplib.SMTPException:
print("\033[31m邮件发送失败!!\033[0m")
if __name__ == '__main__':
if back_up():
copy()
if os.path.exists(f"/root/{bak_time}.sql.tar.gz"):
print("\033[32m自动备份推送完成!!!\033[0m")
smtp()
else:
print("\033[32m自动备份推送失败!!!\033[0m")
exit(12)