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