paramiko是用python語言寫的一個模組,遵循SSH2協議,支援以加密和認證的方式,進行遠端伺服器的連線。paramiko支援Linux, Solaris, BSD, MacOS X, Windows等平臺通過SSH從一個平臺連線到另外一個平臺。利用該模組,可以方便的進行ssh連線和sftp協議進行sftp檔案傳輸。
一、paramiko模組的安裝
paramiko模組依賴PyCrypto模組,而PyCrypto需要GCC庫編譯,不過一般發行版的源裡帶有該模組。這裡以centos6為例,直接藉助以下命令可以直接完成安裝:
1 |
# yum install gcc python-crypto python-paramiko python-devel -y |
windows版下可以安裝windows版的GCC(MinGW),然後編輯安裝pycrypto和paramiko ,下載安成後,直接執行python.exe setup.py build 和 python.exe setup.py install 就可以了。
二、paramiko的連線
使用paramiko模組有兩種連線方式,一種是通過paramiko.SSHClient()函式,另外一種是通過paramiko.Transport()函式。
方法一:
1 2 3 4 |
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("某IP地址",22,"使用者名稱", "口令") |
上面的第二行程式碼的作用是允許連線不在know_hosts檔案中的主機。
方法二:
1 2 3 |
import paramiko t = paramiko.Transport(("主機","埠")) t.connect(username = "使用者名稱", password = "口令") |
如果連線遠端主機需要提供金鑰,上面第二行程式碼可改成:
1 |
t.connect(username = "使用者名稱", password = "口令", hostkey="金鑰") |
三、paramiko ssh連線
以下是一個簡單的通過paramiko模組定義的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 #paramiko.util.log_to_file('/tmp/sshout') def ssh2(ip,username,passwd,cmd): try: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(ip,22,username,passwd,timeout=5) stdin,stdout,stderr = ssh.exec_command(cmd) # stdin.write("Y") #簡單互動,輸入 ‘Y’ print stdout.read() # for x in stdout.readlines(): # print x.strip("n") print '%stOKn'%(ip) ssh.close() except : print '%stErrorn'%(ip) ssh2("192.168.0.102","root","361way","hostname;ifconfig") ssh2("192.168.0.107","root","123456","ifconfig") |
其中第四行的日誌部分,是記錄ssh連線互動時的一些資訊,可以看做是類似於debug的輸出,一般情況下不需要開啟。
stdin.write部分是用於互動情況下,通過該命令可以執行互動。注意這裡可能會引起歧義,這裡的互動並不是ssh連線過程中出現的讓輸入yes的互動,因為paramiko模組在連線過程中會自動處理好yes確認。這裡的互動是指後面的cmd需要的執行的程式可能出現互動的情況下,可以通過該引數進行互動。
stdout標準輸出,在輸出內容比較少時,可以通過直接使用read讀取出所有的輸出;但在輸出內容比較多時,建議通過按行讀取進行處理。不過按行讀取時,每行結尾會有換行符n,這樣輸出的結果很不美觀。可以通過strip進行字串的處理。
在函式呼叫過程中需要注意的是,IP、username、passwd都是屬於字串型的,所以需要加引號。後面執行的cmd,如果有多個命令需要操作時,需要通過分號進行分割。
四、paramiko sftp示例
單個檔案小傳下載的示例:
1 2 3 4 5 6 7 8 9 10 11 12 |
import paramiko #建立一個加密的管道 scp=paramiko.Transport(('192.168.0.102',22)) #建立連線 scp.connect(username='root',password='361way') #建立一個sftp客戶端物件,通過ssh transport操作遠端檔案 sftp=paramiko.SFTPClient.from_transport(scp) #Copy a remote file (remotepath) from the SFTP server to the local host sftp.get('/root/testfile','/tmp/361way') #Copy a local file (localpath) to the SFTP server as remotepath sftp.put('/root/crash-6.1.6.tar.gz','/tmp/crash-6.1.6.tar.gz') scp.close() |
一個目錄下多個檔案上傳下載的示例:
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 |
#!/usr/bin/env python #-*- coding: utf-8 -*- import paramiko,datetime,os hostname='192.168.0.102' username='root' password='361way' port=22 local_dir='/tmp/getfile' remote_dir='/tmp/abc' try: t=paramiko.Transport((hostname,port)) t.connect(username=username,password=password) sftp=paramiko.SFTPClient.from_transport(t) #files=sftp.listdir(dir_path) files=sftp.listdir(remote_dir) for f in files: print '' print '#########################################' print 'Beginning to download file from %s %s ' % (hostname,datetime.datetime.now()) print 'Downloading file:',os.path.join(remote_dir,f) sftp.get(os.path.join(remote_dir,f),os.path.join(local_dir,f))#下載 #sftp.put(os.path.join(local_dir,f),os.path.join(remote_dir,f))#上傳 print 'Download file success %s ' % datetime.datetime.now() print '' print '##########################################' t.close() except Exception: print "connect error!" |
注:本處的目錄下所有檔案進行下載或上傳的示例中,在遇到目錄下還有巢狀的目錄存在時,會將目錄也當做檔案進行處理,所以如果想要更加的完美的話,可以通過引入stat模組下的S_ISDIR方法進行處理
paramiko.transport物件也支援以socket的方式進行連線,如下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import paramiko transport = paramiko.Transport(('localhost',22)) transport.connect(username='root', password = 'password') sftp = paramiko.SFTPClient.from_transport(transport) sftp.get(remotefile,localfile) #如果是上傳則用: #sftp.put(localfile, remotefile) transport.close() #用socket連線 tcpsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) tcpsock.settimeout(5) tcpsock.connect((ip,22),) ssh = paramiko.Transport(tcpsock) ssh.connect(username=user,password=password) sftpConnect=paramiko.SFTPClient.from_transport(ssh) |
五、利用paramiko實現ssh的互動式連線
以下是通過paramiko模組直接用ssh協議登陸到遠端伺服器的操作程式碼,這裡先定義一個interactive模組,程式碼如下:
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import socket import sys # windows does not have termios... try: import termios import tty has_termios = True except ImportError: has_termios = False def interactive_shell(chan): if has_termios: posix_shell(chan) else: windows_shell(chan) def posix_shell(chan): import select oldtty = termios.tcgetattr(sys.stdin) try: tty.setraw(sys.stdin.fileno()) tty.setcbreak(sys.stdin.fileno()) chan.settimeout(0.0) while True: r, w, e = select.select([chan, sys.stdin], [], []) if chan in r: try: x = chan.recv(1024) if len(x) == 0: print 'rn*** EOFrn', break sys.stdout.write(x) sys.stdout.flush() except socket.timeout: pass if sys.stdin in r: x = sys.stdin.read(1) if len(x) == 0: break chan.send(x) finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) # thanks to Mike Looijmans for this code def windows_shell(chan): import threading sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.rnrn") def writeall(sock): while True: data = sock.recv(256) if not data: sys.stdout.write('rn*** EOF ***rnrn') sys.stdout.flush() break sys.stdout.write(data) sys.stdout.flush() writer = threading.Thread(target=writeall, args=(chan,)) writer.start() try: while True: d = sys.stdin.read(1) if not d: break chan.send(d) except EOFError: # user hit ^Z or F6 pass |
程式碼內容可以從paramiko 在github專案上的demo裡獲取。再另外寫一個ssh_inter.py的互動主程式,內容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import paramiko import interactive #記錄日誌 paramiko.util.log_to_file('/tmp/test') #建立ssh連線 ssh=paramiko.SSHClient() ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect('192.168.0.102',port=22,username='root',password='xxxxxx',compress=True) #建立互動式shell連線 channel=ssh.invoke_shell() #建立互動式管道 interactive.interactive_shell(channel) #關閉連線 channel.close() ssh.close() |
執行效果就像我們平時直接使用ssh登入一樣。
六、總結
paramiko模組是一個比較強大的ssh連線模組,以上的示例只是列出了該模組的一些簡單的使用方法,還可以使用threading模組加塊程式併發的速度;也可以使用configparser模組處理配置檔案,而我們將所有IP、使用者資訊操作都放入配置檔案;使用setproctitle模組為執行的程式加一個容易區分的title等。
同樣,雖然連fabric這樣大名鼎鼎的軟體使用的ssh都是用paramiko模組進行的封裝,不過你依然可以選擇不使用它,你也可以選擇pexpect模組實現封裝一個簡易的ssh連線工具、或者使用同樣比較火的salt-ssh模組。