VNFC Support for Ansible actions
[appc.git] / appc-adapters / appc-ansible-adapter / appc-ansible-adapter-bundle / src / main / java / org / onap / appc / adapter / ansible / impl / AnsibleAdapterImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Modifications Copyright (C) 2019 IBM
10  * =============================================================================
11  * Modifications Copyright (C) 2019 Orange
12  * =============================================================================
13  * Licensed under the Apache License, Version 2.0 (the "License");
14  * you may not use this file except in compliance with the License.
15  * You may obtain a copy of the License at
16  *
17  *      http://www.apache.org/licenses/LICENSE-2.0
18  *
19  * Unless required by applicable law or agreed to in writing, software
20  * distributed under the License is distributed on an "AS IS" BASIS,
21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22  * See the License for the specific language governing permissions and
23  * limitations under the License.
24  *
25  * ============LICENSE_END=========================================================
26  */
27
28 package org.onap.appc.adapter.ansible.impl;
29
30 import java.io.*;
31 import java.util.Map;
32 import java.util.HashMap;
33 import java.util.Properties;
34
35 import org.apache.commons.lang.StringUtils;
36 import org.json.JSONException;
37 import org.json.JSONObject;
38 import org.json.JSONArray;
39 import org.onap.appc.adapter.ansible.AnsibleAdapter;
40 import org.onap.appc.adapter.ansible.model.AnsibleMessageParser;
41 import org.onap.appc.adapter.ansible.model.AnsibleResult;
42 import org.onap.appc.adapter.ansible.model.AnsibleResultCodes;
43 import org.onap.appc.adapter.ansible.model.AnsibleServerEmulator;
44 import org.onap.appc.exceptions.APPCException;
45 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
46 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
47
48 import com.att.eelf.configuration.EELFLogger;
49 import com.att.eelf.configuration.EELFManager;
50 import org.onap.appc.encryption.EncryptionTool;
51
52 /**
53  * This class implements the {@link AnsibleAdapter} interface. This interface
54  * defines the behaviors that our service provides.
55  */
56 public class AnsibleAdapterImpl implements AnsibleAdapter {
57
58     /**
59      * The constant used to define the service name in the mapped diagnostic context
60      */
61     @SuppressWarnings("nls")
62     public static final String MDC_SERVICE = "service";
63
64     /**
65      * The constant for the status code for a failed outcome
66      */
67     @SuppressWarnings("nls")
68     public static final String OUTCOME_FAILURE = "failure";
69
70     /**
71      * The constant for the status code for a successful outcome
72      */
73     @SuppressWarnings("nls")
74     public static final String OUTCOME_SUCCESS = "success";
75
76     /**
77      * Adapter Name
78      */
79     private static final String ADAPTER_NAME = "Ansible Adapter";
80     private static final String APPC_EXCEPTION_CAUGHT = "APPCException caught";
81
82     private static final String RESULT_CODE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.result.code";
83     private static final String MESSAGE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.message";
84     private static final String RESULTS_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.results";
85     private static final String OUTPUT_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.output";
86     private static final String ID_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.Id";
87     private static final String LOG_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.log";
88
89     private static final String CLIENT_TYPE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.clientType";
90     private static final String TRUSTSTORE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore";
91     private static final String TRUSTPASSWD_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore.trustPasswd";
92     private static final String TIMEOUT_PROPERTY_NAME = "org.onap.appc.adapter.ansible.timeout";
93     private static final String POLL_INTERVAL_PROPERTY_NAME = "org.onap.appc.adapter.ansible.pollInterval";
94     private static final String SOCKET_TIMEOUT_PROPERTY_NAME = "org.onap.appc.adapter.ansible.socketTimeout";
95     private static final String PASSWORD = "Password";
96     private static final String APPC_PROPS = "/appc.properties";
97     private static final String SDNC_CONFIG_DIR = "SDNC_CONFIG_DIR";
98     private static final String propDir = System.getenv(SDNC_CONFIG_DIR);
99     private static final String SERVERIP = "ServerIP";
100     private Properties props;
101     private int defaultTimeout = 600 * 1000;
102     private int defaultSocketTimeout = 60 * 1000;
103     private int defaultPollInterval = 60 * 1000;
104
105     /**
106      * The logger to be used
107      */
108     private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
109     /**
110      * Connection object
111      **/
112     private ConnectionBuilder httpClient;
113
114     /**
115      * Ansible API Message Handlers
116      **/
117     private AnsibleMessageParser messageProcessor;
118
119     /**
120      * indicator whether in test mode
121      **/
122     private boolean testMode = false;
123
124     /**
125      * server emulator object to be used if in test mode
126      **/
127     private AnsibleServerEmulator testServer;
128
129     /**
130      * This default constructor is used as a work around because the activator
131      * wasn't getting called
132      */
133     public AnsibleAdapterImpl() {
134         initialize();
135     }
136
137     /**
138      * Used for jUnit test and testing interface
139      */
140     public AnsibleAdapterImpl(boolean mode) {
141         testMode = mode;
142         testServer = new AnsibleServerEmulator();
143         messageProcessor = new AnsibleMessageParser();
144     }
145
146     /**
147      * Returns the symbolic name of the adapter
148      *
149      * @return The adapter name
150      * @see org.onap.appc.adapter.rest.AnsibleAdapter#getAdapterName()
151      */
152     @Override
153     public String getAdapterName() {
154         return ADAPTER_NAME;
155     }
156
157     /**
158      * @param rc
159      *            Method posts info to Context memory in case of an error and throws
160      *            a SvcLogicException causing SLI to register this as a failure
161      */
162     @SuppressWarnings("static-method")
163     private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
164
165         svcLogic.setStatus(OUTCOME_FAILURE);
166         svcLogic.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
167         svcLogic.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
168
169         throw new SvcLogicException("Ansible Adapter Error = " + message);
170     }
171
172     /**
173      * initialize the Ansible adapter based on default and over-ride configuration
174      * data
175      */
176     private void initialize() {
177         String path = propDir + APPC_PROPS;
178         File propFile = new File(path);
179         props = new Properties();
180         try {
181             InputStream input = new FileInputStream(propFile);
182             props.load(input);
183         } catch (Exception ex) {
184             logger.error("Error while reading appc.properties file" + ex.getMessage());
185         }
186         // Create the message processor instance 
187         messageProcessor = new AnsibleMessageParser();
188         //continuing for checking timeout
189         try {
190             String timeoutStr = props.getProperty(TIMEOUT_PROPERTY_NAME);
191             defaultTimeout = Integer.parseInt(timeoutStr) * 1000;
192
193         } catch (Exception e) {
194             defaultTimeout = 600 * 1000;
195             logger.error("Error while reading time out property" , e);
196         }
197         //continuing for checking timeout
198         try {
199             String timeoutStr = props.getProperty(SOCKET_TIMEOUT_PROPERTY_NAME);
200             defaultSocketTimeout = Integer.parseInt(timeoutStr) * 1000;
201
202         } catch (Exception e) {
203             defaultSocketTimeout = 60 * 1000;
204             logger.error("Error while reading socket time out property" , e);
205         }
206         //continuing for checking timeout
207         try {
208             String timeoutStr = props.getProperty(POLL_INTERVAL_PROPERTY_NAME);
209             defaultPollInterval = Integer.parseInt(timeoutStr) * 1000;
210
211         } catch (Exception e) {
212             defaultPollInterval = 60 * 1000;
213             logger.error("Error while reading poll interval property" , e);
214         }
215         logger.info("Initialized Ansible Adapter");
216     }
217
218     private ConnectionBuilder getHttpConn(int timeout, String serverIP) {
219
220         String path = propDir + APPC_PROPS;
221         File propFile = new File(path);
222         props = new Properties();
223         InputStream input;
224         try {
225             input = new FileInputStream(propFile);
226             props.load(input);
227         } catch (Exception ex) {
228             // TODO Auto-generated catch block
229             logger.error("Error while reading appc.properties file" + ex.getMessage());
230         }
231         // Create the http client instance
232         // type of client is extracted from the property file parameter
233         // org.onap.appc.adapter.ansible.clientType
234         // It can be :
235         // 1. TRUST_ALL (trust all SSL certs). To be used ONLY in dev
236         // 2. TRUST_CERT (trust only those whose certificates have been stored in the
237         // trustStore file)
238         // 3. DEFAULT (trust only well known certificates). This is standard behavior to
239         // which it will
240         // revert. To be used in PROD
241         ConnectionBuilder httpClientLocal = null;
242         try {
243             String clientType = props.getProperty(CLIENT_TYPE_PROPERTY_NAME);
244             logger.info("Ansible http client type set to " + clientType);
245
246             if ("TRUST_ALL".equals(clientType)) {
247                 logger.info(
248                         "Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
249                 httpClientLocal = new ConnectionBuilder(1, timeout);
250             } else if ("TRUST_CERT".equals(clientType)) {
251                 // set path to keystore file
252                 String trustStoreFile = props.getProperty(TRUSTSTORE_PROPERTY_NAME);
253                 String key = props.getProperty(TRUSTPASSWD_PROPERTY_NAME);
254                 char[] trustStorePasswd = EncryptionTool.getInstance().decrypt(key).toCharArray();
255                 logger.info("Creating http client with trustmanager from " + trustStoreFile);
256                 httpClientLocal = new ConnectionBuilder(trustStoreFile, trustStorePasswd, timeout, serverIP);
257             } else {
258                 logger.info("Creating http client with default behaviour");
259                 httpClientLocal = new ConnectionBuilder(0, timeout);
260             }
261         } catch (Exception e) {
262             logger.error("Error Getting HTTP Connection Builder due to Unknown Exception", e);
263         }
264
265         logger.info("Got HTTP Connection Builder");
266         return httpClientLocal;
267     }
268
269     // Public Method to post request to execute playbook. Posts the following back
270     // to Svc context memory
271     // org.onap.appc.adapter.ansible.req.code : 100 if successful
272     // org.onap.appc.adapter.ansible.req.messge : any message
273     // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request
274     @Override
275     public void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
276
277         String playbookName = StringUtils.EMPTY;
278         String payload = StringUtils.EMPTY;
279         String agentUrl = StringUtils.EMPTY;
280         String user = StringUtils.EMPTY;
281         String password = StringUtils.EMPTY;
282         String id = StringUtils.EMPTY;
283         String timeout = StringUtils.EMPTY;
284         JSONObject jsonPayload;
285
286         try {
287             // create json object to send request
288             jsonPayload = messageProcessor.reqMessage(params);
289
290             logger.info("Initial Payload  = " + jsonPayload.toString());
291             agentUrl = (String) jsonPayload.remove("AgentUrl");
292             user = (String) jsonPayload.remove("User");
293             password = (String)jsonPayload.remove(PASSWORD);
294             if(StringUtils.isNotBlank(password))
295             password = EncryptionTool.getInstance().decrypt(password);
296             id = jsonPayload.getString("Id");
297             timeout = jsonPayload.getString("Timeout");
298             if (StringUtils.isBlank(timeout))
299                 timeout = "600";
300
301             String autoNodeList = (String) jsonPayload.remove("AutoNodeList");
302             if (autoNodeList != null && Boolean.parseBoolean(autoNodeList)) {
303                 JSONArray generatedNodeList = generateNodeList(params, ctx);
304                 if (generatedNodeList.length() > 0) {
305                     jsonPayload.put("NodeList", generatedNodeList);
306                     jsonPayload.put("InventoryNames", "VM");
307                 } else {
308                     doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
309                             "Auto generation of Node List Failed - no elements on the list");
310                 }
311             } else
312                 logger.debug("Auto Node List is DISABLED");
313
314             payload = jsonPayload.toString();
315             ctx.setAttribute("AnsibleTimeout", timeout);
316             logger.info("Updated Payload  = " + payload + " timeout = " + timeout);
317         } catch (APPCException e) {
318             logger.error(APPC_EXCEPTION_CAUGHT, e);
319             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
320                     "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = "
321                             + e.getMessage());
322         } catch (JSONException e) {
323             logger.error("JSONException caught", e);
324             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
325                     "Error constructing request for execution of playbook due to invalid JSON block. Reason = "
326                             + e.getMessage());
327         } catch (NumberFormatException e) {
328             logger.error("NumberFormatException caught", e);
329             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
330                     "Error constructing request for execution of playbook due to invalid parameter values. Reason = "
331                             + e.getMessage());
332         }
333
334         int code = -1;
335         String message = StringUtils.EMPTY;
336
337         try {
338             // post the test request
339             logger.info("Posting ansible request = " + payload + " to url = " + agentUrl);
340             AnsibleResult testResult = postExecRequest(agentUrl, payload, user, password,ctx);
341           if (testResult != null) {
342             logger.info("Received response on ansible post request " + testResult.getStatusMessage());
343             // Process if HTTP was successful
344             if (testResult.getStatusCode() == 200) {
345               testResult = messageProcessor.parsePostResponse(testResult.getStatusMessage());
346             } else {
347               doFailure(ctx, testResult.getStatusCode(),
348                         "Error posting request. Reason = " + testResult.getStatusMessage());
349             }
350             String output = StringUtils.EMPTY;
351             code = testResult.getStatusCode();
352             message = testResult.getStatusMessage();
353             output = testResult.getOutput();
354             ctx.setAttribute(OUTPUT_ATTRIBUTE_NAME, output);
355             String serverIp = testResult.getServerIp();
356             if (StringUtils.isBlank(serverIp))
357             ctx.setAttribute(SERVERIP, serverIp);
358             else
359               ctx.setAttribute(SERVERIP, "");
360             // Check status of test request returned by Agent
361             if (code == AnsibleResultCodes.PENDING.getValue()) {
362               logger.info(String.format("Submission of Test %s successful.", playbookName));
363               // test request accepted. We are in asynchronous case
364             } else {
365               doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
366             }
367           } else {
368             doFailure(ctx, code, "Ansible Test result is null");
369           }
370         } catch (APPCException e) {
371             logger.error(APPC_EXCEPTION_CAUGHT, e);
372             doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
373                     "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
374         }
375
376         ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
377         ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
378         ctx.setAttribute(ID_ATTRIBUTE_NAME, id);
379     }
380
381     /**
382      * Method is used to automatically generate NodeList section base on the svc context
383      *
384      * @see org.onap.appc.adapter.ansible.AnsibleAdapter#reqExecResult(java.util.Map,
385      *      org.onap.ccsdk.sli.core.sli.SvcLogicContext)
386      */
387     private JSONArray generateNodeList(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
388         String vfModuleId = params.get("vf-module-id");
389         String vnfcName = params.get("vnfc-name");
390         String vServerId = params.get("vserver-id");
391         String vnfcType = params.get("vnfc-type");
392         logger.info("GENERATING NODE LIST");
393         JSONArray result = new JSONArray();
394
395         if (vServerId != null && !vServerId.equals(""))
396             logger.debug("Auto Node List filtering parameter vserver-id " + vServerId);
397         else
398             vServerId = null;
399         if (vnfcName != null && !vnfcName.equals(""))
400             logger.debug("Auto Node List filtering parameter vnfc-name " + vnfcName);
401         else
402             vnfcName = null;
403         if (vnfcType != null && !vnfcType.equals(""))
404             logger.debug("Auto Node List filtering parameter vnfc-type " + vnfcType);
405         else
406             vnfcType = null;
407         if (vfModuleId != null && !vfModuleId.equals(""))
408             logger.debug("Auto Node List filtering parameter vf-module-id " + vfModuleId);
409         else
410             vfModuleId = null;
411
412         Map<String, JSONObject> candidates = new HashMap<String, JSONObject>();
413         for (int i = 0; ; i++) {
414             String vmKey = "tmp.vnfInfo.vm[" + Integer.toString(i) + "]";
415             logger.info("Looking for attributes of: " + vmKey);
416             if (ctx.getAttribute(vmKey + ".vnfc-name") != null) {
417                 String vmVnfcName = ctx.getAttribute(vmKey + ".vnfc-name");
418                 String vmVnfcIpv4Address = ctx.getAttribute(vmKey + ".vnfc-ipaddress-v4-oam-vip");
419                 String vmVnfcType = ctx.getAttribute(vmKey + ".vnfc-type");
420
421                 if (vmVnfcName != null && vmVnfcIpv4Address != null && vmVnfcType != null
422                     && !vmVnfcName.equals("") && !vmVnfcIpv4Address.equals("") && !vmVnfcType.equals("")) {
423                     if (vServerId != null) {
424                         String vmVserverId = ctx.getAttribute(vmKey + ".vserver-id");
425                         if (vmVserverId == null || !vmVserverId.equals(vServerId)) {
426                             logger.debug("Auto Node List candidate " + vmVnfcName + " dropped. vserver-id mismatch");
427                             continue;
428                         }
429                     }
430                     if (vfModuleId != null) {
431                         String vmVfModuleId = ctx.getAttribute(vmKey + ".vf-module-id");
432                         if (vmVfModuleId == null || !vmVfModuleId.equals(vfModuleId)) {
433                             logger.debug("Auto Node List candidate " + vmVnfcName + " dropped. vf-module-id mismatch");
434                             continue;
435                         }
436                     }
437                     if (vnfcName != null) {
438                         if (!vmVnfcName.equals(vnfcName)) {
439                             logger.debug("Auto Node List candidate " + vmVnfcName + " dropped. vnfc-name mismatch");
440                             continue;
441                         }
442                     }
443                     if (vnfcType != null) {
444                         if (!vmVnfcType.equals(vnfcType)) {
445                             logger.debug("Auto Node List candidate " + vmVnfcType + " dropped. vnfc-type mismatch");
446                             continue;
447                         }
448                     }
449
450                     logger.info("Auto Node List candidate " + vmVnfcName + " [" + vmVnfcIpv4Address + "," + vmVnfcType + "]");
451
452                     JSONObject vnfTypeCandidates = null;
453                     JSONArray vmList = null;
454                     if (!candidates.containsKey(vmVnfcType)) {
455                         vnfTypeCandidates = new JSONObject();
456                         vmList = new JSONArray();
457                         vnfTypeCandidates.put("site", "site");
458                         vnfTypeCandidates.put("vnfc-type", vmVnfcType);
459                         vnfTypeCandidates.put("vm-info", vmList);
460                         candidates.put(vmVnfcType, vnfTypeCandidates);
461                     } else {
462                         vnfTypeCandidates = candidates.get(vmVnfcType);
463                         vmList = (JSONArray) vnfTypeCandidates.get("vm-info");
464                     }
465
466                     JSONObject candidate = new JSONObject();
467                     candidate.put("ne_id", vmVnfcName);
468                     candidate.put("fixed_ip_address", vmVnfcIpv4Address);
469                     vmList.put(candidate);
470                 } else {
471                     logger.warn("Incomplete information for Auto Node List candidate " + vmKey);
472                 }
473             } else
474                 break;
475         }
476
477         for(JSONObject vnfcCandidates : candidates.values()) {
478             result.put(vnfcCandidates);
479         }
480
481         logger.info("GENERATING NODE LIST COMPLETED");
482         return result;
483     }
484
485     /**
486      * Public method to query status of a specific request It blocks till the
487      * Ansible Server responds or the session times out (non-Javadoc)
488      *
489      * @see org.onap.appc.adapter.ansible.AnsibleAdapter#reqExecResult(java.util.Map,
490      *      org.onap.ccsdk.sli.core.sli.SvcLogicContext)
491      */
492     @Override
493     public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
494
495         // Get URI
496         String reqUri = StringUtils.EMPTY;
497
498         try {
499             String serverIp = ctx.getAttribute(SERVERIP);
500             if (StringUtils.isNotBlank(serverIp))
501                 reqUri = messageProcessor.reqUriResultWithIP(params, serverIp);
502             else
503                 reqUri = messageProcessor.reqUriResult(params);
504             logger.info("Got uri " + reqUri);
505         } catch (APPCException e) {
506             logger.error(APPC_EXCEPTION_CAUGHT, e);
507             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
508                     "Error constructing request to retrieve result due to missing parameters. Reason = "
509                             + e.getMessage());
510             return;
511         } catch (NumberFormatException e) {
512             logger.error("NumberFormatException caught", e);
513             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
514                     "Error constructing request to retrieve result due to invalid parameters value. Reason = "
515                             + e.getMessage());
516             return;
517         }
518
519         int code = -1;
520         String message = StringUtils.EMPTY;
521         String results = StringUtils.EMPTY;
522         String output = StringUtils.EMPTY;
523         try {
524             // Try to retrieve the test results (modify the URL for that)
525             AnsibleResult testResult = queryServer(reqUri, params.get("User"),
526                     EncryptionTool.getInstance().decrypt(params.get(PASSWORD)), ctx);
527             code = testResult.getStatusCode();
528             message = testResult.getStatusMessage();
529
530             if (code == 200 || code == 400 || "FINISHED".equalsIgnoreCase(message)) {
531                 logger.info("Parsing response from ansible Server = " + message);
532                 // Valid HTTP. process the Ansible message
533                 testResult = messageProcessor.parseGetResponse(message);
534                 code = testResult.getStatusCode();
535                 message = testResult.getStatusMessage();
536                 results = testResult.getResults();
537                 output = testResult.getOutput();
538                 ctx.setAttribute(OUTPUT_ATTRIBUTE_NAME, output);
539             }
540             logger.info("Request response = " + message);
541         } catch (APPCException e) {
542             doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
543                     "Exception encountered retrieving result : " + e.getMessage());
544             return;
545         }
546
547         // We were able to get and process the results. Determine if playbook succeeded
548
549         if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()) {
550             message = String.format("Ansible Request  %s finished with Result = %s, Message = %s", params.get("Id"),
551                     OUTCOME_SUCCESS, message);
552             logger.info(message);
553         } else {
554             logger.info(String.format("Ansible Request  %s finished with Result %s, Message = %s", params.get("Id"),
555                     OUTCOME_FAILURE, message));
556             ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
557             ctx.setAttribute(OUTPUT_ATTRIBUTE_NAME, output);
558             doFailure(ctx, code, message);
559             return;
560         }
561
562         // In case of 200,400,FINISHED return 400
563         ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, "400");
564         ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
565         ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
566         ctx.setAttribute(OUTPUT_ATTRIBUTE_NAME, output);
567         ctx.setStatus(OUTCOME_SUCCESS);
568     }
569
570     /**
571      * Public method to get logs from playbook execution for a specific request
572      *
573      * It blocks till the Ansible Server responds or the session times out very
574      * similar to reqExecResult logs are returned in the DG context variable
575      * org.onap.appc.adapter.ansible.log
576      */
577     @Override
578     public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
579
580         String reqUri = StringUtils.EMPTY;
581         try {
582             reqUri = messageProcessor.reqUriLog(params);
583             logger.info("Retrieving results from " + reqUri);
584         } catch (Exception e) {
585             logger.error("Exception caught", e);
586             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
587         }
588
589         String message = StringUtils.EMPTY;
590         try {
591             // Try to retrieve the test results (modify the url for that)
592             AnsibleResult testResult = queryServer(reqUri, params.get("User"),
593                     EncryptionTool.getInstance().decrypt(params.get(PASSWORD)), ctx);
594             message = testResult.getStatusMessage();
595             logger.info("Request output = " + message);
596             ctx.setAttribute(LOG_ATTRIBUTE_NAME, message);
597             ctx.setStatus(OUTCOME_SUCCESS);
598         } catch (Exception e) {
599             logger.error("Exception caught", e);
600             doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
601                     "Exception encountered retreiving output : " + e.getMessage());
602         }
603     }
604
605     /**
606      * Method that posts the request
607      */
608     private AnsibleResult postExecRequest(String agentUrl, String payload, String user, String password,
609             SvcLogicContext ctx) {
610
611         AnsibleResult testResult = null;
612         ConnectionBuilder httpClientLocal = getHttpConn(defaultSocketTimeout, "");
613         if (!testMode) {
614           if(httpClientLocal!=null) {
615             httpClientLocal.setHttpContext(user, password);
616             testResult = httpClientLocal.post(agentUrl, payload);
617             httpClientLocal.close();
618           }
619         } else {
620             testResult = testServer.post(agentUrl, payload);
621         }
622         return testResult;
623     }
624
625     /**
626      * Method to query Ansible server
627      */
628     private AnsibleResult queryServer(String agentUrl, String user, String password, SvcLogicContext ctx) {
629
630         AnsibleResult testResult = new AnsibleResult();
631         int timeout = 600 * 1000;
632         try {
633             timeout = Integer.parseInt(ctx.getAttribute("AnsibleTimeout")) * 1000;
634
635         } catch (Exception e) {
636             timeout = defaultTimeout;
637         }
638         long endTime = System.currentTimeMillis() + timeout;
639
640         while (System.currentTimeMillis() < endTime) {
641             String serverIP = ctx.getAttribute(SERVERIP);
642             ConnectionBuilder httpClientLocal = getHttpConn(defaultSocketTimeout, serverIP);
643             logger.info("Querying ansible GetResult URL = " + agentUrl);
644
645             if (!testMode) {
646               if(httpClientLocal!=null) {
647                 httpClientLocal.setHttpContext(user, password);
648                 testResult = httpClientLocal.get(agentUrl);
649                 httpClientLocal.close();
650               }
651             } else {
652                 testResult = testServer.get(agentUrl);
653             }
654             if (testResult.getStatusCode() != AnsibleResultCodes.IO_EXCEPTION.getValue()
655                     && testResult.getStatusCode() != AnsibleResultCodes.PENDING.getValue()) {
656                 break;
657             }
658             
659             try {
660                 Thread.sleep(defaultPollInterval);
661             } catch (InterruptedException ex) {
662               logger.error("Thread Interrupted Exception", ex);
663               Thread.currentThread().interrupt();
664             }
665
666         }
667         if (testResult.getStatusCode() == AnsibleResultCodes.PENDING.getValue()) {
668             testResult.setStatusCode(AnsibleResultCodes.IO_EXCEPTION.getValue());
669             testResult.setStatusMessage("Request timed out");
670         }
671
672         return testResult;
673     }
674 }