第十八章 Python批量管理主机(paramiko、fabric与pexpect)-阿里云开发者社区

开发者社区> 李振良> 正文

第十八章 Python批量管理主机(paramiko、fabric与pexpect)

简介:
+关注继续查看
本章节主要讲解运维工程师比较感兴趣的知识,那就是运维批量管理,在Python下有paramiko、fabric和pexpect这三个模块可帮助运维实现自动化部署、批量执行命令、文件传输等常规任务,接下来一起看看它们的使用方法吧!
18.1 paramiko
paramiko模块是基于Python实现的SSH远程安全连接,用于SSH远程执行命令、文件传输等功能。
默认Python没有,需要手动安装:pip install paramiko
如安装失败,可以尝试yum安装:yum install python-paramiko
18.1.1 SSH密码认证远程执行命令
#!/usr/bin/python
# -*- coding: utf-8 -*-
import paramiko
import sys
hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
client = paramiko.SSHClient()  # 绑定实例
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, port, username, password, timeout=5)
stdin, stdout, stderr = client.exec_command('df -h')   # 执行bash命令
result = stdout.read()
error = stderr.read()
# 判断stderr输出是否为空,为空则打印执行结果,不为空打印报错信息
if not error:
   print result
else:
   print error
client.close()
18.1.2 私钥认证远程执行命令
#!/usr/bin/python
# -*- coding: utf-8 -*-
import paramiko
import sys
hostname = '192.168.1.215'
port = 22
username = 'root'
key_file = '/root/.ssh/id_rsa'
cmd = " ".join(sys.argv[1:])
def ssh_conn(command):
    client = paramiko.SSHClient()
    key = paramiko.RSAKey.from_private_key_file(key_file)
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname, port, username, pkey=key)
    stdin, stdout, stderr = client.exec_command(command)  # 标准输入,标准输出,错误输出
    result = stdout.read()
    error = stderr.read()
    if not error:
        print result
    else:
        print error
    client.close()
if __name__ == "__main__":
    ssh_conn(cmd)
18.1.3 上传文件到远程服务器
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys
import paramiko

hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
local_path = '/root/test.txt'
remote_path = '/opt/test.txt'
if not os.path.isfile(local_path):
    print local_path + " file not exist!"
    sys.exit(1)
try:
    s = paramiko.Transport((hostname, port))
    s.connect(username = username, password=password)
except Exception as e:
    print e
    sys.exit(1)
sftp = paramiko.SFTPClient.from_transport(s)
# 使用put()方法把本地文件上传到远程服务器
sftp.put(local_path, remote_path)       
# 简单测试是否上传成功
try:
    # 如果远程主机有这个文件则返回一个对象,否则抛出异常               
    sftp.file(remote_path) 
    print "上传成功."
except IOError:
    print "上传失败!"
finally:
    s.close()
18.1.4 从远程服务器下载文件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys
import paramiko

hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
local_path = '/root/test.txt'
remote_path = '/opt/test.txt'
try:
    s = paramiko.Transport((hostname, port))
    s.connect(username=username, password=password)
    sftp = paramiko.SFTPClient.from_transport(s)
except Exception as e:
    print e
    sys.exit(1)

try:
    # 判断远程服务器是否有这个文件
    sftp.file(remote_path)
    # 使用get()方法从远程服务器拉去文件
    sftp.get(remote_path, local_path)       

except IOError as e:
    print remote_path + "remote file not exist!"
    sys.exit(1)
finally:
    s.close()

# 测试是否下载成功
if os.path.isfile(local_path):
    print "下载成功."
else:
    print "下载失败!"
18.1.5 上传目录到远程服务器
paramiko模块并没有实现直接上传目录的类,已经知道了如何上传文件,再写一个上传目录的代码就简单了,利用os库的os.walk()方法遍历目录,再一个个上传:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys
import paramiko

hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
local_path = '/root/abc'
remote_path = '/opt/abc'

# 去除路径后面正斜杠
if local_path[-1] == '/':
    local_path = local_path[0:-1]
if remote_path[-1] == '/':
    remote_path = remote_path[0:-1]

file_list = []
if os.path.isdir(local_path):
    for root, dirs, files in os.walk(local_path):
        for file in files:
            # 获取文件绝对路径
            file_path = os.path.join(root, file) 
            file_list.append(file_path)
else:
    print path + "Directory not exist!"
    sys.exit(1)

try:
    s = paramiko.Transport((hostname, port))
    s.connect(username=username, password=password)
    sftp = paramiko.SFTPClient.from_transport(s)
except Exception as e:
    print e

for local_file in file_list:
    # 替换目标目录
    remote_file = local_file.replace(local_path, remote_path)
    remote_dir = os.path.dirname(remote_file)
    # 如果远程服务器没目标目录则创建
    try:
        sftp.stat(remote_dir)
    except IOError:
        sftp.mkdir(remote_dir)
    print "%s -> %s" % (local_file, remote_file)
    sftp.put(local_file, remote_file)
s.close()
sftp是安全文件传输协议,提供一种安全的加密方法,sftp是SSH的一部分,SFTPClient类实现了sftp客户端,通过已建立的SSH通道传输文件,与其他的操作,如下:
sftp.getcwd()
返回当前工作目录
sftp.chdir(path)
改变工作目录
sftp.chmod(path, mode)
修改权限
sftp.chown(path, uid, gid)
设置属主属组
sftp.close()
关闭sftp
sftp.file(filename, mode='r', bufsize=-1)
读取文件
sftp.from_transport(s)
创建SFTP客户端通道
sftp.listdir(path='.')
列出目录,返回一个列表
sftp.listdir_attr(path='.')
列出目录,返回一个SFTPAttributes列表
sftp.mkdir(path, mode=511)
创建目录
sftp.normalize(path)
返回规范化path
sftp.open(filename, mode='r', bufsize=-1)
在远程服务器打开文件
sftp.put(localpath, remotepath, callback=None)
localpath文件上传到远程服务器remotepath
sftp.get(remotepath, localpath, callback=None)
从远程服务器remotepath拉文件到本地localpath
sftp.readlink(path)
返回一个符号链接目标
sftp.remove(path)
删除文件
sftp.rename(oldpath, newpath)
重命名文件或目录
sftp.rmdir(path)
删除目录
sftp.stat(path)
返回远程服务器文件信息(返回一个对象的属性)
sftp.truncate(path, size)
截取文件大小
sftp.symlink(source, dest)
创建一个软链接(快捷方式)
sftp.unlink(path)
删除软链接

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)


18.2 fabric
fabric模块是在paramiko基础上又做了一层封装,操作起来更方便。主要用于多台主机批量执行任务。
默认Python没有,需要手动安装:pip install fabric
如安装失败,可以尝试yum安装:yum install fabric
Fabric常用API:
API类
描述
示例
local
执行本地命令
local('uname -s')
lcd
切换本地目录
lcd('/opt')
run
执行远程命令
run('uname -s')
cd
切换远程目录
cd('/opt')
sudo
sudo方式执行远程命令
sudo('/etc/init.d/httpd start')
put
上传本地文件或目录到远程主机
put(remote_path, local_path)
get
从远程主机下载文件或目录到本地
put(local_path, remote_path)
open_shell
打开一个shell,类似于SSH连接到了远程主机
open_shell("ifconfig eth0")
prompt
获得用户输入信息
prompt('Please input user password: ')
confirm
获得提示信息确认
confirm('Continue[Y/N]?')
reboot
重启远程主机
reboot()
@task
函数装饰器,引用说明函数可调用,否则不可见

@runs_once
函数装饰器,函数只会执行一次

当我们写好fabric脚本后,需要用fab命令调用执行任务。
命令格式:fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...
fab命令有以下常用选项:
选项
描述
-l
打印可用的命令(函数)
--set=KEY=VALUE,...
逗号分隔,设置环境变量
--shortlist
简短打印可用命令
-c PATH
指定本地配置文件
-D
不加载用户known_hosts文件
-f PATH
指定fabfile文件
-g HOST
逗号分隔要操作的主机
-i PATH
指定私钥文件
-k
不加载来自~/.ssh下的私钥文件
-p PASSWORD
使用密码认证and/or sudo
-P
默认为并行执行方法
--port=PORT
指定SSH连接端口
-R ROLES
根据角色操作,逗号分隔
-s SHELL
指定新shell,默认是'/bin/bash -l -c'
--show=LEVELS
以逗号分隔的输出
--ssh-config-path=PATH
SSH配置文件路径
-t N
设置连接超时时间,单位秒
-T N
设置远程命令超时时间,单位秒
-u USER
连接远程主机用户名
-x HOSTS
以逗号分隔排除主机
-z INT
并发进程数
18.2.1 本地执行命令
from fabric.api import local
def command():
    local('ls')

# fab command
[localhost] local: ls
fabfile.py  fabfile.pyc  tab.py  tab.pyc

Done.
使用fab命令调用,默认寻找当前目录的fabfile.py文件。
18.2.2 远程执行命令
from fabric.api import run
def command():
    run('ls')

# fab -H 192.168.1.120 -u user command
[192.168.1.120] Executing task 'command'
[192.168.1.120] run: ls
[192.168.1.120] Login password for 'user':
[192.168.1.120] out: access.log  a.py
[192.168.1.120] out:


Done.
Disconnecting from 192.168.1.120... done.
如果在多台主机执行,只需要-H后面的IP以逗号分隔即可。
18.2.3 给脚本函数传入位置参数
from fabric.api import run

def hello(name="world"):
    print("Hello %s!" % name)

# fab -H localhost hello
[localhost] Executing task 'hello'
Hello world!

Done.
# fab -H localhost hello:name=Python
[localhost] Executing task 'hello'
Hello Python!

Done.
18.2.4 主机列表组
from fabric.api import run, env
env.hosts = ['root@192.168.1.120:22', 'root@192.168.1.130:22']
env.password = '123.com'
env.exclude_hosts = ['root@192.168.1.120:22']   # 排除主机
def command():
   run('ls')
env作用是定义fabfile全局设定,类似于变量。还有一些常用的属性:
env属性
描述
示例
env.hosts
定义目标主机
env.hosts = ['192.168.1.120:22']
env.exclude_hosts
排除指定主机
env.exclude_hosts = '[192.168.1.1]'
env.user
定义用户名
env.user='root'
env.port
定义端口
env.port='22'
env.password
定义密码
env.password='123'
env.passwords
定义多个密码,不同主机对应不同密码
env.passwords = {'root@192.168.1.120:22': '123'}
env.gateway
定义网关
env.gateway='192.168.1.2'
env.roledefs
定义角色分组
env.roledef = {'web':['192.168.1.11'], 'db':['192.168.1.12']}
env.deploy_release_dir
自定义全局变量,格式:env.+ '变量名'
env.var
18.2.5 定义角色分组
# vi install.py
from fabric.api import run, env
env.roledefs = {
    'web': ['192.168.1.10', '192.168.1.20'],
    'db': ['192.168.1.30', '192.168.1.40']
}
env.password = '123'
@roles('web')
def task1():
   run('yum install httpd -y')

@roles('db')
def task2():
   run('yum install mysql-server -y')

def deploy():
   execute(task1)
   execute(task2)
# fab -f install.py deploy
18.2.6 上传目录到远程主机
from fabric.api import *
env.hosts = ['192.168.1.120']
env.user = 'user'
env.password = '123.com'
def task():
   put('/root/abc', '/home/user')
   run('ls -l /home/user')
 # fab task              
18.2.7 从远程主机下载目录
from fabric.api import *
env.hosts = ['192.168.1.120']
env.user = 'user'
env.password = '123.com'
def task():
   get('/home/user/b', '/opt')
   local('ls -l /opt')
# fab task
18.2.8 打印颜色,有助于关键地方醒目
from fabric.colors import *
def show():
   print green('Successful.')
   print red('Failure!')
   print yellow('Warning.')
# fab show
经过上面演示fabric主要相关功能,是不是觉得很适合批量自动部署呢!没错,通过编写简单的脚本,即可完成复杂的操作。
18.3 pexpect
pexpect是一个用来启动子程序,并使用正则表达式对程序输出做出特定响应,以此实现与其自动交互的Python模块。暂不支持Windows下的Python环境执行。
这里主要讲解run()函数和spawn()类,能完成自动交互,下面简单了解下它们使用。
18.3.1 run()
run()函数用来运行bash命令,类似于os模块中的system()函数。
参数:run(command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None)
例1:执行ls命令
>>> import pexpect
>>> pexpect.run("ls") 
例2:获得命令状态返回值
>>> command_output, exitstatus = pexpect.run("ls", withexitstatus=1)
command_outout是执行结果,exitstatus是退出状态值。
18.3.2 spawn()
spawn()是pexpect模块主要的类,实现启动子程序,使用pty.fork()生成子进程,并调用exec()系列函数执行命令。
参数:spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None)
spawn()类几个常用函数:
expect(pattern, timeout=-1, searchwindowsize=None)
匹配正则表达式,pattern可以是正则表达式。
send(s)
给子进程发送一个字符串
sendline(s='')
就像send(),但添加了一个换行符(os.lineseq)
sendcontrol(char)
发送一个控制符,比如ctrl-c、ctrl-d
例子:ftp交互
用ftp命令登录是这样的,需要手动输入用户名和密码,才能登录进去。
# ftp 192.168.1.10
Connected to 192.168.1.10 (192.168.1.10).
220-FileZilla Server version 0.9.46 beta
220-written by Tim Kosse (tim.kosse@filezilla-project.org)
220 Please visit http://sourceforge.net/projects/filezilla/
Name (192.168.1.10:root): yunwei
331 Password required for yunwei
Password:
230 Logged on
Remote system type is UNIX.
ftp>
下面我们用pexpect帮我们完成输入用户名和密码:
import pexpect
child = pexpect.spawn('ftp 192.168.1.10')
child.expect('Name .*: ')
child.sendline('yunwei')
child.expect('Password:')
child.sendline('yunweipass')
child.expect('ftp> ')
child.sendline('ls')
child.sendline('bye')
child.expect(pexpect.EOF)   # pexpect.EOF程序打印提示信息
print child.before   # 保存命令执行结果
手动输入时,是来自键盘的标准输入,而pexpect是先匹配到关键字,再向子进程发送字符串。
pexpect.EOF打印提示信息,child.before保存的是命令执行结果。
通过上面的例子想必你已经知道pexpect主要功能了,在交互场景下很有用,这里就讲解这么多了,目的是给大家提供一个自动交互实现思路。

小结:
通过对Python下paramiko、fabric和pexpect模块使用,它们各有自己擅长的一面。
paramiko:方便嵌套系统平台中,擅长远程执行命令,文件传输。
fabric:方便与shell脚本结合,擅长批量部署,任务管理。
pexpect:擅长自动交互,比如ssh、ftp、telnet。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
RDIFramework.NET ━ 9.4 角色管理 ━ Web部分
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chinahuyong/article/details/43339781 RDIFramework.NET ━ .NET快速信息化系统开发框架 9.4 角色管理 -Web部分    角色管理模块主要为了方便框架权限的分配,提高权限分配的效率,减少重复设置权限的工作量。
837 0
用python wxpy管理微信公众号,并利用微信获取自己的开源数据。
用python wxpy管理微信公众号,并利用微信获取自己的开源数据。之前了解到itchat 乃至于 wxpy时 是利用tuling聊天机器人的接口。调用接口并保存双方的问答结果可以作为自己的问答词库的一个数据库累计。
4527 0
智能媒体管理(IMM) Python SDK DEMO
SDK For Python 本文以Python为示例 安装 Python 环境 IMM Service 的Python SDK目前只支持 Python 2.6.x, 2.7.x。 请按以下步骤安装Python: 下载并安装最新的 Python 2 安装包。
1520 0
+关注
李振良
6年互联网运维经验,擅长Linux,Python,Docker,MySQL,运维自动化等技术领域。
149
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《Nacos架构&原理》
立即下载
《看见新力量:二》电子书
立即下载
云上自动化运维(CloudOps)白皮书
立即下载