diff options
author | Ganesh Chandrasekaran <ganesh.c@samsung.com> | 2018-07-24 16:40:14 +0900 |
---|---|---|
committer | Ganesh Chandrasekaran <ganesh.c@samsung.com> | 2018-07-24 18:05:43 +0900 |
commit | 8b39ca99c8a49197b05f8fea7de9a1606c810e63 (patch) | |
tree | e32f32de0f9190046e04ac685751c434008fb09c /sshapi-call-node | |
parent | d6a56cd8f71cc196083c6ab16e86d24c7c89f9ef (diff) |
SSHApiCallNode sshExec API Impl
Issue-ID: CCSDK-385
Change-Id: I430f9075ced10edb49062fc12091774e91611b32
Signed-off-by: Ganesh Chandrasekaran <ganesh.c@samsung.com>
Diffstat (limited to 'sshapi-call-node')
16 files changed, 522 insertions, 241 deletions
diff --git a/sshapi-call-node/features/ccsdk-sshapi-call-node/pom.xml b/sshapi-call-node/features/ccsdk-sshapi-call-node/pom.xml index 12ec0686..7b74d6de 100644 --- a/sshapi-call-node/features/ccsdk-sshapi-call-node/pom.xml +++ b/sshapi-call-node/features/ccsdk-sshapi-call-node/pom.xml @@ -36,7 +36,7 @@ </dependency> </dependencies> </dependencyManagement> - +<!-- <dependencies> <dependency> @@ -52,4 +52,5 @@ <version>${project.version}</version> </dependency> </dependencies> + --> </project> diff --git a/sshapi-call-node/provider/pom.xml b/sshapi-call-node/provider/pom.xml index 165003ff..e380d7c2 100755 --- a/sshapi-call-node/provider/pom.xml +++ b/sshapi-call-node/provider/pom.xml @@ -40,6 +40,16 @@ <artifactId>sli-provider</artifactId> </dependency> <dependency> + <groupId>org.onap.appc</groupId> + <artifactId>appc-ssh-adapter-api</artifactId> + <version>1.3.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + </dependency> + <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/SshApiCallNode.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/SshApiCallNode.java index 4efddec8..e4b082cd 100644 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/SshApiCallNode.java +++ b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/SshApiCallNode.java @@ -1,10 +1,11 @@ /*- * ============LICENSE_START======================================================= - * openECOMP : SDN-C + * ONAP : APPC * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights - * reserved. + * Copyright (C) 2017-2018 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 @@ -16,36 +17,89 @@ * 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.plugins.sshapicall; -import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +//import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.ByteArrayOutputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.google.common.base.Strings; +import org.json.JSONObject; +import org.onap.appc.adapter.ssh.SshAdapter; +import org.onap.appc.adapter.ssh.SshConnection; import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; -import java.io.OutputStream; -import java.util.Map; +import org.onap.ccsdk.sli.plugins.sshapicall.model.AuthType; +import org.onap.ccsdk.sli.plugins.sshapicall.model.Parameters; +import org.onap.ccsdk.sli.plugins.sshapicall.model.ParseParam; + + +public class SshApiCallNode implements SvcLogicJavaPlugin { + + private static final EELFLogger logger = EELFManager.getInstance().getApplicationLogger(); + + /** + * Output parameter - SSH command execution status. + */ + String PARAM_OUT_status = "status"; + + /** + * Output parameter - content of SSH command stdout. + */ + String PARAM_OUT_stdout = "stdout"; + + /** + * Output parameter - content of SSH command stderr. + */ + String PARAM_OUT_stderr = "stderr"; + + /** + * Default success status. + */ + int DEF_SUCCESS_STATUS = 0; + + private SshAdapter sshAdapter; -public interface SshApiCallNode extends SvcLogicJavaPlugin { + public void setSshAdapter(SshAdapter sshAdapter) { + this.sshAdapter = sshAdapter; + } /** * Allows Directed Graphs the ability to interact with SSH APIs. - * @param paramMap HashMap<String,String> of parameters passed by the DG to this function + * @param params HashMap<String,String> of parameters passed by the DG to this function * <table border="1"> * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> * <tbody> * <tr><td>templateFileName</td><td>Optional</td><td>full path to template file that can be used to build a request</td><td>/sdncopt/bvc/sshapi/templates/vnf_service-configuration-operation_minimal.json</td></tr> - * <tr><td>sshapiUrl</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> - * <tr><td>sshapiUser</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> - * <tr><td>sshapiPassword</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> - * <tr><td>sshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> - * <tr><td>cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> - * <tr><td>responsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td><td>tmp.sshapi.result</td></tr> + * <tr><td>Url</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> + * <tr><td>Port</td><td>Mandatory</td><td>port to make the SSH connection request to.</td></tr> + * <tr><td>User</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>Password</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> + * <tr><td>SshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> + * <tr><td>ExecTimeout</td><td>Optional</td><td>SSH command execution timeout</td><td>plain_key</td></tr> + * <tr><td>Retry</td><td>Optional</td><td>Make ssh connection with default retry policy</td><td>plain_key</td></tr> + * <tr><td>Cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> + * <tr><td>ResponsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td><td>tmp.sshapi.result</td></tr> + * <tr><td>ResponseType</td><td>Optional</td><td>If we know the response is to be in a specific format (supported are JSON, XML and NONE) </td><td>tmp.sshapi.result</td></tr> * <tr><td>listName[i]</td><td>Optional</td><td>Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details<td></td></tr> - * <tr><td>convertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> - * <tr><td>dumpHeaders</td><td>Optional</td><td>when true writes ssh response content to context memory</td><td>true or false</td></tr> - * <tr><td>returnRequestPayload</td><td>Optional</td><td>used to return payload built in the request</td><td>true or false</td></tr> + * <tr><td>ConvertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>AuthType</td><td>Optional</td><td>Type of authentiation to be used BASIC or sshKey based</td><td>true or false</td></tr> + * <tr><td>EnvParameters</td><td>Optional</td><td>A JSON dictionary which should list key value pairs to be passed to the command execution. + * These values would correspond to instance specific parameters that a command may need to execute an action.</td></tr> + * <tr><td>FileParameters</td><td>Optional</td><td>A JSON dictionary where keys are filenames and values are contents of files. + * The SSH Server will utilize this feature to generate files with keys as filenames and values as content. + * This attribute can be used to generate files that a command may require as part of execution.</td></tr> * </tbody> * </table> * Exec remote command over SSH. Return command execution status. @@ -53,32 +107,140 @@ public interface SshApiCallNode extends SvcLogicJavaPlugin { * * @param ctx Reference to context memory */ - void execCommand(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException; + + public void execCommand(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + ParseParam parser = new ParseParam(); + Parameters p = parser.getParameters(params); + logger.debug("=> Connecting to SSH server..."); + SshConnection sshConnection = getSshConnection(p); + sshConnection.connect(); + try { + logger.debug("=> Connected to SSH server..."); + logger.debug("=> Running SSH command..."); + sshConnection.setExecTimeout(p.sshExecTimeout); + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + int status = sshConnection.execCommand(parser.makeCommand(params), stdout, stderr); + String stdoutRes = stdout.toString(); + String stderrRes = stderr.toString(); + logger.debug("=> executed SSH command"); + + if(status == DEF_SUCCESS_STATUS) { + parser.parseOutput(ctx, stdoutRes); + } + ctx.setAttribute(PARAM_OUT_status, String.format("%01d", status)); + ctx.setAttribute(PARAM_OUT_stdout, stdoutRes); + ctx.setAttribute(PARAM_OUT_stderr, stderrRes); + } finally { + sshConnection.disconnect(); + } + } + + private SshConnection getSshConnection(Parameters p) throws SvcLogicException{ + if (p.authtype == AuthType.BASIC) + return sshAdapter.getConnection(p.sshapiUrl, p.sshapiPort, p.sshapiUser, p.sshapiPassword); + // This is not supported yet in the API, patch has already been added to APPC + else if (p.authtype == AuthType.KEY){ + //return sshAdapter.getConnection(p.sshapiUrl, p.sshapiPort, p.sshKey); + throw new SvcLogicException("SSH Key based Auth method not supported"); + } + else if (p.authtype == AuthType.NONE){ + //return sshAdapter.getConnection(p.sshapiUrl, p.sshapiPort, p.sshKey); + throw new SvcLogicException("SSH Auth type required, BASIC auth in support"); + } + else if (p.authtype == AuthType.UNSPECIFIED){ + if (p.sshapiUser != null && p.sshapiPassword != null) + return sshAdapter.getConnection(p.sshapiUrl, p.sshapiPort, p.sshapiUser, p.sshapiPassword); + else if (p.sshKey != null) + throw new SvcLogicException("SSH Key based Auth method not supported"); + } + throw new SvcLogicException("SSH Auth type required, BASIC auth in support"); + } + /** * Allows Directed Graphs the ability to interact with SSH APIs. - * @param paramMap HashMap<String,String> of parameters passed by the DG to this function + * @param params HashMap<String,String> of parameters passed by the DG to this function * <table border="1"> * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> * <tbody> * <tr><td>templateFileName</td><td>Optional</td><td>full path to template file that can be used to build a request</td><td>/sdncopt/bvc/sshapi/templates/vnf_service-configuration-operation_minimal.json</td></tr> - * <tr><td>sshapiUrl</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> - * <tr><td>sshapiUser</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> - * <tr><td>sshapiPassword</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> - * <tr><td>sshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> - * <tr><td>cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> - * <tr><td>responsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td><td>tmp.sshapi.result</td></tr> + * <tr><td>Url</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> + * <tr><td>Port</td><td>Mandatory</td><td>port to make the SSH connection request to.</td></tr> + * <tr><td>User</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>Password</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> + * <tr><td>SshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> + * <tr><td>ExecTimeout</td><td>Optional</td><td>SSH command execution timeout</td><td>plain_key</td></tr> + * <tr><td>Retry</td><td>Optional</td><td>Make ssh connection with default retry policy</td><td>plain_key</td></tr> + * <tr><td>Cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> + * <tr><td>ResponsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td><td>tmp.sshapi.result</td></tr> + * <tr><td>ResponseType</td><td>Optional</td><td>If we know the response is to be in a specific format (supported are JSON, XML and NONE) </td><td>tmp.sshapi.result</td></tr> * <tr><td>listName[i]</td><td>Optional</td><td>Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details<td></td></tr> - * <tr><td>convertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> - * <tr><td>dumpHeaders</td><td>Optional</td><td>when true writes ssh response content to context memory</td><td>true or false</td></tr> - * <tr><td>returnRequestPayload</td><td>Optional</td><td>used to return payload built in the request</td><td>true or false</td></tr> + * <tr><td>ConvertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>AuthType</td><td>Optional</td><td>Type of authentiation to be used BASIC or sshKey based</td><td>true or false</td></tr> + * <tr><td>EnvParameters</td><td>Optional</td><td>A JSON dictionary which should list key value pairs to be passed to the command execution. + * These values would correspond to instance specific parameters that a command may need to execute an action.</td></tr> + * <tr><td>FileParameters</td><td>Optional</td><td>A JSON dictionary where keys are filenames and values are contents of files. + * The SSH Server will utilize this feature to generate files with keys as filenames and values as content. + * This attribute can be used to generate files that a command may require as part of execution.</td></tr> * </tbody> * </table> - * Exec remote command over SSH with pseudo-tty. Return command execution status. - * Command output is written to out stream only as pseudo-tty writes to one stream only. + * Exec remote command over SSH. Return command execution status. + * Command output is written to out or err stream. * - * @param ctx Reference to context memory + * @param ctx Reference to context memory + */ + + public void execWithStatusCheck(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + execCommand(params, ctx); + int status = Integer.parseInt(ctx.getAttribute(PARAM_OUT_status)); + if(status != DEF_SUCCESS_STATUS) { + StringBuilder errmsg = new StringBuilder(); + errmsg.append("SSH command returned error status [").append(status).append(']'); + String stderrRes = ctx.getAttribute(PARAM_OUT_stderr); + String stdoutRes = ctx.getAttribute(PARAM_OUT_stdout); + if((stderrRes != null) && !stderrRes.isEmpty()) { + errmsg.append(". Error: [").append(stderrRes).append(']'); + } else if ((stdoutRes != null) && !stdoutRes.isEmpty()) { + errmsg.append(". Error: [").append(stdoutRes).append(']'); + } + throw new SvcLogicException(errmsg.toString()); + } + } + + /** + * Allows Directed Graphs the ability to interact with SSH APIs. + * @param params HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> + * <tbody> + * <tr><td>templateFileName</td><td>Optional</td><td>full path to template file that can be used to build a request</td><td>/sdncopt/bvc/sshapi/templates/vnf_service-configuration-operation_minimal.json</td></tr> + * <tr><td>Url</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> + * <tr><td>Port</td><td>Mandatory</td><td>port to make the SSH connection request to.</td></tr> + * <tr><td>User</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>Password</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> + * <tr><td>SshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> + * <tr><td>ExecTimeout</td><td>Optional</td><td>SSH command execution timeout</td><td>plain_key</td></tr> + * <tr><td>Retry</td><td>Optional</td><td>Make ssh connection with default retry policy</td><td>plain_key</td></tr> + * <tr><td>Cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> + * <tr><td>ResponsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td><td>tmp.sshapi.result</td></tr> + * <tr><td>ResponseType</td><td>Optional</td><td>If we know the response is to be in a specific format (supported are JSON, XML and NONE) </td><td>tmp.sshapi.result</td></tr> + * <tr><td>listName[i]</td><td>Optional</td><td>Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details<td></td></tr> + * <tr><td>ConvertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>AuthType</td><td>Optional</td><td>Type of authentiation to be used BASIC or sshKey based</td><td>true or false</td></tr> + * <tr><td>EnvParameters</td><td>Optional</td><td>A JSON dictionary which should list key value pairs to be passed to the command execution. + * These values would correspond to instance specific parameters that a command may need to execute an action.</td></tr> + * <tr><td>FileParameters</td><td>Optional</td><td>A JSON dictionary where keys are filenames and values are contents of files. + * The SSH Server will utilize this feature to generate files with keys as filenames and values as content. + * This attribute can be used to generate files that a command may require as part of execution.</td></tr> + * </tbody> + * </table> + * Exec remote command over SSH. Return command execution status. + * Command output is written to out or err stream. + * + * @param ctx Reference to context memory */ - void execCommandWithPty(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException ; + public void execCommandWithPty(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + } } diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/AuthType.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/AuthType.java deleted file mode 100644 index 30816227..00000000 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/AuthType.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.onap.ccsdk.sli.plugins.sshapicall.impl; - -public enum AuthType { - NONE, BASIC, DIGEST, OAUTH, Unspecified; - - public static AuthType fromString(String s) { - if ("basic".equalsIgnoreCase(s)) - return BASIC; - if ("digest".equalsIgnoreCase(s)) - return DIGEST; - if ("oauth".equalsIgnoreCase(s)) - return OAUTH; - if ("none".equalsIgnoreCase(s)) - return NONE; - if ("unspecified".equalsIgnoreCase(s)) - return Unspecified; - throw new IllegalArgumentException("Invalid value for format: " + s); - } -} diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/SshapiCallNodeImpl.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/SshapiCallNodeImpl.java deleted file mode 100644 index 9283b8b5..00000000 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/SshapiCallNodeImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * openECOMP : SDN-C - * ================================================================================ - * * Copyright (C) 2017 AT&T Intellectual Property. - * ================================================================================ - * 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.plugins.sshapicall.impl; - -import org.onap.ccsdk.sli.core.sli.SvcLogicContext; -import org.onap.ccsdk.sli.core.sli.SvcLogicException; -import org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode; - -import java.io.OutputStream; -import java.util.Map; - -public class SshapiCallNodeImpl implements SshApiCallNode { - @Override - public void execCommand(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException { - //TODO: Implementation - } - - @Override - public void execCommandWithPty(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException { - //TODO: Implementation - } -} diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryException.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/AuthType.java index 9a1d3a31..2ec48c70 100644 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryException.java +++ b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/AuthType.java @@ -7,12 +7,13 @@ * 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. @@ -23,10 +24,18 @@ package org.onap.ccsdk.sli.plugins.sshapicall.model; -public class RetryException extends Exception { +public enum AuthType { + NONE, BASIC, KEY, UNSPECIFIED; - public RetryException(String message) { - super(message); + public static AuthType fromString(String s) { + if ("basic".equalsIgnoreCase(s)) + return BASIC; + if ("key".equalsIgnoreCase(s)) + return KEY; + if ("unspecified".equalsIgnoreCase(s)) + return UNSPECIFIED; + if ("none".equalsIgnoreCase(s)) + return NONE; + throw new IllegalArgumentException("Invalid value for format: " + s); } - } diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/Format.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Format.java index 75604de6..e50a96c6 100644 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/Format.java +++ b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Format.java @@ -22,7 +22,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.ccsdk.sli.plugins.sshapicall.impl; +package org.onap.ccsdk.sli.plugins.sshapicall.model; public enum Format { JSON, XML, NONE; diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/JsonParser.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/JsonParser.java index e840053f..f40f1ad7 100644 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/JsonParser.java +++ b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/JsonParser.java @@ -47,11 +47,10 @@ public final class JsonParser { @SuppressWarnings("unchecked") public static Map<String, String> convertToProperties(String s) - throws SvcLogicException { + throws JSONException { checkNotNull(s, "Input should not be null."); - try { JSONObject json = new JSONObject(s); Map<String, Object> wm = new HashMap<>(); Iterator<String> ii = json.keys(); @@ -89,8 +88,5 @@ public final class JsonParser { } } return mm; - } catch (JSONException e) { - throw new SvcLogicException("Unable to convert JSON to properties" + e.getLocalizedMessage(), e); - } } } diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/Parameters.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Parameters.java index a392cc73..a22da5bc 100644 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/impl/Parameters.java +++ b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Parameters.java @@ -21,22 +21,25 @@ * ============LICENSE_END========================================================= */ -package org.onap.ccsdk.sli.plugins.sshapicall.impl; +package org.onap.ccsdk.sli.plugins.sshapicall.model; import java.util.Set; public class Parameters { - public String templateFileName; public String sshapiUrl; + public int sshapiPort; public String sshapiUser; public String sshapiPassword; - public Format sshKey; + public String sshKey; + public long sshExecTimeout; + public String sshFileParameters; + public String sshEnvParameters; + public boolean sshWithRetry; public String cmd; public String responsePrefix; + public Format responseType; + public Set<String> listNameList; public boolean convertResponse; - public Boolean dumpHeaders; - public String requestBody; public AuthType authtype; - public Boolean returnRequestPayload; } diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/ParseParam.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/ParseParam.java new file mode 100644 index 00000000..caf49ef9 --- /dev/null +++ b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/ParseParam.java @@ -0,0 +1,274 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * * Copyright (C) 2017 AT&T Intellectual Property. + * ================================================================================ + * 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.plugins.sshapicall.model; + +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.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +public class ParseParam { + private static final Logger log = LoggerFactory.getLogger(ParseParam.class); + /** + * Default SSH command timeout + */ + private long dEF_timeout = 12000; + /** + * Default SSH connection port. + */ + private int dEF_port = 22; + /** + * Default SSH command timeout + */ + private final String FILE_PARAMETERS_OPT_KEY = "FileParameters"; + /** + * Default SSH connection port. + */ + private final String ENV_PARAMETERS_OPT_KEY = "EnvParameters"; + private Parameters p = new Parameters(); + + public Parameters getParameters(Map<String, String> paramMap) throws SvcLogicException { + p.sshapiUrl = parseParam(paramMap, "Url", true, null); + p.sshapiPort = Integer.parseInt(parseParam(paramMap, "Port", true, Integer.toString(dEF_port))); + p.sshapiUser = parseParam(paramMap, "User", false, null); + p.sshapiPassword = parseParam(paramMap, "Password", false, null); + p.sshKey = parseParam(paramMap, "SshKey", false, null); + p.sshExecTimeout = Long.parseLong(parseParam(paramMap, "ExecTimeout", false, Long.toString(dEF_timeout))); + p.sshWithRetry = Boolean.valueOf(parseParam(paramMap, "Retry", false, "false")); + p.cmd = parseParam(paramMap, "Cmd", true, null); + p.responsePrefix = parseParam(paramMap, "ResponsePrefix", false, null); + p.responseType = Format.fromString(parseParam(paramMap, "ResponseType", false, "none")); + p.listNameList = getListNameList(paramMap); + p.convertResponse = Boolean.valueOf(parseParam(paramMap, "ConvertResponse", false, "true")); + p.authtype = AuthType.fromString(parseParam(paramMap, "AuthType", false, "unspecified")); + + return p; + } + + public void parseOutput (SvcLogicContext ctx, String outMessage) throws SvcLogicException { + if (p.convertResponse) { + if (p.responseType == Format.NONE) { + classifyOutput(ctx, outMessage); + } else if (p.responseType == Format.JSON) { + classifyOutput(ctx, outMessage); + } else if (p.responseType == Format.XML) { + classifyOutput(ctx, outMessage, p.listNameList); + } + } + } + + private void classifyOutput(SvcLogicContext ctx, String outMessage, Set<String> listNameList) throws SvcLogicException { + Map<String, String> mm = XmlParser.convertToProperties(outMessage, p.listNameList); + toCtx (ctx, mm); + } + + private void toCtx (SvcLogicContext ctx, Map<String, String> mm) { + if (mm != null) { + for (Map.Entry<String, String> entry : mm.entrySet()) { + if (p.responsePrefix != null) { + ctx.setAttribute(p.responsePrefix + "." + entry.getKey(), entry.getValue()); + log.info("+++ " + p.responsePrefix + "." + entry.getKey() + ": [" + entry.getValue() + "]"); + } else { + ctx.setAttribute(entry.getKey(), entry.getValue()); + log.info("+++ " + entry.getKey() + ": [" + entry.getValue() + "]"); + } + } + } + } + + private void classifyOutput(SvcLogicContext ctx, String outMessage) throws SvcLogicException { + try { + Map<String, String> mm = JsonParser.convertToProperties(outMessage); + toCtx (ctx, mm); + } catch (org.codehaus.jettison.json.JSONException e) { + log.info("Output not in JSON format"); + putToProperties(ctx, p.responsePrefix); + } catch (Exception e) { + throw new SvcLogicException("error parsing response file"); + } + } + + private void putToProperties(SvcLogicContext ctx, String outMessage) throws SvcLogicException { + + try { + Properties prop = new Properties(); + prop.load(new ByteArrayInputStream(outMessage.getBytes(StandardCharsets.UTF_8))); + for (Object key : prop.keySet()) { + String name = (String) key; + String value = prop.getProperty(name); + if (value != null && value.trim().length() > 0) { + if (p.responsePrefix != null) { + ctx.setAttribute(p.responsePrefix + "." + name, value.trim()); + log.info("+++ " + p.responsePrefix + "." + name + ": [" + value + "]"); + } else { + ctx.setAttribute(name, value.trim()); + log.info("+++ " + name + ": [" + value + "]"); + } + } + } + } catch (Exception e) { + throw new SvcLogicException( "Error parsing response file."); + } + } + + public String makeCommand (Map<String, String> params) { + JSONObject jsonPayload = new JSONObject(); + final String[] optionalTestParams = {ENV_PARAMETERS_OPT_KEY, FILE_PARAMETERS_OPT_KEY}; + parseParam(params, optionalTestParams, jsonPayload); + JSONObject envParams = (JSONObject) jsonPayload.remove(ENV_PARAMETERS_OPT_KEY); + JSONObject fileParams = (JSONObject) jsonPayload.remove(FILE_PARAMETERS_OPT_KEY); + + StringBuilder constructedCommand = new StringBuilder(); + constructedCommand.append(parseFileParam(fileParams)).append(p.cmd).append(" ").append(parseEnvParam(envParams)); + + return constructedCommand.toString(); + } + + 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)); + } + } + 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("; "); + } + } + return fileParamBuilder.toString(); + } + + 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); + } + + private Set<String> getListNameList(Map<String, String> paramMap) { + Set<String> ll = new HashSet<>(); + for (Map.Entry<String,String> entry : paramMap.entrySet()) + if (entry.getKey().startsWith("listName")) + ll.add(entry.getValue()); + return ll; + } + + private String parseParam(Map<String, String> paramMap, String name, boolean required, String def) + throws SvcLogicException { + String s = paramMap.get(name); + + if (s == null || s.trim().length() == 0) { + if (!required) + return def; + throw new SvcLogicException("Parameter " + name + " is required in sshapiCallNode"); + } + + s = s.trim(); + StringBuilder value = new StringBuilder(); + int i = 0; + int i1 = s.indexOf('%'); + while (i1 >= 0) { + int i2 = s.indexOf('%', i1 + 1); + if (i2 < 0) + break; + + String varName = s.substring(i1 + 1, i2); + String varValue = System.getenv(varName); + if (varValue == null) + varValue = "%" + varName + "%"; + + value.append(s.substring(i, i1)); + value.append(varValue); + + i = i2 + 1; + i1 = s.indexOf('%', i); + } + value.append(s.substring(i)); + + log.info("Parameter {}: [{}]", name, value); + return value.toString(); + } +} diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryPolicy.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryPolicy.java deleted file mode 100644 index fef0f81a..00000000 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryPolicy.java +++ /dev/null @@ -1,62 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * openECOMP : SDN-C - * ================================================================================ - * * Copyright (C) 2017 AT&T Intellectual Property. - * ================================================================================ - * 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.plugins.sshapicall.model; - -public class RetryPolicy { - private String[] hostnames; - private Integer maximumRetries; - - public Integer getMaximumRetries() { - return maximumRetries; - } - - public String getNextHostName(String uri) throws RetryException { - Integer position = null; - - for (int i = 0; i < hostnames.length; i++) { - if (uri.contains(hostnames[i])) { - position = i; - break; - } - } - - if(position == null){ - throw new RetryException("No match found for the provided uri[" + uri + "] " + - "so the next host name could not be retreived"); - } - position++; - - if (position > hostnames.length - 1) { - position = 0; - } - return hostnames[position]; - } - - public RetryPolicy(String[] hostnames, Integer maximumRetries){ - this.hostnames = hostnames; - this.maximumRetries = maximumRetries; - } - - -} diff --git a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryPolicyStore.java b/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryPolicyStore.java deleted file mode 100644 index 65575b8c..00000000 --- a/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/RetryPolicyStore.java +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * openECOMP : SDN-C - * ================================================================================ - * * Copyright (C) 2017 AT&T Intellectual Property. - * ================================================================================ - * 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.plugins.sshapicall.model; - -import java.util.HashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RetryPolicyStore { - private static final Logger log = LoggerFactory.getLogger(RetryPolicyStore.class); - - HashMap<String, RetryPolicy> retryPolicies; - public String proxyServers; - - public String getProxyServers() { - return proxyServers; - } - - public void setProxyServers(String admServers) { - this.proxyServers = admServers; - String[] adminServersArray = admServers.split(","); - RetryPolicy adminPortalRetry = new RetryPolicy(adminServersArray, adminServersArray.length); - retryPolicies.put("dme2proxy", adminPortalRetry); - } - - public RetryPolicyStore() { - retryPolicies = new HashMap<>(); - } - - public RetryPolicy getRetryPolicy(String policyName) { - return (this.retryPolicies.get(policyName)); - } - -} diff --git a/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-context.xml b/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-context.xml index 4cce6ffa..50c9b2aa 100644 --- a/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-context.xml +++ b/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-context.xml @@ -40,12 +40,8 @@ <!-- context:property-placeholder location="file:${SDNC_CONFIG_DIR}/ueb.properties" /--> - <bean id="sshapiCallNode" class="org.onap.ccsdk.sli.plugins.sshapicall.impl.SshapiCallNodeImpl"> + <bean id="sshapiCallNode" class="org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode"> <property name="uebServers" value="${servers}" /> - <property name="retryPolicyStore" ref="retryPolicyStore"/> </bean> - <bean id="retryPolicyStore" class="org.onap.ccsdk.sli.plugins.sshapicall.model.RetryPolicyStore"> - <property name="proxyServers" value="${proxyUrl}" /> - </bean> </beans> diff --git a/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-osgi-context.xml b/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-osgi-context.xml index c8e9a99e..25631d6a 100644 --- a/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-osgi-context.xml +++ b/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-osgi-context.xml @@ -29,6 +29,6 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - <service ref="sshapiCallNode" interface="org.onap.ccsdk.sli.plugins.sshapicall.impl.SshapiCallNodeImpl" /> + <service ref="sshapiCallNode" interface="org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode" /> </beans:beans> diff --git a/sshapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/sshapi-call-node-blueprint.xml b/sshapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/sshapi-call-node-blueprint.xml index b24aaf1e..7a542432 100755 --- a/sshapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/sshapi-call-node-blueprint.xml +++ b/sshapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/sshapi-call-node-blueprint.xml @@ -3,7 +3,7 @@ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" odl:use-default-for-reference-types="true"> - <bean id="sshapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.sshapicall.impl.SshapiCallNodeImpl" /> + <bean id="sshapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode" /> diff --git a/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/sshapicall/TestJsonParser.java b/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/sshapicall/TestJsonParser.java index 9df790ae..66be7300 100644 --- a/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/sshapicall/TestJsonParser.java +++ b/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/sshapicall/TestJsonParser.java @@ -31,6 +31,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import org.codehaus.jettison.json.JSONException; import org.junit.Test; import org.onap.ccsdk.sli.core.sli.SvcLogicException; import org.onap.ccsdk.sli.plugins.sshapicall.model.JsonParser; @@ -50,8 +51,12 @@ public class TestJsonParser { String line; while ((line = in.readLine()) != null) b.append(line).append('\n'); - - Map<String, String> mm = JsonParser.convertToProperties(b.toString()); + Map<String, String> mm = null; + try { + mm = JsonParser.convertToProperties(b.toString()); + } catch (JSONException e){ + throw new SvcLogicException(e.getMessage()); + } logProperties(mm); @@ -60,7 +65,12 @@ public class TestJsonParser { @Test(expected = NullPointerException.class) public void testNullString() throws SvcLogicException { - JsonParser.convertToProperties(null); + Map<String, String> mm = null; + try { + mm = JsonParser.convertToProperties(null); + } catch (JSONException e){ + throw new SvcLogicException(e.getMessage()); + } } private void logProperties(Map<String, String> mm) { |