軟件開發與測試人員經常會在遠程 Linux/UNIX 系統上執行命令或腳本,有時還會有批量執行任務的需求。常見的辦法是經過本地工具(例如 Putty)鏈接遠程計算機,輸入命令執行,可是當遇到須要集成這些任務到代碼或者開發、測試框架中時,每每就沒有很好的解決方案了。sshxcute 就是這樣一個框架工具集,它基於 JSCH 構建,容許工程師利用 Java 代碼經過 SSH 鏈接遠程批量執行 Linux/UNIX 系統上的命令或者腳本,同時加入了判斷成功與否,取回輸出等多種實用功能。sshxcute 不論是針對軟件開發、測試仍是系統部署,都簡化了自動化流程與系統環境部署的步驟。程序員
隨着現代企業內信息的迅速增加,更多的企業創建了數據中心甚至雲計算網絡,對於軟件開發測試行業人員來講,須要更多的依賴於遠程主機,從部署環境到遠程執行都須要利用客戶端工具鏈接。對於開發人員,當部署環境須要輸入不少命令時,每次等待命令執行完畢才能輸入下一個命令,另外一種經常使用的解決方法是把全部命令寫在一個腳本里,但這也須要經過客戶端工具(例如 Putty)遠程登陸後才能執行,但開發人員的利器畢竟是集成開發環境(IDE),這些環境部署工做既費時又費力。對於測試人員,當本地已經作好了一個自動化測試框架或者程序時,須要先在被測系統上部署環境,如何集成進已有的程序或者框架是件必須考慮的事情,若是在每次運行自動化測試前均手動登陸遠程主機部署環境,這也下降了測試人員的工做效率。圖 1 展現了針對上述技術人員的一般使用場景。正則表達式
回頁首shell
分析上述場景,對於軟件開發與測試人員一個典型困惑就是,沒有一個容許自動化的、批量的、帶有檢查命令成功與否的遠程執行工具或者框架,若是存在一個基於 Java 的遠程執行類庫,開發人員能夠在本身的集成開發環境(IDE)中經過運行一個 Java 類就能夠部署環境,測試人員能夠集成該類庫到本身的自動化程序或者一樣適用 IDE 來,就能夠遠程執行命令或者腳本。編程
上述分析能夠參考圖 2。數組
目前 JSch 正是這樣一個知足上述基本需求的類庫,JSch 是 SSH2 的一個純 Java 實現。它能夠鏈接到一個 sshd 服務器,使用端口轉發,X11 轉發,文件傳輸等等。可是這個類庫畢竟偏向底層,上手與實際運行起來不太方便,sshxcute 框架正是基於 JSch 封裝的,提供了更爲便捷的 API 藉口,更加靈活實用的功能,從而可讓開發與測試人員更加駕輕就熟的使用。sshxcute 是一個框架,它容許工程師利用 Java 代碼經過 SSH 鏈接遠程執行 Linux/UNIX 系統上的命令或者腳本,這種方式不論是針對軟件測試仍是系統部署,都簡化了自動化測試與系統環境部署的步驟。bash
SSHXCUTE 的設計旨在:服務器
最小的系統需求 – 僅僅開啓 SSH 鏈接便可。網絡
易用性 – 工程師利用 Java 代碼執行命令或腳本。框架
內置命令 / 腳本任務執行功能。ssh
易擴展 – 用戶能夠自定義任務類型並集成於 sshxcute 框架。
下面的章節分別介紹瞭如何使用 sshxcute 框架,如何配置它的運行時參數選項以及如何利用該框架的 Java API 進行擴展從而從容應用到本身的項目中。
首先,必須確保 JDK 版本在 5.0 以上,而後須要確認 sshxcute.jar 已經在環境變量中的 $CLASSPATH 中,而後才能夠開始。若是是用集成開發環境(IDE)下,必須將 sshxcute.jar 加入項目構建路徑下,接下來展現的是如何在 Eclipse IDE 中配置 Java Build Path。右鍵單擊項目 > 屬性 > Java 構建路徑。更多的配置步驟請在互聯網上搜索。
一般經過 SSH 協議在遠程 Linux/UNIX 系統上執行命令時,如下是必須的步驟:
打開 SSH 鏈接客戶端(例如 Putty,SSHClient 等)
輸入 IP
輸入用戶名、密碼登陸
登陸成功後輸入執行命令
斷開登陸
前三個步驟能夠經過 sshxcute 的 Java API 模擬實現:
// 新建一個 ConnBean 對象,三個參數依次是 ip 地址、用戶名、密碼 ConnBean cb = new ConnBean("ip ", "username","password"); // 將上面新建的 ConnBean 做爲參數傳遞給 SSHExec 的靜態單例方法,獲得一個 SSHExec 的實例 ssh = SSHExec.getInstance(cb); // 利用上面獲得的 SSHExec 實例鏈接主機 ssh.connect();
第五步斷開登陸的實現以下:
ssh.disconnect();
第四步是 sshxcute 框架的核心所在——自動執行命令或者腳本。接下來的部分將主要介紹這個主題。
這是 sshxcute 框架內自帶的任務類型,接下來 3.4 小節講到的遠程執行 shell 腳本也是自帶的任務類型。先來看一段代碼再來詳細解釋。若是讀者已經具有了面向對象編程經驗,那麼下面的內容將會被發現如此熟悉與簡單。
CustomTask sampleTask = new ExecCommand("echo 123"); ssh.exec(sampleTask);
ExecCommand 類繼承了 CustomTask 類,咱們新建一個 ExecCommand 對象,他的引用類型是 CustomTask。下圖展現了 ExecCommand、ExecShellScript 和 CustomTask 的類圖,從中能夠看出他們的關係,ExecCommand、ExecShellScript 是 CustomTask 的子類。
ExecCommand 的構造函數只接收一個字符串類型變量。注意 ExecCommand 能夠執行多個命令,只須要用分隔符「,」分隔各個命令便可。例如:
CustomTask sampleTask = new ExecCommand("echo 123", "echo 456, "echo 789");
ExecCommand 的構造函數是:
public ExecCommand(String...args)
把 ExecCommand 對象做爲參數傳入 SSHExec.exec(CustomTask) 方法,這樣就能夠直接運行命令了。
遠程執行 shell 腳本幾乎與 3.3 小節的遠程執行命令一致。例如,若是想執行 /home/tsadmin 路徑下的 sshxcute_test.sh 腳本,而且帶兩個參數「hello world」,能夠這樣調用:
CustomTask ct1 = \ new ExecShellScript("/home/tsadmin","./sshxcute_test.sh","hello world"); ssh.exec(ct1);
ExecShellScript 的構造函數是:
public ExecShellScript(String workingDir, String shellPath, String args)
public ExecShellScript(String shellPath, String args)
public ExecShellScript(String shellPath)
其中 workingDir 表明執行前先切換到路徑,shellPath 表明腳本執行路徑,args 表明參數列表。
一般會遇到一種狀況,要執行的腳本是存在於本地的,必須先把其上傳到遠程的主機上。這項工做 sshxcute 一樣能夠爲完成,例如,想要把 c:/data2/data 目錄下的全部文件上傳到遠程機器上的 /home/tsadmin 目錄下,能夠
ssh.uploadAllDataToServer("c:/data2/data", "/home/tsadmin");
若是想只上傳單一文件,例如只上傳路徑下的 c:/data/sshxcute_test.sh 到 /home/tsadmin,能夠這樣
ssh.uploadSingleDataToServer("c:/data/sshxcute_test.sh","/home/tsadmin");
這裏要注意下,必須把順序搞清楚,上傳的步驟必須在執行前,鏈接成功後。例如:
CustomTask ct1 = new ExecShellScript("/home/tsadmin","./sshxcute_test.sh","hello world"); ssh.connect(); // 鏈接成功後 ssh.uploadSingleDataToServer("data/sshxcute_test.sh", "/home/tsadmin"); ssh.exec(ct1); // 執行前
固然這裏不只僅限於必須執行腳本,若是你只想上傳文件能夠單獨執行 uploadSingleDataToServer() 或者 uploadSingleDataToServer() 方法。
全部的任務類型,包括上面已經講解過的 ExecCommand、ExecShellScript 還有接下來會說明的自定義類任務,在執行完畢後,都會返回一個結果對象(Result)。這個結果對象包含了命令或者腳本的返回代碼(return code)、標準輸入、錯誤輸出。還有它會提供一個布爾類型的 isSuccess 變量供程序員判斷是否任務執行成功,在 4.1 章節斷定任務成功與否的過濾關鍵字,將會詳細介紹 sshxcute 是如何判斷任務執行成功與否的,這個判斷的條件也是能夠配置的。
例如,SSHExec.exec(CustomTask) 總會返回一個結果對象,能夠利用本身的邏輯代碼打印一些有用的信息。代碼相見清單 9
Result res = ssh.exec(task); if (res.isSuccess) { System.out.println("Return code: " + res.rc); System.out.println("sysout: " + res.sysout); } else { System.out.println("Return code: " + res.rc); System.out.println("error message: " + res.error_msg); }
下面的例子囊括上面全部的技術話題,作個小結。假設需求是在遠程的 Linux 服務器(ip 是 9.125.71.115)上執行一個 shell 腳本—— sshxcute_test.sh。它的內容見清單 10。
#!/bin/bash if [ $# -ne 2 ];then echo "usage: sshxcute_test.sh username password" exit 1 fi export USERNAME=$1 export PASSWORD=$2 if [ "$USERNAME" = "hello" -a "$PASSWORD" = "world" ];then echo "Login success" exit 0 fi echo "Login falied" exit 2
實現代碼見清單 11。
// 新建一個 SSHExec 引用 SSHExec ssh = null; // 下面全部的代碼都放在 try-catch 塊中 try { // 實例化一個 ConnBean 對象,參數依次是 IP 地址、用戶名和密碼 ConnBean cb = new ConnBean("9.125.71.115", "username","password"); // 將剛剛實例化的 ConnBean 對象做爲參數傳遞給 SSHExec 的單例方法獲得一個 SSHExec 對象 ssh = SSHExec.getInstance(cb); // 新建一個 ExecCommand 對象,引用必須是其繼承的 CustomTask 類 CustomTask ct1 = new ExecCommand("chmod 755 /home/tsadmin/sshxcute_test.sh"); // 新建一個 ExecShellScript 對象,引用必須是其繼承的 CustomTask 類 CustomTask ct2 = new ExecShellScript("/home/tsadmin","./sshxcute_test.sh" ,"hello world"); // 鏈接服務器 ssh.connect(); // 上傳 shell 腳本到 /home/tsadmin 目錄 ssh.uploadSingleDataToServer("data/sshxcute_test.sh", "/home/tsadmin"); // 執行命令 ssh.exec(ct1); // 執行腳本而且返回一個 Result 對象 Result res = ssh.exec(ct2); // 檢查執行結果,若是執行成功打印輸出,若是執行失敗,打印錯誤信息 if (res.isSuccess) { System.out.println("Return code: " + res.rc); System.out.println("sysout: " + res.sysout); } else { System.out.println("Return code: " + res.rc); System.out.println("error message: " + res.error_msg); } } catch (TaskExecFailException e) { System.out.println(e.getMessage()); e.printStackTrace(); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } finally { ssh.disconnect(); }
全部的輸出與日誌都將保存在執行 sshxcute 的路徑下,日誌的名稱爲 sshxcute.log。若是你把 sshxcute.jar 導入到你的 Eclipse 項目中,它將保存在項目的根目錄下。
Sshxcute 框架容許配置不少參數,具體的 API 調用格式爲:
SSHExec.setOption(String optionName, String/int/long value);
這個語句聲明必須放在任何任務執行前面,配置才生效。
下面的小節具體講解各個參數的使用方法。
回顧一下 3.5 小節結果對象所講的,每當執行 SSHExec.exec(CustomTask) 後,都會返回一個包含告終果信息的 Result 對象。這個對象有個布爾變量—— isSuccess,用來斷定任務是否執行成功。那麼 SSHXCUTE 是如何斷定命令或者腳本成功與否的,它依賴於什麼?
默認地,有兩個條件斷定任務是否執行成功。第一是命令或者腳本的返回碼(return code),它必須是 0;第二是打印輸入的信息,若是不包含如下字符串,則能夠斷定成功。
usage
usage
not found
fail
Fail
error
Error
exception
Exception
not a valid
返回碼是不可配置的,它必須是 0,這個是默認不可更改的,由於在 Linux/UNIX 系統中執行命令或腳本成功返回值即爲 0。
錯誤信息過濾條件是能夠配置的,若是你想這樣假定:若是標準輸出包含「error」,「fail」,「exception」字符串則斷定任務執行失敗。能夠參考清單 12 的代碼。
String[] reset_keyword = { "error",」fail」,」exception」 }; CustomTask ct1 = new ExecCommand("exit 0"); ct1.resetErrSysoutKeyword(reset_keyword);
這裏要注意 resetErrSysoutKeyword 方法參數的數組不支持正則表達式,須要羅列出全部的斷定爲錯誤的字符串。
當有多個任務執行的時候,並非全部的任務都會成功。若是遇到失敗,不想繼續下面的任務的時候,能夠關閉 HALT_ON_FAILURE 開關:
SSHExec.setOption(IOptionName.HALT_ON_FAILURE, false);
若是遇到失敗,仍然想繼續執行剩下的任務,能夠打開 HALT_ON_FAILURE 開關:
SSHExec.setOption(IOptionName.HALT_ON_FAILURE, true);
默認 HALT_ON_FAILURE 是設爲 false 的。
例如,咱們要依次執行「pwd」 > 「ABCD」 >「echo $HOME」,其中顯而易見第二個命令是錯誤的,任務執行應當在此失敗,若是你想忽略這個錯誤的任務,繼續執行剩下的「echo $HOME」命令任務,你能夠關閉 HALT_ON_FAILURE 開關,要麼乾脆不改變默認值。請看下面的代碼:
SSHExec.setOption(IOptionName.HALT_ON_FAILURE, false); ConnBean cb = new ConnBean("rfidic-1.svl.ibm.com", "tsadmin","u7i8o9p0"); ssh = SSHExec.getInstance(cb); CustomTask ct1 = new ExecCommand("pwd"); CustomTask ct2 = new ExecCommand("ABCD"); CustomTask ct3 = new ExecCommand("echo $HOME"); ssh.connect(); Result r1 = ssh.exec(ct1); Result r2 = ssh.exec(ct2); Result r3 = ssh.exec(ct3); // 此句仍然執行
默認的 SSH 鏈接端口是 22,若是你的遠程主機用的不是這個端口而是 18,你能夠這樣設置:
SSHExec.setOption(IOptionName.SSH_PORT_NUMBER, 18);
SSHXCUTE 會把命令或者腳本的錯誤輸出臨時存放在一個本地文件中。這僅僅是內部須要,用戶沒必要改變這個文件的路徑。默認的的存儲路徑是 $USERHOME/sshxcute_err.msg。
例如:
C:\Documents and Settings\Administrator\sshxcute_err.msg 對於 Windows
/home/user/sshxcute_err.msg 對於 Linux/UNIX
若是想改變錯誤臨時文件路徑請這樣作:
SSHExec.setOption(IOptionName.ERROR_MSG_BUFFER_TEMP_FILE_PATH, "c:\\123.err");
當多任務依次執行時,能夠設置它們之間的間隔時間。這意味着當前一個任務執行結束後,要睡眠(sleep)也就是等待一段時間再繼續執行下一個任務。這在一種情形下極其有用,好比前一個任務執行須要很長時間,後一個任務要徹底等它結束後才能開始。參考下面代碼:
SSHExec.setOption(IOptionName.INTEVAL_TIME_BETWEEN_TASKS, 5000l);
注意這個參數的類型必須是長整形,因此別忘記加「l」。
每一個任務都要在必定時間內完成,若是超過這個時間,程序會自動退出。這個時間就叫作超時(TIMOUT)。你能夠這樣設置 timeout 參數:
SSHExec.setOption(IOptionName.TIMEOUT, 36000l);
注意這個參數的類型必須是長整形,因此別忘記加「l」。
要打印出全部參數設置選項,參考下面代碼:
SSHExec.showEnvConfig(); // 輸出: ****************************************************** The list below shows sshxcute configuration parameter ****************************************************** TIMEOUT => 36000 HALT_ON_FAILURE => true INTEVAL_TIME_BETWEEN_TASKS => 5000 SSH_PORT_NUMBER => 22 ERROR_MSG_BUFFER_TEMP_FILE_PATH => c:\123.err
這個章節是爲想基於 sshxcute 框架作擴展的開發人員讀的。開發者能夠開發適用於本身項目的各類任務。全部的任務都必須繼承 CustomTask 類。這個類包含如下抽象方法:
/** * 經過命令或腳本的輸出檢查任務是否執行成功 * * @param stdout * @return 若是成功返回 true,失敗返回 false */ protected abstract Boolean checkStdOut(String stdout); /** * 經過返回代碼檢查任務是否執行成功 * * @param exitCode * @return 若是成功返回 true,失敗返回 false */ protected abstract Boolean checkExitCode(int exitCode); /** * 返回要在遠程機器上執行的命令 * * @return 命令行字符串 */ public abstract String getCommand(); /** * 任務的描述信息 * * @return 任務描述信息字符串 */ public abstract String getInfo();
若是想新建一種任務類型,只要覆蓋 CustomTask 中的這些方法便可。下面是一個實際的場景來展現如何擴展並適用自定義類型的任務。
產品 A 有一個 deployMetadata.sh 的 shell 腳本。開發組不想使用 ExecShellScript 類,而想使用自定義類型的任務。DeployMetadata.sh 的使用方法見清單 22
/opt/ProductA/bin/deployMetadata.sh – dba_user=system – dba_password=pw4dba
實現代碼以下:
public class DeployMetadata extends CustomTask{ protected String dba_user = ""; protected String dba_password = ""; public DeployMetadata(String dba_user, String dba_password){ this.dba_user = dba_user; this.dba_password = dba_password; } public Boolean checkStdOut(String stdout){ Iterator<String> iter = err_sysout_keyword_list.iterator(); while(iter.hasNext()){ if (stdout.contains(iter.next())) { return false; } } return true; } public Boolean checkExitCode(int exitCode){ if (exitCode == 0) return true; else return false; } public String getCommand(){ return "/opt/ProductA/bin/deployMetadata.sh" + " -dba_user=" + dba_user + " -dba_password=" + dba_password; } public String getInfo(){ return "Deploy metadata "; } }
CustomTask task1 = new DeployMetadata ("system","pw4dba"); ssh.connect(); ssh.exec(task1);
軟件開發與測試人員能夠應用 sshxcute 框架經過 Java 自動化地遠程執行 Linux/UNIX 系統上的命令或腳本,同事批量執行也是支持的,ssxhcute 提供的多種參數選項配置也很是實用,不論是針對軟件開發、測試仍是系統部署,都簡化了自動化流程與系統環境部署的步驟。若是讀者有興趣也能夠下載 sshxcute 源代碼分析,定製適用於本身項目的遠程執行自動化工具。