summaryrefslogtreecommitdiffstats
path: root/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java')
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapter.java59
-rwxr-xr-xadaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapterPropertiesProvider.java32
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java172
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java446
-rwxr-xr-xadaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterPropertiesProviderImpl.java192
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SshConnection.java217
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/Constants.java93
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/JsonParser.java89
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java412
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java102
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java100
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java93
-rw-r--r--adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SshException.java37
13 files changed, 2044 insertions, 0 deletions
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapter.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapter.java
new file mode 100644
index 000000000..346910a39
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapter.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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;
+
+import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
+import org.onap.ccsdk.sli.core.sli.SvcLogicException;
+import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin;
+
+import java.util.Map;
+
+/**
+ * This interface defines the operations that the Saltstack adapter exposes.
+ */
+public interface SaltstackAdapter extends SvcLogicJavaPlugin {
+ /**
+ * Returns the symbolic name of the adapter
+ *
+ * @return The adapter name
+ */
+ String getAdapterName();
+
+ /* Method to post a single command request for execution on SaltState server
+ * The response from Saltstack comes in json format and it is automatically put
+ * to context for DGs access, with a certain prefix*/
+ void reqExecCommand(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException;
+
+ /* Method for execution of saltstack SLS command on SaltState server
+ * The response from Saltstack comes in json format and it is automatically put
+ * to context for DGs access, with a certain prefix*/
+ void reqExecSLS(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException;
+
+ /* When SLS file is created/available then this Method can be used to post
+ * the file to saltstack server and execute the SLS file on SaltState server
+ * The response from Saltstack comes in json format and it is automatically put
+ * to context for DGs access, with a certain prefix*/
+ void reqExecSLSFile(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException;
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapterPropertiesProvider.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapterPropertiesProvider.java
new file mode 100755
index 000000000..a6b707afc
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/SaltstackAdapterPropertiesProvider.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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;
+
+import java.util.Properties;
+
+public interface SaltstackAdapterPropertiesProvider {
+
+ public Properties getProperties();
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java
new file mode 100644
index 000000000..48469fdd5
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/ConnectionBuilder.java
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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.impl;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SshException;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResult;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResultCodes;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Returns a custom SSH client
+ * - based on options
+ * - can create one with ssl using an X509 certificate that does NOT have a known CA
+ * - create one which trusts ALL SSL certificates
+ * - return default sshclient (which only trusts known CAs from default cacerts file for process) this is the default
+ * option
+ **/
+public class ConnectionBuilder {
+
+ private static final EELFLogger logger = EELFManager.getInstance().getLogger(ConnectionBuilder.class);
+ SshConnection sshConnection;
+
+ /**
+ * Constructor that initializes an ssh client based on username and password
+ **/
+ public ConnectionBuilder(String host, String port, String userName, String userPasswd) {
+ sshConnection = new SshConnection(host, Integer.parseInt(port), userName, userPasswd);
+ }
+
+ /**
+ * Constructor that initializes an ssh client based on ssh certificate
+ * This is still not supported in 1.3.0 version
+ **/
+ public ConnectionBuilder(String host, String port, String certFile) {
+ sshConnection = new SshConnection(host, Integer.parseInt(port), 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 connectNExecute(String cmd, long execTimeout) throws IOException {
+ return connectNExecute(cmd, false, execTimeout);
+ }
+
+ /**
+ * 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 withRetry make a SSH connection with default retry.
+ * @return command execution status
+ */
+ public SaltstackResult connectNExecute(String cmd, boolean withRetry, long execTimeout)
+ throws IOException {
+
+ SaltstackResult result = new SaltstackResult();
+ ByteArrayOutputStream out = null;
+ ByteArrayOutputStream errs = null;
+ if (execTimeout >= 0) {
+ sshConnection.setExecTimeout(execTimeout);
+ }
+
+ try {
+ if (withRetry) {
+ sshConnection.connectWithRetry();
+ } else {
+ sshConnection.connect();
+ }
+ out = new ByteArrayOutputStream();
+ errs = new ByteArrayOutputStream();
+ int resultCode = sshConnection.execCommand(cmd, out, errs);
+ sshConnection.disconnect();
+ if (resultCode != 0) {
+ return sortExitStatus(resultCode, errs.toString(), cmd);
+ }
+ result.setStatusCode(SaltstackResultCodes.SUCCESS.getValue());
+ result.setStatusMessage("Success");
+ result.setOutputMessage(out);
+ } catch (SshException io) {
+ if (io.toString().equalsIgnoreCase("Authentication failed")) {
+ logger.error(io.toString());
+ result.setStatusCode(SaltstackResultCodes.USER_UNAUTHORIZED.getValue());
+ result.setStatusMessage(io.toString());
+ return result;
+ }
+ logger.error("Caught Exception", io);
+ result.setStatusCode(SaltstackResultCodes.SSH_EXCEPTION.getValue());
+ result.setStatusMessage(io.getMessage());
+ } catch (Exception io) {
+ logger.error("Caught Exception", io);
+ result.setStatusCode(SaltstackResultCodes.SSH_EXCEPTION.getValue());
+ result.setStatusMessage(io.getMessage());
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ if (errs != null) {
+ errs.close();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Print Reasonable error messages based on SSH Exit status code
+ * */
+ public SaltstackResult sortExitStatus(int exitStatus, String errMess, String cmd) {
+ SaltstackResult result = new SaltstackResult();
+ if (exitStatus == 255 || exitStatus == 1) {
+ String errMessage = "Error executing command [" + cmd + "] over SSH [" + sshConnection.toString()
+ + "]. Exit Code " + exitStatus + " and Error message : " +
+ "Malformed configuration. " + errMess;
+ 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. " + errMess;
+ 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. " + errMess;
+ 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 : " + errMess;
+ logger.error(errMessage);
+ result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
+ result.setStatusMessage(errMessage);
+ }
+ return result;
+ }
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java
new file mode 100644
index 000000000..34321ea59
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterImpl.java
@@ -0,0 +1,446 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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.impl;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.onap.ccsdk.sli.adaptors.saltstack.SaltstackAdapter;
+import org.onap.ccsdk.sli.adaptors.saltstack.SaltstackAdapterPropertiesProvider;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackMessageParser;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResult;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResultCodes;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackServerEmulator;
+import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
+import org.onap.ccsdk.sli.core.sli.SvcLogicException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * This class implements the {@link SaltstackAdapter} interface. This interface defines the behaviors
+ * that our service provides.
+ */
+public class SaltstackAdapterImpl implements SaltstackAdapter {
+
+ /**
+ * The constant for the status code for a failed outcome
+ */
+ @SuppressWarnings("nls")
+ public static final String OUTCOME_FAILURE = "failure";
+ /**
+ * The constant for the status code for a successful outcome
+ */
+ @SuppressWarnings("nls")
+ public static final String OUTCOME_SUCCESS = "success";
+ public static final String CONNECTION_RETRY = "withRetry";
+ private static final String APPC_EXCEPTION_CAUGHT = "APPCException caught";
+ /**
+ * Adapter Name
+ */
+ private static final String ADAPTER_NAME = "Saltstack Adapter";
+ private static final String RESULT_CODE_ATTRIBUTE_NAME = "org.onap.appc.adapter.saltstack.result.code";
+ private static final String MESSAGE_ATTRIBUTE_NAME = "org.onap.appc.adapter.saltstack.message";
+ private static final String ID_ATTRIBUTE_NAME = "org.onap.appc.adapter.saltstack.Id";
+ 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_PASSWD = "org.onap.appc.adapter.saltstack.userPasswd";
+ private static final String SS_SERVER_SSH_KEY = "org.onap.appc.adapter.saltstack.sshKey";
+
+ private static final String COMMAND_IN_JSON_OUT = " --out=json --static ";
+ private static final String COMMAND_CHANGE_DEFAULT_DIR = " cd /srv/salt/ ;";
+
+ /**
+ * The logger to be used
+ */
+ private static final EELFLogger logger = EELFManager.getInstance().getLogger(SaltstackAdapterImpl.class);
+ /**
+ * Connection object
+ **/
+ private ConnectionBuilder sshClient;
+
+ /**
+ * Saltstack API Message Handlers
+ **/
+ private SaltstackMessageParser messageProcessor;
+
+ /**
+ * indicator whether in test mode
+ **/
+ private boolean testMode = false;
+
+ /**
+ * server emulator object to be used if in test mode
+ **/
+ private SaltstackServerEmulator testServer;
+
+ /**
+ * This default constructor is used as a work around because the activator wasn't getting called
+ */
+ public SaltstackAdapterImpl() throws SvcLogicException {
+ initialize(new SaltstackAdapterPropertiesProviderImpl());
+ }
+
+ public SaltstackAdapterImpl(SaltstackAdapterPropertiesProvider propProvider) throws SvcLogicException {
+ initialize(propProvider);
+ }
+
+ /**
+ * Used for jUnit test and testing interface
+ */
+ public SaltstackAdapterImpl(boolean mode) {
+ testMode = mode;
+ testServer = new SaltstackServerEmulator();
+ messageProcessor = new SaltstackMessageParser();
+ }
+
+ /**
+ * Returns the symbolic name of the adapter
+ *
+ * @return The adapter name
+ * @see SaltstackAdapter#getAdapterName()
+ */
+ @Override
+ public String getAdapterName() {
+ return ADAPTER_NAME;
+ }
+
+ /**
+ * 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")
+ private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
+ logger.error(APPC_EXCEPTION_CAUGHT, message);
+ svcLogic.setStatus(OUTCOME_FAILURE);
+ svcLogic.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
+ svcLogic.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
+ throw new SvcLogicException("Saltstack Adapter Error = " + message);
+ }
+
+ /**
+ * initialize the Saltstack adapter based on default and over-ride configuration data
+ */
+ private void initialize(SaltstackAdapterPropertiesProvider propProvider) throws SvcLogicException {
+
+
+ Properties props = propProvider.getProperties();
+
+ // Create the message processor instance
+ messageProcessor = new SaltstackMessageParser();
+
+ // Create the ssh client instance
+ // type of client is extracted from the property file parameter
+ // org.onap.appc.adapter.saltstack.clientType
+ // It can be :
+ // 1. BASIC. SSH Connection using username and password
+ // 2. SSH_CERT (trust only those whose certificates have been stored in the SSH KEY file)
+ // 3. DEFAULT SSH Connection without any authentication
+
+ try {
+ String clientType = props.getProperty(CLIENT_TYPE_PROPERTY_NAME);
+ logger.info("Saltstack ssh client type set to " + clientType);
+
+ if ("BASIC".equalsIgnoreCase(clientType)) {
+ logger.info("Creating ssh client connection");
+ // set path to keystore file
+ String sshHost = props.getProperty(SS_SERVER_HOSTNAME);
+ String sshPort = reqServerPort(props);
+ String sshUserName = props.getProperty(SS_SERVER_USERNAME);
+ String sshPassword = props.getProperty(SS_SERVER_PASSWD);
+ sshClient = new ConnectionBuilder(sshHost, sshPort, sshUserName, sshPassword);
+ } else if ("SSH_CERT".equalsIgnoreCase(clientType)) {
+ // set path to keystore file
+ String sshKey = props.getProperty(SS_SERVER_SSH_KEY);
+ String sshHost = props.getProperty(SS_SERVER_HOSTNAME);
+ String sshPort = reqServerPort(props);
+ logger.info("Creating ssh client with ssh KEY from " + sshKey);
+ sshClient = new ConnectionBuilder(sshHost, sshPort, sshKey);
+ } else {
+ logger.info("No saltstack-adapter.properties defined so reading from DG props");
+ sshClient = null;
+ }
+ } catch (NumberFormatException e) {
+ logger.error("Error Initializing Saltstack Adapter due to Unknown Exception", e);
+ throw new SvcLogicException("Saltstack Adapter Property file parsing Error = port in property file has to be an integer.");
+ } catch (Exception e) {
+ logger.error("Error Initializing Saltstack Adapter due to Exception", e);
+ throw new SvcLogicException("Saltstack Adapter Property file parsing Error = " + e.getMessage());
+ }
+ logger.info("Initialized Saltstack Adapter");
+ }
+
+ private String reqServerPort(Properties props) {
+ // use default port if null
+ if (props.getProperty(SS_SERVER_PORT) == null) {
+ return "22";
+ }
+ return props.getProperty(SS_SERVER_PORT);
+ }
+
+ private void setSSHClient(Map<String, String> params) throws SvcLogicException {
+ 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);
+ }
+ }
+ }
+
+ private String parseEnvParam(JSONObject envParams) {
+ StringBuilder envParamBuilder = new StringBuilder();
+ if (envParams != null) {
+ for (Object key : envParams.keySet()) {
+ if (envParamBuilder.length() > 0) {
+ envParamBuilder.append(", ");
+ }
+ envParamBuilder.append(key + "=" + envParams.get((String) key));
+ logger.info("EnvParameters : " + envParamBuilder);
+ }
+ }
+ return envParamBuilder.toString();
+ }
+
+ private String parseFileParam(JSONObject fileParams) {
+ StringBuilder fileParamBuilder = new StringBuilder();
+ if (fileParams != null) {
+ for (Object key : fileParams.keySet()) {
+ fileParamBuilder.append("echo -e \"" + fileParams.get((String) key) + "\" > /srv/salt/" + key).append("; ");
+ logger.info("FileParameters : " + fileParamBuilder);
+ }
+ }
+ return fileParamBuilder.toString();
+ }
+
+ private String putToCommands(SvcLogicContext ctx, String slsFileName,
+ String applyTo, JSONObject envParams, JSONObject fileParams) throws SvcLogicException {
+
+ StringBuilder constructedCommand = new StringBuilder();
+ try {
+ File file = new File(slsFileName);
+ String slsFile = file.getName();
+ if (!slsFile.substring(slsFile.lastIndexOf("."),
+ slsFile.length()).equalsIgnoreCase(".sls")) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(), "Input file " +
+ "is not of type .sls");
+ }
+ try(InputStream in = new FileInputStream(file)){
+ byte[] data = new byte[(int) file.length()];
+ in.read(data);
+ String str = new String(data, "UTF-8");
+ String slsWithoutExtn = stripExtension(slsFile);
+ constructedCommand.append(parseFileParam(fileParams)).append("echo -e \"").append(str).append("\" > /srv/salt/").
+ append(slsFile).append("; ").append(COMMAND_CHANGE_DEFAULT_DIR).append(" salt '").
+ append(applyTo).append("' state.apply ").append(slsWithoutExtn).append(" ").append(parseEnvParam(envParams)).append(COMMAND_IN_JSON_OUT);
+ }
+ logger.info("Command to be executed on server : " + constructedCommand.toString());
+
+ } catch (FileNotFoundException e) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(), "Input SLS file " +
+ "not found in path : " + slsFileName + ". " + e.getMessage());
+ } catch (IOException e) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(), "Input SLS file " +
+ "error in path : " + slsFileName + ". " + e.getMessage());
+ } catch (StringIndexOutOfBoundsException e) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(), "Input file " +
+ "is not of type .sls");
+ }
+ return constructedCommand.toString();
+ }
+
+ private String stripExtension(String str) {
+ if (str == null) {
+ return null;
+ }
+ int pos = str.lastIndexOf(".");
+ if (pos == -1) {
+ return str;
+ }
+ return str.substring(0, pos);
+ }
+
+ private String putToCommands(String slsName, String applyTo, JSONObject envParams, JSONObject fileParams) {
+
+ StringBuilder constructedCommand = new StringBuilder();
+
+ constructedCommand.append(parseFileParam(fileParams)).append(COMMAND_CHANGE_DEFAULT_DIR).append(" salt '").append(applyTo)
+ .append("' state.apply ").append(slsName).append(" ").append(parseEnvParam(envParams)).append(COMMAND_IN_JSON_OUT);
+
+ logger.info("Command to be executed on server : " + constructedCommand.toString());
+ return constructedCommand.toString();
+ }
+
+ private void checkResponseStatus(SaltstackResult testResult, SvcLogicContext ctx, String reqID, boolean slsExec)
+ throws SvcLogicException {
+
+ // Check status of test request returned by Agent
+ if (testResult.getStatusCode() != SaltstackResultCodes.FINAL_SUCCESS.getValue()) {
+ ctx.setAttribute(ID_ATTRIBUTE_NAME, reqID);
+ doFailure(ctx, testResult.getStatusCode(), "Request for execution of command failed. Reason = " + testResult.getStatusMessage());
+ } else {
+ logger.info(String.format("Execution of request : successful."));
+ ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(testResult.getStatusCode()));
+ ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, OUTCOME_SUCCESS);
+ ctx.setAttribute(ID_ATTRIBUTE_NAME, reqID);
+ }
+ }
+
+ // Public Method to post single command request to execute saltState. Posts the following back
+ // to Svc context memory
+ // org.onap.appc.adapter.saltstack.req.code : 100 if successful
+ // org.onap.appc.adapter.saltstack.req.messge : any message
+ // 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 {
+ String reqID;
+ boolean slsExec;
+ SaltstackResult testResult;
+ setSSHClient(params);
+ try {
+ reqID = messageProcessor.reqId(params);
+ String commandToExecute = messageProcessor.reqCmd(params);
+ slsExec = messageProcessor.reqIsSLSExec(params);
+ long execTimeout = messageProcessor.reqExecTimeout(params);
+ testResult = execCommand(ctx, params, commandToExecute, execTimeout);
+ testResult = messageProcessor.parseResponse(ctx, reqID, testResult, slsExec);
+ checkResponseStatus(testResult, ctx, reqID, slsExec);
+ } catch (IOException e) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(),
+ "IOException in file stream : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Public Method to post SLS command request to execute saltState on server. Posts the following back
+ * to Svc context memory
+ * <p>
+ * org.onap.appc.adapter.saltstack.req.code : 200 if successful
+ * org.onap.appc.adapter.saltstack.req.messge : any message
+ * org.onap.appc.adapter.saltstack.req.Id : a unique uuid to reference the request
+ */
+ @Override
+ public void reqExecSLS(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
+ String reqID;
+ SaltstackResult testResult;
+ setSSHClient(params);
+ try {
+ reqID = messageProcessor.reqId(params);
+ String slsName = messageProcessor.reqSlsName(params);
+ String applyTo = messageProcessor.reqApplyToDevices(params);
+ long execTimeout = messageProcessor.reqExecTimeout(params);
+ JSONObject envParams = messageProcessor.reqEnvParameters(params);
+ JSONObject fileParams = messageProcessor.reqFileParameters(params);
+
+ String commandToExecute = putToCommands(slsName, applyTo, envParams, fileParams);
+ testResult = execCommand(ctx, params, commandToExecute, execTimeout);
+ testResult = messageProcessor.parseResponse(ctx, reqID, testResult, true);
+ checkResponseStatus(testResult, ctx, reqID, true);
+ } catch (IOException e) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(),
+ "IOException in file stream : " + e.getMessage());
+ } catch (JSONException e) {
+ doFailure(ctx, SaltstackResultCodes.INVALID_COMMAND.getValue(), e.getMessage());
+ }
+ }
+
+ /**
+ * Public Method to post SLS file request to execute saltState. Posts the following back
+ * to Svc context memory
+ * <p>
+ * org.onap.appc.adapter.saltstack.req.code : 100 if successful
+ * org.onap.appc.adapter.saltstack.req.messge : any message
+ * org.onap.appc.adapter.saltstack.req.Id : a unique uuid to reference the request
+ */
+ @Override
+ public void reqExecSLSFile(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
+ String reqID;
+ SaltstackResult testResult;
+ setSSHClient(params);
+ try {
+ reqID = messageProcessor.reqId(params);
+ String slsFile = messageProcessor.reqSlsFile(params);
+ String applyTo = messageProcessor.reqApplyToDevices(params);
+ long execTimeout = messageProcessor.reqExecTimeout(params);
+ JSONObject envParams = messageProcessor.reqEnvParameters(params);
+ JSONObject fileParams = messageProcessor.reqFileParameters(params);
+
+ String commandToExecute = putToCommands(ctx, slsFile, applyTo, envParams, fileParams);
+ testResult = execCommand(ctx, params, commandToExecute, execTimeout);
+ testResult = messageProcessor.parseResponse(ctx, reqID, testResult, true);
+ checkResponseStatus(testResult, ctx, reqID, true);
+ } catch (IOException e) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(),
+ "IOException in file stream : " + e.getMessage());
+ }
+ }
+
+ public SaltstackResult execCommand(SvcLogicContext ctx, Map<String, String> params, String commandToExecute,
+ long execTimeout)
+ throws SvcLogicException {
+
+ //convert execTimeout to Milliseconds
+ execTimeout = execTimeout * 1000;
+ SaltstackResult testResult = new SaltstackResult();
+ try {
+ if (params.get(CONNECTION_RETRY) == null) {
+ if (!testMode) {
+ testResult = sshClient.connectNExecute(commandToExecute, execTimeout);
+ } else {
+ testResult = testServer.mockReqExec(params);
+ }
+ } else if (params.get(CONNECTION_RETRY).equalsIgnoreCase("true")) {
+ if (!testMode) {
+ testResult = sshClient.connectNExecute(commandToExecute, true, execTimeout);
+ } else {
+ testResult = testServer.mockReqExec(params);
+ }
+ } else {
+ if (!testMode) {
+ testResult = sshClient.connectNExecute(commandToExecute, execTimeout);
+ } else {
+ testResult = testServer.mockReqExec(params);
+ }
+ }
+ } catch (IOException e) {
+ doFailure(ctx, SaltstackResultCodes.IO_EXCEPTION.getValue(),
+ "IOException in file stream : " + e.getMessage());
+ }
+ return testResult;
+ }
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterPropertiesProviderImpl.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterPropertiesProviderImpl.java
new file mode 100755
index 000000000..8f0d9857a
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SaltstackAdapterPropertiesProviderImpl.java
@@ -0,0 +1,192 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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.impl;
+
+import org.onap.ccsdk.sli.adaptors.saltstack.SaltstackAdapterPropertiesProvider;
+import org.onap.ccsdk.sli.core.sli.ConfigurationException;
+import org.onap.ccsdk.sli.core.utils.JREFileResolver;
+import org.onap.ccsdk.sli.core.utils.KarafRootFileResolver;
+import org.onap.ccsdk.sli.core.utils.PropertiesFileResolver;
+import org.onap.ccsdk.sli.core.utils.common.CoreDefaultFileResolver;
+import org.onap.ccsdk.sli.core.utils.common.SdncConfigEnvVarFileResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Vector;
+
+/**
+ * Responsible for determining the properties file to use and instantiating the
+ * <code>SqlResource</code> Service. The priority for properties file
+ * resolution is as follows:
+ * <p>
+ * <ol>
+ * <li>A directory identified by the system environment variable
+ * <code>SDNC_CONFIG_DIR</code></li>
+ * <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li>
+ * <li>A directory identified by the JRE argument
+ * <code>sql-resource.properties</code></li>
+ * <li>A <code>sql-resource.properties</code> file located in the karaf root
+ * directory</li>
+ * </ol>
+ */
+public class SaltstackAdapterPropertiesProviderImpl implements SaltstackAdapterPropertiesProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SaltstackAdapterPropertiesProviderImpl.class);
+
+ /**
+ * The name of the properties file for database configuration
+ */
+ private static final String SALTSTACKADAPTER_PROP_FILE_NAME = "saltstack-adapter.properties";
+
+ /**
+ * A prioritized list of strategies for resolving sql-resource properties files.
+ */
+ private Vector<PropertiesFileResolver> saltstackAdapterPropertiesFileResolvers = new Vector<>();
+
+ /**
+ * The configuration properties for the db connection.
+ */
+ private Properties properties;
+
+ /**
+ * Set up the prioritized list of strategies for resolving dblib properties
+ * files.
+ */
+ public SaltstackAdapterPropertiesProviderImpl() {
+ saltstackAdapterPropertiesFileResolvers
+ .add(new SdncConfigEnvVarFileResolver("Using property file (1) from environment variable"));
+ saltstackAdapterPropertiesFileResolvers.add(new CoreDefaultFileResolver("Using property file (2) from default directory"));
+
+ saltstackAdapterPropertiesFileResolvers.add(
+ new JREFileResolver("Using property file (3) from JRE argument", SaltstackAdapterPropertiesProviderImpl.class));
+ saltstackAdapterPropertiesFileResolvers.add(new KarafRootFileResolver("Using property file (4) from karaf root", this));
+
+ // determines properties file as according to the priority described in the
+ // class header comment
+ final File propertiesFile = determinePropertiesFile(this);
+ if (propertiesFile != null) {
+ try (FileInputStream fileInputStream = new FileInputStream(propertiesFile)) {
+ properties = new Properties();
+ properties.load(fileInputStream);
+ } catch (final IOException e) {
+ LOG.error("Failed to load properties for file: {}", propertiesFile.toString(),
+ new ConfigurationException("Failed to load properties for file: " + propertiesFile.toString(),
+ e));
+ }
+ } else {
+ // Try to read properties as resource
+
+ InputStream propStr = getClass().getResourceAsStream("/" + SALTSTACKADAPTER_PROP_FILE_NAME);
+ if (propStr != null) {
+ properties = new Properties();
+ try {
+ properties.load(propStr);
+ propStr.close();
+ } catch (IOException e) {
+ properties = null;
+ }
+ }
+
+ }
+
+ if (properties == null) {
+ reportFailure("Missing configuration properties resource(3)", new ConfigurationException(
+ "Missing configuration properties resource(3): " + SALTSTACKADAPTER_PROP_FILE_NAME));
+
+ LOG.info("Defaulting org.onap.appc.adapter.saltstack.clientType to NONE");
+
+ properties = new Properties();
+ properties.setProperty("org.onap.appc.adapter.saltstack.clientType", "NONE");
+ }
+ }
+
+ /**
+ * Reports the method chosen for properties resolution to the
+ * <code>Logger</code>.
+ *
+ * @param message Some user friendly message
+ * @param fileOptional The file location of the chosen properties file
+ * @return the file location of the chosen properties file
+ */
+ private static File reportSuccess(final String message, final Optional<File> fileOptional) {
+ if (fileOptional.isPresent()) {
+ final File file = fileOptional.get();
+ LOG.info("{} {}", message, file.getPath());
+ return file;
+ }
+ return null;
+ }
+
+ /**
+ * Reports fatal errors. This is the case in which no properties file could be
+ * found.
+ *
+ * @param message An appropriate fatal error message
+ * @param configurationException An exception describing what went wrong during resolution
+ */
+ private static void reportFailure(final String message, final ConfigurationException configurationException) {
+
+ LOG.error("{}", message, configurationException);
+ }
+
+ /**
+ * Extract svclogic config properties.
+ *
+ * @return the svclogic config properties
+ */
+ public Properties getProperties() {
+ return properties;
+ }
+
+ /**
+ * Determines the sql-resource properties file to use based on the following priority:
+ * <ol>
+ * <li>A directory identified by the system environment variable
+ * <code>SDNC_CONFIG_DIR</code></li>
+ * <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li>
+ * <li>A directory identified by the JRE argument
+ * <code>sql-resource.properties</code></li>
+ * <li>A <code>sql-resource.properties</code> file located in the karaf root
+ * directory</li>
+ * </ol>
+ */
+ File determinePropertiesFile(final SaltstackAdapterPropertiesProviderImpl resourceProvider) {
+
+ for (final PropertiesFileResolver sliPropertiesFileResolver : saltstackAdapterPropertiesFileResolvers) {
+ final Optional<File> fileOptional = sliPropertiesFileResolver.resolveFile(SALTSTACKADAPTER_PROP_FILE_NAME);
+ if (fileOptional.isPresent()) {
+ return reportSuccess(sliPropertiesFileResolver.getSuccessfulResolutionMessage(), fileOptional);
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SshConnection.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SshConnection.java
new file mode 100644
index 000000000..25d2d8402
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/impl/SshConnection.java
@@ -0,0 +1,217 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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.impl;
+
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyPair;
+import org.apache.sshd.client.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.client.session.ClientSession;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.Constants;
+import org.onap.ccsdk.sli.adaptors.saltstack.model.SshException;
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+/**
+ * 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 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 void connect() {
+ sshClient = SshClient.setUpDefaultClient();
+ sshClient.start();
+ try {
+ clientSession =
+ sshClient.connect(username, host, port).getSession();
+ if (password != null) {
+ clientSession.addPasswordIdentity(password);
+ } else if (keyFile != null) {
+ Path keyFilePath = Paths.get(keyFile);
+ KeyPairProvider keyPairProvider = new FileKeyPairProvider(keyFilePath);
+ KeyPair keyPair = keyPairProvider.loadKeys().iterator().next();
+ clientSession.addPublicKeyIdentity(keyPair);
+ }
+ AuthFuture authFuture = clientSession.auth();
+ authFuture.await(AUTH_TIMEOUT);
+ if (!authFuture.isSuccess()) {
+ throw new SshException("Error establishing ssh connection to [" + username + "@" + host + ":" + port
+ + "]. Authentication failed.");
+ }
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SshException("Error establishing ssh connection to [" + username + "@" + host + ":" + port + "].",
+ e);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("SSH: connected to [" + toString() + "]");
+ }
+ }
+
+ public void connectWithRetry() {
+ int retryCount;
+ int retryDelay;
+ int retriesLeft;
+ retryCount = Constants.DEFAULT_CONNECTION_RETRY_COUNT;
+ retryDelay = Constants.DEFAULT_CONNECTION_RETRY_DELAY;
+ retriesLeft = retryCount + 1;
+ do {
+ try {
+ 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);
+ }
+
+ 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 int execCommand(String cmd, OutputStream out, OutputStream err) {
+ return execCommand(cmd, out, err, false);
+ }
+
+ public int execCommandWithPty(String cmd, OutputStream out) {
+ return execCommand(cmd, out, out, true);
+ }
+
+ private int execCommand(String cmd, OutputStream out, OutputStream err, boolean usePty) {
+ 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.wait(timeout);
+ openFuture.verify();
+ Integer exitStatusI = client.getExitStatus();
+ if (exitStatusI == null) {
+ throw new SshException("Error executing command [" + cmd + "] over SSH [" + username + "@" + host
+ + ":" + port + "]. Operation timed out.");
+ }
+ exitStatus = exitStatusI;
+ } finally {
+ client.close(false);
+ }
+ return exitStatus;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e1) {
+ throw new SshException(
+ "Error executing command [" + cmd + "] over SSH [" + username + "@" + host + ":" + port + "]", e1);
+ }
+ }
+
+ 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/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/Constants.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/Constants.java
new file mode 100644
index 000000000..a1826c4e1
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/Constants.java
@@ -0,0 +1,93 @@
+/*-
+ * ============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.model;
+
+public class Constants {
+ public static final String NETCONF_SCHEMA = "sdnctl";
+ public static final String SDNCTL_SCHEMA = "sdnctl";
+ public static final String DEVICE_AUTHENTICATION_TABLE_NAME = "DEVICE_AUTHENTICATION";
+ public static final String CONFIGFILES_TABLE_NAME = "CONFIGFILES";
+ public static final String DEVICE_INTERFACE_LOG_TABLE_NAME = "DEVICE_INTERFACE_LOG";
+ public static final String FILE_CONTENT_TABLE_FIELD_NAME = "FILE_CONTENT";
+ public static final String FILE_NAME_TABLE_FIELD_NAME = "FILE_NAME";
+ public static final String USER_NAME_TABLE_FIELD_NAME = "USER_NAME";
+ public static final String PASSWORD_TABLE_FIELD_NAME = "PASSWORD";
+ public static final String PORT_NUMBER_TABLE_FIELD_NAME = "PORT_NUMBER";
+ public static final String VNF_TYPE_TABLE_FIELD_NAME = "VNF_TYPE";
+ public static final String SERVICE_INSTANCE_ID_FIELD_NAME = "SERVICE_INSTANCE_ID";
+ public static final String REQUEST_ID_FIELD_NAME = "REQUEST_ID";
+ public static final String CREATION_DATE_FIELD_NAME = "CREATION_DATE";
+ public static final String LOG_FIELD_NAME = "LOG";
+ public static final String SDC_ARTIFACTS_TABLE_NAME = "ASDC_ARTIFACTS";
+ public static final String PAYLOAD = "payload";
+ public static final String CONNECTION_RETRY_DELAY = "org.onap.appc.ssh.connection.retry.delay";
+ public static final String CONNECTION_RETRY_COUNT = "org.onap.appc.ssh.connection.retry.count";
+ public static final int DEFAULT_CONNECTION_RETRY_DELAY = 60;
+ public static final int DEFAULT_CONNECTION_RETRY_COUNT = 5;
+ public static final int DEFAULT_SSH_COMMAND_RETRY_COUNT = 3;
+ public static final int DEFAULT_CHECKACTIVE_RETRY_COUNT = 3;
+ public static final int DEFAULT_CHECKACTIVE_RETRY_DELAY = 30;
+ public static final int DEFAULT_STOP_RETRY_COUNT = 3;
+ public static final int DEFAULT_STOP_RETRY_DELAY = 30;
+ public static final String PARAM_IN_CONNECTION_DETAILS = "connection-details";
+ public static final String PARAM_IN_NODE_NAME = "node-name";
+ public static final String PARAM_IN_NODE_STATUS = "node-status";
+ public static final String PARAM_IN_VM_URL = "vm-url";
+ public static final String SKIP_EXECUTION_INSTALLER_BIN_FILE = "Skip-execution-installer-bin-file";
+ public static final String SKIP_DEPLOY = "Skip-deploy";
+ public static final String UPGRADE_VERSION = "upgrade-version";
+ public static final String STATE_COMMAND = "/opt/jnetx/skyfall-scp/asp-state.sh | grep -o UP | wc -l";
+ public static final String VNFC_STATE_COMMAND = "/opt/jnetx/skyfall-scp/asp-state.sh";
+ public static final String RESTART_NODE_COMMAND = "/opt/jnetx/skyfall-scp/asp-stop.sh --restart -f --nodes";
+ public static final String START_NODE_COMMAND = "/opt/jnetx/skyfall-scp/asp-start.sh -f --nodes";
+ public static final String STOP_NODE_COMMAND = "/opt/jnetx/skyfall-scp/asp-stop.sh -f --nodes";
+ public static final int STATE_COMMAND_RESULT = 18;
+ public static final String FE_STATE_TRUE_TEST_COMMAND = "ssh -t -q fe1 /opt/omni/bin/swmml -e display-platform-status | grep -o TRUE | wc -l";
+ public static final int FE_STATE_TRUE_TEST_RESULT = 22;
+ public static final String FE_STATE_FALSE_TEST_COMMAND = "ssh -t -q fe1 /opt/omni/bin/swmml -e display-platform-status | grep -o FALSE | wc -l";
+ public static final int FE_STATE_FALSE_TEST_RESULT = 2;
+ public static final String FE_OPERATIONAL_TEST_COMMAND = "ssh -t -q fe1 /opt/omni/bin/swmml -e display-platform-status | grep -o 'NOT FULLY OPERATIONAL' | wc -l";
+ public static final int FE_OPERATIONAL_TEST_RESULT = 2;
+ public static final String SMP_CHECK_ACTIVE_STATE_COMMAND = "cat skyfall-scp/runtime/SCP_SMP_*/smp/log/system.log| grep SSS | tail -1";
+ public static final String SMP_STATE_ACTIVE = "SMP is active";
+ public static final String SMP_STATE_INACTIVE = "SMP is not active";
+ public static final String RSYNC_COMMAND = "yes n | /opt/jnetx/skyfall-scp/asp-rsync.sh --check | grep -o 'is active' | wc -l";
+ public static final int RSYNC_COMMAND_RESULT = 9;
+ public static final String PARAM_IN_TIMEOUT = "timeout";
+ public static final String PARAM_IN_FILE_URL = "source-file-url";
+ public static final String DOWNLOAD_COMMAND = "wget -N %s";
+ public static final String[] VM_NAMES = new String[]{"fe1", "fe2", "be1", "be2", "be3", "be4", "be5", "smp1", "smp2"};
+ public static final String DEFAULT_DISK_SPACE = "10240000";
+ public static final String DF_COMMAND_TEMPLATE = "ssh %s df | grep vda1 | grep -v grep | tr -s ' '|cut -d ' ' -f4";
+ public static final String DG_OUTPUT_STATUS_MESSAGE = "output.status.message";
+ public static final String ATTRIBUTE_ERROR_MESSAGE = "error-message";
+ public static final String CONNECTION_DETAILS_FIELD_NAME = "connection-details";
+ public static final String VNF_HOST_IP_ADDRESS_FIELD_NAME = "vnf-host-ip-address";
+ public static final String VNF_HOST_IP2_ADDRESS_FIELD_NAME = "vnf-host-ip2-address";
+ public static final String DG_ERROR_FIELD_NAME = "org.openecom.appc.dg.error";
+
+ private Constants() {
+ }
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/JsonParser.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/JsonParser.java
new file mode 100644
index 000000000..3eb353a17
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/JsonParser.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java
new file mode 100644
index 000000000..3bb401ad4
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackMessageParser.java
@@ -0,0 +1,412 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2017-2018 Samsung Electronics. 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;
+
+/**
+ * This module implements the APP-C/Saltstack Server interface
+ * based on the REST API specifications
+ */
+
+import com.google.common.base.Strings;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Class that validates and constructs requests sent/received from
+ * Saltstack Server
+ */
+public class SaltstackMessageParser {
+
+ 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 CMD_EXEC = "Cmd"; //cmd
+ private static final String IS_SLS_EXEC = "SlsExec"; //slsExec
+ private static final String SS_REQ_ID = "Id";
+ private static final String SLS_FILE_LOCATION = "SlsFile"; //slsFile
+ private static final String SLS_NAME = "SlsName"; //slsName
+ private static final String MINION_TO_APPLY = "NodeList"; //applyTo
+ private static final String EXEC_TIMEOUT_TO_APPLY = "Timeout"; //execTimeout
+ private static final String FILE_PARAMETERS_OPT_KEY = "FileParameters";
+ private static final String ENV_PARAMETERS_OPT_KEY = "EnvParameters";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SaltstackMessageParser.class);
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate PORT number.
+ */
+ public String reqPortResult(Map<String, String> params) throws SvcLogicException {
+ // use default port if null
+ if (params.get(SS_AGENT_PORT_KEY) == null) {
+ return "22";
+ }
+ return params.get(SS_AGENT_PORT_KEY);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate HOST name.
+ */
+ public String reqHostNameResult(Map<String, String> params) throws SvcLogicException {
+
+ throwIfMissingMandatoryParam(params, SS_AGENT_HOSTNAME_KEY);
+ 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 request ID.
+ */
+ public String reqId(Map<String, String> params) {
+
+ if (params.get(SaltstackMessageParser.SS_REQ_ID) == null) {
+ return UUID.randomUUID().toString();
+ } else if (params.get(SaltstackMessageParser.SS_REQ_ID).equalsIgnoreCase("")) {
+ return UUID.randomUUID().toString();
+ }
+ return params.get(SaltstackMessageParser.SS_REQ_ID);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate command to execute.
+ */
+ public String reqCmd(Map<String, String> params) throws SvcLogicException {
+
+ throwIfMissingMandatoryParam(params, CMD_EXEC);
+ return params.get(SaltstackMessageParser.CMD_EXEC);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate SLS file location to execute.
+ */
+ public String reqSlsFile(Map<String, String> params) throws SvcLogicException {
+
+ throwIfMissingMandatoryParam(params, SLS_FILE_LOCATION);
+ return params.get(SaltstackMessageParser.SLS_FILE_LOCATION);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate SLS file location to execute.
+ */
+ public String reqSlsName(Map<String, String> params) throws SvcLogicException {
+
+ throwIfMissingMandatoryParam(params, SLS_NAME);
+ String slsName = params.get(SaltstackMessageParser.SLS_NAME);
+ try {
+ if (slsName.substring(slsName.lastIndexOf("."), slsName.length()).equalsIgnoreCase(".sls")) {
+ return stripExtension(slsName);
+ }
+ } catch (StringIndexOutOfBoundsException e) {
+ return slsName;
+ }
+ return slsName;
+ }
+
+ private String stripExtension(String str) {
+ if (str == null) {
+ return null;
+ }
+ int pos = str.lastIndexOf(".");
+ if (pos == -1) {
+ return str;
+ }
+ return str.substring(0, pos);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate minions/vnfc to execute the SLS file.
+ */
+ public String reqApplyToDevices(Map<String, String> params) {
+
+ if (params.get(SaltstackMessageParser.MINION_TO_APPLY) == null) {
+ return "*";
+ } else if (params.get(SaltstackMessageParser.MINION_TO_APPLY).equalsIgnoreCase("")) {
+ return "*";
+ }
+ return params.get(SaltstackMessageParser.MINION_TO_APPLY);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate minions/vnfc to execute the SLS file.
+ */
+ public long reqExecTimeout(Map<String, String> params) {
+
+ if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY) == null) {
+ return -1;
+ } else if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY).equalsIgnoreCase("")) {
+ return -1;
+ }
+ return Long.parseLong(params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY));
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate EnvParameters to execute the SLS file.
+ */
+ public JSONObject reqEnvParameters(Map<String, String> params) throws JSONException {
+
+ JSONObject jsonPayload = new JSONObject();
+ final String[] optionalTestParam = {SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY};
+ parseParam(params, optionalTestParam, jsonPayload);
+
+ return (JSONObject) jsonPayload.remove(SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate EnvParameters to execute the SLS file.
+ */
+ public JSONObject reqFileParameters(Map<String, String> params) throws JSONException {
+
+ JSONObject jsonPayload = new JSONObject();
+ final String[] optionalTestParam = {SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY};
+ parseParam(params, optionalTestParam, jsonPayload);
+
+ return (JSONObject) jsonPayload.remove(SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY);
+ }
+
+ private void parseParam(Map<String, String> params, String[] optionalTestParams, JSONObject jsonPayload)
+ throws JSONException {
+
+ Set<String> optionalParamsSet = new HashSet<>();
+ Collections.addAll(optionalParamsSet, optionalTestParams);
+
+ //@formatter:off
+ params.entrySet()
+ .stream()
+ .filter(entry -> optionalParamsSet.contains(entry.getKey()))
+ .filter(entry -> !Strings.isNullOrEmpty(entry.getValue()))
+ .forEach(entry -> parseParam(entry, jsonPayload));
+ //@formatter:on
+ }
+
+ private void parseParam(Map.Entry<String, String> params, JSONObject jsonPayload)
+ throws JSONException {
+ String key = params.getKey();
+ String payload = params.getValue();
+
+ switch (key) {
+ case ENV_PARAMETERS_OPT_KEY:
+ JSONObject paramsJson = new JSONObject(payload);
+ jsonPayload.put(key, paramsJson);
+ break;
+
+ case FILE_PARAMETERS_OPT_KEY:
+ jsonPayload.put(key, getFilePayload(payload));
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Return payload with escaped newlines
+ */
+ private JSONObject getFilePayload(String payload) {
+ String formattedPayload = payload.replace("\n", "\\n").replace("\r", "\\r");
+ return new JSONObject(formattedPayload);
+ }
+
+ /**
+ * Method that validates that the Map has enough information
+ * to query Saltstack server for a result. If so, it returns
+ * the appropriate IsSLSExec true or false.
+ */
+ public boolean reqIsSLSExec(Map<String, String> params) throws SvcLogicException {
+
+ final String[] mandatoryTestParams = {CMD_EXEC, IS_SLS_EXEC};
+
+ for (String key : mandatoryTestParams) {
+ throwIfMissingMandatoryParam(params, key);
+ }
+
+ return params.get(SaltstackMessageParser.IS_SLS_EXEC).equalsIgnoreCase("true");
+ }
+
+ /**
+ * 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 {
+
+ throwIfMissingMandatoryParam(params, USER_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 {
+
+ throwIfMissingMandatoryParam(params, PASS_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 parseResponse(SvcLogicContext ctx, String pfx,
+ SaltstackResult saltstackResult, boolean slsExec) throws IOException {
+ int code = saltstackResult.getStatusCode();
+ boolean executionStatus = true;
+ boolean retCodeFound = false;
+ if (code != SaltstackResultCodes.SUCCESS.getValue()) {
+ return saltstackResult;
+ }
+ ByteArrayOutputStream outStream = saltstackResult.getOutputMessage();
+ String outMessage = outStream.toString();
+ try {
+ Map<String, String> mm = JsonParser.convertToProperties(outMessage);
+ if (mm != null) {
+ for (Map.Entry<String, String> entry : mm.entrySet()) {
+ if (entry.getKey().contains("retcode")) {
+ retCodeFound = true;
+ if (!entry.getValue().equalsIgnoreCase("0")) {
+ executionStatus = false;
+ }
+ }
+ ctx.setAttribute(pfx + "." + entry.getKey(), entry.getValue());
+ LOGGER.info("+++ " + pfx + "." + entry.getKey() + ": [" + entry.getValue() + "]");
+ }
+ }
+ } catch (org.codehaus.jettison.json.JSONException e) {
+ if (slsExec) {
+ return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE.getValue(), "error parsing response file"
+ + " : Output has to be in JSON format");
+ }
+ 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"
+ + " : " + e.getMessage());
+ } finally {
+ if (outStream != null) {
+ outStream.close();
+ }
+ }
+ if (slsExec) {
+ if (!retCodeFound) {
+ if (outMessage != null && !outMessage.equalsIgnoreCase("")) {
+ return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
+ outMessage);
+ }
+ return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
+ "error in executing configuration at the server, check your command input");
+ }
+ if (!executionStatus) {
+ if (outMessage != null && !outMessage.equalsIgnoreCase("")) {
+ return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
+ outMessage);
+ }
+ return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
+ "error in executing configuration at the server, check your command input");
+ }
+ }
+ saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
+ return saltstackResult;
+ }
+
+ public SaltstackResult putToProperties(SvcLogicContext ctx, String pfx,
+ SaltstackResult saltstackResult) throws IOException {
+
+ ByteArrayOutputStream buffer = saltstackResult.getOutputMessage();
+ InputStream inputStream = null;
+ try {
+ byte[] bytes = buffer.toByteArray();
+ Properties prop = new Properties();
+ inputStream = new ByteArrayInputStream(bytes);
+ prop.load(inputStream);
+ 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." +
+ " Error = " + e.getMessage());
+ } finally {
+ if (buffer != null && inputStream != null) {
+ buffer.close();
+ inputStream.close();
+ }
+ }
+ saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
+ return saltstackResult;
+ }
+
+ private void throwIfMissingMandatoryParam(Map<String, String> params, String key) throws SvcLogicException {
+ if (!params.containsKey(key)) {
+ throw new SvcLogicException(String.format(
+ "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",
+ key));
+ }
+ if (Strings.isNullOrEmpty(params.get(key))) {
+ throw new SvcLogicException(String.format(
+ "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",
+ key));
+ }
+ }
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java
new file mode 100644
index 000000000..727cfe314
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResult.java
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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 java.io.ByteArrayOutputStream;
+
+/**
+ * Simple class to store code and message returned by POST/GET to an Saltstack Server
+ */
+public class SaltstackResult {
+
+ private static final String EMPTY_VALUE = "UNKNOWN";
+
+ private int statusCode;
+ private String statusMessage;
+ private String results;
+ private ByteArrayOutputStream out;
+ private int sshExitStatus;
+
+ public SaltstackResult() {
+ this(-1, EMPTY_VALUE, EMPTY_VALUE, -1);
+ }
+
+ public SaltstackResult(int code, String message) {
+ this(code, message, EMPTY_VALUE, -1);
+ }
+
+ public SaltstackResult(int code, String message, String result, int sshCode) {
+ statusCode = code;
+ statusMessage = message;
+ results = result;
+ sshExitStatus = sshCode;
+ }
+
+ void set(int code, String message, String results) {
+ this.statusCode = code;
+ this.statusMessage = message;
+ this.results = results;
+ }
+
+ public ByteArrayOutputStream getOutputMessage() {
+ return out;
+ }
+
+ public void setOutputMessage(ByteArrayOutputStream out) {
+ this.out = out;
+ }
+
+ public int getStatusCode() {
+ return this.statusCode;
+ }
+
+ public void setStatusCode(int code) {
+ this.statusCode = code;
+ }
+
+ public String getStatusMessage() {
+ return this.statusMessage;
+ }
+
+ public void setStatusMessage(String message) {
+ this.statusMessage = message;
+ }
+
+ public String getResults() {
+ return this.results;
+ }
+
+ public void setResults(String results) {
+ this.results = results;
+ }
+
+ public int getSshExitStatus() {
+ return sshExitStatus;
+ }
+
+ public void setSshExitStatus(int sshExitStatus) {
+ this.sshExitStatus = sshExitStatus;
+ }
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java
new file mode 100644
index 000000000..932554983
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackResultCodes.java
@@ -0,0 +1,100 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * enum of the various codes that APP-C uses to resolve different
+ * status of response from Saltstack Server
+ **/
+
+public enum SaltstackResultCodes {
+
+ // @formatter:off
+ SUCCESS(400),
+ KEYSTORE_EXCEPTION(622),
+ CERTIFICATE_ERROR(610),
+ IO_EXCEPTION(611),
+ HOST_UNKNOWN(625),
+ USER_UNAUTHORIZED(613),
+ UNKNOWN_EXCEPTION(699),
+ OPERATION_TIMEOUT(659),
+ SSL_EXCEPTION(697),
+ SSH_EXCEPTION(695),
+ INVALID_COMMAND(698),
+ INVALID_RESPONSE(601),
+ INVALID_RESPONSE_FILE(600),
+ PENDING(100),
+ REJECTED(101),
+ FINAL_SUCCESS(200),
+ CHECK_CTX_FOR_CMD_SUCCESS(250),
+ COMMAND_EXEC_FAILED_STATUS(670),
+ REQ_FAILURE(401),
+ MESSAGE(1),
+ CODE(0),
+ INITRESPONSE(0),
+ FINALRESPONSE(1);
+ // @formatter:on
+
+ private final Set<Integer> initCodes = new HashSet<>(Arrays.asList(100, 101));
+ private final Set<Integer> finalCodes = new HashSet<>(Arrays.asList(200, 500));
+ private final ArrayList<Set<Integer>> codeSets = new ArrayList<>(Arrays.asList(initCodes, finalCodes));
+ private final Set<String> messageSet = new HashSet<>(Arrays.asList("PENDING", "FINISHED", "TERMINATED"));
+ private final int value;
+
+ SaltstackResultCodes(int value) {
+ this.value = value;
+ }
+
+ ;
+
+ public int getValue() {
+ return value;
+ }
+
+ public boolean checkValidCode(int type, int code) {
+ return codeSets.get(type).contains(code);
+ }
+
+ public String getValidCodes(int type) {
+ StringBuilder sb = new StringBuilder("[ ");
+ codeSets.get(type).stream().forEach(s -> sb.append(s).append(","));
+ return sb.append("]").toString();
+ }
+
+ public boolean checkValidMessage(String message) {
+ return messageSet.contains(message);
+ }
+
+ public String getValidMessages() {
+ StringBuilder sb = new StringBuilder("[ ");
+ messageSet.stream().forEach(s -> sb.append(s).append(","));
+ return sb.append("]").toString();
+ }
+}
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java
new file mode 100644
index 000000000..78976562b
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SaltstackServerEmulator.java
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : CCSDK
+ * ================================================================================
+ * Copyright (C) 2018 Samsung Electronics. 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=========================================================
+ */
+
+
+
+/*
+ * Class to emulate responses from the Saltstack Server that is compliant with the APP-C Saltstack Server
+ * Interface. Used for jUnit tests to verify code is working. In tests it can be used
+ * as a replacement for methods from ConnectionBuilder class
+ */
+
+package org.onap.ccsdk.sli.adaptors.saltstack.model;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+public class SaltstackServerEmulator {
+
+ private static final String SALTSTATE_FILE_NAME = "fileName";
+ private final EELFLogger logger = EELFManager.getInstance().getLogger(SaltstackServerEmulator.class);
+
+ /**
+ * 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
+ **/
+ public SaltstackResult mockReqExec(Map<String, String> params) {
+ SaltstackResult result = new SaltstackResult();
+
+ try {
+ if (params.get("Test") == "fail") {
+ result = rejectRequest(result, "Mocked: Fail");
+ } else {
+ String fileName = params.get(SALTSTATE_FILE_NAME);
+ if (fileName == null) {
+ throw new FileNotFoundException("No response file found");
+ }
+ result = acceptRequest(result, fileName);
+ }
+ } catch (Exception e) {
+ logger.error("Exception caught", e);
+ rejectRequest(result, e.getMessage());
+ }
+ return result;
+ }
+
+ private SaltstackResult rejectRequest(SaltstackResult result, String Message) {
+ result.setStatusCode(SaltstackResultCodes.REJECTED.getValue());
+ result.setStatusMessage("Rejected");
+ return result;
+ }
+
+ private SaltstackResult acceptRequest(SaltstackResult result, String fileName) throws IOException {
+ result.setStatusCode(SaltstackResultCodes.SUCCESS.getValue());
+ result.setStatusMessage("Success");
+ Path path = Paths.get(fileName);
+ byte[] data = Files.readAllBytes(path);
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream(data.length);
+ byteOut.write(data, 0, data.length);
+ result.setOutputMessage(byteOut);
+ return result;
+ }
+} \ No newline at end of file
diff --git a/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SshException.java b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SshException.java
new file mode 100644
index 000000000..037a1e891
--- /dev/null
+++ b/adaptors/saltstack-adapter/saltstack-adapter-provider/src/main/java/org/onap/ccsdk/sli/adaptors/saltstack/model/SshException.java
@@ -0,0 +1,37 @@
+/*-
+ * ============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.model;
+
+public class SshException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public SshException(String message) {
+ super(message);
+ }
+
+ public SshException(String message, Throwable cause) {
+ super(message, cause);
+ }
+} \ No newline at end of file