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

简介:

18.1 paramiko

paramiko模块是基于Python实现的SSH远程安全连接,用于SSH远程执行命令、文件传输等功能。

默认Python没有,需要手动安装:pip install paramiko

如安装失败,可以尝试yum安装:yum install python-paramiko

18.1.1 SSH密码认证远程执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/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 私钥认证远程执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/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 上传文件到远程服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/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 :
     =  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 从远程服务器下载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/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 :
     =  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()方法遍历目录,再一个个上传:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/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 :
     =  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

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 本地执行命令

1
2
3
4
5
6
7
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 远程执行命令

1
2
3
4
5
6
7
8
9
10
11
12
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 给脚本函数传入位置参数

1
2
3
4
5
6
7
8
9
10
11
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 主机列表组

1
2
3
4
5
6
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 定义角色分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 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 上传目录到远程主机

1
2
3
4
5
6
7
8
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 从远程主机下载目录

1
2
3
4
5
6
7
8
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 打印颜色,有助于关键地方醒目

1
2
3
4
5
6
from  fabric.colors  import  *
def  show():
    print  green( 'Successful.' )
    print  red( 'Failure!' )
    print  yellow( 'Warning.' )
# fab show

经过上面演示fabric主要相关功能,是不是觉得很适合批量自动部署呢!没错,通过编写简单的脚本,即可完成复杂的操作。


博客地址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python运维开发群


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
2
3
4
5
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命令登录是这样的,需要手动输入用户名和密码,才能登录进去。

1
2
3
4
5
6
7
8
9
10
11
# 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帮我们完成输入用户名和密码:

1
2
3
4
5
6
7
8
9
10
11
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。




本文转自 李振良OK 51CTO博客,原文链接:http://blog.51cto.com/lizhenliang/1880856,如需转载请自行联系原作者
相关文章
|
2月前
|
测试技术 网络安全 数据安全/隐私保护
Paramiko是一个用于处理SSHv2协议的Python库
Paramiko是一个用于处理SSHv2协议的Python库
37 3
|
3月前
|
Unix Linux 网络安全
python中连接linux好用的模块paramiko(附带案例)
该文章详细介绍了如何使用Python的Paramiko模块来连接Linux服务器,包括安装配置及通过密码或密钥进行身份验证的示例。
132 1
|
4月前
|
SQL 关系型数据库 MySQL
Python之MySQL操作及Paramiko模块操作
Python之MySQL操作及Paramiko模块操作
|
5月前
|
数据挖掘 Python
🚀告别繁琐!Python I/O管理实战,文件读写效率飙升的秘密
【7月更文挑战第29天】在 Python 编程中,高效的文件 I/O 对性能至关重要。
51 4
|
5月前
|
数据挖掘 数据处理 Python
🔍深入Python系统编程腹地:文件系统操作与I/O管理,打造高效数据处理流水线
【7月更文挑战第29天】深入Python系统编程腹地:文件系统操作与I/O管理,打造高效数据处理流水线
42 3
|
5月前
|
安全 数据安全/隐私保护 Python
|
5月前
|
JSON 监控 开发者
Python I/O管理新篇章:优化你的程序,让数据流动更顺畅
【7月更文挑战第30天】在数据驱动时代, Python I/O操作效率至关重要。理解I/O瓶颈,使用缓冲技术(如调整`open`的`buffering`参数),并发与异步I/O(借助`asyncio`),高效序列化(json, msgpack),及监控调试(cProfile)能显著提升性能。示例展示了缓冲读取和异步文件操作的最佳实践。不断学习可助开发者优化数据流。
72 2
|
5月前
|
API Python
Python高手修炼手册:精通文件系统操作,掌控I/O管理,提升编程效率
【7月更文挑战第30天】在 Python 编程中, 文件系统操作与 I/O 管理是连接程序与数据的关键。初学者常因路径错误和权限问题受挫, 而高手能自如管理文件。传统 `os` 和 `os.path` 模块易出错, `pathlib` 提供了更直观的对象导向 API。I/O 方面, 同步操作会阻塞程序, 异步 (如使用 `aiofiles`) 则能大幅提升并发能力。真正的高手不仅掌握 API, 更能预见性能瓶颈并优化代码, 实现高效与优雅。
45 1
|
5月前
|
监控 Python
paramiko 模块 ---Python脚本监控当前系统的CPU、内存、根目录、IP地址等信息
paramiko 模块 ---Python脚本监控当前系统的CPU、内存、根目录、IP地址等信息