面向 Java 開發與測試人員的遠程執行 Linux/UNIX 系統上任務的框架 -- sshxcu

軟件開發與測試人員經常會在遠程 Linux/UNIX 系統上執行命令或腳本,有時還會有批量執行任務的需求。常見的辦法是經過本地工具(例如 Putty)鏈接遠程計算機,輸入命令執行,可是當遇到須要集成這些任務到代碼或者開發、測試框架中時,每每就沒有很好的解決方案了。sshxcute 就是這樣一個框架工具集,它基於 JSCH 構建,容許工程師利用 Java 代碼經過 SSH 鏈接遠程批量執行 Linux/UNIX 系統上的命令或者腳本,同時加入了判斷成功與否,取回輸出等多種實用功能。sshxcute 不論是針對軟件開發、測試仍是系統部署,都簡化了自動化流程與系統環境部署的步驟。程序員


一般的使用場景

隨着現代企業內信息的迅速增加,更多的企業創建了數據中心甚至雲計算網絡,對於軟件開發測試行業人員來講,須要更多的依賴於遠程主機,從部署環境到遠程執行都須要利用客戶端工具鏈接。對於開發人員,當部署環境須要輸入不少命令時,每次等待命令執行完畢才能輸入下一個命令,另外一種經常使用的解決方法是把全部命令寫在一個腳本里,但這也須要經過客戶端工具(例如 Putty)遠程登陸後才能執行,但開發人員的利器畢竟是集成開發環境(IDE),這些環境部署工做既費時又費力。對於測試人員,當本地已經作好了一個自動化測試框架或者程序時,須要先在被測系統上部署環境,如何集成進已有的程序或者框架是件必須考慮的事情,若是在每次運行自動化測試前均手動登陸遠程主機部署環境,這也下降了測試人員的工做效率。圖 1 展現了針對上述技術人員的一般使用場景。正則表達式

圖 1. 針對技術人員的一般使用場景

圖 1. 針對技術人員的一般使用場景

回頁首shell

上述場景中的問題與解決方案

分析上述場景,對於軟件開發與測試人員一個典型困惑就是,沒有一個容許自動化的、批量的、帶有檢查命令成功與否的遠程執行工具或者框架,若是存在一個基於 Java 的遠程執行類庫,開發人員能夠在本身的集成開發環境(IDE)中經過運行一個 Java 類就能夠部署環境,測試人員能夠集成該類庫到本身的自動化程序或者一樣適用 IDE 來,就能夠遠程執行命令或者腳本。編程

上述分析能夠參考圖 2。數組

圖 2. 解決方案

圖 2. 解決方案

目前 JSch 正是這樣一個知足上述基本需求的類庫,JSch 是 SSH2 的一個純 Java 實現。它能夠鏈接到一個 sshd 服務器,使用端口轉發,X11 轉發,文件傳輸等等。可是這個類庫畢竟偏向底層,上手與實際運行起來不太方便,sshxcute 框架正是基於 JSch 封裝的,提供了更爲便捷的 API 藉口,更加靈活實用的功能,從而可讓開發與測試人員更加駕輕就熟的使用。sshxcute 是一個框架,它容許工程師利用 Java 代碼經過 SSH 鏈接遠程執行 Linux/UNIX 系統上的命令或者腳本,這種方式不論是針對軟件測試仍是系統部署,都簡化了自動化測試與系統環境部署的步驟。bash

SSHXCUTE 的設計旨在:服務器

  • 最小的系統需求 – 僅僅開啓 SSH 鏈接便可。網絡

  • 易用性 – 工程師利用 Java 代碼執行命令或腳本。框架

  • 內置命令 / 腳本任務執行功能。ssh

  • 易擴展 – 用戶能夠自定義任務類型並集成於 sshxcute 框架。

下面的章節分別介紹瞭如何使用 sshxcute 框架,如何配置它的運行時參數選項以及如何利用該框架的 Java API 進行擴展從而從容應用到本身的項目中。

回頁首

sshxcute 框架使用指南

配置

首先,必須確保 JDK 版本在 5.0 以上,而後須要確認 sshxcute.jar 已經在環境變量中的 $CLASSPATH 中,而後才能夠開始。若是是用集成開發環境(IDE)下,必須將 sshxcute.jar 加入項目構建路徑下,接下來展現的是如何在 Eclipse IDE 中配置 Java Build Path。右鍵單擊項目 > 屬性 > Java 構建路徑。更多的配置步驟請在互聯網上搜索。

圖 1. Eclipse 中配置 Java 構建路徑步驟

圖 1. Eclipse 中配置 Java 構建路徑步驟

初始化準備工做

一般經過 SSH 協議在遠程 Linux/UNIX 系統上執行命令時,如下是必須的步驟:

  1. 打開 SSH 鏈接客戶端(例如 Putty,SSHClient 等)

  2. 輸入 IP

  3. 輸入用戶名、密碼登陸

  4. 登陸成功後輸入執行命令

  5. 斷開登陸

前三個步驟能夠經過 sshxcute 的 Java API 模擬實現:

清單 1. 登陸遠程主機
 // 新建一個 ConnBean 對象,三個參數依次是 ip 地址、用戶名、密碼
 ConnBean cb = new ConnBean("ip ", "username","password"); 
 // 將上面新建的 ConnBean 做爲參數傳遞給 SSHExec 的靜態單例方法,獲得一個 SSHExec 的實例
 ssh = SSHExec.getInstance(cb); 
 // 利用上面獲得的 SSHExec 實例鏈接主機
 ssh.connect();

第五步斷開登陸的實現以下:

清單 2. 斷開遠程主機
 ssh.disconnect();

第四步是 sshxcute 框架的核心所在——自動執行命令或者腳本。接下來的部分將主要介紹這個主題。

遠程執行命令

這是 sshxcute 框架內自帶的任務類型,接下來 3.4 小節講到的遠程執行 shell 腳本也是自帶的任務類型。先來看一段代碼再來詳細解釋。若是讀者已經具有了面向對象編程經驗,那麼下面的內容將會被發現如此熟悉與簡單。

清單 3. 遠程執行命令
 CustomTask sampleTask = new ExecCommand("echo 123"); 
 ssh.exec(sampleTask);

ExecCommand 類繼承了 CustomTask 類,咱們新建一個 ExecCommand 對象,他的引用類型是 CustomTask。下圖展現了 ExecCommand、ExecShellScript 和 CustomTask 的類圖,從中能夠看出他們的關係,ExecCommand、ExecShellScript 是 CustomTask 的子類。

圖 2. sshxcute 框架類圖

圖 2. sshxcute 框架類圖

ExecCommand 的構造函數只接收一個字符串類型變量。注意 ExecCommand 能夠執行多個命令,只須要用分隔符「,」分隔各個命令便可。例如:

清單 4. 遠程順序執行多個命令
 CustomTask sampleTask = new ExecCommand("echo 123", "echo 456, "echo 789");

ExecCommand 的構造函數是:

public ExecCommand(String...args)

把 ExecCommand 對象做爲參數傳入 SSHExec.exec(CustomTask) 方法,這樣就能夠直接運行命令了。

遠程執行 shell 腳本

遠程執行 shell 腳本幾乎與 3.3 小節的遠程執行命令一致。例如,若是想執行 /home/tsadmin 路徑下的 sshxcute_test.sh 腳本,而且帶兩個參數「hello world」,能夠這樣調用:

清單 5. 遠程執行 shell 腳本
 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 目錄下,能夠

清單 6. 上傳文件夾下所有文件到遠程主機
 ssh.uploadAllDataToServer("c:/data2/data", "/home/tsadmin");

若是想只上傳單一文件,例如只上傳路徑下的 c:/data/sshxcute_test.sh 到 /home/tsadmin,能夠這樣

清單 7. 上傳單一文件到遠程主機
 ssh.uploadSingleDataToServer("c:/data/sshxcute_test.sh","/home/tsadmin");

這裏要注意下,必須把順序搞清楚,上傳的步驟必須在執行前,鏈接成功後。例如:

清單 8. 執行順序注意,上傳腳本後才能執行成功
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

清單 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。

清單 10. sshxcute_test.sh 腳本內容
 #!/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。

清單 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 的高級配置選項

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 的代碼。

清單 12. 修改 sshxcute 默認斷定任務成功失敗與否的方法
 String[] reset_keyword = { "error",」fail」,」exception」 }; 
 CustomTask ct1 = new ExecCommand("exit 0"); 
 ct1.resetErrSysoutKeyword(reset_keyword);

這裏要注意 resetErrSysoutKeyword 方法參數的數組不支持正則表達式,須要羅列出全部的斷定爲錯誤的字符串。

多任務執行時遇到任務失敗是繼續仍是中止

當有多個任務執行的時候,並非全部的任務都會成功。若是遇到失敗,不想繼續下面的任務的時候,能夠關閉 HALT_ON_FAILURE 開關:

清單 13. 遇到失敗時中止執行接下來的任務
 SSHExec.setOption(IOptionName.HALT_ON_FAILURE, false);

若是遇到失敗,仍然想繼續執行剩下的任務,能夠打開 HALT_ON_FAILURE 開關:

清單 14. 遇到失敗時繼續執行接下來的任務
 SSHExec.setOption(IOptionName.HALT_ON_FAILURE, true);

默認 HALT_ON_FAILURE 是設爲 false 的。

例如,咱們要依次執行「pwd」 > 「ABCD」 >「echo $HOME」,其中顯而易見第二個命令是錯誤的,任務執行應當在此失敗,若是你想忽略這個錯誤的任務,繼續執行剩下的「echo $HOME」命令任務,你能夠關閉 HALT_ON_FAILURE 開關,要麼乾脆不改變默認值。請看下面的代碼:

清單 15. 遇到失敗時繼續執行接下來的任務
 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 端口設置

默認的 SSH 鏈接端口是 22,若是你的遠程主機用的不是這個端口而是 18,你能夠這樣設置:

清單 16. 修改 SSH 鏈接端口爲 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

若是想改變錯誤臨時文件路徑請這樣作:

清單 17. 修改錯誤輸入臨時存放文件路徑
 SSHExec.setOption(IOptionName.ERROR_MSG_BUFFER_TEMP_FILE_PATH, "c:\\123.err");

不一樣任務之間的間隔時間

當多任務依次執行時,能夠設置它們之間的間隔時間。這意味着當前一個任務執行結束後,要睡眠(sleep)也就是等待一段時間再繼續執行下一個任務。這在一種情形下極其有用,好比前一個任務執行須要很長時間,後一個任務要徹底等它結束後才能開始。參考下面代碼:

清單 18. 修改不一樣任務之間的間隔時間
 SSHExec.setOption(IOptionName.INTEVAL_TIME_BETWEEN_TASKS, 5000l);

注意這個參數的類型必須是長整形,因此別忘記加「l」。

超時

每一個任務都要在必定時間內完成,若是超過這個時間,程序會自動退出。這個時間就叫作超時(TIMOUT)。你能夠這樣設置 timeout 參數:

清單 19. 修改不一樣任務之間的間隔時間
 SSHExec.setOption(IOptionName.TIMEOUT, 36000l);

注意這個參數的類型必須是長整形,因此別忘記加「l」。

打印全部參數

要打印出全部參數設置選項,參考下面代碼:

清單 20. 打印全部參數
 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

回頁首

API 擴展

這個章節是爲想基於 sshxcute 框架作擴展的開發人員讀的。開發者能夠開發適用於本身項目的各類任務。全部的任務都必須繼承 CustomTask 類。這個類包含如下抽象方法:

清單 21. 全部任務的父類—— 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

清單 22. 產品小組 A 的需求,執行 deployMetadata.sh 腳本的命令格式
 /opt/ProductA/bin/deployMetadata.sh – dba_user=system – dba_password=pw4dba

實現

實現代碼以下:

清單 23. deployMetadata.sh 的任務模型代碼
 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 "; 
 } 
 }

使用方法

清單 24. deployMetadata.sh 的任務在 sshxcute 框架中的使用方法
 CustomTask task1 = new DeployMetadata ("system","pw4dba"); 
 ssh.connect(); 
 ssh.exec(task1);

結束語

軟件開發與測試人員能夠應用 sshxcute 框架經過 Java 自動化地遠程執行 Linux/UNIX 系統上的命令或腳本,同事批量執行也是支持的,ssxhcute 提供的多種參數選項配置也很是實用,不論是針對軟件開發、測試仍是系統部署,都簡化了自動化流程與系統環境部署的步驟。若是讀者有興趣也能夠下載 sshxcute 源代碼分析,定製適用於本身項目的遠程執行自動化工具。

sshxcute jar包下載

相關文章
相關標籤/搜索