原文地址:https://www.ibm.com/developerworks/cn/opensource/os-sshxcute/

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

通常的使用场景

JSch 是 SSH2 的一个纯 Java 实现。它可以连接到一个 sshd 服务器,使用端口转发,X11 转发,文件传输等等。但是这个类库毕竟偏向底层,上手与实际运行起来不太方便,sshxcute 框架正是基于 JSch 封装的,提供了更为便捷的 API 借口,更加灵活实用的功能,从而可以让开发与测试人员更加得心应手的使用。sshxcute 是一个框架,它允许工程师利用 Java 代码通过 SSH 连接远程执行 Linux/UNIX 系统上的命令或者脚本,这种方式不管是针对软件测试还是系统部署,都简化了自动化测试与系统环境部署的步骤。

SSHXCUTE 的设计旨在:

  • 最小的系统需求 – 仅仅开启 SSH 连接即可。
  • 易用性 – 工程师利用 Java 代码执行命令或脚本。
  • 内置命令 / 脚本任务执行功能。
  • 易扩展 – 用户可以自定义任务类型并集成于 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. 登录远程主机
1
2
3
4
5
6
// 新建一个 ConnBean 对象,三个参数依次是 ip 地址、用户名、密码
ConnBean cb = new ConnBean("ip ", "username","password");
// 将上面新建的 ConnBean 作为参数传递给 SSHExec 的静态单例方法,得到一个 SSHExec 的实例
ssh = SSHExec.getInstance(cb);
// 利用上面得到的 SSHExec 实例连接主机
ssh.connect();

第五步断开登录的实现如下:

清单 2. 断开远程主机
1
ssh.disconnect();

第四步是 sshxcute 框架的核心所在——自动执行命令或者脚本。接下来的部分将主要介绍这个主题。

远程执行命令

这是 sshxcute 框架内自带的任务类型,接下来 3.4 小节讲到的远程执行 shell 脚本也是自带的任务类型。先来看一段代码再来详细解释。如果读者已经具备了面向对象编程经验,那么下面的内容将会被发现如此熟悉与简单。

清单 3. 远程执行命令
1
2
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. 远程顺序执行多个命令
1
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 脚本
1
2
3
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. 上传文件夹下全部文件到远程主机
1
ssh.uploadAllDataToServer("c:/data2/data", "/home/tsadmin");

如果想只上传单一文件,例如只上传路径下的 c:/data/sshxcute_test.sh 到 /home/tsadmin,可以这样

清单 7. 上传单一文件到远程主机
1
ssh.uploadSingleDataToServer("c:/data/sshxcute_test.sh","/home/tsadmin");

这里要注意下,必须把顺序搞清楚,上传的步骤必须在执行前,连接成功后。例如:

清单 8. 执行顺序注意,上传脚本后才能执行成功
1
2
3
4
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. 根据结果对象打印输出或者错误信息
1
2
3
4
5
6
7
8
9
10
11
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 脚本内容
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/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. 一个具体实例的实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 新建一个 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(); 
}
Logo

更多推荐