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

本章節主要講解運維工程師比較感興趣的知識,那就是運維批量管理,在Python下有paramiko、fabric和pexpect這三個模塊可幫助運維實現自動化部署、批量執行命令、文件傳輸等常規任務,接下來一塊兒看看它們的使用方法吧!python

18.1 paramikomysql

paramiko模塊是基於Python實現的SSH遠程安全鏈接,用於SSH遠程執行命令、文件傳輸等功能。web

默認Python沒有,須要手動安裝:pip install paramiko正則表達式

如安裝失敗,能夠嘗試yum安裝:yum install python-paramikosql

18.1.1 SSH密碼認證遠程執行命令shell

#!/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 私鑰認證遠程執行命令windows

#!/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 上傳文件到遠程服務器api

#!/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 上傳目錄到遠程服務器bash

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

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主要相關功能,是否是以爲很適合批量自動部署呢!沒錯,經過編寫簡單的腳本,便可完成複雜的操做。


博客地址: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:執行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。

image.png

相關文章
相關標籤/搜索