DevOps自动化平台开发之 Shell脚本执行的封装

 基础知识

基于如下技术栈开发DevOps平台

Spring Boot

Shell

Ansible

Git

Gitlab

Docker

K8S

Vue

 1、spring boot starter的封装使用

2、Shell脚本的编写

3、Ansible 脚本的编写

4、Docker 的使用与封装设计

本篇介绍如何使用Java封装Linux命令和Shell脚本的使用

将其设计成spring boot starter

maven依赖pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.9</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.devops</groupId><artifactId>ssh-client-pool-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version><name>ssh-client-pool-spring-boot-starter</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.hierynomus</groupId><artifactId>sshj</artifactId><version>0.26.0</version></dependency><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.60</version></dependency><dependency><groupId>com.fasterxml.uuid</groupId><artifactId>java-uuid-generator</artifactId><version>3.1.4</version></dependency><dependency><groupId>net.sf.expectit</groupId><artifactId>expectit-core</artifactId><version>0.9.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.8.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

具体的封装代码: 

package com.devops.ssh.autoconfigure;import com.devops.ssh.pool.SshClientPoolConfig;
import com.devops.ssh.pool.SshClientsPool;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author Gary*/
@Configuration
@EnableConfigurationProperties(SshClientPoolProperties.class)
public class SshClientPoolAutoConfiguration {private final SshClientPoolProperties properties;public SshClientPoolAutoConfiguration(SshClientPoolProperties properties) {this.properties = properties;}@Bean@ConditionalOnMissingBean(SshClientsPool.class)SshClientsPool sshClientsPool() {return new SshClientsPool(sshClientPoolConfig());}SshClientPoolConfig sshClientPoolConfig() {SshClientPoolConfig poolConfig = new SshClientPoolConfig(properties.getMaxActive(),properties.getMaxIdle(),properties.getIdleTime(),properties.getMaxWait());if(properties.getSshj()!=null) {poolConfig.setServerCommandPromotRegex(properties.getSshj().getServerCommandPromotRegex());}if (properties.getSshClientImplClass()!=null) {try {poolConfig.setSshClientImplClass(Class.forName(properties.getSshClientImplClass()));} catch (ClassNotFoundException e) {e.printStackTrace();}}return poolConfig;}
}

package com.devops.ssh.autoconfigure;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties("devops.ssh-client-pool")
public class SshClientPoolProperties {/*** Max number of "idle" connections in the pool. Use a negative value to indicate* an unlimited number of idle connections.*/private int maxIdle = 20;/*** */private int idleTime = 120*1000;/*** Max number of connections that can be allocated by the pool at a given time.* Use a negative value for no limit.*/private int maxActive = 20;/*** Maximum amount of time (in milliseconds) a connection allocation should block* before throwing an exception when the pool is exhausted. Use a negative value* to block indefinitely.*/private int maxWait = 120*1000;private String sshClientImplClass = "com.devops.ssh.SshClientSSHJ";private SshClientProperites sshj;public int getMaxIdle() {return maxIdle;}public void setMaxIdle(int maxIdle) {this.maxIdle = maxIdle;}public int getIdleTime() {return idleTime;}public void setIdleTime(int idleTime) {this.idleTime = idleTime;}public int getMaxActive() {return maxActive;}public void setMaxActive(int maxActive) {this.maxActive = maxActive;}public int getMaxWait() {return maxWait;}public void setMaxWait(int maxWait) {this.maxWait = maxWait;}public String getSshClientImplClass() {return sshClientImplClass;}public void setSshClientImplClass(String sshClientImplClass) {this.sshClientImplClass = sshClientImplClass;}public SshClientProperites getSshj() {return sshj;}public void setSshj(SshClientProperites sshj) {this.sshj = sshj;}public static class SshClientProperites{private String serverCommandPromotRegex;public String getServerCommandPromotRegex() {return serverCommandPromotRegex;}public void setServerCommandPromotRegex(String serverCommandPromotRegex) {this.serverCommandPromotRegex = serverCommandPromotRegex;}}}

package com.devops.ssh.exception;/*** Ssh auth failed* @author Gary**/
public class AuthException extends SshException{public AuthException(String message) {this(message, null);}public AuthException(String message, Throwable error) {super(message, error);}/*** */private static final long serialVersionUID = -3961786667342327L;}
package com.devops.ssh.exception;/*** The ssh connection is disconnected* @author Gary**/
public class LostConnectionException extends SshException{private static final long serialVersionUID = -3961870786667342727L;public LostConnectionException(String message) {this(message, null);}public LostConnectionException(String message, Throwable error) {super(message, error);}
}
package com.devops.ssh.exception;public class SshException extends Exception{/*** */private static final long serialVersionUID = 2052615275027564490L;public SshException(String message, Throwable error) {super(message);if(error != null) {initCause(error);}}public SshException(String message) {this(message, null);}}
package com.devops.ssh.exception;/*** Timeout Exception* @author Gary**/
public class TimeoutException extends SshException {public TimeoutException(String message) {this(message, null);}public TimeoutException(String message, Throwable error) {super(message, error);}/****/private static final long serialVersionUID = -39618386667342727L;}
package com.devops.ssh.pool;import com.devops.ssh.SshClient;
import com.devops.ssh.SshClientSSHJ;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;/**** The configuration of SshClientPool library* <p>SshClientPoolConfig is a subclass of GenericKeyedObjectPoolConfig to control the pool behavior* <p>Also, you can replace the build-in {@link SshClient} implementation by {@link SshClientPoolConfig#setSshClientImplClass(Class)} if you want** @author Gary*/
public class SshClientPoolConfig extends GenericKeyedObjectPoolConfig<SshClientWrapper>{private Class<?> sshClientImplClass;private String serverCommandPromotRegex;public SshClientPoolConfig() {super();}/*** quick way to create SshClientPoolConfig* set TestOnBorrow to true* set TestOnReturn to true* set TestWhileIdle to true* set JmxEnabled to false* @param maxActive maxTotalPerKey* @param maxIdle maxIdlePerKey* @param idleTime idle time* @param maxWaitTime maxWaitMillis*/public SshClientPoolConfig(int maxActive, int maxIdle, long idleTime,  long maxWaitTime){this.setMaxTotalPerKey(maxActive);this.setMaxIdlePerKey(maxIdle);this.setMaxWaitMillis(maxWaitTime);this.setBlockWhenExhausted(true);this.setMinEvictableIdleTimeMillis(idleTime);this.setTimeBetweenEvictionRunsMillis(idleTime);this.setTestOnBorrow(true);this.setTestOnReturn(true);this.setTestWhileIdle(true);this.setJmxEnabled(false);}public Class<?> getSshClientImplClass() {return sshClientImplClass;}/*** replace the build-in {@link SshClient} by {@link SshClientPoolConfig#setSshClientImplClass(Class)}* @param sshClientImplClass the implementation of {@link SshClient}*/public void setSshClientImplClass(Class<?> sshClientImplClass) {this.sshClientImplClass = sshClientImplClass;}/**** @return regex string used to match promot from server*/public String getServerCommandPromotRegex() {return serverCommandPromotRegex;}/*** see {@link SshClientSSHJ#setCommandPromotRegexStr(String)}* @param serverCommandPromotRegex regex string used to match promot from server*/public void setServerCommandPromotRegex(String serverCommandPromotRegex) {this.serverCommandPromotRegex = serverCommandPromotRegex;}}
package com.devops.ssh.pool;import com.devops.ssh.*;
import com.devops.ssh.exception.SshException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.UUID;/*** A wrapper class of {@link SshClient} used by {@link SshClientsPool}** @author Gary**/
public class SshClientWrapper implements SshClientEventListener {private final static Logger logger = LoggerFactory.getLogger(SshClientWrapper.class);private String id;private SshClient client;SshClientEventListener listener;SshClientConfig config;public String getId() {return this.id;}public void setListener(SshClientEventListener listener) {this.listener = listener;}public SshClientConfig getConfig() {return this.config;}public SshClientWrapper(SshClientConfig config, SshClientPoolConfig poolConfig) {this.id = UUID.randomUUID().toString();this.config = config;this.client = SshClientFactory.newInstance(config, poolConfig);}public SshClientWrapper setEventListener(SshClientEventListener listener) {this.listener = listener;this.client.setEventListener(this);return this;}public SshClientWrapper connect(int timeoutInSeconds) throws SshException {client.connect(timeoutInSeconds);return this;}public SshClientWrapper auth() throws SshException{if(null!=this.config.getPassword() && this.config.getPassword().length()>0) {client.authPassword();}else if(null!=this.config.getPrivateKeyPath() && this.config.getPrivateKeyPath().length()>0) {client.authPublickey();}else {client.authPublickey();}return this;}public SshClientWrapper startSession() throws SshException{client.startSession(true);return this;}public SshResponse executeCommand(String command, int timeoutInSeconds){SshResponse response = client.executeCommand(command, timeoutInSeconds);return response;}public void disconnect() {client.disconnect();}@Overridepublic boolean equals(Object obj) {if(obj instanceof SshClientWrapper){return id.equals(((SshClientWrapper)obj).getId());}return false;}@Overridepublic int hashCode(){return id.hashCode();}public SshClientState getState() {return client.getState();}@Overridepublic String toString() {return "["+this.id+"|"+this.config.getHost()+"|"+this.config.getPort()+"|"+this.getState()+"]";}@Overridepublic void didExecuteCommand(Object client) {this.listener.didExecuteCommand(this);}@Overridepublic void didDisConnected(Object client) {this.listener.didDisConnected(this);}@Overridepublic void didConnected(Object client) {this.listener.didConnected(this);}}
package com.devops.ssh;import com.devops.ssh.exception.SshException;/*** Ssh Client used to connect to server instance and execute command. The build-in implementation is {@link SshClientSSHJ}<p>** Client can be used in chain mode, {@link SshClient}.{@link #init(SshClientConfig)}.{@link #connect(int)}.{@link #authPassword()}.{@link #startSession(boolean)}.{@link #executeCommand(String, int)}<p>** At last, close the client with {@link #disconnect()}** <p>Set an {@link SshClientEventListener} with {@link #setEventListener(SshClientEventListener)} to be notified when its event occurs* <p>* @author Gary**/
public interface SshClient {/*** pass the {@link SshClientConfig} to client* @param config the information used to connect to server* @return SshClient itself*/public SshClient init(SshClientConfig config);/*** connect to server, and timeout if longer than {@code timeoutInSeconds}* @param timeoutInSeconds timeout in seconds* @return SshClient itself* @throws SshException if server is unreachable, usually the host and port is incorrect*/public SshClient connect(int timeoutInSeconds) throws SshException;/*** auth with password* @return SshClient itself* @throws SshException if username or password is incorrect*/public SshClient authPassword() throws SshException;/*** auth with key* @return SshClient itself* @throws SshException if username or public key is incorrect*/public SshClient authPublickey() throws SshException;/*** start session* @param shellMode <tt>true</tt>: communicate with server interactively in session, just like command line* <p><tt>false</tt>: only execute command once in session* @return SshClient itself* @throws SshException when start session failed**/public SshClient startSession(boolean shellMode) throws SshException;/**** @param command execute the {@code command} on server instance, and timeout if longer than {@code timeoutInSeconds}.* @param timeoutInSeconds timeout in seconds* @return SshResponse**/public SshResponse executeCommand(String command, int timeoutInSeconds);/*** set the listener on SshClient* @param listener notify listener when events occur in SshClient* @return SshClient itself*/public SshClient setEventListener(SshClientEventListener listener);/*** disconnect from server*/public void disconnect();/*** state of SshClient** @return SshClientState the state of ssh client* <p><tt>inited</tt> before {@link #startSession(boolean)} success* <p><tt>connected</tt> after {@link #startSession(boolean)} success* <p><tt>disconnected</tt> after {@link #disconnect()}, or any connection problem occurs*/public SshClientState getState();}
package com.devops.ssh;/**** Configuration used by {@link SshClient} to connect to remote server instance** @author Gary**/
public class SshClientConfig {private String host;private int port;private String username;private String password;private String privateKeyPath;private String id;/**** @return host address*/public String getHost() {return host;}/*** @param host host address, usually the ip address of remote server*/public void setHost(String host) {this.host = host;}/**** @return ssh port of the remote server*/public int getPort() {return port;}/*** @param port ssh port of the remote server*/public void setPort(int port) {this.port = port;}/**** @return ssh username of the remote server*/public String getUsername() {return username;}/**** @param username ssh username of the remote server*/public void setUsername(String username) {this.username = username;}/**** @return ssh password of the remote server*/public String getPassword() {return password;}/**** @param password ssh password of the remote server*/public void setPassword(String password) {this.password = password;}/*** @return ssh local key file path of the remote server*/public String getPrivateKeyPath() {return privateKeyPath;}/*** @param privateKeyPath local key file path of the remote server*/public void setPrivateKeyPath(String privateKeyPath) {this.privateKeyPath = privateKeyPath;}/**** @return id of the config*/public String getId() {return id;}/**** @param host           server host address* @param port           server ssh port* @param username       server ssh username* @param password       server ssh password* @param privateKeyPath local security key used to connect to server*/public SshClientConfig(String host, int port, String username, String password, String privateKeyPath) {this.id = host + port + username;if (null != password && password.length() > 0) {this.id += password;}if (privateKeyPath != null) {this.id += privateKeyPath;}this.host = host;this.port = port;this.username = username;this.password = password;this.privateKeyPath = privateKeyPath;}public SshClientConfig() {}@Overridepublic boolean equals(Object obj) {if (obj instanceof SshClientConfig) {return id.equals(((SshClientConfig) obj).getId());}return false;}@Overridepublic int hashCode() {return id.hashCode();}@Overridepublic String toString() {return this.id;}
}
package com.devops.ssh;/**** Set listener to a SshClient by {@link SshClient#setEventListener(SshClientEventListener)}* @author Gary**/
public interface SshClientEventListener {/*** after SshClient finished executing command* @param client the ssh client*/public void didExecuteCommand(Object client);/*** after SshClient disconnnect from the remote server* @param client the ssh client*/public void didDisConnected(Object client);/*** after SshClient start the ssh session* @param client the ssh client*/public void didConnected(Object client);
}
package com.devops.ssh;import com.devops.ssh.pool.SshClientPoolConfig;/**** Factory of {@link SshClient} implementation* <p> Create a new instance of {@link SshClientSSHJ} with {@link #newInstance(SshClientConfig)}* <p> Create a custom implementation of {@link SshClient} with {@link #newInstance(SshClientConfig, SshClientPoolConfig)}** @author Gary**/
public class SshClientFactory {/*** Create a new instance of {@link SshClientSSHJ}* @param config ssh connection configuration of the remote server* @return SshClient in inited state*/public static SshClient newInstance(SshClientConfig config){return newInstance(config, null);}/*** Create a custom implementation of {@link SshClient}* @param config ssh connection configuration of the remote server* @param poolConfig customized configuration* @return SshClient in inited state* @throws RuntimeException if SshClientImplClass in {@code poolConfig} is invalid*/public static SshClient newInstance(SshClientConfig config, SshClientPoolConfig poolConfig){try {SshClient client = null;if (poolConfig==null || poolConfig.getSshClientImplClass()==null){client = new SshClientSSHJ();}else {client = (SshClient)poolConfig.getSshClientImplClass().newInstance();}client.init(config);if(client instanceof SshClientSSHJ && poolConfig!=null && poolConfig.getServerCommandPromotRegex()!=null) {((SshClientSSHJ)client).setCommandPromotRegexStr(poolConfig.getServerCommandPromotRegex());}return client;} catch (InstantiationException e) {throw new RuntimeException("new instance failed", e);} catch (IllegalAccessException e) {throw new RuntimeException("new instance failed", e);}}}
package com.devops.ssh;import com.devops.ssh.exception.AuthException;
import com.devops.ssh.exception.LostConnectionException;
import com.devops.ssh.exception.SshException;
import com.devops.ssh.exception.TimeoutException;
import com.devops.ssh.pool.SshClientPoolConfig;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
import net.sf.expectit.Expect;
import net.sf.expectit.ExpectBuilder;
import net.sf.expectit.ExpectIOException;
import net.sf.expectit.Result;
import net.sf.expectit.matcher.Matcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.SocketException;
import java.nio.channels.ClosedByInterruptException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;import static net.sf.expectit.filter.Filters.removeColors;
import static net.sf.expectit.filter.Filters.removeNonPrintable;
import static net.sf.expectit.matcher.Matchers.contains;
import static net.sf.expectit.matcher.Matchers.regexp;/**** build-in {@link SshClient} implementation  with <a href="https://github.com/hierynomus/sshj">hierynomus/SshJ</a>** <p>Trouble and shooting:* <p>Problem: {@link #authPublickey()} throw exceptions contains "net.schmizz.sshj.common.Buffer$BufferException:Bad item length"* <p>Solution: may caused by key file format issue,use ssh-keygen on a remote Linux server to generate the key*** @author Gary**/
public class SshClientSSHJ implements SshClient {private final static Logger logger = LoggerFactory.getLogger(SshClientSSHJ.class);private SshClientConfig clientConfig;private SSHClient client;private Expect expect = null;private Session session = null;private Shell shell = null;private boolean shellMode = false;private SshClientState state = SshClientState.inited;private SshClientEventListener eventListener;public String commandPromotRegexStr = "[\\[]?.+@.+~[\\]]?[#\\$] *";public Matcher<Result> commandPromotRegex = regexp(commandPromotRegexStr);// initialize DefaultConfig will consume resources, so we should cache itprivate static DefaultConfig defaultConfig = null;public static DefaultConfig getDefaultConfig() {if(defaultConfig==null) {defaultConfig = new DefaultConfig();}return defaultConfig;}/*** used in shell mode, once it start session with server, the server will return promot to client* <p>the promot looks like [centos@ip-172-31-31-82 ~]$* <p>if the build-in one does not fit, you can change it by {@link SshClientPoolConfig#setServerCommandPromotRegex(String)}* @param promot used to match promot from server*/public void setCommandPromotRegexStr(String promot) {this.commandPromotRegexStr = promot;this.commandPromotRegex = regexp(this.commandPromotRegexStr);}@Overridepublic SshClient init(SshClientConfig config) {this.clientConfig = config;return this;}private void validate() throws SshException {if(this.clientConfig == null) {throw new SshException("missing client config");}}@Overridepublic SshClient connect(int timeoutInSeconds) throws SshException{this.validate();if (timeoutInSeconds <= 0) {timeoutInSeconds = Integer.MAX_VALUE;} else {timeoutInSeconds = timeoutInSeconds * 1000;}return this.connect(timeoutInSeconds, false);}private SshClient connect(int timeoutInSeconds, boolean retry) throws SshException{logger.debug("connecting to " + this.clientConfig.getHost() + " port:" + this.clientConfig.getPort() + " timeout in:"+ (timeoutInSeconds / 1000) + " s");client = new SSHClient(getDefaultConfig());try {client.setConnectTimeout(timeoutInSeconds);client.addHostKeyVerifier(new PromiscuousVerifier());// client.loadKnownHosts();client.connect(this.clientConfig.getHost().trim(), this.clientConfig.getPort());logger.debug("connected to " + this.clientConfig.getHost().trim() + " port:" + this.clientConfig.getPort());} catch (TransportException e) {if(!retry) {logger.error("sshj get exception when connect and will retry one more time ", e);try {Thread.sleep(1000);} catch (InterruptedException e1) {}return this.connect(timeoutInSeconds, true);}else {String errorMessage ="connect to " + this.clientConfig.getHost().trim() + " failed";logger.error(errorMessage, e);throw new SshException(errorMessage, e);}} catch (Exception e) {String errorMessage ="connect to " + this.clientConfig.getHost().trim() + " failed";logger.error(errorMessage, e);throw new SshException(errorMessage, e);}return this;}@Overridepublic SshClient setEventListener(SshClientEventListener listener) {this.eventListener = listener;return this;}@Overridepublic SshClient authPassword() throws SshException {try {logger.debug("auth with password");client.authPassword(this.clientConfig.getUsername(), this.clientConfig.getPassword());} catch (Exception e) {String errorMessage = "ssh auth " + this.clientConfig.getHost() + " fail";logger.error(errorMessage, e);throw new AuthException(errorMessage, e);}return this;}@Overridepublic SshClient authPublickey() throws SshException {try {logger.debug("auth with key:"+this.clientConfig.getUsername()+","+this.clientConfig.getPrivateKeyPath());if (this.clientConfig.getPrivateKeyPath() != null) {KeyProvider keys = client.loadKeys(this.clientConfig.getPrivateKeyPath());client.authPublickey(this.clientConfig.getUsername(), keys);} else {client.authPublickey(this.clientConfig.getUsername());}} catch (Exception e) {String errorMessage = "ssh auth " + this.clientConfig.getHost() + " fail";logger.error(errorMessage, e);throw new AuthException(errorMessage, e);}return this;}@Overridepublic SshClient startSession(boolean shellMode) {logger.info("start session " + (shellMode ? " in shellMode" : ""));try {session = client.startSession();this.shellMode = shellMode;if (shellMode) {session.allocateDefaultPTY();shell = session.startShell();shell.changeWindowDimensions(1024, 1024, 20, 20);this.renewExpect(60);expect.expect(commandPromotRegex);}this.state = SshClientState.connected;try {if(this.eventListener!=null) {this.eventListener.didConnected(this);}} catch (Exception e) {}} catch (Exception e) {if(e instanceof ExpectIOException) {ExpectIOException ioException = (ExpectIOException)e;logger.error("start session fail with server input:"+ioException.getInputBuffer().replaceAll("[\\\n\\\r]", ""), e);}else {logger.error("start session fail", e);}this.disconnect();throw new RuntimeException("start session fail." + e.getMessage());} finally {// close expecttry {if (expect != null) {expect.close();}} catch (IOException e) {logger.error("close IO error", e);}expect = null;}return this;}@Overridepublic SshResponse executeCommand(String command, int timeoutInSeconds) {if (this.shellMode) {return this.sendCommand(command, timeoutInSeconds);} else {return this.executeCommand_(command, timeoutInSeconds);}}private SshResponse executeCommand_(String command, int timeoutInSeconds) {logger.info("execute command: " + command);SshResponse response = new SshResponse();try {Command cmd = session.exec(command);if (timeoutInSeconds < 0) {cmd.join(Long.MAX_VALUE, TimeUnit.SECONDS);} else {cmd.join(timeoutInSeconds, TimeUnit.SECONDS);}BufferedReader reader = new BufferedReader(new InputStreamReader(cmd.getInputStream(), "UTF-8"));BufferedReader error_reader = new BufferedReader(new InputStreamReader(cmd.getErrorStream(), "UTF-8"));List<String> outputLines = new ArrayList<>();logger.debug("finish executing command on " + this.clientConfig.getHost() + ", console:");String outputLine;while ((outputLine = error_reader.readLine()) != null) {logger.debug(outputLine);outputLines.add(outputLine);}while ((outputLine = reader.readLine()) != null) {logger.debug(outputLine);outputLines.add(outputLine);}response.setStdout(outputLines);logger.info("execute ssh command on " + this.clientConfig.getHost() + " completed, with exit status:" + cmd.getExitStatus());response.setCode(cmd.getExitStatus());} catch (Exception e) {if (e.getCause() instanceof InterruptedException || e.getCause() instanceof java.util.concurrent.TimeoutException) {logger.error("execute ssh on " + this.clientConfig.getHost() + " timeout");response.setException(new TimeoutException("execute ssh command timeout"));} else {logger.error("execute ssh on " + this.clientConfig.getHost() + ", command error", e);response.setException(new SshException("execute ssh command error "+e.getMessage()));}}finally {try {if(this.eventListener!=null) {this.eventListener.didExecuteCommand(this);}} catch (Exception e) {}}return response;}private SshResponse sendCommand(String command, int timeoutInSeconds) {SshResponse response = new SshResponse();if (this.state != SshClientState.connected) {response.setException(new LostConnectionException("client not connected"));response.setCode(0);return response;}try {this.renewExpect(timeoutInSeconds);// start expectlogger.info(this + " execute command : " + command);expect.send(command);logger.debug(this + " command sent ");if (!command.endsWith("\n")) {expect.send("\n");logger.debug(this + " command \\n sent ");}Result result2 = expect.expect(contains(command));Result result = expect.expect(commandPromotRegex);logger.debug("command execute success with raw output");logger.debug("------------------------------------------");String[] inputArray = result.getInput().split("\\r\\n");List<String> stout = new ArrayList<String>();if(inputArray.length>0) {for(int i=0;i<inputArray.length;i++) {logger.debug(inputArray[i]);if(i==inputArray.length-1 && inputArray[i].matches(commandPromotRegexStr)) {break;}stout.add(inputArray[i]);}}logger.debug("------------------------------------------");response.setStdout(stout);response.setCode(0);logger.info("execute ssh command on " + this.clientConfig.getHost() + " completed, with code:" + 0);} catch (Exception e) {response.setCode(1);response.setException(new SshException(e.getMessage()));logger.error("execute command fail", e);if(e instanceof ArrayIndexOutOfBoundsException) {// server may be shutdownresponse.setException(new LostConnectionException("lost connection"));this.disconnect();} else if (e instanceof ClosedByInterruptException) {response.setException(new TimeoutException("execute command timeout"));this.sendCtrlCCommand();}else if (e.getCause() instanceof SocketException) {// the socket may be closedresponse.setException(new LostConnectionException("lost connection"));this.disconnect();} else if (e.getMessage().contains("timeout")) {response.setException(new TimeoutException("execute command timeout"));this.sendCtrlCCommand();}else {this.sendCtrlCCommand();}} finally {// close expecttry {if (expect != null) {expect.close();}} catch (IOException e) {logger.error("close IO error", e);}expect = null;try {if(this.eventListener!=null) {this.eventListener.didExecuteCommand(this);}} catch (Exception e) {}}return response;}private void renewExpect(int timeoutInSeconds) throws IOException {if (expect!=null) {try {expect.close();}catch(Exception e) {e.printStackTrace();}}expect = new ExpectBuilder().withOutput(shell.getOutputStream()).withInputs(shell.getInputStream(), shell.getErrorStream()).withInputFilters(removeColors(), removeNonPrintable()).withExceptionOnFailure().withTimeout(timeoutInSeconds, TimeUnit.SECONDS).build();}private void sendCtrlCCommand() {try {logger.debug("send ctr-c command ... ");expect.send("\03");expect.expect(commandPromotRegex);logger.debug("send ctr-c command success ");} catch (IOException e1) {logger.error("send ctrl+c command fail", e1);}}@Overridepublic void disconnect() {if(this.state== SshClientState.disconnected) {return;}this.state = SshClientState.disconnected;try {if (shell != null) {shell.close();}} catch (IOException e) {logger.error("close ssh shell error", e);}try {if (session != null) {session.close();}} catch (IOException e) {logger.error("close sesion error", e);}try {if (client != null) {client.disconnect();client.close();}} catch (IOException e) {logger.error("close ssh conenction error", e);}logger.debug("ssh disconnect");try {if(this.eventListener!=null) {this.eventListener.didDisConnected(this);}} catch (Exception e) {}}@Overridepublic SshClientState getState() {return this.state;}
}
package com.devops.ssh;/**** state of SshClient, See {@link SshClient#getState()} for more information** @author Gary**/
public enum SshClientState {inited,connected,disconnected
}
package com.devops.ssh;import java.util.ArrayList;
import java.util.List;/**** Response return from {@link SshClient#executeCommand(String, int)}** @author Gary**/
public class SshResponse {private int code;private Exception exception;private List<String> stdout = new ArrayList<String>();/*** @return 0*/public int getCode() {return code;}public void setCode(int code) {this.code = code;}/**** @return the exception in {@link SshClient#executeCommand(String, int)}*/public Exception getException() {return exception;}public void setException(Exception exception) {this.exception = exception;}/**** @return the output from remote server after send command*/public List<String> getStdout() {return stdout;}public void setStdout(List<String> stdout) {this.stdout = stdout;}}

 运行测试Linux命令

echo 'yes'

 运行测试 shell 脚本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/8855.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件上传漏洞

什么是文件上传漏洞&#xff1f; 攻击者上传了一个可执行文件如木马&#xff0c;病毒&#xff0c;恶意脚本&#xff0c;WebShell等到服务器执行&#xff0c;并最终获得网站控制权限的高危漏洞。 webshell 非法用户可利用上传的恶意脚本文件控制整个网站&#xff0c;甚至控制服…

直线导轨的主要功能

直线导轨是一种常见的机械结构&#xff0c;用于工业机器人、数控机床和其他自动化装置中。它的作用是提供一个准确的直线运动轨道&#xff0c;使得设备能够在预定的路径上进行精确的移动。 直线导轨作为一种重要的机械基础件&#xff0c;在现代工业中得到了广泛的应用。它主要的…

min_free_kbytes

转自&#xff1a;技术分享 | MemAvailable 是怎么计算的-腾讯云开发者社区-腾讯云 背景 前两天安装 OceanBase 时遇到一个小问题&#xff1a; 很明显&#xff0c;安装OB时要求服务器可用内存至少 8G&#xff0c;不达标就无法安装。为了凑这3台10G内存的服务器我已经费了不少劲…

Yunfly 一款高效、性能优异的node.js企业级web框架

介绍 Yunfly 一款高性能 Node.js WEB 框架, 使用 Typescript 构建我们的应用。 使用 Koa2 做为 HTTP 底层框架, 使用 routing-controllers 、 typedi 来高效构建我们的 Node 应用。 Yunfly 在 Koa 框架之上提升了一个抽象级别, 但仍然支持 Koa 中间件。在此基础之上, 提供了一…

【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群功能分析)

探究Redis服务启动的过程机制的技术原理和流程分析的指南&#xff08;集群功能分析&#xff09; Redis集群的出现背景提升性能扩展性 Redis集群概述Redis Cluster特性分布Redis Cluster的Failover机制Redis集群节点宕机集群如何判断节点是否挂掉集群进入失败状态的必要条件 Fai…

jmeter随记2:压测

jmeter随记1:压测 简述一、压测步骤二、观察cpu和内存占用情况三、查看磁盘占用情况 简述 关于压测&#xff0c;jmeter更直观的作用是用来编写压测脚本【请求和压测策略】&#xff0c;然后在linux服务器上执行&#xff0c;也可以在本地执行&#xff0c;压测执行脚本在启动jmet…

Linux:入门学习知识及常见指令

文章目录 入门介绍操作系统的概念Linux机器的使用Linux上的指令 对文件知识的补充文件的定义和一些含义文件和目录的存储绝对路径和相对路径 ls指令pwd指令cd指令touch指令mkdir指令rmdir指令rm指令man指令cp指令mv指令cat指令more指令echo指令输出重定向 less指令find指令grep…

5、joern安装

文章目录 一、安装环境二、安装步骤1、joern地址2、安装中可能出现的问题 一、安装环境 1. java 8   2. gradle 2.0.0(按照官网步骤安装)   3. Graphviz (sudo apt install graphviz-dev)   4. python > 3.5 二、安装步骤 1、joern地址 joern地址 2、安装中可能出…

Docker配置阿里云容器镜像加速

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

白话机器学习笔记(三)评估已建立的模型

模型评估 在进行回归和分类时&#xff0c;为了进行预测&#xff0c;我们定义了函数 f θ ( x ) f_\theta(x) fθ​(x)&#xff0c;然后根据训练数据求出了函数的参数 θ \theta θ。 如何预测函数 f θ ( x ) f_\theta(x) fθ​(x)的精度&#xff1f;看它能否很好的拟合训练数…

【玩转Linux】标准io缓冲区的操作

(꒪ꇴ꒪ ),hello我是祐言博客主页&#xff1a;C语言基础,Linux基础,软件配置领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff01;送给读者的一句鸡汤&#x1f914;&#xff1a;集中起来的意志可以击穿顽石!作者水平很有限&#xff0c;如果发现错误&#x…

[Golang] Viper原理以及详细使用案例

文章目录 什么是 Viper&#xff1f;基础配置引入依赖&#xff1a;动态监听原理分析&#xff1a;监听原理分析 Config.yaml文件配置Viper文件配置 什么是 Viper&#xff1f; 介绍&#xff1a;用于处理配置文件中解析和读取配置文件 优点&#xff1a;支持多种配置格式&#xff0…

新老联手,火花四溅?大众汽车与小鹏汽车达成长期合作框架协议

7 月 26 日资讯&#xff0c;大众汽车宣布与小鹏汽车达成长期合作框架协议&#xff0c;并在官网中正式宣布&#xff0c;大众是老牌油车领军代表&#xff0c;小鹏则是新势力中的佼佼者&#xff0c;新老强强联手&#xff0c;又会碰撞出怎样的火花呢&#xff1f; 现阶段大众计划与…

Postman学习之常用断言

什么是断言&#xff1f; 断言——就是结果中的特定属性或值与预期做对比&#xff0c;如果一致&#xff0c;则用例通过&#xff0c;如果不一致&#xff0c;断言失败&#xff0c;用例失败。断言&#xff0c;是一个完整测试用例所不可或缺的一部分&#xff0c;没有断言的测试用例…

leetcode每日一练-第98题- 验证二叉搜索树

一、思路 因为要验证多个节点是否是二叉搜索树,因此使用递归 二、解题方法 设计一个递归函数 helper(root, lower, upper) 来递归判断&#xff0c;函数表示考虑以 root 为根的子树&#xff0c;判断子树中所有节点的值是否都在 (l,r)的范围内&#xff08;注意是开区间&#x…

git配置

git查看配置&#xff1a;git config --global --list git配置&#xff1a; git config --global user.name "yumlu" git config --global user.email "yumlucisco.com" git config --global core.editorvim git权限&#xff0c;添加密钥&#xff1a; …

工厂方法模式——多态工厂的实现

1、简介 1.1、概述 在工厂方法模式中&#xff0c;不再提供一个统一的工厂类来创建所有的产品对象&#xff0c;而是针对不同的产品提供不同的工厂&#xff0c;系统提供一个与产品等级结构对应的工厂等级结构。 1.2、定义 工厂方法模式&#xff08;Factory Method Pattern&am…

Filebeat学习笔记

Filebeat基本概念 简介 Filebeat是一种轻量级日志采集器&#xff0c;内置有多种模块&#xff08;auditd、Apache、Nginx、System、MySQL等&#xff09;&#xff0c;针对常见格式的日志大大简化收集、解析和可视化过程&#xff0c;只需一条命令即可。之所以能实现这一点&#…

设计模式原则

1、设计模式七大原则 1.1 设计模式的目的 编写软件过程中&#xff0c;程序员面临着来自 耦合性&#xff0c;内聚性以及可维护性&#xff0c;可扩展性&#xff0c;重用性&#xff0c;灵活性 等多方面的挑战&#xff0c;设计模式是为了让程序(软件)&#xff0c;具有更好 代码重…

Mkdocs中利用Js实现大小圈鼠标拖动样式

在docs/javascripts/extra.js下复制粘贴&#xff1a; var CURSOR;Math.lerp (a, b, n) > (1 - n) * a n * b;const getStyle (el, attr) > {try {return window.getComputedStyle? window.getComputedStyle(el)[attr]: el.currentStyle[attr];} catch (e) {}return …