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

 基础知识

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

Spring Boot

Shell

Ansible

Git

Gitlab

Docker

K8S

Vue

DevOps自动化平台开发之 Shell脚本执行的封装_第1张图片

 1、spring boot starter的封装使用

2、Shell脚本的编写

3、Ansible 脚本的编写

4、Docker 的使用与封装设计

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

将其设计成spring boot starter

DevOps自动化平台开发之 Shell脚本执行的封装_第2张图片

maven依赖pom文件



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.7.9
         
    
    com.devops
    ssh-client-pool-spring-boot-starter
    0.0.1-SNAPSHOT
    ssh-client-pool-spring-boot-starter
    Demo project for Spring Boot
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter
        
        
            org.springframework.boot
            spring-boot-autoconfigure
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            com.hierynomus
            sshj
            0.26.0
        
        
            org.bouncycastle
            bcprov-jdk15on
            1.60
        
        
            com.fasterxml.uuid
            java-uuid-generator
            3.1.4
        
        
            net.sf.expectit
            expectit-core
            0.9.0
        

        
            org.apache.commons
            commons-pool2
            2.8.0
        
        
            junit
            junit
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    


具体的封装代码: 

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
 * 

SshClientPoolConfig is a subclass of GenericKeyedObjectPoolConfig to control the pool behavior *

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{ 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();
	}

	@Override
	public boolean equals(Object obj) {
		if(obj instanceof SshClientWrapper){
			return id.equals(((SshClientWrapper)obj).getId());
		}
		return false;
	}

	@Override
	public int hashCode(){
		return id.hashCode();
	}

	public SshClientState getState() {
		return client.getState();
	}

	@Override
	public String toString() {
		return "["+this.id+"|"
					+this.config.getHost()+"|"
					+this.config.getPort()+"|"
					+this.getState()+"]";
	}

	@Override
	public void didExecuteCommand(Object client) {
		this.listener.didExecuteCommand(this);
	}

	@Override
	public void didDisConnected(Object client) {
		this.listener.didDisConnected(this);
	}

	@Override
	public 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}

* * Client can be used in chain mode, {@link SshClient}.{@link #init(SshClientConfig)}.{@link #connect(int)}.{@link #authPassword()}.{@link #startSession(boolean)}.{@link #executeCommand(String, int)}

* * At last, close the client with {@link #disconnect()} * *

Set an {@link SshClientEventListener} with {@link #setEventListener(SshClientEventListener)} to be notified when its event occurs *

* @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 true: communicate with server interactively in session, just like command line *

false: 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 *

inited before {@link #startSession(boolean)} success *

connected after {@link #startSession(boolean)} success *

disconnected 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() {

	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof SshClientConfig) {
			return id.equals(((SshClientConfig) obj).getId());
		}
		return false;
	}

	@Override
	public int hashCode() {
		return id.hashCode();
	}

	@Override
	public 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
 * 

Create a new instance of {@link SshClientSSHJ} with {@link #newInstance(SshClientConfig)} *

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 hierynomus/SshJ
 *
 * 

Trouble and shooting: *

Problem: {@link #authPublickey()} throw exceptions contains "net.schmizz.sshj.common.Buffer$BufferException:Bad item length" *

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 commandPromotRegex = regexp(commandPromotRegexStr); // initialize DefaultConfig will consume resources, so we should cache it private 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 *

the promot looks like [centos@ip-172-31-31-82 ~]$ *

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); } @Override public SshClient init(SshClientConfig config) { this.clientConfig = config; return this; } private void validate() throws SshException { if(this.clientConfig == null) { throw new SshException("missing client config"); } } @Override public 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; } @Override public SshClient setEventListener(SshClientEventListener listener) { this.eventListener = listener; return this; } @Override public 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; } @Override public 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; } @Override public 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 expect try { if (expect != null) { expect.close(); } } catch (IOException e) { logger.error("close IO error", e); } expect = null; } return this; } @Override public 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 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 expect logger.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 stout = new ArrayList(); if(inputArray.length>0) { for(int i=0;i

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 stdout = new ArrayList();

	/**
	 * @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 getStdout() {
		return stdout;
	}

	public void setStdout(List stdout) {
		this.stdout = stdout;
	}



}

DevOps自动化平台开发之 Shell脚本执行的封装_第3张图片

 运行测试Linux命令

echo 'yes'

DevOps自动化平台开发之 Shell脚本执行的封装_第4张图片

 运行测试 shell 脚本

DevOps自动化平台开发之 Shell脚本执行的封装_第5张图片

DevOps自动化平台开发之 Shell脚本执行的封装_第6张图片

你可能感兴趣的:(自动化,运维)