2012年10月25日 星期四

autoEM project: 系列一:ssh遠端指令

實際開始做電路之後,遇到很多不太乾淨的東西,跑em算是其中的一個。
跑em中文曰電磁模擬,將電路的實體架構畫出來,去解出其中電磁的實際分佈狀況;這通常要用到大量的電腦資源,在自己電腦上跑會把你資源佔光幾小時,連東方或minecraft都不能打,這當然是無法接受的,這時就要上工作站跑。
而上工作站跑EM就是十足髒髒的事情,大略描述為:
1. 在本地端畫好電磁模擬檔
2. 利用filezilla, scp等把檔案丟上工作站 ,找到你的模擬檔,還要看哪個工作站還在正常運作,不然連一連斷線就GG了。
3. 利用ssh連上工作站,刪除上次的模擬資料,跑模擬
4. 等模擬結束,這時候還不能斷線(這個問題可以用nohup, screen去解決)
5. 用filezilla下載模擬結果,還要再事模擬結果放到原資料夾
整個過程就髒髒的…(這個詞是從BonoGod那邊學來的),最近受到柏翰學長的啟發,決定來寫一個可以自動管理這個流程的小程式,用Shell script譔寫,之後再改用python的譔寫。

程式功能與目標:
1. 免去手動上傳檔案與上工作站執行指令,在本地程式下達upload, run, download指令即可。
2. 支援一次批次大量模擬,自動監視各工作站的使用量,選擇負擔最低的工作站上傳。
3. 監視進行中的模擬,在結束時發出通知。

為這個小程式遇到不少值得記錄的東西,就把它寫一個系列文,這是第一篇,記錄shell 和python上,透過ssh執行指令的各種方式。

1. shell script:

上傳檔案的部分我一開始是選用rsync的方式,使用方式:
rsync -av -e ssh user@hostname:path
後來發現rsync要求兩邊都要有rsync,而工作站上沒有安裝rsync…orz。
所以還是要回去用scp,其實功能不算差很多:
scp file user@server:file //upload
scp file user@server:file //download

下指令的部分則是使用:
ssh user@hostname 'instruction'
這樣的問題是每一次指令都需要使用者輸入密碼,加上上傳下載檔案,跑一趟em都要使用者輸入3次密碼,這樣還是很沒效率。
解法方式是設定ssh公鑰,或者用類似expect的方式來模擬使用者輸入。我是用前者的作法,在code裡加上一個add worstation 的指令,自動產生公鑰並上傳到指定的工作站上,之後即不會要求每次執行都要輸入密碼,這部分詳述資料請見參考資料一。

另外如何在ssh被切斷之後,執行的程序還能繼續?
這部分有滿多解決方案的,最簡單的是用ssh本身就有的功能:
ssh -f user@hostname 'instruction'
這樣指令就會自動在遠端背景執行。
或者如柏翰學長使用的方式,用at來執行:
echo "em -v $file > em.log 2>&1 &" > sub_file
at now +$delay minutes < sub_file
或者就要透過nohup或是screen/tmux兩種方式:
ssh user@hostname 'nohup instruction &'
ssh user@hostname 'screen -S autoEM -d -m instruction'
這兩個方式略有不同,nohup會讓程式忽略所有SIGHUP(hangup)的信號,ssh登出時的hangup信號就不會把該指令給刪除。
Screen則是直接創造一個virtual console(其實我也不確定是不是這樣叫orz),-d -m會detach這個screen,指令就在裡面繼續跑,之後再用screen -r將程式給要回來;不過又發現有工作站上沒有screen或tmux(......),如果遇到這個問題,可能就真的只能跟管理員反映了...。

2. python:

如果改成用python來寫呢?
Python裡已經有SSH2 protocal的套件庫paramiko,比較可惜的是這個套件目前載不到python3 的版本,只能先用2.7寫。
Python 寫起來就直覺多了,paramiko已經把大部分ftp的指令都整合在套件庫裡,包括sftp跟ssh,document頁面在:http://www.lag.net/paramiko/
大家可以載一下它們的安裝檔,裡面的demo.py就有很多範例可以用,包括sftp, ssh:
ssh = paramiko.SSHClient()  
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
ssh.connect(info.hostname, username=info.username, password = info.password)  
stdin, stdout, stderr = ssh.exec_command('source ~/.bashrc; nohup em ~/autoEM/%s em.log 2>&1 &' % filename)  
stdout.readlines() 
stderr.readlines()
因為paramiko已經把ssh包在裡面,我沒辦法像shell一樣,用ssh -f的方式去下背景指令,因此就用nohup的方式;比較有趣的是,指令中,我一定要重導向輸入跟標準/錯誤輸出(不能讓nohup自行產生nohup.out),在下完指令後也一定要stderr.readlines(),否則我ssh切斷之後nohup還是會被切掉,這部分原因不明。

3. 小結:

這篇主要記錄如何透過ssh,在BASH, python上對遠端工作站下達指令,目前這部分已經完成,下一篇可能會談一下主程式譔寫時遇到的問題,或中途寫下的筆記。

4. 參考資料:

1. 鳥哥的網頁,有關ssh的部分:
http://linux.vbird.org/linux_server/0310telnetssh.php
2. python paramiko package:
http://www.lag.net/paramiko/

5. 致謝:

本篇文章感謝:
陳柏翰學長在自動執行EM的shell script上的分享。
黃偉寧前輩在nohup, screen程式譔寫上的指導,雖然在討論過程中,前輩提供了無數種解決方案,模擬使用者行為,其他指令等,但我最後都沒有用上Orz(應該說…有聽沒有懂,我們之間至少差了20dB左右)。

沒有留言:

張貼留言