2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
25 package org.onap.ccsdk.sli.adaptors.ansible.impl;
28 import java.util.Properties;
29 import org.apache.commons.lang.StringUtils;
30 import org.json.JSONException;
31 import org.json.JSONObject;
32 import org.onap.ccsdk.sli.adaptors.ansible.AnsibleAdapter;
33 import org.onap.ccsdk.sli.adaptors.ansible.AnsibleAdapterPropertiesProvider;
34 import org.onap.ccsdk.sli.adaptors.ansible.model.AnsibleMessageParser;
35 import org.onap.ccsdk.sli.adaptors.ansible.model.AnsibleResult;
36 import org.onap.ccsdk.sli.adaptors.ansible.model.AnsibleResultCodes;
37 import org.onap.ccsdk.sli.adaptors.ansible.model.AnsibleServerEmulator;
38 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
39 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
40 import com.att.eelf.configuration.EELFLogger;
41 import com.att.eelf.configuration.EELFManager;
44 * This class implements the {@link AnsibleAdapter} interface. This interface defines the behaviors
45 * that our service provides.
47 public class AnsibleAdapterImpl implements AnsibleAdapter {
51 * The constant used to define the service name in the mapped diagnostic context
53 @SuppressWarnings("nls")
54 public static final String MDC_SERVICE = "service";
57 * The constant for the status code for a failed outcome
59 @SuppressWarnings("nls")
60 public static final String OUTCOME_FAILURE = "failure";
63 * The constant for the status code for a successful outcome
65 @SuppressWarnings("nls")
66 public static final String OUTCOME_SUCCESS = "success";
71 private static final String ADAPTER_NAME = "Ansible Adapter";
72 private static final String APPC_EXCEPTION_CAUGHT = "APPCException caught";
74 private static final String RESULT_CODE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.result.code";
75 private static final String MESSAGE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.message";
76 private static final String RESULTS_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.results";
77 private static final String ID_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.Id";
78 private static final String LOG_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.log";
79 private static final String OUTPUT_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.output";
81 private static final String CLIENT_TYPE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.clientType";
82 private static final String TRUSTSTORE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore";
83 private static final String TRUSTPASSD_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore.trustPasswd";
85 private static final String PASSD = "Password";
88 * The logger to be used
90 private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
96 private ConnectionBuilder httpClient;
99 * Ansible API Message Handlers
101 private AnsibleMessageParser messageProcessor;
104 * indicator whether in test mode
106 private boolean testMode = false;
109 * server emulator object to be used if in test mode
111 private AnsibleServerEmulator testServer;
114 * This default constructor is used as a work around because the activator wasn't getting called
116 public AnsibleAdapterImpl() {
117 initialize(new AnsibleAdapterPropertiesProviderImpl());
119 public AnsibleAdapterImpl(AnsibleAdapterPropertiesProvider propProvider) {
120 initialize(propProvider);
124 * Used for jUnit test and testing interface
126 public AnsibleAdapterImpl(boolean mode) {
128 testServer = new AnsibleServerEmulator();
129 messageProcessor = new AnsibleMessageParser();
133 * Returns the symbolic name of the adapter
135 * @return The adapter name
136 * @see org.onap.appc.adapter.rest.AnsibleAdapter#getAdapterName()
139 public String getAdapterName() {
144 * @param rc Method posts info to Context memory in case of an error and throws a
145 * SvcLogicException causing SLI to register this as a failure
147 @SuppressWarnings("static-method")
148 private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
150 svcLogic.setStatus(OUTCOME_FAILURE);
151 svcLogic.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
152 svcLogic.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
154 throw new SvcLogicException("Ansible Adapter Error = " + message);
158 * initialize the Ansible adapter based on default and over-ride configuration data
160 private void initialize(AnsibleAdapterPropertiesProvider propProvider) {
163 Properties props = propProvider.getProperties();
165 // Create the message processor instance
166 messageProcessor = new AnsibleMessageParser();
168 // Create the http client instance
169 // type of client is extracted from the property file parameter
170 // org.onap.appc.adapter.ansible.clientType
172 // 1. TRUST_ALL (trust all SSL certs). To be used ONLY in dev
173 // 2. TRUST_CERT (trust only those whose certificates have been stored in the trustStore file)
174 // 3. DEFAULT (trust only well known certificates). This is standard behavior to which it will
175 // revert. To be used in PROD
178 String clientType = props.getProperty(CLIENT_TYPE_PROPERTY_NAME);
179 logger.info("Ansible http client type set to " + clientType);
181 if ("TRUST_ALL".equals(clientType)) {
183 "Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
184 httpClient = new ConnectionBuilder(1);
185 } else if ("TRUST_CERT".equals(clientType)) {
186 // set path to keystore file
187 String trustStoreFile = props.getProperty(TRUSTSTORE_PROPERTY_NAME);
188 String key = props.getProperty(TRUSTPASSD_PROPERTY_NAME);
189 char[] trustStorePasswd = key.toCharArray();
190 logger.info("Creating http client with trustmanager from " + trustStoreFile);
191 httpClient = new ConnectionBuilder(trustStoreFile, trustStorePasswd);
193 logger.info("Creating http client with default behaviour");
194 httpClient = new ConnectionBuilder(0);
196 } catch (Exception e) {
197 logger.error("Error Initializing Ansible Adapter due to Unknown Exception", e);
200 logger.info("Initialized Ansible Adapter");
203 // Public Method to post request to execute playbook. Posts the following back
204 // to Svc context memory
205 // org.onap.appc.adapter.ansible.req.code : 100 if successful
206 // org.onap.appc.adapter.ansible.req.messge : any message
207 // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request
209 public void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
211 String playbookName = StringUtils.EMPTY;
212 String payload = StringUtils.EMPTY;
213 String agentUrl = StringUtils.EMPTY;
214 String user = StringUtils.EMPTY;
215 String password = StringUtils.EMPTY;
216 String id = StringUtils.EMPTY;
218 JSONObject jsonPayload;
221 // create json object to send request
222 jsonPayload = messageProcessor.reqMessage(params);
224 agentUrl = (String) jsonPayload.remove("AgentUrl");
225 user = (String) jsonPayload.remove("User");
226 password = (String) jsonPayload.remove(PASSD);
227 id = jsonPayload.getString("Id");
228 payload = jsonPayload.toString();
229 logger.info("Updated Payload = " + payload);
230 } catch (SvcLogicException e) {
231 logger.error(APPC_EXCEPTION_CAUGHT, e);
232 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
233 "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = "
235 } catch (JSONException e) {
236 logger.error("JSONException caught", e);
237 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
238 "Error constructing request for execution of playbook due to invalid JSON block. Reason = "
240 } catch (NumberFormatException e) {
241 logger.error("NumberFormatException caught", e);
242 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
243 "Error constructing request for execution of playbook due to invalid parameter values. Reason = "
248 String message = StringUtils.EMPTY;
251 // post the test request
252 logger.info("Posting request = " + payload + " to url = " + agentUrl);
253 AnsibleResult testResult = postExecRequest(agentUrl, payload, user, password);
255 // Process if HTTP was successful
256 if (testResult.getStatusCode() == 200) {
257 testResult = messageProcessor.parsePostResponse(testResult.getStatusMessage());
259 doFailure(ctx, testResult.getStatusCode(),
260 "Error posting request. Reason = " + testResult.getStatusMessage());
263 code = testResult.getStatusCode();
264 message = testResult.getStatusMessage();
266 // Check status of test request returned by Agent
267 if (code == AnsibleResultCodes.PENDING.getValue()) {
268 logger.info(String.format("Submission of Test %s successful.", playbookName));
269 // test request accepted. We are in asynchronous case
271 doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
273 } catch (SvcLogicException e) {
274 logger.error(APPC_EXCEPTION_CAUGHT, e);
275 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
276 "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
279 ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
280 ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
281 ctx.setAttribute(ID_ATTRIBUTE_NAME, id);
285 * Public method to query status of a specific request It blocks till the Ansible Server
286 * responds or the session times out (non-Javadoc)
288 * @see org.onap.ccsdk.sli.adaptors.ansible.AnsibleAdapter#reqExecResult(java.util.Map,
289 * org.onap.ccsdk.sli.core.sli.SvcLogicContext)
292 public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
295 String reqUri = StringUtils.EMPTY;
298 reqUri = messageProcessor.reqUriResult(params);
299 logger.info("Got uri ", reqUri );
300 } catch (SvcLogicException e) {
301 logger.error(APPC_EXCEPTION_CAUGHT, e);
302 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
303 "Error constructing request to retrieve result due to missing parameters. Reason = "
306 } catch (NumberFormatException e) {
307 logger.error("NumberFormatException caught", e);
308 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
309 "Error constructing request to retrieve result due to invalid parameters value. Reason = "
315 String message = StringUtils.EMPTY;
316 String results = StringUtils.EMPTY;
319 // Try to retrieve the test results (modify the URL for that)
320 AnsibleResult testResult = queryServer(reqUri, params.get("User"), params.get(PASSD));
321 code = testResult.getStatusCode();
322 message = testResult.getStatusMessage();
325 logger.info("Parsing response from Server = " + message);
326 // Valid HTTP. process the Ansible message
327 testResult = messageProcessor.parseGetResponse(message);
328 code = testResult.getStatusCode();
329 message = testResult.getStatusMessage();
330 results = testResult.getResults();
333 logger.info("Request response = " + message);
334 } catch (SvcLogicException e) {
335 logger.error(APPC_EXCEPTION_CAUGHT, e);
336 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
337 "Exception encountered retrieving result : " + e.getMessage());
341 // We were able to get and process the results. Determine if playbook succeeded
343 if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()) {
344 message = String.format("Ansible Request %s finished with Result = %s, Message = %s", params.get("Id"),
345 OUTCOME_SUCCESS, message);
346 logger.info(message);
348 logger.info(String.format("Ansible Request %s finished with Result %s, Message = %s", params.get("Id"),
349 OUTCOME_FAILURE, message));
350 ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
351 doFailure(ctx, code, message);
355 ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(400));
356 ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
357 ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
358 ctx.setStatus(OUTCOME_SUCCESS);
362 * Public method to get logs from playbook execution for a specific request
364 * It blocks till the Ansible Server responds or the session times out very similar to
365 * reqExecResult logs are returned in the DG context variable org.onap.appc.adapter.ansible.log
368 public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
370 String reqUri = StringUtils.EMPTY;
372 reqUri = messageProcessor.reqUriLog(params);
373 logger.info("Retrieving results from " + reqUri);
374 } catch (Exception e) {
375 logger.error("Exception caught", e);
376 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
379 String message = StringUtils.EMPTY;
381 // Try to retrieve the test results (modify the url for that)
382 AnsibleResult testResult = queryServer(reqUri, params.get("User"), params.get(PASSD));
383 message = testResult.getStatusMessage();
384 logger.info("Request output = " + message);
385 ctx.setAttribute(LOG_ATTRIBUTE_NAME, message);
386 ctx.setStatus(OUTCOME_SUCCESS);
387 } catch (Exception e) {
388 logger.error("Exception caught", e);
389 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
390 "Exception encountered retreiving output : " + e.getMessage());
395 * Public method to get output from playbook execution for a specific request
397 * It blocks till the Ansible Server responds or the session times out very similar to
398 * reqExecResult and output is returned in the DG context variable org.onap.appc.adapter.ansible.output
401 public void reqExecOutput(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
403 String reqUri = StringUtils.EMPTY;
405 reqUri = messageProcessor.reqUriOutput(params);
406 logger.info("Retrieving results from " + reqUri);
407 } catch (Exception e) {
408 logger.error("Exception caught", e);
409 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
412 String message = StringUtils.EMPTY;
414 // Try to retrieve the test results (modify the url for that)
415 AnsibleResult testResult = queryServer(reqUri, params.get("User"), params.get(PASSD));
416 message = testResult.getStatusMessage();
417 logger.info("Request output = " + message);
418 ctx.setAttribute(OUTPUT_ATTRIBUTE_NAME, message);
419 ctx.setStatus(OUTCOME_SUCCESS);
420 } catch (Exception e) {
421 logger.error("Exception caught", e);
422 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
423 "Exception encountered retreiving output : " + e.getMessage());
428 * Method that posts the request
430 private AnsibleResult postExecRequest(String agentUrl, String payload, String user, String password) {
432 AnsibleResult testResult;
435 httpClient.setHttpContext(user, password);
436 testResult = httpClient.post(agentUrl, payload);
438 testResult = testServer.Post(agentUrl, payload);
444 * Method to query Ansible server
446 private AnsibleResult queryServer(String agentUrl, String user, String password) {
448 AnsibleResult testResult;
450 logger.info("Querying url = " + agentUrl);
453 testResult = httpClient.get(agentUrl);
455 testResult = testServer.Get(agentUrl);