Subversion Repositories DevTools

Rev

Rev 6914 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/**     This class handles remote execution of programs
 *  It is only used by the build daemons
 *  It is not used in Escrow
 *  
 *  It is used by the master build thread as well as the ABT instance created
 *  by the master build thread.
 * 
 */

package com.erggroup.buildtool.abt;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;

import com.sshtools.net.SocketTransport;
import com.sshtools.ssh.PasswordAuthentication;
import com.sshtools.ssh.SshAuthentication;
import com.sshtools.ssh.SshClient;
import com.sshtools.ssh.SshConnector;
import com.sshtools.ssh.SshSession;
import com.sshtools.ssh2.Ssh2Context;

public class RemoteExecution {

        /**
         *      Interface to assist in logging the output of the remote executor
         *      Required as the class is called in two contexts
         *              1) By the build daemon - where logging is to an mLogger
         *              2) Within ABT - where logging is via ant
         */
        public interface LogOutput{
            void data(String message);          // Log data from the command
            void info(String message);          // Log Info from the class
            void fatal(String message);         // Log fatal messages
        }
        
        /**
         * Name or Address of the target machine
         * 
         */
        private String mTargetMachine;

        /**
         * User name
         */
        private String mUserName;

        /**
         * Password
         */
        private String mPassword;

        /** Logger
         * 
         */
        LogOutput mLogger;

        /**
         * Constructor
         * @param logger 
         */
        public RemoteExecution(LogOutput logger)
        {
                mLogger = logger;
        }

        /**Determine the connection credentials
         * These are held in a properties file
         * 
         * @return true - we have credentials
         */
        public boolean getCredentials()
        {
                InputStream input;
                Properties prop;
                String credentialsFile = System.getenv("GBE_DPKG_SSH_PROPERTIES");
                if (credentialsFile == null)
                {
                        mLogger.fatal("Packager Server SSH credendials. Location Not specified");
                        return false;
                }
                
                File propFile = new File(credentialsFile);
                if (!propFile.isFile())
                {
                        mLogger.fatal("Packager Server SSH credendials. Is not a file: " + credentialsFile);
                        return false;
                }
                
                try {
                        input = new FileInputStream(propFile);

                        prop = new Properties();
                        prop.load(input);
                        
                } catch (IOException e) {
                        mLogger.fatal("Packager Server SSH credendials. Cannot open: " + credentialsFile + ". Error: " + e.getMessage());
                        return false;
                }
                
                mTargetMachine = prop.getProperty("ssh_server");
                mUserName = prop.getProperty("ssh_user");
                mPassword = prop.getProperty("ssh_password");
                
                if ( mTargetMachine == null || mUserName == null || mPassword == null )
                {
                        mLogger.fatal("Packager Server SSH credendials. Not all properties found");
                        return false;
                }
                
                return true;
        }
        
        
        /**
         * Test the connection to the target machine
         * Used each build cycle to report if the target machine is not
         * available.
         * @param mLogger               - Logger to use for notification of errors
         * 
         *  @return     true - if the connection was successfully established
         */
        public boolean testConnection()
        {
                int     exitCode = -1;
                
                mLogger.info("Test remote execution authentication");
                exitCode = execute(null);
                mLogger.info("Test remote execution authentication complete");
                return exitCode == 0;
        }

        /**
         * Execute command
         * @param   command     - Command string to be processed. May be null or of zero length
         * 
         * @return  command exit code if positive
         *          <br>internal exist code if negative
         *          <br>zero - command executed correctly
         * 
         */
        public int execute(final String command)
        {
                int     exitCode = -1;
                if (getCredentials())
                {
                        try {

                                /**
                                 * Create an SshConnector instance
                                 */
                                SshConnector con = SshConnector.createInstance();
                                con.getContext().setPreferredPublicKey(Ssh2Context.PUBLIC_KEY_SSHDSS);

                                /**
                                 * Connect to the host
                                 */
                                final SshClient ssh = con.connect(new SocketTransport(mTargetMachine, 22), mUserName, true);

                                /**
                                 * Authenticate the user using password authentication
                                 */
                                PasswordAuthentication pwd = new PasswordAuthentication();
                                pwd.setPassword(mPassword);

                                if( ssh.authenticate(pwd) != SshAuthentication.COMPLETE && ssh.isConnected())
                                {
                                        mLogger.fatal("Connection to \"" + mTargetMachine + "\" not authenticated");
                                        return -1;
                                }

                                /**
                                 * Start a session send a command and read the commands output
                                 */
                                if (ssh.isAuthenticated()) {

                                        exitCode = 0;
                                        if ( command != null && command.length() > 0)
                                        {
                                                final SshSession session = ssh.openSessionChannel();
                                                session.executeCommand(command);

                                                //      Create two thread
                                                //      One to read stdout and one to read stderr
                                                //
                                                Thread t1 = new Thread(new Runnable(){
                                                        public void run(){
                                                                try {
                                                                        BufferedReader in = new BufferedReader(new InputStreamReader(session.getInputStream()));
                                                                        String line = null;
                                                                        while((line = in.readLine()) != null) {
                                                                                mLogger.data(line);
                                                                        }
                                                                } catch (IOException e) {
                                                                }
                                                        }
                                                });
                                                Thread t2 = new Thread(new Runnable(){
                                                        public void run(){
                                                                try {
                                                                        BufferedReader in = new BufferedReader(new InputStreamReader(session.getStderrInputStream()));
                                                                        String line = null;
                                                                        while((line = in.readLine()) != null) {
                                                                                mLogger.data("[ERR]" + line);
                                                                        }
                                                                } catch (IOException e) {
                                                                }
                                                        }
                                                });

                                                //      Start both threads
                                                //      Then wait for them to complete
                                                //      They will complete when the input is exhausted
                                                t1.start();
                                                t2.start();

                                                t1.join();
                                                t2.join();
                                                
                                session.close();
                                exitCode = session.exitCode();
                                if (exitCode ==  SshSession.EXITCODE_NOT_RECEIVED )
                                {
                                    mLogger.info("Exit Code not received. Force zero");
                                    exitCode = 0;
                                }
                                                mLogger.info("Exit Code:" + exitCode);
                                        }
                                }

                                ssh.disconnect();
                        } catch (Throwable t) {
                                t.printStackTrace();
                                mLogger.info("Remote connection exception: " + t.getMessage());
                                return -2;
                        }
                }
                
                return exitCode;
        }

}