五、批量运维管理器pexpect详解

一、批量运维管理器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]# pip install pexpect             --linux安装,直接pip说明配置了本地的pip源
    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类

#spawn是pexpect的主要类接口,功能是启动和控制子应用程序,以下是它的构造函数
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参数为匹配缓冲区字符串的位立置,默认是从开始位置匹配。
"""
#其中command参数可以是任意已知的系统命令:
            child = pexpect.spawn('/usr/bin/ftp')                     ##启动ftp客户端命令
            child = pexpect.spawn('/usr/bin/ssh user@example.com')    #启动ssh远程连接
            child = pexpect.spawn('ls -latr /tmp')                    #运行ls命令
            
#当子程序需要参数时,还可以使用Python列表来代替参数项,如:
			child = pexpect.spawn('/usr/bin/ftp', [])
            child = pexpect.spawn('/usr/bin/ssh', ['user@example.com'])
            child = pexpect.spawn('ls', ['-latr', '/tmp'])
            
#pexpect不会解析shell命令当中的元字符,包括重定向">"、管道":"或通配符"*",当然,我们可以通过一个技巧来解决这个问题,将存在这三个特殊元字符的命令作为/bin/bash的参数进行调用,例如:
 child = pexpect.spawn('/bin/bash -c "ls -l : grep LOG > logs.txt"')
 child.expect(pexpect.EOF)
"""
当使用spawn()方法启动了一个程序并返回程序控制句柄后,就可以使用expect()方法来等待指定的关键字了。关键字可以是字符串、正则表达式、EOF、TIMEOUT或者以上类型组成的列表,用来匹配子程序返回的结果。如果只提供字符串等非列表,则匹配成功后返回0,如果提供列表,则返回匹配成功的列表元素的索引,匹配失败会抛出异常。
"""

#我们可以通过将命令的参数以Python列表的形式进行替换,从而使我们的语法变成更加清晰,下面的代码等价于上面的。
shell_cmd= 'ls -l : grep LOG > logs.txt'
child= pexpect.spawn('/bin/bash',['-c', shell_cmd])
child.expect(pexpect.EOF)  #如果捕捉到pexpect.EOF则说明子程序已退出。命令执行完成会返回eof

#有时候调试代码时,希望获取pexpect的输入与输出信息,以便更了解匹配的情况pexpect提供了两种途径,一种为写到日志文件,另一种为输出到标准输出。写到日志文件的
#实现方法如下:
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')  #启动ssh远程连接
fout = open("mylog.txt",'wb')                      #打开文件,以二进制模式打开并写入
child.logfile = fout                               #标准输入输出写入到文件中
child.expect('password:')                          #如果出现password字眼
child.sendline("1")                                #交互输入密码
child.expect("#")                                  #如果遇到#号代表已连接
child.sendline('ls /root')                         #输出shell指令
child.expect("#") 								   #匹配到#代表指令结束
fout.close()


import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.183.131')  #启动ssh远程连接
fout = open("mylog.txt",'wb')                      #打开文件,以二进制模式打开并写入
child.logfile = fout                               #标准输入输出写入到文件中
child.expect('password:')                          #如果出现password字眼
child.sendline("1")                                #交互输入密码
child.expect("#")                                  #如果遇到#号代表已连接
child.sendline('ls /root')                         #输出shell指令
child.expect(pexpect.EOF)                          #捕捉到pexpect.EOF则说明子程序已退出。命令执行完成会返回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'")           #定义pexpect对象,子程序为输出foobar
print(child.expect(['bar','foo','foobar']))      #匹配列表中内容在子程序中最先出现的那个元素.
#result:1     匹配到foo,因为foo为子程序中的第一个出现的元素.

"""
参数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')   #启动ssh远程连接
fout = open("new_mylog.txt",'wb')                   #定义获取输入输出信息方法
child.logfile = fout                                #定义输入输出的文件
child.expect(["password:"])                         #匹配password字符
child.sendline("1")                                  #匹配成功返回1
print("before:"+str(child.before))                  #获取匹配前的内容,也就是一开始的子程序接口
print("after:"+str(child.after))                    #获取匹配后的内容,也就是except匹配字符的内容
+++++result:
[root@python3 pexpect_mode]# py test.py 
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
##sendline()和send()的区别就是sendline()发送的是带回车符的字符串。

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)#参数command可以是系统已知的任意命令,如没有写绝对路径时将会尝试搜索命令的路径.
    #events是一个字典,定义了expect及sendline方法的对应关系,spawn方式的例子如下:
--普通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:"])  			 #匹配password
child.sendline("1")                      #命中输入密码
child.expect(pexpect.EOF)                #检测的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()       #创建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()                  #断开ssh连接
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 #使用unicode编码
"Python提供了__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性"
#如果你是python3请忽略
import pexpect
fout = open("run_ftp.txt",'wb')
child = pexpect.spawn('lftp 192.168.183.131 -u zhangsan',timeout=5) #运行ftp命令
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()  #调用interact()让出控制权



---单纯下载文件!!
from __future__ import unicode_literals #使用unicode编码
"Python提供了__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性"
#如果你是python3请忽略
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) #运行ftp命令
child.expect('(?i)password') #匹配密码输入提示,正则匹配不区分大小写
child.sendline('123456')  #输入密码
time.sleep(1)     #连接ftp需要时间
child.logfile = fout
child.expect('(?i)lftp.*>')
time.sleep(1)
child.sendline("get /tmp/%s.sql" % bak_time)  #获取mysql的备份文件
# child.interact()
child.expect('(?i)lftp.*>')     #不区分大小写的lftp后面为任意字符
child.sendline('exit')
child.logfile = fout
child.expect(pexpect.EOF)
print("\033[33m获取文件完毕!\033[0m")
child.close()

1.4.2 远程文件自动打包并下载

#!/usr/bin/evn python
# -*- coding: utf-8 -*-
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)  # 运行ssh程序
    fout = open('sql_back.log', 'wb')  # 执行日志放到log里
    child.logfile = fout
    target_file = f'/tmp/{bak_time}.sql'  # 获取sql备份文件
    try:
        child.expect('(?i)password') #匹配password字符串不区分大小写
        child.sendline(password)
        child.expect("#")            #匹配#号代表连接成功
        child.sendline(f'tar -zcPf {bak_time}.sql.tar.gz ' + target_file)  #打包mysql备份
        # print(child.before)  # 输出匹配前的动作
        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 = "********"  # 此处为pop密码
    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)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

运维神经科主任

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值