java实现代码通过“代理”批量操作linux机器

需求:有大量的linux机器需要进行发布更新以及其他命令操作,而且网段和本地不通,需要通过代理的方式进行,

按以往的方式,只能一个一个手动上去执行命令,或者传文件上去,这样耗费大量的时间。这个时候想到用代码

来实现这种重复操作,

关键技术:session的登录,xhell通道代理的建立。

具体实现步骤

一 :建立本地代理

       由于不在同一网段,需要用工具建立一个通道(代理),可以选择SecureCRT,xhell,小红帽等工具建立一个能远程到目标服务器的通道(代理,默认端口设置为1080,使用socks进行动态转发)

二:代码实现

      博主用的框架是springboot,主要方便jar包的引入

      1:关键jar包的引入,pom.xml文件

需要实现Jsch包中的UserInfo,UIKeyboardInteractive接口,用以保存用户信息,以及进行键盘交互式认证并执行命令

    <dependency>
      <groupId>com.jcraft</groupId>
      <artifactId>jsch</artifactId>
      <version>0.1.54</version>
    </dependency>

方便调试运行,写成测试类,

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

      

主类全部代码

package com.hg.test;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.ProxySOCKS5;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.util.StringUtils;

@Slf4j
public class Testsshkylin2 extends BaseTest{

  private int timeout = 60000;
  private ExecutorService ES = Executors.newFixedThreadPool(50);

  @Test
  public void test() throws IOException {
    String con = read("/******");//此处我写成了json,方便读取,这个用户可以自定义,只要能读取就行,里面主要存放用户名,密码,以及个别判断字段
    ObjectMapper objectMapper = new ObjectMapper();
    JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, Server.class);
    List<Server> list = objectMapper.readValue(con, javaType);//此处,我新建了一个实体类Server,方便参数的读取,
    exec(list);
  }
  public void exec( List<Server> list) throws IOException {
    List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
    list
        .stream().filter(server ->!server.isHh())
        .forEach(s -> {
          tasks.add(buile(s,s.name));
        });
    long ss = System.currentTimeMillis();
    try {
      List<Future<Integer>> results = ES.invokeAll(tasks);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    log.info("执行任务消耗了 :" + (System.currentTimeMillis() - ss) + "毫秒");

  }

  private Callable<Integer> buile(Server s,String name){
    return () -> {
      try {
        readRemote(s,name);
      } catch (SocketTimeoutException e) {
        log.error("{}\t 网络不通", s.ip);
      } catch (Exception e) {
        String err=e.getLocalizedMessage();
        if(err.equalsIgnoreCase("Auth cancel"))
          return 1;
        log.error("{}\t{}", s.ip, e.getLocalizedMessage());
      }
      return 1;
    };
  }

  public void readRemote(final Server s,String staname) throws JSchException, IOException, InterruptedException {
    String host = s.ip;
    String username = s.user;
    String pass = s.pass;
    JSch jsch = new JSch();
    Session session = jsch.getSession(username, host);
    session.setProxy(new ProxySOCKS5("127.0.0.1"));//路由用本地
    session.setPassword(pass); // 设置密码
    session.setUserInfo(new MyUserInfo(s)); //需要实现Jsch包中的UserInfo,UIKeyboardInteractive接口,用以保存用户信息,以及进行键盘交互式认证并执行命令。
    Properties config = new Properties();
    config.put("StrictHostKeyChecking", "no");//在代码里需要跳过检测。否则会报错找不到主机
    session.setConfig(config); // 为Session对象设置properties
    session.setTimeout(timeout); // 设置timeout时间
    session.connect(); // 通过Session建立与远程服务器的连接回话

    ChannelShell channelShell = (ChannelShell) session.openChannel("shell");
    PipedInputStream pipeIn = new PipedInputStream();
    PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);
    channelShell.setInputStream(pipeIn);

    ByteArrayOutputStream output = new ByteArrayOutputStream();
    channelShell.setOutputStream(output);
    channelShell.connect(timeout);

    output.reset();
    pipeOut.write(("\n").getBytes());
    wateForuser(output);

    //命令最好一个一个执行,不然可能会出现,先后顺序错乱
    sendFile(session,new File("D:\\shellwork\\d.xml"));//上传文件
    exec("service cloud_ds restart",pipeOut,output);//服务重启
    String status=exec("systemctl status cloud_ds",pipeOut,output);//查看服务状态获取返回值
    log.info("===================="+status);
    pipeOut.close();
    pipeIn.close();
    channelShell.disconnect();
    session.disconnect();
  }

  private void sendFile(Session session, File file) {
    log.info("put file {}", file.getName());
    try {
      Channel channel = session.openChannel("sftp");
      channel.connect();
      ChannelSftp c = (ChannelSftp) channel;
      int mode = ChannelSftp.OVERWRITE;
      c.put(file.getAbsolutePath(), "./", mode);
      c.quit();
      c.disconnect();
      channel.disconnect();
    } catch (Exception e) {
      log.error("上传文件失败 {} -{} -{}", session.getHost(), file.getName(), e.getLocalizedMessage());
    }
  }


  private String exec(String cmd, PipedOutputStream pipeOut, ByteArrayOutputStream output) throws IOException {
    pipeOut.write((cmd + "\n").getBytes());
    return wateFor("#", output);
  }

  private void wateForuser(ByteArrayOutputStream output) {

    long start = System.currentTimeMillis();
    while (true) {
      String str = new String(output.toByteArray());
      if (StringUtils.hasText(str)) {
        if (str.indexOf("#") >= 0 || str.indexOf("$") >= 0) {
          output.reset();
          return;
        }
      }
      if (System.currentTimeMillis() - start > timeout) {
        throw new RuntimeException("登录时等待超时");
      } else {
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }

    }
  }

  private String wateFor(String con, ByteArrayOutputStream output) {
    long start = System.currentTimeMillis();
    String str = null;
    while (true) {
      str = output.toString();
      if (str.indexOf(con) >= 0) {
        output.reset();
        return str;
      }
      if (System.currentTimeMillis() - start > timeout) {
        throw new RuntimeException("等待超时");
      } else {
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
        }
      }
    }
  }


  private static class MyUserInfo implements UserInfo {

    private Server server;

    private MyUserInfo(Server ip) {
      this.server = ip;
    }

    public String getPassphrase() {
      System.out.println("getPassphrase");
      return null;
    }

    public String getPassword() {
      log.info("{}\t密码错误", server.ip);
      return null;
    }

    public boolean promptPassword(String s) {
//      System.out.println("promptPassword:" + s);
      return true;
    }

    public boolean promptPassphrase(String s) {
      System.out.println("promptPassphrase:" + s);
      return false;
    }

    public boolean promptYesNo(String s) {
      System.out.println("promptYesNo:" + s);
      return true;//notice here!
    }

    public void showMessage(String s) {
//      System.out.println("showMessage:" + s);
    }
  }

}
 

大致就是这样,具体可以尝试一下,

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值