paramiko是用python語言寫的一個模塊,遵循SSH2協議,支持以加密和認證的方式,進行遠程服務器的連接。paramiko支持Linux, Solaris, BSD, MacOS X, Windows等平台通過SSH從一個平台連接到另外一個平台。利用該模塊,可以方便的進行ssh連接和sftp協議進行sftp文件傳輸。
一、paramiko模塊的安裝
paramiko模塊依賴PyCrypto模塊,而PyCrypto需要GCC庫編譯,不過一般發行版的源里帶有該模塊。這里以ubuntu為例,直接借助以下命令可以直接完成安裝:
sudo pip3 install paramiko
二、paramiko的連接
使用paramiko模塊有兩種連接方式,一種是通過paramiko.SSHClient()函數,另外一種是通過paramiko.Transport()函數。
方法一:
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("某IP地址",22,"用戶名", "口令")
上面的第二行代碼的作用是允許連接不在know_hosts文件中的主機。
方法二:
import paramiko
t = paramiko.Transport(("主機","端口"))
t.connect(username = "用戶名", password = "口令")
如果連接遠程主機需要提供密鑰,上面第二行代碼可改成:
t.connect(username = "用戶名", password = "口令", hostkey="密鑰")
三、paramiko ssh連接
以下是一個簡單的通過paramiko模塊定義的ssh連接並執行命令的函數,如下:
#!/usr/bin/python3
#-*- 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") #interact with server, typing Y
print(stdout.read())
# for x in stdout.readlines():
# print x.strip("n")
print('%s OK\n'%(ip))
ssh.close()
except :
print('%s Error\n' %(ip))
ssh2("45.76.22.112","root","password","ls")
其中第四行的日志部分,是記錄ssh連接交互時的一些信息,可以看做是類似於debug的輸出,一般情況下不需要開啟。
stdin.write部分是用於交互情況下,通過該命令可以執行交互。注意這里可能會引起歧義,這里的交互並不是ssh連接過程中出現的讓輸入yes的交互,因為paramiko模塊在連接過程中會自動處理好yes確認。這里的交互是指后面的cmd需要的執行的程序可能出現交互的情況下,可以通過該參數進行交互。
stdout標准輸出,在輸出內容比較少時,可以通過直接使用read讀取出所有的輸出;但在輸出內容比較多時,建議通過按行讀取進行處理。不過按行讀取時,每行結尾會有換行符n,這樣輸出的結果很不美觀。可以通過strip進行字符串的處理。
在函數調用過程中需要注意的是,**IP、username、passwd都是屬於字符串型的,所以需要加引號。后面執行的cmd,如果有多個命令需要操作時,需要通過分號進行分割。
四、paramiko sftp示例
單個文件小傳下載的示例:
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()
一個目錄下多個文件上傳下載的示例:
#!/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的方式進行連接,如下示例:
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模塊,代碼如下:
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的交互主程序,內容如下:
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模塊。