python linux互動模組(paramiko、fabric與pexpect)

2020-08-13 14:20:13

python linux互動模組(paramiko、fabric與pexpect)

paramiko封裝類,包含上傳檔案,下載檔案,上傳目錄及執行命令

paramiko異常Exception拋出詳細報錯原因

paramiko模組實現互動式操作 paramiko參數詳細說明

來自:https://download.csdn.net/download/xiaoxiong0612/12709703

 

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) 刪除軟鏈接

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 = ['[email protected]:22''[email protected]:22']

env.password = '123.com'

env.exclude_hosts = ['[email protected]: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 = {'[email protected]: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主要相關功能,是不是覺得很適合批次自動部署呢!沒錯,通過編寫簡單的指令碼,即可完成複雜的操作。

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。