加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Jsch远程操作服务器(上)

(2017-12-13 16:02:03)
标签:

jsch

远程操作服务器

分类: 软件测试技术

测试开发这个工作最后都有可能会开发测试平台,测试工具等,这也难免会遇到这样的问题 :如何操作远程的服务器?如向服务器发送命令,收集或是判断命令执行结果,以及记录日志,拉取远程服务器上的测试报告等操作。

前阶段用公司的WFSCF框架开发测试平台,遇到了这样的问题,研究了几天,发现java有这样的功能:JSch!!!

JSch SSH2的一个纯Java实现。它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己的 程序中。同时该项目也提供一个J2ME版本用来在手机上直连SSHD服务器。同时该开发包也提供 J2ME 下的版本。(http://www.oschina.net/p/jsch

下面介绍一下常用的功能:

一, 远程连接主机

首先要下载对应的jsch.jar包,然后添加相应的引用:

Import  com.jcraft.jsch.Channel;

Import  com.jcraft.jsch.ChannelExec;

Import  com.jcraft.jsch.ChannelShell;

Import  com.jcraft.jsch.JSch;

Import  com.jcraft.jsch.JSchException;

Import  com.jcraft.jsch.Session;

1Jsch远程连接主机的时候,最常用的方法是通过IP地址,端口号,用户名和密码新建session,如下所示:

Public  JschUtils(String host,intport,Stringusername,Stringpassword) throwsJSchException{

              jsch = newJSch();

              session = jsch.getSession(username, host, port);

              session.setPassword(password);

             

              java.util.Propertiesconfig = new;

              config.put("StrictHostKeyChecking", "no");

              session.setConfig(config);

              session.connect(3000);

              log.info("建立连接");

       }

通过调用这个JschUtils函数,传递IP地址,端口号,用户名和密码,即可建立与该服务器的连接,当然这个session变量需要定义成全局变量,方便其他函数使用。

2)如果你是通过添加授权,kerberos,添加信件IP等方式的话,连接方式就不太一样了,具体的要根据具体的情况去网上查询,下面提供一个我们公司的示例,具体操作步骤如下:

Ø  在要操作的服务器上给平台机器添加work权限

vi /home/work/.k5login

....

host/XXX-YYY—ZZZ

....

Ø  在平台WF容器的conf文件夹中添加登录配置loginkeytab.conf

 

# pwd

/opt/web/CommercialTestPlatform/conf

# cat loginkeytab.conf

com.sun.security.jgss.krb5.initiate{                              

com.sun.security.auth.module.Krb5LoginModule required

debug="false"

useKeyTab="true"

doNotPrompt="true"

keyTab="/etc/krb5.keytab"

storeKey="true"

principal="host/XXX-YYY—ZZZ ";

};

注意:principal必须是本机受权的用户名,也是就是(1)中添加的。

 

Ø  在平台服务器上执行下面的命令:

/usr/bin/kinit -k -t /etc/krb5.keytab

 

Ø  通过jsch连接服务器

publicJschUtils(String host,intport) throwsJSchException{

              String name = "work";

              Session session; 

              try {

                  System.setProperty("java.security.auth.login.config", "$pwd/loginkeytab.conf");

                     System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");

                     System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");

 

                     JSch jsch = new JSch();

                     jsch.setKnownHosts( "/root/.ssh/known_hosts" );

                     loggerHelper.info("Workname:" + name+" hosts:"+host);

                     session = jsch.getSession(name, host, port);

                    

                     session.setTimeout(3000);

                     session.setServerAliveInterval(1000);

                     java.util.Properties config = new ;

                     config.put("StrictHostKeyChecking", "no");

                     config.put("PreferredAuthentications","gssapi-with-mic,publickey" );

                     config.put("kex", "diffie-hellman-group1-sha1");

                     session.setConfig(config);

                    

                     session.connect();

              session.sendKeepAliveMsg();

 

              } catch (Exception e) {

                     //公钥方式登录

                     JSch jsch = new JSch();

                     jsch.addIdentity("/root/.ssh/id_rsa");

                     jsch.setKnownHosts( "/root/.ssh/known_hosts" );

                     session = jsch.getSession(name, host, port);

                     session.setTimeout(3000);

                     java.util.Properties Config = new ;

                     Config.put("StrictHostKeyChecking", "no");

                     session.setConfig(Config);

                     session.connect();

              }

              return session;}

      在这个函数中我们通过了kerberos授权和添加信息的IP地址,当然还是需要IP地址,端口号,用户名和密码的。此时的IP地址指向连接到哪儿台服务器,用户名和密码就是授权的了,此时的用户名和密码被定义成了全局变量,所以不用传参。

二, 远程执行命令

连接远程服务器的目的是为操作服务器,所以我们要解决的第二个知识点就是如何向远程服务器发送命令?当然我们在执行命令的时候,不能只把命令发过去就不管了,要把命令执行的输出返回。如果命令常时间没有任何输出的时候,就需要断开与服务器的连接,故需要一个超时时间。

根据上面的分析,我们的示例代码如下:

         public String execAndResult( String cmd,longtimeout)throwsException{

              ChannelExecchannelExec = null;

              StringBufferresult = newStringBuffer();

              InputStreamin=  null;

              try {

                     log.info("要执行的命令:" + cmd);

                     channelExec = (ChannelExec)session.openChannel( "exec" );

                     channelExec.setCommand(cmd );

                     channelExec.setInputStream(null );

                     channelExec.setErrStream(System.err );

                     in= channelExec.getInputStream();

                     channelExec.connect();

                     intres = -1;

                     byte[] tmp = newbyte[ 1024 ];

                     longstarttime = System.currentTimeMillis();

                     while ( true ) {

                            while ( in.available() > 0 ) {

                                   inti= in.read( tmp, 0, 1024 );

                                   if ( i< 0 ) break;

                                   result.append(new String( tmp, 0, i) + "\n");

                                   starttime = System.currentTimeMillis();

                            }

                            if (System.currentTimeMillis() - starttime>= timeout * 1000) {

                                   log.info("" + timeout + "秒内没有日志输出,中断程序");

                                   break;

                            }

                            if ( channelExec.isClosed() ) {

                                   res = channelExec.getExitStatus();

                                   break;

                            }

                     }

              } catch (Exception e) {

                     e.printStackTrace();

              } finally {

                     if (channelExec !=null) {

                            channelExec.disconnect();

                     }

                     if (in!=null) {

                            in.close();

                     }

                     session.disconnect();

                     log.info("关闭连接");

              }

              returnresult.toString();

       }

代码解析:

(1)  函数execAndResult()为在远程服务器上执行命令cmd,如果命令在timeout期间没有任何输出的话则中断连接。

(2)  在调用函数前,需要先调用上面的连接服务器函数,连接到服务器,并初化session变量。命令执行的输出存放在StringBufferresult变量中,并作为函数的返回值返回给调用者。

(3)  通过ChannelExec执行命令和获取命令执行结果,channelExec.getInputStream()获取命令输出流,如果in.available()<0则没有输出任何信息。

(4)  在命令没有输出的时候,判断一下是否超时,如果不超时继续读取,如果超时则断开远程连接。

三, 记录远程命令执行日志

很多时候我们执行的命令会有大量的输出,而我们又不能一直盯着输出看,此时我们就需要把输出记录成日志,这样方便我们排查。当然程序有的时候会有日志记录,我们执行的命令就不一定有了,所以我们要人工来添加。

如我们在部署环境的时候,需要记录部署日志,但是同时也要根据输出来判断部署结果。此时就要检测什么时候算是部署成功,什么情况下是部署失败?那么我们就需要设置一个成功的标志,检查到有这个标志输出的时候就算成功,示例代码如下:

 

       publicboolean exec( String cmd,String outputFileName,String sucessStr,long timeout)

              throws Exception{

              BufferedWriterout =newBufferedWriter(newFileWriter(outputFileName));

              ChannelExecchannelExec = null;

              InputStreamin= null;

              try {

                     log.info("要执行的命令:" + cmd);

                     channelExec = (ChannelExec)session.openChannel( "exec" );

                     channelExec.setCommand(cmd );

                     channelExec.setInputStream(null );

                     channelExec.setErrStream(System.err );

                     channelExec.setPty(true);

                     in= channelExec.getInputStream();

                     channelExec.connect();

                     intres = -1;

                     byte[] tmp = newbyte[ 1024 ];

                     longstarttime = System.currentTimeMillis();

                     while ( true ) {

                            while ( in.available() > 0 ) {

                                   inti= in.read( tmp, 0, 1024 );

                                   if ( i< 0 ) break;

                                   String tmp2 = newString(tmp, 0, i) + "\n";

                                   if (sucessStr!=null) {

                                          if (tmp2.indexOf(sucessStr) > -1) {

                                                 out.write(tmp2);

                                                 returntrue;

                                          }

                                   }

                                   out.write(tmp2);

                                   starttime = System.currentTimeMillis();

                            }

                           

                            if (System.currentTimeMillis() - starttime>= timeout * 1000) {

                                   log.info("" + timeout + "秒内没有日志输出,中断程序");

                                   break;

                            }

                            if ( channelExec.isClosed()) {

                                   log.info("没有输出了,关闭了" + (System.currentTimeMillis() - starttime));

                                   res = channelExec.getExitStatus();

                                   log.info("channel 关闭");

                                   break;

                            }

                     }

                    

              } catch (Exception e) {

                     e.printStackTrace();

              } finally {

                     out.close();

                     if (channelExec !=null) {

                            channelExec.disconnect();

                     }

                     if (in !=null) {

                            in.close();

                     }

                     session.disconnect();

                     log.info("关闭连接");

              }

              returnfalse;

}

代码解析:

(1)  函数exec()通过参数来执行命令,并把命令输出给保存到outputFileName指定的文件中。

(2)  在命令执行的时候,判断输出,如果输出包含sucessStr指定的成功标志,则返回True;其他的情况如超时,或是报错等都会返回False

(3)  在异常的情况下,需要记录异常并且关闭与远程主机的连接,不然一次连接启动一个进程,会把服务器给卡死的。

(4)当然我们也可以去掉记录日志的部署,单纯地根据命令的输出来判断命令执行成功或是失败,这个就比较简单,我们在此就不展示代码了。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有