diff options
Diffstat (limited to 'appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap')
4 files changed, 341 insertions, 162 deletions
diff --git a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/AnsibleAdapterImpl.java b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/AnsibleAdapterImpl.java index ae0b8594f..fafe3deb1 100644 --- a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/AnsibleAdapterImpl.java +++ b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/AnsibleAdapterImpl.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * ONAP : APPC * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Copyright (C) 2017 Amdocs * ============================================================================= @@ -25,7 +25,9 @@ package org.onap.appc.adapter.ansible.impl; import java.util.Map; import java.util.Properties; - +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; import org.apache.commons.lang.StringUtils; import org.json.JSONException; import org.json.JSONObject; @@ -34,22 +36,20 @@ import org.onap.appc.adapter.ansible.model.AnsibleMessageParser; import org.onap.appc.adapter.ansible.model.AnsibleResult; import org.onap.appc.adapter.ansible.model.AnsibleResultCodes; import org.onap.appc.adapter.ansible.model.AnsibleServerEmulator; -import org.onap.appc.configuration.Configuration; -import org.onap.appc.configuration.ConfigurationFactory; import org.onap.appc.exceptions.APPCException; import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicException; import com.att.eelf.configuration.EELFLogger; import com.att.eelf.configuration.EELFManager; +import org.onap.appc.encryption.EncryptionTool; /** - * This class implements the {@link AnsibleAdapter} interface. This interface defines the behaviors - * that our service provides. + * This class implements the {@link AnsibleAdapter} interface. This interface + * defines the behaviors that our service provides. */ public class AnsibleAdapterImpl implements AnsibleAdapter { - /** * The constant used to define the service name in the mapped diagnostic context */ @@ -84,19 +84,22 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { private static final String CLIENT_TYPE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.clientType"; private static final String TRUSTSTORE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore"; private static final String TRUSTPASSWD_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore.trustPasswd"; - + private static final String TIMEOUT_PROPERTY_NAME = "org.onap.appc.adapter.ansible.timeout"; + private static final String POLL_INTERVAL_PROPERTY_NAME = "org.onap.appc.adapter.ansible.pollInterval"; + private static final String SOCKET_TIMEOUT_PROPERTY_NAME = "org.onap.appc.adapter.ansible.socketTimeout"; private static final String PASSWORD = "Password"; + private static final String APPC_PROPS = "/appc.properties"; + private static final String SDNC_CONFIG_DIR = "SDNC_CONFIG_DIR"; + private static final String propDir = System.getenv(SDNC_CONFIG_DIR); + private Properties props; + private int defaultTimeout = 600 * 1000; + private int defaultSocketTimeout = 60 * 1000; + private int defaultPollInterval = 60 * 1000; /** * The logger to be used */ private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class); - - /** - * A reference to the adapter configuration object. - */ - private Configuration configuration; - /** * Connection object **/ @@ -118,7 +121,8 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { private AnsibleServerEmulator testServer; /** - * This default constructor is used as a work around because the activator wasn't getting called + * This default constructor is used as a work around because the activator + * wasn't getting called */ public AnsibleAdapterImpl() { initialize(); @@ -145,8 +149,9 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { } /** - * @param rc Method posts info to Context memory in case of an error and throws a - * SvcLogicException causing SLI to register this as a failure + * @param rc + * 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 { @@ -159,25 +164,72 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { } /** - * initialize the Ansible adapter based on default and over-ride configuration data + * initialize the Ansible adapter based on default and over-ride configuration + * data */ private void initialize() { + String path = propDir + APPC_PROPS; + File propFile = new File(path); + props = new Properties(); + try { + InputStream input = new FileInputStream(propFile); + props.load(input); + } catch (Exception ex) { + logger.error("Error while reading appc.properties file" + ex.getMessage()); + } + // Create the message processor instance + messageProcessor = new AnsibleMessageParser(); + //continuing for checking timeout + try { + String timeoutStr = props.getProperty(TIMEOUT_PROPERTY_NAME); + defaultTimeout = Integer.parseInt(timeoutStr) * 1000; - configuration = ConfigurationFactory.getConfiguration(); - Properties props = configuration.getProperties(); + } catch (Exception e) { + defaultTimeout = 600 * 1000; + } + //continuing for checking timeout + try { + String timeoutStr = props.getProperty(SOCKET_TIMEOUT_PROPERTY_NAME); + defaultSocketTimeout = Integer.parseInt(timeoutStr) * 1000; - // Create the message processor instance - messageProcessor = new AnsibleMessageParser(); + } catch (Exception e) { + defaultSocketTimeout = 60 * 1000; + } + //continuing for checking timeout + try { + String timeoutStr = props.getProperty(POLL_INTERVAL_PROPERTY_NAME); + defaultPollInterval = Integer.parseInt(timeoutStr) * 1000; + + } catch (Exception e) { + defaultPollInterval = 60 * 1000; + } + logger.info("Initialized Ansible Adapter"); + } + + private ConnectionBuilder getHttpConn(int timeout, String serverIP) { + String path = propDir + APPC_PROPS; + File propFile = new File(path); + props = new Properties(); + InputStream input; + try { + input = new FileInputStream(propFile); + props.load(input); + } catch (Exception ex) { + // TODO Auto-generated catch block + logger.error("Error while reading appc.properties file" + ex.getMessage()); + } // Create the http client instance // type of client is extracted from the property file parameter // org.onap.appc.adapter.ansible.clientType // It can be : // 1. TRUST_ALL (trust all SSL certs). To be used ONLY in dev - // 2. TRUST_CERT (trust only those whose certificates have been stored in the trustStore file) - // 3. DEFAULT (trust only well known certificates). This is standard behavior to which it will + // 2. TRUST_CERT (trust only those whose certificates have been stored in the + // trustStore file) + // 3. DEFAULT (trust only well known certificates). This is standard behavior to + // which it will // revert. To be used in PROD - + ConnectionBuilder httpClient = null; try { String clientType = props.getProperty(CLIENT_TYPE_PROPERTY_NAME); logger.info("Ansible http client type set to " + clientType); @@ -185,30 +237,31 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { if ("TRUST_ALL".equals(clientType)) { logger.info( "Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments"); - httpClient = new ConnectionBuilder(1); + httpClient = new ConnectionBuilder(1, timeout); } else if ("TRUST_CERT".equals(clientType)) { // set path to keystore file String trustStoreFile = props.getProperty(TRUSTSTORE_PROPERTY_NAME); String key = props.getProperty(TRUSTPASSWD_PROPERTY_NAME); - char[] trustStorePasswd = key.toCharArray(); + char[] trustStorePasswd = EncryptionTool.getInstance().decrypt(key).toCharArray(); logger.info("Creating http client with trustmanager from " + trustStoreFile); - httpClient = new ConnectionBuilder(trustStoreFile, trustStorePasswd); + httpClient = new ConnectionBuilder(trustStoreFile, trustStorePasswd, timeout, serverIP); } else { logger.info("Creating http client with default behaviour"); - httpClient = new ConnectionBuilder(0); + httpClient = new ConnectionBuilder(0, timeout); } } catch (Exception e) { - logger.error("Error Initializing Ansible Adapter due to Unknown Exception", e); + logger.error("Error Getting HTTP Connection Builder due to Unknown Exception", e); } - logger.info("Initialized Ansible Adapter"); + logger.info("Got HTTP Connection Builder"); + return httpClient; } // Public Method to post request to execute playbook. Posts the following back // to Svc context memory - // org.onap.appc.adapter.ansible.req.code : 100 if successful - // org.onap.appc.adapter.ansible.req.messge : any message - // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request + // org.onap.appc.adapter.ansible.req.code : 100 if successful + // org.onap.appc.adapter.ansible.req.messge : any message + // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request @Override public void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { @@ -218,7 +271,7 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { String user = StringUtils.EMPTY; String password = StringUtils.EMPTY; String id = StringUtils.EMPTY; - + String timeout = StringUtils.EMPTY; JSONObject jsonPayload; try { @@ -227,10 +280,16 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { agentUrl = (String) jsonPayload.remove("AgentUrl"); user = (String) jsonPayload.remove("User"); - password = (String) jsonPayload.remove(PASSWORD); + password = (String)jsonPayload.remove(PASSWORD); + if(StringUtils.isNotBlank(password)) + password = EncryptionTool.getInstance().decrypt(password); id = jsonPayload.getString("Id"); + timeout = jsonPayload.getString("Timeout"); + if (StringUtils.isBlank(timeout)) + timeout = "600"; payload = jsonPayload.toString(); - logger.info("Updated Payload = " + payload); + ctx.setAttribute("AnsibleTimeout", timeout); + logger.info("Updated Payload = " + payload + " timeout = " + timeout); } catch (APPCException e) { logger.error(APPC_EXCEPTION_CAUGHT, e); doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), @@ -253,9 +312,9 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { try { // post the test request - logger.info("Posting request = " + payload + " to url = " + agentUrl); - AnsibleResult testResult = postExecRequest(agentUrl, payload, user, password); - + logger.info("Posting ansible request = " + payload + " to url = " + agentUrl); + AnsibleResult testResult = postExecRequest(agentUrl, payload, user, password,ctx); + logger.info("Received response on ansible post request " + testResult.getStatusMessage()); // Process if HTTP was successful if (testResult.getStatusCode() == 200) { testResult = messageProcessor.parsePostResponse(testResult.getStatusMessage()); @@ -263,10 +322,16 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { doFailure(ctx, testResult.getStatusCode(), "Error posting request. Reason = " + testResult.getStatusMessage()); } - + String output = StringUtils.EMPTY; code = testResult.getStatusCode(); message = testResult.getStatusMessage(); - + output = testResult.getOutput(); + ctx.setAttribute(OUTPUT_ATTRIBUTE_NAME, output); + String serverIp = testResult.getServerIp(); + if (StringUtils.isBlank(serverIp)) + ctx.setAttribute("ServerIP", serverIp); + else + ctx.setAttribute("ServerIP", ""); // Check status of test request returned by Agent if (code == AnsibleResultCodes.PENDING.getValue()) { logger.info(String.format("Submission of Test %s successful.", playbookName)); @@ -286,8 +351,8 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { } /** - * Public method to query status of a specific request It blocks till the Ansible Server - * responds or the session times out (non-Javadoc) + * Public method to query status of a specific request It blocks till the + * Ansible Server responds or the session times out (non-Javadoc) * * @see org.onap.appc.adapter.ansible.AnsibleAdapter#reqExecResult(java.util.Map, * org.onap.ccsdk.sli.core.sli.SvcLogicContext) @@ -299,8 +364,12 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { String reqUri = StringUtils.EMPTY; try { - reqUri = messageProcessor.reqUriResult(params); - logger.info("Got uri ", reqUri ); + String serverIp = ctx.getAttribute("ServerIP"); + if (StringUtils.isNotBlank(serverIp)) + reqUri = messageProcessor.reqUriResultWithIP(params, serverIp); + else + reqUri = messageProcessor.reqUriResult(params); + logger.info("Got uri " + reqUri); } catch (APPCException e) { logger.error(APPC_EXCEPTION_CAUGHT, e); doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), @@ -319,23 +388,23 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { String message = StringUtils.EMPTY; String results = StringUtils.EMPTY; String output = StringUtils.EMPTY; - try { // Try to retrieve the test results (modify the URL for that) - AnsibleResult testResult = queryServer(reqUri, params.get("User"), params.get(PASSWORD)); + AnsibleResult testResult = queryServer(reqUri, params.get("User"), + EncryptionTool.getInstance().decrypt(params.get(PASSWORD)), ctx); code = testResult.getStatusCode(); message = testResult.getStatusMessage(); if (code == 200 || code == 400 || "FINISHED".equalsIgnoreCase(message)) { - logger.info("Parsing response from Server = " + message); + logger.info("Parsing response from ansible Server = " + message); // Valid HTTP. process the Ansible message testResult = messageProcessor.parseGetResponse(message); code = testResult.getStatusCode(); message = testResult.getStatusMessage(); results = testResult.getResults(); output = testResult.getOutput(); + ctx.setAttribute(OUTPUT_ATTRIBUTE_NAME, output); } - logger.info("Request response = " + message); } catch (APPCException e) { doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), @@ -369,8 +438,9 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { /** * Public method to get logs from playbook execution for a specific request * - * It blocks till the Ansible Server responds or the session times out very similar to - * reqExecResult logs are returned in the DG context variable org.onap.appc.adapter.ansible.log + * It blocks till the Ansible Server responds or the session times out very + * similar to reqExecResult logs are returned in the DG context variable + * org.onap.appc.adapter.ansible.log */ @Override public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { @@ -387,7 +457,8 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { String message = StringUtils.EMPTY; try { // Try to retrieve the test results (modify the url for that) - AnsibleResult testResult = queryServer(reqUri, params.get("User"), params.get(PASSWORD)); + AnsibleResult testResult = queryServer(reqUri, params.get("User"), + EncryptionTool.getInstance().decrypt(params.get(PASSWORD)), ctx); message = testResult.getStatusMessage(); logger.info("Request output = " + message); ctx.setAttribute(LOG_ATTRIBUTE_NAME, message); @@ -402,13 +473,15 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { /** * Method that posts the request */ - private AnsibleResult postExecRequest(String agentUrl, String payload, String user, String password) { + private AnsibleResult postExecRequest(String agentUrl, String payload, String user, String password, + SvcLogicContext ctx) { AnsibleResult testResult; - + ConnectionBuilder httpClient = getHttpConn(defaultSocketTimeout, ""); if (!testMode) { httpClient.setHttpContext(user, password); testResult = httpClient.post(agentUrl, payload); + httpClient.close(); } else { testResult = testServer.Post(agentUrl, payload); } @@ -418,16 +491,45 @@ public class AnsibleAdapterImpl implements AnsibleAdapter { /** * Method to query Ansible server */ - private AnsibleResult queryServer(String agentUrl, String user, String password) { + private AnsibleResult queryServer(String agentUrl, String user, String password, SvcLogicContext ctx) { - AnsibleResult testResult; + AnsibleResult testResult = new AnsibleResult(); + int timeout = 600 * 1000; + try { + timeout = Integer.parseInt(ctx.getAttribute("AnsibleTimeout")) * 1000; - logger.info("Querying url = " + agentUrl); + } catch (Exception e) { + timeout = defaultTimeout; + } + long endTime = System.currentTimeMillis() + timeout; - if (!testMode) { - testResult = httpClient.get(agentUrl); - } else { - testResult = testServer.Get(agentUrl); + while (System.currentTimeMillis() < endTime) { + String serverIP = ctx.getAttribute("ServerIP"); + ConnectionBuilder httpClient = getHttpConn(defaultSocketTimeout, serverIP); + logger.info("Querying ansible GetResult URL = " + agentUrl); + + if (!testMode) { + httpClient.setHttpContext(user, password); + testResult = httpClient.get(agentUrl); + httpClient.close(); + } else { + testResult = testServer.Get(agentUrl); + } + if (testResult.getStatusCode() != AnsibleResultCodes.IO_EXCEPTION.getValue() + && testResult.getStatusCode() != AnsibleResultCodes.PENDING.getValue()) { + break; + } + + try { + Thread.sleep(defaultPollInterval); + } catch (InterruptedException ex) { + + } + + } + if (testResult.getStatusCode() == AnsibleResultCodes.PENDING.getValue()) { + testResult.setStatusCode(AnsibleResultCodes.IO_EXCEPTION.getValue()); + testResult.setStatusMessage("Request timed out"); } return testResult; diff --git a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/ConnectionBuilder.java b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/ConnectionBuilder.java index 902ae59b4..77c9af680 100644 --- a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/ConnectionBuilder.java +++ b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/impl/ConnectionBuilder.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * ONAP : APPC * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Copyright (C) 2017 Amdocs * ================================================================================ @@ -25,6 +25,7 @@ package org.onap.appc.adapter.ansible.impl; +import java.io.Closeable; import java.io.FileInputStream; import java.io.IOException; import java.security.KeyManagementException; @@ -34,14 +35,16 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import javax.net.ssl.SSLContext;; +import javax.net.ssl.SSLContext; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; @@ -54,18 +57,20 @@ import org.onap.appc.adapter.ansible.model.AnsibleResult; import org.onap.appc.adapter.ansible.model.AnsibleResultCodes; import com.att.eelf.configuration.EELFLogger; import com.att.eelf.configuration.EELFManager; +import org.json.JSONObject; +import javax.net.ssl.SSLException; +import org.onap.appc.exceptions.APPCException; +import org.apache.commons.lang.StringUtils; /** - * Returns a custom http 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 httpclient (which only trusts known CAs from default cacerts file for process) this is the default - * option + * Returns a custom http 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 httpclient (which only trusts + * known CAs from default cacerts file for process) this is the default option **/ -public class ConnectionBuilder { - +public class ConnectionBuilder implements Closeable { + private static final String STATUS_CODE_KEY = "StatusCode"; private static final EELFLogger logger = EELFManager.getInstance().getLogger(ConnectionBuilder.class); private CloseableHttpClient httpClient = null; @@ -74,8 +79,10 @@ public class ConnectionBuilder { /** * Constructor that initializes an http client based on certificate **/ - public ConnectionBuilder(String certFile) throws KeyStoreException, CertificateException, IOException, - KeyManagementException, NoSuchAlgorithmException { + + + public ConnectionBuilder(String certFile, int timeout) throws KeyStoreException, CertificateException, IOException, + KeyManagementException, NoSuchAlgorithmException{ /* Point to the certificate */ try(FileInputStream fs = new FileInputStream(certFile)) { @@ -93,43 +100,55 @@ public class ConnectionBuilder { SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - httpClient = HttpClients.custom().setSSLSocketFactory(factory).build(); + RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build(); + httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build(); } } /** - * Constructor which trusts all certificates in a specific java keystore file (assumes a JKS - * file) + * Constructor which trusts all certificates in a specific java keystore file + * (assumes a JKS file) **/ - public ConnectionBuilder(String trustStoreFile, char[] trustStorePasswd) throws KeyStoreException, IOException, - KeyManagementException, NoSuchAlgorithmException, CertificateException { + public ConnectionBuilder(String trustStoreFile, char[] trustStorePasswd, int timeout, String serverIP) + throws KeyStoreException, IOException, KeyManagementException, NoSuchAlgorithmException, + CertificateException ,APPCException{ /* Load the specified trustStore */ KeyStore keystore = KeyStore.getInstance("JKS"); FileInputStream readStream = new FileInputStream(trustStoreFile); keystore.load(readStream, trustStorePasswd); + if (StringUtils.isNotBlank(serverIP)) { + SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(); + SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, new NoopHostnameVerifier()); - SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keystore).build(); - SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, - SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build(); + httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build(); + } else { + SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keystore).build(); + SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build(); + httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build(); + } - httpClient = HttpClients.custom().setSSLSocketFactory(factory).build(); } /** - * Constructor that trusts ALL SSl certificates (NOTE : ONLY FOR DEV TESTING) if Mode == 1 or - * Default if Mode == 0 + * Constructor that trusts ALL SSl certificates (NOTE : ONLY FOR DEV TESTING) if + * Mode == 1 or Default if Mode == 0 */ - public ConnectionBuilder(int mode) - throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + + public ConnectionBuilder(int mode, int timeout) + throws SSLException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException,APPCException{ + RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build(); if (mode == 1) { SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(); SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - httpClient = HttpClients.custom().setSSLSocketFactory(factory).build(); + httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build(); } else { - httpClient = HttpClients.createDefault(); + httpClient = HttpClients.custom().setDefaultRequestConfig(config).build(); } } @@ -187,7 +206,16 @@ public class ConnectionBuilder { HttpEntity entity = response.getEntity(); String responseOutput = entity != null ? EntityUtils.toString(entity) : null; int responseCode = response.getStatusLine().getStatusCode(); - result.setStatusCode(responseCode); + logger.info("GetResult response for ansible GET URL" + agentUrl + " returned " + responseOutput); + JSONObject postResponse = new JSONObject(responseOutput); + if (postResponse.has(STATUS_CODE_KEY)) { + int codeStatus = postResponse.getInt(STATUS_CODE_KEY); + if (codeStatus == AnsibleResultCodes.PENDING.getValue()) + result.setStatusCode(codeStatus); + else + result.setStatusCode(responseCode); + } else + result.setStatusCode(responseCode); result.setStatusMessage(responseOutput); } catch (IOException io) { result.setStatusCode(AnsibleResultCodes.IO_EXCEPTION.getValue()); @@ -196,4 +224,16 @@ public class ConnectionBuilder { } return result; } + + @Override + public void close() { + if (httpClient != null) { + try { + httpClient.close(); + } catch (IOException e) { + logger.error("Caught IOException during httpClient close", e); + } + } + } + } diff --git a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleMessageParser.java b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleMessageParser.java index 5f6259c03..77738d7dd 100644 --- a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleMessageParser.java +++ b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleMessageParser.java @@ -40,22 +40,22 @@ import org.onap.appc.exceptions.APPCException; import com.google.common.base.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.commons.lang.StringUtils; /** - * Class that validates and constructs requests sent/received from - * Ansible Server + * Class that validates and constructs requests sent/received from Ansible + * Server */ public class AnsibleMessageParser { private static final String STATUS_MESSAGE_KEY = "StatusMessage"; private static final String STATUS_CODE_KEY = "StatusCode"; - + private static final String SERVER_IP_KEY = "AnsibleServer"; private static final String PLAYBOOK_NAME_KEY = "PlaybookName"; private static final String AGENT_URL_KEY = "AgentUrl"; private static final String PASS_KEY = "Password"; private static final String USER_KEY = "User"; private static final String ID_KEY = "Id"; - private static final String LOCAL_PARAMETERS_OPT_KEY = "LocalParameters"; private static final String FILE_PARAMETERS_OPT_KEY = "FileParameters"; private static final String ENV_PARAMETERS_OPT_KEY = "EnvParameters"; @@ -67,20 +67,19 @@ public class AnsibleMessageParser { private static final Logger LOGGER = LoggerFactory.getLogger(AnsibleMessageParser.class); /** - * Accepts a map of strings and - * a) validates if all parameters are appropriate (else, throws an exception) and - * b) if correct returns a JSON object with appropriate key-value pairs to send to the server. + * Accepts a map of strings and a) validates if all parameters are appropriate + * (else, throws an exception) and b) if correct returns a JSON object with + * appropriate key-value pairs to send to the server. * - * Mandatory parameters, that must be in the supplied information to the Ansible Adapter - * 1. URL to connect to - * 2. credentials for URL (assume username password for now) - * 3. Playbook name + * Mandatory parameters, that must be in the supplied information to the Ansible + * Adapter 1. URL to connect to 2. credentials for URL (assume username password + * for now) 3. Playbook name * */ public JSONObject reqMessage(Map<String, String> params) throws APPCException { - final String[] mandatoryTestParams = {AGENT_URL_KEY, PLAYBOOK_NAME_KEY, USER_KEY, PASS_KEY}; - final String[] optionalTestParams = {ENV_PARAMETERS_OPT_KEY, NODE_LIST_OPT_KEY, LOCAL_PARAMETERS_OPT_KEY, - TIMEOUT_OPT_KEY, VERSION_OPT_KEY, FILE_PARAMETERS_OPT_KEY, ACTION_OPT_KEY}; + final String[] mandatoryTestParams = { AGENT_URL_KEY, PLAYBOOK_NAME_KEY, USER_KEY, PASS_KEY }; + final String[] optionalTestParams = { ENV_PARAMETERS_OPT_KEY, NODE_LIST_OPT_KEY, LOCAL_PARAMETERS_OPT_KEY, + TIMEOUT_OPT_KEY, VERSION_OPT_KEY, FILE_PARAMETERS_OPT_KEY, ACTION_OPT_KEY }; JSONObject jsonPayload = new JSONObject(); @@ -99,13 +98,13 @@ public class AnsibleMessageParser { } /** - * Method that validates that the Map has enough information - * to query Ansible server for a result. If so, it returns - * the appropriate url, else an empty string. + * Method that validates that the Map has enough information to query Ansible + * server for a result. If so, it returns the appropriate url, else an empty + * string. */ public String reqUriResult(Map<String, String> params) throws APPCException { - final String[] mandatoryTestParams = {AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY}; + final String[] mandatoryTestParams = { AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY }; for (String key : mandatoryTestParams) { throwIfMissingMandatoryParam(params, key); @@ -114,13 +113,30 @@ public class AnsibleMessageParser { } /** - * Method that validates that the Map has enough information - * to query Ansible server for logs. If so, it populates the appropriate - * returns the appropriate url, else an empty string. + * Method that validates that the Map has enough information to query Ansible + * server for a result. If so, it returns the appropriate url, else an empty + * string. + */ + public String reqUriResultWithIP(Map<String, String> params, String serverIP) throws APPCException { + + final String[] mandatoryTestParams = { AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY }; + + for (String key : mandatoryTestParams) { + throwIfMissingMandatoryParam(params, key); + } + String[] arr1 = params.get(AGENT_URL_KEY).split("//", 2); + String[] arr2 = arr1[1].split(":", 2); + return arr1[0] + "//" + serverIP + ":" + arr2[1] + "?Id=" + params.get(ID_KEY) + "&Type=GetResult"; + } + + /** + * Method that validates that the Map has enough information to query Ansible + * server for logs. If so, it populates the appropriate returns the appropriate + * url, else an empty string. */ public String reqUriLog(Map<String, String> params) throws APPCException { - final String[] mandatoryTestParams = {AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY}; + final String[] mandatoryTestParams = { AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY }; for (String mandatoryParam : mandatoryTestParams) { throwIfMissingMandatoryParam(params, mandatoryParam); @@ -129,8 +145,8 @@ public class AnsibleMessageParser { } /** - * This method parses response from the Ansible Server when we do a post - * and returns an AnsibleResult object. + * This method parses response from the Ansible Server when we do a post and + * returns an AnsibleResult object. */ public AnsibleResult parsePostResponse(String input) throws APPCException { AnsibleResult ansibleResult; @@ -139,7 +155,11 @@ public class AnsibleMessageParser { int code = postResponse.getInt(STATUS_CODE_KEY); String msg = postResponse.getString(STATUS_MESSAGE_KEY); - + String serverIP = ""; + if (postResponse.has(SERVER_IP_KEY)) + serverIP = postResponse.getString(SERVER_IP_KEY); + else + serverIP = ""; int initResponseValue = AnsibleResultCodes.INITRESPONSE.getValue(); boolean validCode = AnsibleResultCodes.CODE.checkValidCode(initResponseValue, code); if (!validCode) { @@ -148,6 +168,14 @@ public class AnsibleMessageParser { } ansibleResult = new AnsibleResult(code, msg); + if (StringUtils.isNotBlank(serverIP)) + ansibleResult.setServerIp(serverIP); + + if (!postResponse.isNull("Output")) { + LOGGER.info("Processing results-output in post response"); + JSONObject output = postResponse.getJSONObject("Output"); + ansibleResult.setOutput(output.toString()); + } } catch (JSONException e) { LOGGER.error("JSONException: Error parsing response", e); @@ -157,8 +185,8 @@ public class AnsibleMessageParser { } /** - * This method parses response from an Ansible server when we do a GET for a result - * and returns an AnsibleResult object. + * This method parses response from an Ansible server when we do a GET for a + * result and returns an AnsibleResult object. **/ public AnsibleResult parseGetResponse(String input) throws APPCException { @@ -175,14 +203,14 @@ public class AnsibleMessageParser { return ansibleResult; } - private AnsibleResult parseGetResponseNested(AnsibleResult ansibleResult, JSONObject postRsp) throws APPCException { + private AnsibleResult parseGetResponseNested(AnsibleResult ansibleResult, JSONObject postRsp) throws APPCException { int codeStatus = postRsp.getInt(STATUS_CODE_KEY); String messageStatus = postRsp.getString(STATUS_MESSAGE_KEY); int finalCode = AnsibleResultCodes.FINAL_SUCCESS.getValue(); - boolean valCode = - AnsibleResultCodes.CODE.checkValidCode(AnsibleResultCodes.FINALRESPONSE.getValue(), codeStatus); + boolean valCode = AnsibleResultCodes.CODE.checkValidCode(AnsibleResultCodes.FINALRESPONSE.getValue(), + codeStatus); if (!valCode) { throw new APPCException("Invalid FinalResponse code = " + codeStatus + " received. MUST be one of " @@ -206,8 +234,8 @@ public class AnsibleMessageParser { while (hosts.hasNext()) { String host = hosts.next(); - LOGGER.info("Processing host = {}", host); - + LOGGER.info("Processing host = {}", + (host.matches("^[\\w\\-.]+$")) ? host : "[unexpected value, logging suppressed]"); try { JSONObject hostResponse = results.getJSONObject(host); int subCode = hostResponse.getInt(STATUS_CODE_KEY); @@ -221,8 +249,8 @@ public class AnsibleMessageParser { } catch (JSONException e) { LOGGER.error("JSONException: Error parsing response", e); ansibleResult.setStatusCode(AnsibleResultCodes.INVALID_RESPONSE.getValue()); - ansibleResult.setStatusMessage(String.format( - "Error processing response message = %s from host %s", results.getString(host), host)); + ansibleResult.setStatusMessage(String.format("Error processing response message = %s from host %s", + results.getString(host), host)); break; } } @@ -236,7 +264,7 @@ public class AnsibleMessageParser { ansibleResult.setStatusCode(AnsibleResultCodes.INVALID_RESPONSE.getValue()); ansibleResult.setStatusMessage("Results not found in GET for response"); } - if(!postRsp.isNull("Output")) { + if (!postRsp.isNull("Output")) { LOGGER.info("Processing results-output in response"); JSONObject output = postRsp.getJSONObject("Output"); ansibleResult.setOutput(output.toString()); @@ -250,13 +278,11 @@ public class AnsibleMessageParser { 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 -> parseOptionalParam(entry, jsonPayload)); - //@formatter:on + // @formatter:off + params.entrySet().stream().filter(entry -> optionalParamsSet.contains(entry.getKey())) + .filter(entry -> !Strings.isNullOrEmpty(entry.getValue())) + .forEach(entry -> parseOptionalParam(entry, jsonPayload)); + // @formatter:on } private void parseOptionalParam(Map.Entry<String, String> params, JSONObject jsonPayload) { @@ -264,35 +290,35 @@ public class AnsibleMessageParser { String payload = params.getValue(); switch (key) { - case TIMEOUT_OPT_KEY: - int timeout = Integer.parseInt(payload); - if (timeout < 0) { - throw new NumberFormatException(" : specified negative integer for timeout = " + payload); - } - jsonPayload.put(key, payload); - break; - - case VERSION_OPT_KEY: - jsonPayload.put(key, payload); - break; - - case LOCAL_PARAMETERS_OPT_KEY: - case ENV_PARAMETERS_OPT_KEY: - JSONObject paramsJson = new JSONObject(payload); - jsonPayload.put(key, paramsJson); - break; - - case NODE_LIST_OPT_KEY: - JSONArray paramsArray = new JSONArray(payload); - jsonPayload.put(key, paramsArray); - break; - - case FILE_PARAMETERS_OPT_KEY: - jsonPayload.put(key, getFilePayload(payload)); - break; - - default: - break; + case TIMEOUT_OPT_KEY: + int timeout = Integer.parseInt(payload); + if (timeout < 0) { + throw new NumberFormatException(" : specified negative integer for timeout = " + payload); + } + jsonPayload.put(key, payload); + break; + + case VERSION_OPT_KEY: + jsonPayload.put(key, payload); + break; + + case LOCAL_PARAMETERS_OPT_KEY: + case ENV_PARAMETERS_OPT_KEY: + JSONObject paramsJson = new JSONObject(payload); + jsonPayload.put(key, paramsJson); + break; + + case NODE_LIST_OPT_KEY: + JSONArray paramsArray = new JSONArray(payload); + jsonPayload.put(key, paramsArray); + break; + + case FILE_PARAMETERS_OPT_KEY: + jsonPayload.put(key, getFilePayload(payload)); + break; + + default: + break; } } diff --git a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleResult.java b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleResult.java index de6b180ef..b67f3c74b 100644 --- a/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleResult.java +++ b/appc-adapters/appc-ansible-adapter/appc-ansible-adapter-bundle/src/main/java/org/onap/appc/adapter/ansible/model/AnsibleResult.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * ONAP : APPC * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Copyright (C) 2017 Amdocs * ============================================================================= @@ -34,6 +34,7 @@ public class AnsibleResult { private String statusMessage; private String results; private String output; + private String serverIp; public AnsibleResult() { this(-1, EMPTY_VALUE, EMPTY_VALUE); @@ -75,6 +76,10 @@ public class AnsibleResult { public void setResults(String results) { this.results = results; } + + public void setServerIp(String serverIp) { + this.serverIp = serverIp; + } void set(int code, String message, String results, String output) { this.statusCode = code; @@ -95,4 +100,10 @@ public class AnsibleResult { public String getResults() { return this.results; } + + public String getServerIp() { + return this.serverIp; + } + + } |