python遠程批量執行
我並不是一個專業的開發,我一直在學習linux運維,對於python也是接觸不久,所以代碼寫的並不是很規范簡潔。
前段時間一個同學找我一起做一個自動化運維平台,我對python的django還沒有了解,並且對於HTML和JS這類開發學習還沒有涉及,所以我說我做些后台的實現,前端就交給我的同學做。不扯淡了,下面說下我做批量執行的思路。
- 用到的模塊:paramiko
- 功能:很簡單就是批量執行命令,類似於ansible,本來想用Fabric,但是想一想還是用paramiko,因為我在學習ansible,ansible里面就有paramiko。后期還要將配置文件里面的主機組放到數據庫里面。這里我想使用的mongodb,因為我的主機配置文件寫的是字典的形式,保存在文檔數據庫中更為方便些。
- 配置文件格式:這里為了方便獲取信息,直接寫成了字典的形式,本來前期想用pickle模塊進行序列化輸入到文件中,但是后來發現如果主機要是多的話,手動輸入還是太麻煩了。
- 類:為了后期更好的添加功能,我直接將paramiko的SSHClient寫成了類。后面要添加上傳文本,下載等功能,這就用到了SFTP的功能。
- 函數:這里面的函數就是對文件進行條件輸出,找到符合的主機組名稱。
- 講解下paramiko:paramiko模塊在我理解就是依賴ssh遠程的一個模塊。
(1)、paramiko安裝方式:使用pip安裝即可。
(2)、paramiko核心組件-----SSHClient
SSHClient使用ssh的通道實現和主機的連接和命令執行。
SSHClient中的方法 | 參數和參數說明 |
connect(實現ssh連接和校驗) | hostname:目標主機地址 port:主機端口 username:校驗的用戶名 password:登錄密碼 pkey:私鑰方式身份驗證 key_filename:用於私鑰身份驗證的文件名 timeout:連接超時設置 allow_agent:這是布爾型,設置False的時候禁止使用ssh代理 look_for_keys:也是布爾型,禁止在.ssh下面找私鑰文件 compress:設置壓縮 |
exec_command(遠程執行命令) | stdin,stdout,stderr:這三個分別是標准輸入、輸出、錯誤,用來獲取命令執行結果,並不算方法的參數 command:執行命令的字符串,帶雙引號。 bufsize:文件緩沖大小,默認為1
|
load_system_host_keys(加載本地的公鑰文件) | filename:指定遠程主機的公鑰記錄文件 |
set_missing_host_key_policy(遠程主機沒有密鑰) | AutoAddPolicy:自動添加主機名和主機密鑰到本地的HostKeys對象 RejectPolicy:自動拒絕未知的主機名和密鑰(默認) WarningPolicy:接受未知主機,但是會有警告 |
(3)paramiko的核心組件SFTPClient類
實現遠程文件的操作,比如上傳、下載、權限、狀態等。
SFTPClient類的方法 | 參數和參數說明 |
from_transport(使用一個已經通過已經連通的SFTP客戶端通道) | t:使用的是已經驗證過的傳輸對象 |
put(上傳本地文件到SFTP服務器) | localpath:本地文件的路徑 remotepath:遠程路徑 callback:獲取已接收的字節數和總傳輸的字節數 confirm:文件上傳完畢后是否調用stat()方法,確定文件大小 |
get(從SFTP服務器上下載文件到本地) | remotepath:需下載的文件路徑 localpath:保存本地的文件路徑 callback:和put的一樣。 |
mkdir:簡歷目錄 remove:刪除 rename:重命名 stat:獲取遠程文件信息 listdir:獲取指定目錄列表 |
-
(4)、還有個invoke_shell的用法經常使用
invoke_shell(*args, **kwds) Request an interactive shell session on this channel. If the server allows it, the channel will then be directly connected to the stdin, stdout, and stderr of the shell. Normally you would call get_pty before this, in which case the shell will operate through the pty, and the channel will be connected to the stdin and stdout of the pty. When the shell exits, the channel will be closed and can’t be reused. You must open a new channel if you wish to open another shell.
在這個通道請求一個交互式的shell會話,如果服務允許,這個通道將會直接連接標准輸入、標准輸入和錯誤的shell,通常我們會在使用它之前調用get_pty的用法,這樣shell會話是通過偽終端處理的,並且會話連接標准輸入和輸出,當我們shell退出的時候,這個通道也會關閉,並且能再次使用,你必修重新開另一個shell。
-
(4)實踐堡壘機(摘自劉天斯老師的《python自動化運維》)

#定義服務器信息 hostname = "192.168.0.158" username = "root" password = "aixocm" #定義登錄日志和密碼提示符 port = 22 passinfo = '\'s password:' paramiko.util.log_to_file('syslogin.log') #登錄堡壘機,自動添加hostkeys信息到主機 ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname=blip,username=bluser,password=blpasswd) #創建會話 channel = ssh.invoke_shell() channel.settimeout(100) #通道執行shell的ssh連接 buff = '' resp = '' channel.send('ssh '+username+'@'+hostname+'\n') while not buff.endswith(passinfo): try: resp = channel.recv(9999) except Exception,e: print 'Error info:%s connection time.' % (str(e)) channel.close() ssh.close() sys.exit() buff += resp if not buff.find('yes/no') == -1: channel.send('yes\n') buff = '' channel.send(password+'\n') buff = '' while not buff.endswith('# '): resp = channel.recv(9999) if not resp.find(passinfo) == -1: print 'Error info:Authentication failed.' channel.close() ssh.close()
- 配置文件:代碼:
{"hostname":"web","host_ip":["192.168.0.157","192.168.0.158","192.168.0.159"]}
類:#!/usr/bin/env pyth#coding:utf-8import paramiko
class action(object): def __init__(self, IP, username, command): self.IP = IP self.username = username self.command = command def ssh_connect(self): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(hostname=self.IP, username=self.username) stdin,stdout,stderr=ssh.exec_command(self.command) print "######################> %s <####################" %(self.IP) print stderr.read() print stdout.read() ssh.close()
except Exception,e: print "######################> %s <####################" %(self.IP) print
執行主函數:
from simple1 import action
def get_values(hostname): conf_file=open('scn.conf','r') lines = conf_file.readlines() for line in lines: line = line.strip("\n") line = eval(line) if hostname == line["hostname"]: return(line) break conf_file.close() if __name__ == "__main__": hostname = raw_input("write your hostname:") username = raw_input("write your username:") command = raw_input("write your excute command:") host = get_values(hostname) host_ip = list(host["host_ip"]) for i in range(0,len(host_ip)): conn = action(host_ip[i],username,command) conn.ssh_connect()
注意這里面我沒有添加password和port,port默認使用的ssh的22號端口,password我直接使用ssh-keygen和ssh-copy-id進行無密碼登錄。