reqExec API implemented for saltstack

Issue-ID: CCSDK-320
Change-Id: I5c4eb36924f36ebc9a7786d2bd46c923d0c05ab0
Signed-off-by: Ganesh Chandrasekaran <ganesh.c@samsung.com>
diff --git a/saltstack-adapter/README.md b/saltstack-adapter/README.md
index 0c3161a..cf21e10 100644
--- a/saltstack-adapter/README.md
+++ b/saltstack-adapter/README.md
@@ -31,4 +31,32 @@
 
 ***Requirements and benefits of the chosen SSH method:***
 1) The SaltStack server should have it’s SSH enabled.
-2) Such execution method will give the DGs and adaptor with more refined control on the SaltStack server.
\ No newline at end of file
+2) Such execution method will give the DGs and adaptor with more refined control on the SaltStack server.
+==================================================================================================================
+
+
+***Defining Saltstack server properties:*** Can be done with 2 different methods. 
+1) Saltstack server details are found in the property file named saltstack-adapter.properties. Param has to be given with following types. 
+    "org.onap.appc.adapter.saltstack.clientType"; -> Supported types are (BASIC || SSH_CERT || BOTH).
+    "org.onap.appc.adapter.saltstack.host"; ->  Saltstack server's host name IP address.
+    "org.onap.appc.adapter.saltstack.port"; ->  Saltstack server's port to make SSH connection to.
+    "org.onap.appc.adapter.saltstack.userName"; ->  Saltstack server's SSH UserName.
+    "org.onap.appc.adapter.saltstack.userPasswd"; ->  Saltstack server's SSH Password.
+    "org.onap.appc.adapter.saltstack.sshKey"; ->  Saltstack server's SSH KEY file location.
+2) All the server related details can also be passed as param to the adaptor from the Directed Graphs. Param has to be given with following types. 
+    "HostName";  ->  Saltstack server's host name IP address.
+    "Port"; ->  Saltstack server's port to make SSH connection to.
+    "Password"; ->  Saltstack server's SSH UserName.
+    "User"; ->  Saltstack server's SSH Password.
+  Note: SSH_CERT based Auth is not supported in this method.
+  
+***Using Saltstack Adaptor Commands and params to pass in:*** reqExecCommand:
+Method to execute a single command on SaltState server. The command entered should request the output in JSON format, this can be done by appending json-out outputter as specified in https://docs.saltstack.com/en/latest/ref/output/all/salt.output.json_out.html#module-salt.output.json_out and https://docs.saltstack.com/en/2017.7/ref/cli/salt-call.html The response from Saltstack comes in json format and it is automatically put to context for DGs access, with a certain request-ID as prefix.
+Example command will look like: 
+1) Command to test if all VNFC are running: "salt * test.ping --out=json --static"
+2) To check Network interfaces on your minions: "salt '*' network.interfaces --out=json --static"
+3) Restart Minion service after upgrade process: "salt minion1 service.restart salt-minion --out=json --static"
+Note: If using --out=json, you will probably want --static as well. Without the static option, you will get a separate JSON string per minion which makes JSON output invalid as a whole. This is due to using an iterative outputter. So if you want to feed it to a JSON parser, use --static as well.
+
+This "reqExecCommand" method gives the Operator/Directed Graphs to execute commands in a fine-tuned manner, which also means the operator/DG-creator should know what to expect as output as a result of command execution. By this way using DGs, the operator can check for success/failure of the executed comment. 
+If the output is not in JSON format, then the adaptor still tries to convert it into properties, in addition "reqID.completeResult" param will have the whole result for DG access. 
diff --git a/saltstack-adapter/saltstack-adapter-features/ccsdk-saltstack-adapter/pom.xml b/saltstack-adapter/saltstack-adapter-features/ccsdk-saltstack-adapter/pom.xml
index fa442ac..92f404e 100644
--- a/saltstack-adapter/saltstack-adapter-features/ccsdk-saltstack-adapter/pom.xml
+++ b/saltstack-adapter/saltstack-adapter-features/ccsdk-saltstack-adapter/pom.xml
@@ -15,39 +15,36 @@
     <packaging>feature</packaging>
 
     <name>ccsdk-sli-adaptors :: saltstack-adapter:: ${project.artifactId}</name>
-	<dependencyManagement>
-		<dependencies>
-			<dependency>
-				<groupId>org.opendaylight.controller</groupId>
-				<artifactId>mdsal-artifacts</artifactId>
-				<version>${odl.mdsal.version}</version>
-				<type>pom</type>
-				<scope>import</scope>
-			</dependency>
-			<dependency>
-				<groupId>org.opendaylight.mdsal.model</groupId>
-				<artifactId>mdsal-model-artifacts</artifactId>
-				<version>${odl.mdsal.model.version}</version>
-				<type>pom</type>
-				<scope>import</scope>
-			</dependency>
-		</dependencies>
-	</dependencyManagement>
-    <dependencies>
-
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>mdsal-artifacts</artifactId>
+                <version>${odl.mdsal.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.mdsal.model</groupId>
+                <artifactId>mdsal-model-artifacts</artifactId>
+                <version>${odl.mdsal.model.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+ <!--   <dependencies>
         <dependency>
             <groupId>org.onap.ccsdk.sli.core</groupId>
             <artifactId>ccsdk-sli</artifactId>
             <type>xml</type>
             <classifier>features</classifier>
         </dependency>
-
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>saltstack-adapter-provider</artifactId>
             <version>${project.version}</version>
         </dependency>
-
-
     </dependencies>
+    -->
 </project>
diff --git a/saltstack-adapter/saltstack-adapter-provider/pom.xml b/saltstack-adapter/saltstack-adapter-provider/pom.xml
index f4e0450..41bf7c6 100644
--- a/saltstack-adapter/saltstack-adapter-provider/pom.xml
+++ b/saltstack-adapter/saltstack-adapter-provider/pom.xml
@@ -44,6 +44,20 @@
 			<version>1.2</version>
 		</dependency>
 
+		<!-- Needed to run SSH -->
+		<dependency>
+			<groupId>org.apache.sshd</groupId>
+			<artifactId>sshd-core</artifactId>
+			<version>0.12.0</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.onap.appc</groupId>
+			<artifactId>appc-common</artifactId>
+			<version>1.4.0-SNAPSHOT</version>
+		</dependency>
+
 		<!-- Needed to run test cases -->
 		<dependency>
 			<groupId>org.glassfish.jersey.core</groupId>
@@ -60,6 +74,18 @@
 		</dependency>
 
 		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>1.3.2</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.codehaus.jettison</groupId>
+			<artifactId>jettison</artifactId>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
 			<groupId>junit</groupId>
 			<artifactId>junit</artifactId>
 			<scope>test</scope>
@@ -108,5 +134,22 @@
 
 	</dependencies>
 
-
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<extensions>true</extensions>
+				<configuration>
+					<instructions>
+						<Export-Service>org.onap.appc.adapter.ssh.SshAdapter</Export-Service>
+						<Private-Package>org.onap.appc.adapter.ssh.impl.*</Private-Package>
+						<Import-Package>!org.apache.log,!org.apache.commons.logging,!groovy.lang,!javax.jms,!org.codehaus.commons.compiler,!org.codehaus.groovy.*,!org.codehaus.janino,!com.ibm.icu.*,!com.sun.faces.*,!org.jasypt.*,*</Import-Package>
+						<Embed-Dependency>!dblib-provider,jasypt,eelf-core,logback-core,logback-classic;scope=compile|runtime;inline=false</Embed-Dependency>
+						<Embed-Transitive>true</Embed-Transitive>
+					</instructions>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
 </project>
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java
index 7702dc8..5dee9f5 100644
--- a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java
@@ -24,11 +24,18 @@
 
 package org.onap.ccsdk.sli.adaptors.saltstack.impl;
 
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
-import java.security.KeyManagementException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.RandomStringUtils;
 import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResult;
 import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResultCodes;
 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
@@ -47,30 +54,142 @@
 public class ConnectionBuilder {
 
     private static final EELFLogger logger = EELFManager.getInstance().getLogger(ConnectionBuilder.class);
-
+    SshConnection sshConnection;
 
     /**
-     * Constructor that initializes an ssh client based on certificate
+     * Constructor that initializes an ssh client based on username and password
      **/
-    public ConnectionBuilder(String userName, String userPasswd) throws KeyStoreException, CertificateException, IOException,
-            KeyManagementException, NoSuchAlgorithmException, SvcLogicException {
-
-
+    public ConnectionBuilder(String host, String port, String userName, String userPasswd) {
+        sshConnection = new SshConnection(host, Integer.parseInt(port), userName, userPasswd);
     }
 
     /**
-     * Constructor which trusts all certificates in a specific java keystore file (assumes a JKS
-     * file)
+     * Constructor that initializes an ssh client based on ssh certificate
      **/
-    public ConnectionBuilder(String certFile) throws KeyStoreException, IOException,
-            KeyManagementException, NoSuchAlgorithmException, CertificateException {
-
+    public ConnectionBuilder(String host, String port, String certFile) {
+        sshConnection = new SshConnection(host, Integer.parseInt(port), certFile);
     }
 
     /**
-     * Connect to SSH server.
+     * Constructor that initializes an ssh client based on ssh username password and certificate
+     **/
+    public ConnectionBuilder(String host, String port, String userName, String userPasswd,
+                             String certFile) {
+
+        sshConnection = new SshConnection(host, Integer.parseInt(port), userName, userPasswd, certFile);
+    }
+
+    /**
+     * 1. Connect to SSH server.
+     * 2. Exec remote command over SSH. Return command execution status.
+     * Command output is written to out or err stream.
+     *
+     * @param cmd Commands to execute
+     * @return command execution status
      */
-    public SaltstackResult connect(String agentUrl, String payload) {
+    public SaltstackResult connectNExecute(String cmd) {
+        return connectNExecute(cmd,-1,-1);
+    }
+
+    /**
+     * 1. Connect to SSH server with retry enabled.
+     * 2. Exec remote command over SSH. Return command execution status.
+     * Command output is written to out or err stream.
+     *
+     * @param cmd Commands to execute
+     * @param retryDelay delay between retry to make a SSH connection.
+     * @param retryCount number of count retry to make a SSH connection.
+     * @return command execution status
+     */
+    public SaltstackResult connectNExecute(String cmd, int retryCount, int retryDelay) {
+
+        SaltstackResult result = new SaltstackResult();
+        try {
+            if (retryCount != -1) {
+                result = sshConnection.connectWithRetry(retryCount, retryDelay);
+            } else {
+                result = sshConnection.connect();
+            }
+            if (result.getStatusCode() != SaltstackResultCodes.SUCCESS.getValue()) {
+                return result;
+            }
+            String outFilePath = "/tmp/"+ RandomStringUtils.random(5,true,true);
+            String errFilePath = "/tmp/"+ RandomStringUtils.random(5,true,true);
+            OutputStream out = new FileOutputStream(outFilePath);
+            OutputStream errs = new FileOutputStream(errFilePath);
+            result = sshConnection.execCommand(cmd, out, errs);
+            sshConnection.disconnect();
+            out.close();
+            errs.close();
+            if (result.getSshExitStatus() != 0) {
+                return sortExitStatus(result.getSshExitStatus(), errFilePath, cmd);
+            }
+            if (result.getStatusCode() != SaltstackResultCodes.SUCCESS.getValue()) {
+                return result;
+            }
+            result.setStatusMessage("Success");
+            result.setOutputFileName(outFilePath);
+        } catch (Exception io) {
+            logger.error("Caught Exception", io);
+            result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
+            result.setStatusMessage(io.getMessage());
+        }
+        return result;
+    }
+
+    public SaltstackResult sortExitStatus (int exitStatus, String errFilePath, String cmd)  {
+        SaltstackResult result = new SaltstackResult();
+        String err;
+        StringWriter writer = new StringWriter();
+        try {
+            IOUtils.copy(new FileInputStream(new File(errFilePath)), writer, "UTF-8");
+            err = writer.toString();
+        } catch (Exception e){
+            err = "";
+        }
+        if (exitStatus == 255 || exitStatus == 1) {
+            String errMessage = "Error executing command [" + cmd + "] over SSH [" + sshConnection.toString()
+                    + "]. Exit Code " + exitStatus + " and Error message : " +
+                    "Malformed configuration. "+ err;
+            logger.error(errMessage);
+            result.setStatusCode(SaltstackResultCodes.INVALID_COMMAND.getValue());
+            result.setStatusMessage(errMessage);
+        } else if (exitStatus == 5 || exitStatus == 65) {
+            String errMessage = "Error executing command [" + cmd + "] over SSH [" + sshConnection.toString()
+                    + "]. Exit Code " + exitStatus + " and Error message : " +
+                    "Host not allowed to connect. "+ err;
+            logger.error(errMessage);
+            result.setStatusCode(SaltstackResultCodes.USER_UNAUTHORIZED.getValue());
+            result.setStatusMessage(errMessage);
+        } else if (exitStatus == 67 || exitStatus == 73) {
+            String errMessage = "Error executing command [" + cmd + "] over SSH [" + sshConnection.toString()
+                    + "]. Exit Code " + exitStatus + " and Error message : " +
+                    "Key exchange failed. "+ err;
+            logger.error(errMessage);
+            result.setStatusCode(SaltstackResultCodes.CERTIFICATE_ERROR.getValue());
+            result.setStatusMessage(errMessage);
+        } else {
+            String errMessage = "Error executing command [" + cmd + "] over SSH [" + sshConnection.toString()
+                    + "]. Exit Code " + exitStatus + " and Error message : "+ err;
+            logger.error(errMessage);
+            result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
+            result.setStatusMessage(errMessage);
+        }
+        return result;
+    }
+
+    /**
+     * 1. Connect to SSH server.
+     * 2. Exec remote command over SSH. Return command execution status.
+     * Command output is written to out or err stream.
+     *
+     * @param commands list of commands to execute
+     * @param payloadSLS has the SLS file location that is to be sent to server
+     * @param retryDelay delay between retry to make a SSH connection.
+     * @param retryCount number of count retry to make a SSH connection.
+     * @return command execution status
+     */
+    public SaltstackResult connectNExecuteSLS(String commands, String payloadSLS, int retryDelay, int retryCount) {
 
         SaltstackResult result = new SaltstackResult();
         try {
@@ -104,8 +223,6 @@
      * Command output is written to out or err stream.
      *
      * @param cmd command to execute
-     * @param out content of sysout will go to this stream
-     * @param err content of syserr will go to this stream
      * @return command execution status
      */
     public SaltstackResult execute(String cmd) {
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java
index 6ff5e57..5fe130f 100644
--- a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java
@@ -79,7 +79,12 @@
     private static final String ID_ATTRIBUTE_NAME = "org.onap.appc.adapter.saltstack.Id";
     private static final String LOG_ATTRIBUTE_NAME = "org.onap.appc.adapter.saltstack.log";
 
+    public static final String CONNECTION_RETRY_DELAY = "retryDelay";
+    public static final String CONNECTION_RETRY_COUNT = "retryCount";
+
     private static final String CLIENT_TYPE_PROPERTY_NAME = "org.onap.appc.adapter.saltstack.clientType";
+    private static final String SS_SERVER_HOSTNAME = "org.onap.appc.adapter.saltstack.host";
+    private static final String SS_SERVER_PORT = "org.onap.appc.adapter.saltstack.port";
     private static final String SS_SERVER_USERNAME = "org.onap.appc.adapter.saltstack.userName";
     private static final String SS_SERVER_PASSWORD = "org.onap.appc.adapter.saltstack.userPasswd";
     private static final String SS_SERVER_SSH_KEY = "org.onap.appc.adapter.saltstack.sshKey";
@@ -134,7 +139,7 @@
      * Returns the symbolic name of the adapter
      *
      * @return The adapter name
-     * @see org.onap.appc.adapter.rest.SaltstackAdapter#getAdapterName()
+     * @see SaltstackAdapter#getAdapterName()
      */
     @Override
     public String getAdapterName() {
@@ -146,7 +151,7 @@
         this.timeout = timeout;
     }
     /**
-     * @param rc Method posts info to Context memory in case of an error and throws a
+     *  Method posts info to Context memory in case of an error and throws a
      *        SvcLogicException causing SLI to register this as a failure
      */
     @SuppressWarnings("static-method")
@@ -182,22 +187,32 @@
             String clientType = props.getProperty(CLIENT_TYPE_PROPERTY_NAME);
             logger.info("Saltstack ssh client type set to " + clientType);
 
-            if ("BASIC".equals(clientType)) {
+            if ("BASIC".equalsIgnoreCase(clientType)) {
                 logger.info("Creating ssh client connection");
                 // set path to keystore file
-                String trustStoreFile = props.getProperty(SS_SERVER_USERNAME);
-                String key = props.getProperty(SS_SERVER_PASSWORD);
-                //TODO: Connect to SSH Saltstack server (using username and password) and return client to execute command
-                sshClient = null;
-            } else if ("SSH_CERT".equals(clientType)) {
+                String sshHost = props.getProperty(SS_SERVER_HOSTNAME);
+                String sshPort = props.getProperty(SS_SERVER_PORT);
+                String sshUserName = props.getProperty(SS_SERVER_USERNAME);
+                String sshPassword = props.getProperty(SS_SERVER_PASSWORD);
+                sshClient = new ConnectionBuilder(sshHost, sshPort, sshUserName, sshPassword);
+            } else if ("SSH_CERT".equalsIgnoreCase(clientType)) {
                 // set path to keystore file
-                String key = props.getProperty(SS_SERVER_SSH_KEY);
-                logger.info("Creating ssh client with ssh KEY from " + key);
-                //TODO: Connect to SSH Saltstack server (using SSH Key) and return client to execute command
-                sshClient = null;
+                String sshKey = props.getProperty(SS_SERVER_SSH_KEY);
+                String sshHost = props.getProperty(SS_SERVER_HOSTNAME);
+                String sshPort = props.getProperty(SS_SERVER_PORT);
+                logger.info("Creating ssh client with ssh KEY from " + sshKey);
+                sshClient = new ConnectionBuilder(sshHost, sshPort, sshKey);
+            } else if ("BOTH".equalsIgnoreCase(clientType)) {
+                // set path to keystore file
+                String sshKey = props.getProperty(SS_SERVER_SSH_KEY);
+                String sshHost = props.getProperty(SS_SERVER_HOSTNAME);
+                String sshUserName = props.getProperty(SS_SERVER_USERNAME);
+                String sshPassword = props.getProperty(SS_SERVER_PASSWORD);
+                String sshPort = props.getProperty(SS_SERVER_PORT);
+                logger.info("Creating ssh client with ssh KEY from " + sshKey);
+                sshClient = new ConnectionBuilder(sshHost, sshPort, sshUserName, sshPassword, sshKey);
             } else {
-                logger.info("Creating ssh client without any Auth");
-                //TODO: Connect to SSH Saltstack server without any Auth
+                logger.info("No saltstack-adapter.properties defined so reading from DG props");
                 sshClient = null;
             }
         } catch (Exception e) {
@@ -214,7 +229,49 @@
     //  org.onap.appc.adapter.saltstack.req.Id : a unique uuid to reference the request
     @Override
     public void reqExecCommand(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
-        //TODO: to implement
+        String reqID;
+        SaltstackResult testResult;
+        if (sshClient == null){
+            logger.info("saltstack-adapter.properties not defined so reading saltstack host and " +
+                                "auth details from DG's parameters");
+            String sshHost = messageProcessor.reqHostNameResult(params);
+            String sshPort = messageProcessor.reqPortResult(params);
+            String sshUserName = messageProcessor.reqUserNameResult(params);
+            String sshPassword = messageProcessor.reqPasswordResult(params);
+            logger.info("Creating ssh client with BASIC Auth");
+            if(!testMode)
+                sshClient = new ConnectionBuilder(sshHost, sshPort, sshUserName, sshPassword);
+        }
+        try {
+            reqID = params.get("Id");
+            String commandToExecute = params.get("cmd");
+            testResult = execCommand(params, commandToExecute);
+            testResult = messageProcessor.parseResponse(ctx, reqID, testResult);
+
+            // Check status of test request returned by Agent
+            if (testResult.getStatusCode() == SaltstackResultCodes.FINAL_SUCCESS.getValue()) {
+                logger.info(String.format("Execution of request-ID : %s successful.", reqID));
+                testResult.setResults("Success");
+            } else {
+                doFailure(ctx, testResult.getStatusCode(), "Request for execution of command failed. Reason = " + testResult.getStatusMessage());
+                return;
+            }
+        } catch (SvcLogicException e) {
+            logger.error(APPC_EXCEPTION_CAUGHT, e);
+            doFailure(ctx, SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue(),
+                      "Request for execution of command failed. Reason = "
+                              + e.getMessage());
+            return;
+        } catch (Exception e) {
+            logger.error("Exception caught", e);
+            doFailure(ctx, SaltstackResultCodes.INVALID_COMMAND.getValue(),
+                      "Request for execution of command failed. Reason = "
+                              + e.getMessage());
+            return;
+        }
+        ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(testResult.getStatusCode()));
+        ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, testResult.getResults());
+        ctx.setAttribute(ID_ATTRIBUTE_NAME, reqID);
     }
 
     /**
@@ -243,4 +300,24 @@
         //TODO: to implement
 
     }
+
+    public SaltstackResult execCommand(Map<String, String> params, String commandToExecute){
+        SaltstackResult testResult;
+        if (params.get(CONNECTION_RETRY_DELAY) != null && params.get(CONNECTION_RETRY_COUNT) != null) {
+            int retryDelay = Integer.parseInt(params.get(CONNECTION_RETRY_DELAY));
+            int retryCount = Integer.parseInt(params.get(CONNECTION_RETRY_COUNT));
+            if(!testMode)
+                testResult = sshClient.connectNExecute(commandToExecute, retryCount, retryDelay);
+            else {
+                testResult = testServer.MockReqExec(params);
+            }
+        } else {
+            if(!testMode)
+                testResult = sshClient.connectNExecute(commandToExecute);
+            else {
+                testResult = testServer.MockReqExec(params);
+            }
+        }
+        return testResult;
+    }
 }
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SshConnection.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SshConnection.java
new file mode 100644
index 0000000..41e6102
--- /dev/null
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SshConnection.java
@@ -0,0 +1,250 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.adaptors.saltstack.impl;
+
+import org.onap.appc.encryption.EncryptionTool;
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResult;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResultCodes;
+import org.onap.ccsdk.sli.core.sli.SvcLogicException;
+
+import java.io.OutputStream;
+import java.security.KeyPair;
+
+/**
+ * Implementation of SshConnection interface based on Apache MINA SSHD library.
+ */
+class SshConnection {
+
+    private static final EELFLogger logger = EELFManager.getInstance().getApplicationLogger();
+
+    private static final long AUTH_TIMEOUT = 60000;
+    private static final long EXEC_TIMEOUT = 120000;
+
+    private String host;
+    private int port;
+    private String username;
+    private String password;
+    private long timeout = EXEC_TIMEOUT;
+    private String keyFile;
+    private SshClient sshClient;
+    private ClientSession clientSession;
+
+    public static final int DEFAULT_CONNECTION_RETRY_DELAY = 60;
+    public static final int DEFAULT_CONNECTION_RETRY_COUNT = 5;
+
+    public SshConnection(String host, int port, String username, String password, String keyFile) {
+        this.host = host;
+        this.port = port;
+        this.username = username;
+        this.password = password;
+        this.keyFile = keyFile;
+    }
+
+    public SshConnection(String host, int port, String username, String password) {
+        this(host, port, username, password, null);
+    }
+
+    public SshConnection(String host, int port, String keyFile) {
+        this(host, port, null, null, keyFile);
+    }
+
+    public SaltstackResult connect() {
+        SaltstackResult result = new SaltstackResult();
+        sshClient = SshClient.setUpDefaultClient();
+        sshClient.start();
+        try {
+            clientSession =
+                sshClient.connect(EncryptionTool.getInstance().decrypt(username), host, port).await().getSession();
+            if (password != null) {
+                clientSession.addPasswordIdentity(EncryptionTool.getInstance().decrypt(password));
+            }
+            if (keyFile != null) {
+                KeyPairProvider keyPairProvider = new FileKeyPairProvider(new String[] {
+                    keyFile
+                });
+                KeyPair keyPair = keyPairProvider.loadKeys().iterator().next();
+                clientSession.addPublicKeyIdentity(keyPair);
+            }
+            AuthFuture authFuture = clientSession.auth();
+            authFuture.await(AUTH_TIMEOUT);
+            if (!authFuture.isSuccess()) {
+                String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port
+                    + "]. Authentication failed.";
+                result.setStatusCode(SaltstackResultCodes.USER_UNAUTHORIZED.getValue());
+                result.setStatusMessage(errMessage);
+            }
+        } catch (RuntimeException e) {
+            String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port + "]." +
+                                                "Runtime Exception : "+ e.getMessage();
+            result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
+            result.setStatusMessage(errMessage);
+        } catch (Exception e) {
+            String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port + "]." +
+                                                "Host Unknown : " + e.getMessage();
+            result.setStatusCode(SaltstackResultCodes.HOST_UNKNOWN.getValue());
+            result.setStatusMessage(errMessage);
+        }
+        if (logger.isDebugEnabled()) {
+            logger.debug("SSH: connected to [" + toString() + "]");
+        }
+        result.setStatusCode(SaltstackResultCodes.SUCCESS.getValue());
+        return result;
+    }
+
+    public SaltstackResult connectWithRetry(int retryCount, int retryDelay) {
+        int retriesLeft;
+        SaltstackResult result = new SaltstackResult();
+        if(retryCount == 0)
+        retryCount = DEFAULT_CONNECTION_RETRY_COUNT;
+        if(retryDelay == 0)
+        retryDelay = DEFAULT_CONNECTION_RETRY_DELAY;
+        retriesLeft = retryCount + 1;
+        do {
+            try {
+                result = this.connect();
+                break;
+            } catch (RuntimeException e) {
+                if (retriesLeft > 1) {
+                    logger.debug("SSH Connection failed. Waiting for change in server's state.");
+                    waitForConnection(retryDelay);
+                    retriesLeft--;
+                    logger.debug("Retrying SSH connection. Attempt [" + Integer.toString(retryCount - retriesLeft + 1)
+                        + "] out of [" + retryCount + "]");
+                } else {
+                    throw e;
+                }
+          }
+        } while (retriesLeft > 0);
+        return result;
+    }
+
+    public void disconnect() {
+        try {
+            if (logger.isDebugEnabled()) {
+                logger.debug("SSH: disconnecting from [" + toString() + "]");
+            }
+            clientSession.close(false);
+        } finally {
+            if (sshClient != null) {
+                sshClient.stop();
+            }
+        }
+    }
+
+    public void setExecTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+
+    public SaltstackResult execCommand(String cmd, OutputStream out, OutputStream err) {
+        return execCommand(cmd, out, err, false);
+    }
+
+    public SaltstackResult execCommandWithPty(String cmd, OutputStream out) {
+        return execCommand(cmd, out, out, true);
+    }
+
+    private SaltstackResult execCommand(String cmd, OutputStream out, OutputStream err, boolean usePty) {
+        SaltstackResult result = new SaltstackResult();
+        try {
+            if (logger.isDebugEnabled()) {
+                logger.debug("SSH: executing command");
+            }
+            ChannelExec client = clientSession.createExecChannel(cmd);
+            client.setUsePty(usePty); // use pseudo-tty?
+            client.setOut(out);
+            client.setErr(err);
+            OpenFuture openFuture = client.open();
+            int exitStatus;
+            try {
+                client.waitFor(ClientChannel.CLOSED, timeout);
+                openFuture.verify();
+                Integer exitStatusI = client.getExitStatus();
+                if (exitStatusI == null) {
+                    String errMessage = "Error executing command [" + cmd + "] over SSH [" + username + "@" + host
+                            + ":" + port + "]. SSH operation timed out.";
+                    result.setStatusCode(SaltstackResultCodes.OPERATION_TIMEOUT.getValue());
+                    result.setStatusMessage(errMessage);
+                    return result;
+                }
+                exitStatus = exitStatusI;
+            } finally {
+                client.close(false);
+            }
+            result.setSshExitStatus(exitStatus);
+            return result;
+        } catch (RuntimeException e) {
+            String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port + "]." +
+                    "Runtime Exception : "+ e.getMessage();
+            result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
+            result.setStatusMessage(errMessage);
+        } catch (Exception e1) {
+            String errMessage = "Error executing command [" + cmd + "] over SSH [" + username + "@" + host + ":" +
+                    port + "]"+ e1.getMessage();
+            result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
+            result.setStatusMessage(errMessage);
+        }
+        result.setStatusCode(SaltstackResultCodes.SUCCESS.getValue());
+        return result;
+    }
+
+    private void waitForConnection(int retryDelay) {
+        long time = retryDelay * 1000L;
+        long future = System.currentTimeMillis() + time;
+        if (time != 0) {
+            while (System.currentTimeMillis() < future && time > 0) {
+                try {
+                    Thread.sleep(time);
+                } catch (InterruptedException e) {
+                    /*
+                     * This is rare, but it can happen if another thread interrupts us while we are sleeping. In that
+                     * case, the thread is resumed before the delay time has actually expired, so re-calculate the
+                     * amount of delay time needed and reenter the sleep until we get to the future time.
+                     */
+                    time = future - System.currentTimeMillis();
+                }
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        String address = host;
+        if (username != null) {
+            address = username + '@' + address + ':' + port;
+        }
+        return address;
+    }
+}
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/JsonParser.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/JsonParser.java
new file mode 100644
index 0000000..f33799f
--- /dev/null
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/JsonParser.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * openECOMP : SDN-C
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights
+ * 			reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.adaptors.saltstack.model;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public final class JsonParser {
+
+    private static final Logger log = LoggerFactory.getLogger(JsonParser.class);
+
+    private JsonParser() {
+        // Preventing instantiation of the same.
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Map<String, String> convertToProperties(String s)
+        throws JSONException {
+
+        checkNotNull(s, "Input should not be null.");
+
+            JSONObject json = new JSONObject(s);
+            Map<String, Object> wm = new HashMap<>();
+            Iterator<String> ii = json.keys();
+            while (ii.hasNext()) {
+                String key1 = ii.next();
+                wm.put(key1, json.get(key1));
+            }
+
+            Map<String, String> mm = new HashMap<>();
+
+            while (!wm.isEmpty())
+                for (String key : new ArrayList<>(wm.keySet())) {
+                    Object o = wm.get(key);
+                    wm.remove(key);
+
+                    if (o instanceof Boolean || o instanceof Number || o instanceof String) {
+                        mm.put(key, o.toString());
+
+                        log.info("Added property: {} : {}", key, o.toString());
+                    } else if (o instanceof JSONObject) {
+                        JSONObject jo = (JSONObject) o;
+                        Iterator<String> i = jo.keys();
+                        while (i.hasNext()) {
+                            String key1 = i.next();
+                            wm.put(key + "." + key1, jo.get(key1));
+                        }
+                    } else if (o instanceof JSONArray) {
+                        JSONArray ja = (JSONArray) o;
+                        mm.put(key + "_length", String.valueOf(ja.length()));
+
+                        log.info("Added property: {}_length: {}", key, String.valueOf(ja.length()));
+
+                        for (int i = 0; i < ja.length(); i++)
+                            wm.put(key + '[' + i + ']', ja.get(i));
+                    }
+                }
+            return mm;
+    }
+}
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java
index 5a548f8..1ea3151 100644
--- a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java
@@ -28,15 +28,21 @@
  * This module implements the APP-C/Saltstack Server interface
  * based on the REST API specifications
  */
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Set;
 import java.util.UUID;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
 import com.google.common.base.Strings;
 import org.slf4j.Logger;
@@ -53,10 +59,10 @@
     private static final String STATUS_CODE_KEY = "StatusCode";
 
     private static final String SALTSTATE_NAME_KEY = "SaltStateName";
-    private static final String AGENT_URL_KEY = "AgentUrl";
+    private static final String SS_AGENT_HOSTNAME_KEY = "HostName";
+    private static final String SS_AGENT_PORT_KEY = "Port";
     private static final String PASS_KEY = "Password";
     private static final String USER_KEY = "User";
-    private static final String ID_KEY = "Id";
 
     private static final String LOCAL_PARAMETERS_OPT_KEY = "LocalParameters";
     private static final String FILE_PARAMETERS_OPT_KEY = "FileParameters";
@@ -80,7 +86,7 @@
      *
      */
     public JSONObject reqMessage(Map<String, String> params) throws SvcLogicException {
-        final String[] mandatoryTestParams = {AGENT_URL_KEY, SALTSTATE_NAME_KEY, USER_KEY, PASS_KEY};
+        final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SALTSTATE_NAME_KEY, USER_KEY, PASS_KEY};
         final String[] optionalTestParams = {ENV_PARAMETERS_OPT_KEY, NODE_LIST_OPT_KEY, LOCAL_PARAMETERS_OPT_KEY,
                 TIMEOUT_OPT_KEY, VERSION_OPT_KEY, FILE_PARAMETERS_OPT_KEY, ACTION_OPT_KEY};
 
@@ -95,7 +101,7 @@
 
         // Generate a unique uuid for the test
         String reqId = UUID.randomUUID().toString();
-        jsonPayload.put(ID_KEY, reqId);
+        jsonPayload.put(SS_AGENT_HOSTNAME_KEY, reqId);
 
         return jsonPayload;
     }
@@ -103,60 +109,121 @@
     /**
      * Method that validates that the Map has enough information
      * to query Saltstack server for a result. If so, it returns
-     * the appropriate url, else an empty string.
+     * the appropriate PORT number.
      */
-    public String reqUriResult(Map<String, String> params) throws SvcLogicException {
+    public String reqPortResult(Map<String, String> params) throws SvcLogicException {
 
-        final String[] mandatoryTestParams = {AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY};
+        final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY, PASS_KEY};
 
         for (String key : mandatoryTestParams) {
             throwIfMissingMandatoryParam(params, key);
         }
-        return params.get(AGENT_URL_KEY) + "?Id=" + params.get(ID_KEY) + "&Type=GetResult";
+        return params.get(SS_AGENT_PORT_KEY);
     }
 
     /**
      * Method that validates that the Map has enough information
-     * to query Saltstack server for logs. If so, it populates the appropriate
-     * returns the appropriate url, else an empty string.
+     * to query Saltstack server for a result. If so, it returns
+     * the appropriate HOST name.
      */
-    public String reqUriLog(Map<String, String> params) throws SvcLogicException {
+    public String reqHostNameResult(Map<String, String> params) throws SvcLogicException {
 
-        final String[] mandatoryTestParams = {AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY};
+        final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY, PASS_KEY};
 
-        for (String mandatoryParam : mandatoryTestParams) {
-            throwIfMissingMandatoryParam(params, mandatoryParam);
+        for (String key : mandatoryTestParams) {
+            throwIfMissingMandatoryParam(params, key);
         }
-        return params.get(AGENT_URL_KEY) + "?Id=" + params.get(ID_KEY) + "&Type=GetLog";
+        return params.get(SS_AGENT_HOSTNAME_KEY);
+    }
+
+    /**
+     * Method that validates that the Map has enough information
+     * to query Saltstack server for a result. If so, it returns
+     * the appropriate Saltstack server login user name.
+     */
+    public String reqUserNameResult(Map<String, String> params) throws SvcLogicException {
+
+        final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY, PASS_KEY};
+
+        for (String key : mandatoryTestParams) {
+            throwIfMissingMandatoryParam(params, key);
+        }
+        return params.get(USER_KEY);
+    }
+
+    /**
+     * Method that validates that the Map has enough information
+     * to query Saltstack server for a result. If so, it returns
+     * the appropriate Saltstack server login password.
+     */
+    public String reqPasswordResult(Map<String, String> params) throws SvcLogicException {
+
+        final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY, PASS_KEY};
+
+        for (String key : mandatoryTestParams) {
+            throwIfMissingMandatoryParam(params, key);
+        }
+        return params.get(PASS_KEY);
     }
 
     /**
      * This method parses response from the Saltstack Server when we do a post
      * and returns an SaltstackResult object.
      */
-    public SaltstackResult parsePostResponse(String input) throws SvcLogicException {
-        SaltstackResult saltstackResult;
-        try {
-            JSONObject postResponse = new JSONObject(input);
-
-            int code = postResponse.getInt(STATUS_CODE_KEY);
-            String msg = postResponse.getString(STATUS_MESSAGE_KEY);
-
-            int initResponseValue = SaltstackResultCodes.INITRESPONSE.getValue();
-            boolean validCode = SaltstackResultCodes.CODE.checkValidCode(initResponseValue, code);
-            if (!validCode) {
-                throw new SvcLogicException("Invalid InitResponse code  = " + code + " received. MUST be one of "
-                        + SaltstackResultCodes.CODE.getValidCodes(initResponseValue));
-            }
-
-            saltstackResult = new SaltstackResult(code, msg);
-
-        } catch (JSONException e) {
-            saltstackResult = new SaltstackResult(600, "Error parsing response = " + input + ". Error = " + e.getMessage());
+    public SaltstackResult parseResponse(SvcLogicContext ctx, String pfx, SaltstackResult saltstackResult) {
+        int code = saltstackResult.getStatusCode();
+        if (code != SaltstackResultCodes.SUCCESS.getValue()) {
+            return saltstackResult;
         }
+        try {
+            File file = new File(saltstackResult.getOutputFileName());
+            InputStream in = new FileInputStream(file);
+            byte[] data = new byte[(int) file.length()];
+            in.read(data);
+            String str = new String(data, "UTF-8");
+            in.close();
+            Map<String, String> mm = JsonParser.convertToProperties(str);
+            if (mm != null) {
+                for (Map.Entry<String,String> entry : mm.entrySet()) {
+                    ctx.setAttribute(pfx + entry.getKey(), entry.getValue());
+                    LOGGER.info("+++ " + pfx + entry.getKey() + ": [" + entry.getValue() + "]");
+                }
+            }
+        } catch (FileNotFoundException e){
+            return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "Error parsing response file = "
+                    + saltstackResult.getOutputFileName() + ". Error = " + e.getMessage());
+        } catch (JSONException e) {
+            LOGGER.info("Output not in JSON format");
+            return putToProperties(ctx, pfx, saltstackResult);
+        } catch (Exception e) {
+            return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "Error parsing response file = "
+                    + saltstackResult.getOutputFileName() + ". Error = " + e.getMessage());
+        }
+        saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
         return saltstackResult;
     }
 
+    public SaltstackResult putToProperties(SvcLogicContext ctx, String pfx, SaltstackResult saltstackResult) {
+        try {
+            File file = new File(saltstackResult.getOutputFileName());
+            InputStream in = new FileInputStream(file);
+            Properties prop = new Properties();
+            prop.load(in);
+            ctx.setAttribute(pfx + "completeResult", prop.toString());
+            for (Object key : prop.keySet()) {
+                String name = (String) key;
+                String value = prop.getProperty(name);
+                if (value != null && value.trim().length() > 0) {
+                    ctx.setAttribute(pfx + name, value.trim());
+                    LOGGER.info("+++ " + pfx + name + ": [" + value + "]");
+                }
+            }
+        } catch (Exception e) {
+            saltstackResult = new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "Error parsing response file = "
+                    + saltstackResult.getOutputFileName() + ". Error = " + e.getMessage());
+        }
+        return saltstackResult;
+    }
     /**
      * This method parses response from an Saltstack server when we do a GET for a result
      * and returns an SaltstackResult object.
@@ -169,8 +236,8 @@
             JSONObject postResponse = new JSONObject(input);
             saltstackResult = parseGetResponseNested(saltstackResult, postResponse);
         } catch (JSONException e) {
-            saltstackResult = new SaltstackResult(SaltstackResultCodes.INVALID_PAYLOAD.getValue(),
-                    "Error parsing response = " + input + ". Error = " + e.getMessage(), "");
+            saltstackResult = new SaltstackResult(SaltstackResultCodes.INVALID_COMMAND.getValue(),
+                    "Error parsing response = " + input + ". Error = " + e.getMessage(), "", -1);
         }
         return saltstackResult;
     }
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java
index f1fb40d..0587302 100644
--- a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java
@@ -24,6 +24,8 @@
 
 package org.onap.ccsdk.sli.adaptors.saltstack.model;
 
+import java.io.OutputStream;
+
 /**
  *  Simple class to store code and message returned by POST/GET to an Saltstack Server
  */
@@ -34,19 +36,22 @@
     private int statusCode;
     private String statusMessage;
     private String results;
+    private String out;
+    private int sshExitStatus;
 
     public SaltstackResult() {
-        this(-1, EMPTY_VALUE, EMPTY_VALUE);
+        this(-1, EMPTY_VALUE, EMPTY_VALUE, -1);
     }
 
     public SaltstackResult(int code, String message) {
-        this(code, message, EMPTY_VALUE);
+        this(code, message, EMPTY_VALUE, -1);
     }
 
-    public SaltstackResult(int code, String message, String result) {
+    public SaltstackResult(int code, String message, String result, int sshCode) {
         statusCode = code;
         statusMessage = message;
         results = result;
+        sshExitStatus = sshCode;
     }
 
     public void setStatusCode(int code) {
@@ -67,6 +72,14 @@
         this.results = results;
     }
 
+    public void setOutputFileName (String out) {
+        this.out = out;
+    }
+
+    public String getOutputFileName() {
+        return out;
+    }
+
     public int getStatusCode() {
         return this.statusCode;
     }
@@ -78,4 +91,12 @@
     public String getResults() {
         return this.results;
     }
+
+    public int getSshExitStatus() {
+        return sshExitStatus;
+    }
+
+    public void setSshExitStatus(int sshExitStatus) {
+        this.sshExitStatus = sshExitStatus;
+    }
 }
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java
index e520dda..ab88c21 100644
--- a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java
@@ -44,9 +44,11 @@
     HOST_UNKNOWN(625),
     USER_UNAUTHORIZED(613),
     UNKNOWN_EXCEPTION(699),
+    OPERATION_TIMEOUT(659),
     SSL_EXCEPTION(697),
-    INVALID_PAYLOAD(698),
+    INVALID_COMMAND(698),
     INVALID_RESPONSE(601),
+    INVALID_RESPONSE_FILE(600),
     PENDING(100),
     REJECTED(101),
     FINAL_SUCCESS(200),
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java
index a9bf7e7..9737efd 100644
--- a/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java
+++ b/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java
@@ -32,6 +32,7 @@
 
 package org.onap.ccsdk.sli.adaptors.saltstack.model;
 
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.apache.commons.lang.StringUtils;
@@ -56,14 +57,36 @@
      * Returns an saltstack object result. The response code is always the ssh code 200 (i.e connection successful)
      * payload is json string as would be sent back by Saltstack Server
      **/
+    public SaltstackResult MockReqExec(Map<String, String> params) {
+        SaltstackResult result = new SaltstackResult();
+
+        try {
+            if (params.get("Test") == "fail") {
+                result = rejectRequest(result, "Must provide a valid Id");
+            } else {
+                result = acceptRequest(result);
+            }
+        } catch (Exception e) {
+            logger.error("JSONException caught", e);
+            rejectRequest(result, e.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * Method that emulates the response from an Saltstack Server
+     * when presented with a request to execute a saltState
+     * Returns an saltstack object result. The response code is always the ssh code 200 (i.e connection successful)
+     * payload is json string as would be sent back by Saltstack Server
+     **/
     //TODO: This class is to be altered completely based on the SALTSTACK server communicaiton.
-    public SaltstackResult Connect(String agentUrl, String payload) {
+    public SaltstackResult Connect(Map<String, String> params) {
         SaltstackResult result = new SaltstackResult();
 
         try {
             // Request must be a JSON object
 
-            JSONObject message = new JSONObject(payload);
+            JSONObject message = new JSONObject();
             if (message.isNull("Id")) {
                 rejectRequest(result, "Must provide a valid Id");
             } else if (message.isNull(SALTSTATE_NAME)) {
@@ -120,19 +143,15 @@
         return getResult;
     }
 
-    private void rejectRequest(SaltstackResult result, String Message) {
-        result.setStatusCode(200);
-        JSONObject response = new JSONObject();
-        response.put(STATUS_CODE, SaltstackResultCodes.REJECTED.getValue());
-        response.put(STATUS_MESSAGE, Message);
-        result.setStatusMessage(response.toString());
+    private SaltstackResult rejectRequest(SaltstackResult result, String Message) {
+        result.setStatusCode(SaltstackResultCodes.REJECTED.getValue());
+        result.setStatusMessage("Rejected");
+        return result;
     }
 
-    private void acceptRequest(SaltstackResult result) {
-        result.setStatusCode(200);
-        JSONObject response = new JSONObject();
-        response.put(STATUS_CODE, SaltstackResultCodes.PENDING.getValue());
-        response.put(STATUS_MESSAGE, "PENDING");
-        result.setStatusMessage(response.toString());
+    private SaltstackResult acceptRequest(SaltstackResult result) {
+        result.setStatusCode(SaltstackResultCodes.SUCCESS.getValue());
+        result.setStatusMessage("Success");
+        return result;
     }
 }
\ No newline at end of file
diff --git a/saltstack-adapter/saltstack-adapter-provider/src/test/java/org/onap/appc/adapter/impl/TestSaltstackAdapterImpl.java b/saltstack-adapter/saltstack-adapter-provider/src/test/java/org/onap/appc/adapter/impl/TestSaltstackAdapterImpl.java
index 5ca6e6e..d7b3303 100644
--- a/saltstack-adapter/saltstack-adapter-provider/src/test/java/org/onap/appc/adapter/impl/TestSaltstackAdapterImpl.java
+++ b/saltstack-adapter/saltstack-adapter-provider/src/test/java/org/onap/appc/adapter/impl/TestSaltstackAdapterImpl.java
@@ -71,26 +71,173 @@
         svcContext = null;
     }
 
-    @Test
-    public void reqExecCommand_shouldSetPending() throws IllegalStateException, IllegalArgumentException {
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetFailed() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("HostName", "test");
+        params.put("Port", "10");
+        params.put("User", "test");
+        params.put("Password", "test");
+        params.put("Test", "fail");
+            adapter.reqExecCommand(params, svcContext);
+            String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+            TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+            assertEquals("101", status);
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetUserFailed() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("HostName", "test");
+        params.put("Port", "10");
+        params.put("Password", "test");
+        params.put("Test", "fail");
+        adapter.reqExecCommand(params, svcContext);
+        String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+        TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+        assertEquals("101", status);
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetHostFailed() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("Port", "10");
+        params.put("User", "test");
+        params.put("Password", "test");
+        params.put("Test", "fail");
+        adapter.reqExecCommand(params, svcContext);
+        String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+        TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+        assertEquals("101", status);
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetPortFailed() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("HostName", "test");
+        params.put("User", "test");
+        params.put("Password", "test");
+        params.put("Test", "fail");
+        adapter.reqExecCommand(params, svcContext);
+        String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+        TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+        assertEquals("101", status);
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetPasswordFailed() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("HostName", "test");
+        params.put("Port", "10");
+        params.put("User", "test");
+        params.put("Test", "fail");
+        adapter.reqExecCommand(params, svcContext);
+        String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+        TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+        assertEquals("101", status);
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetMandatoryFailed() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("Test", "fail");
+        adapter.reqExecCommand(params, svcContext);
+        String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+        TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+        assertEquals("101", status);
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetSuccess() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
 
         params.put("PlaybookName", "test_playbook.yaml");
-
+        params.put("HostName", "test");
+        params.put("Port", "10");
+        params.put("User", "test");
+        params.put("Password", "test");
+        params.put("Test", "success");
         try {
             adapter.reqExecCommand(params, svcContext);
             String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
             TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
-           // System.out.println("Comparing " + PENDING + " and " + status);
-            //assertEquals(PENDING, status);
-            assertEquals(null, status);
-        } catch (SvcLogicException e) {
-            String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
-            fail(e.getMessage() + " Code = " + status);
-        } catch (Exception e) {
+            assertEquals("400", status);
+        } catch (NullPointerException e) {
             fail(e.getMessage() + " Unknown exception encountered ");
         }
     }
 
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetSuccessWithRetry() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("PlaybookName", "test_playbook.yaml");
+        params.put("HostName", "test");
+        params.put("Port", "10");
+        params.put("User", "test");
+        params.put("Password", "test");
+        params.put("Test", "success");
+        params.put("retryDelay", "10");
+        params.put("retryCount", "10");
+        try {
+            adapter.reqExecCommand(params, svcContext);
+            String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+            TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+            assertEquals("400", status);
+        } catch (NullPointerException e) {
+            fail(e.getMessage() + " Unknown exception encountered ");
+        }
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetSuccessWithRetryZero() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("PlaybookName", "test_playbook.yaml");
+        params.put("HostName", "test");
+        params.put("Port", "10");
+        params.put("User", "test");
+        params.put("Password", "test");
+        params.put("Test", "success");
+        params.put("retryDelay", "0");
+        params.put("retryCount", "0");
+        try {
+            adapter.reqExecCommand(params, svcContext);
+            String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+            TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+            assertEquals("400", status);
+        } catch (NullPointerException e) {
+            fail(e.getMessage() + " Unknown exception encountered ");
+        }
+    }
+
+    @Test(expected = SvcLogicException.class)
+    public void reqExecCommand_shouldSetSuccessWithNoRetry() throws SvcLogicException,
+            IllegalStateException, IllegalArgumentException {
+
+        params.put("PlaybookName", "test_playbook.yaml");
+        params.put("HostName", "test");
+        params.put("Port", "10");
+        params.put("User", "test");
+        params.put("Password", "test");
+        params.put("Test", "success");
+        params.put("retryDelay", "-1");
+        params.put("retryCount", "-1");
+        try {
+            adapter.reqExecCommand(params, svcContext);
+            String status = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.code");
+            TestId = svcContext.getAttribute("org.onap.appc.adapter.saltstack.result.Id");
+            assertEquals("400", status);
+        } catch (NullPointerException e) {
+            fail(e.getMessage() + " Unknown exception encountered ");
+        }
+    }
     @Test
     public void reqExecSLS_shouldSetSuccess() throws IllegalStateException, IllegalArgumentException {