软件开发与测试人员经常会在远程 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 源代码分析,定制适用于本身项目的远程执行自动化工具。