Fix sonar issues in AnsibleMessageParser
[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) 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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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.
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.appc.adapter.ansible.impl;
26
27 import java.util.Map;
28 import java.util.Properties;
29 import java.lang.*;
30
31 import org.onap.appc.configuration.Configuration;
32 import org.onap.appc.configuration.ConfigurationFactory;
33 import org.onap.appc.exceptions.APPCException;
34
35 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
36 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
37
38 import org.json.JSONObject;
39 import org.json.JSONException;
40
41 import org.onap.appc.adapter.ansible.AnsibleAdapter;
42
43 import org.onap.appc.adapter.ansible.model.AnsibleResult;
44 import org.onap.appc.adapter.ansible.model.AnsibleMessageParser;
45 import org.onap.appc.adapter.ansible.model.AnsibleResultCodes;
46 import org.onap.appc.adapter.ansible.model.AnsibleServerEmulator;
47
48 import com.att.eelf.configuration.EELFLogger;
49 import com.att.eelf.configuration.EELFManager;
50
51
52
53 /**
54  * This class implements the {@link AnsibleAdapter} interface. This interface
55  * defines the behaviors that our service provides.
56  */
57 public class AnsibleAdapterImpl implements AnsibleAdapter {
58
59     /**
60      * The constant used to define the adapter name in the mapped diagnostic
61      * context
62      */
63
64
65     @SuppressWarnings("nls")
66     public static final String MDC_ADAPTER = "Ansible Adapter";
67
68     /**
69      * The constant used to define the service name in the mapped diagnostic
70      * context
71      */
72     @SuppressWarnings("nls")
73     public static final String MDC_SERVICE = "service";
74
75     /**
76      * The constant for the status code for a failed outcome
77      */
78     @SuppressWarnings("nls")
79     public static final String OUTCOME_FAILURE = "failure";
80
81     /**
82      * The constant for the status code for a successful outcome
83      */
84     @SuppressWarnings("nls")
85     public static final String OUTCOME_SUCCESS = "success";
86
87     /**
88      * Adapter Name
89      **/
90     private static final String ADAPTER_NAME = "Ansible Adapter";
91
92
93     /**
94      * The logger to be used
95      */
96     private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
97
98     /**
99      * A reference to the adapter configuration object.
100      */
101     private Configuration configuration;
102     ;
103
104     /**
105      * can Specify a X509 certificate file for use if required ...
106      * Must be initialized with setCertFile
107      **/
108     private String certFile = "";
109
110
111     /**
112      * Connection object
113      **/
114     ConnectionBuilder http_client;
115
116     /**
117      * Ansible API Message Handlers
118      **/
119     private AnsibleMessageParser messageProcessor;
120
121     /**
122      * indicator whether in test mode
123      **/
124     private boolean testMode = false;
125
126     /**
127      * server emulator object to be used if in test mode
128      **/
129     private AnsibleServerEmulator testServer;
130
131     /**
132      * This default constructor is used as a work around because the activator
133      * wasnt getting called
134      */
135     public AnsibleAdapterImpl() {
136         initialize();
137     }
138
139
140     /**
141      * @param props not used
142      */
143     public AnsibleAdapterImpl(Properties props) {
144         initialize();
145     }
146
147
148     /**
149      * Used for jUnit test and testing interface
150      **/
151     public AnsibleAdapterImpl(boolean Mode) {
152         testMode = Mode;
153         testServer = new AnsibleServerEmulator();
154         messageProcessor = new AnsibleMessageParser();
155     }
156
157     /**
158      * Returns the symbolic name of the adapter
159      *
160      * @return The adapter name
161      * @see org.onap.appc.adapter.rest.AnsibleAdapter#getAdapterName()
162      */
163     @Override
164     public String getAdapterName() {
165         return ADAPTER_NAME;
166     }
167
168
169     /**
170      * @param rc Method posts info to Context memory in case of an error
171      *           and throws a SvcLogicException causing SLI to register this as a failure
172      */
173     @SuppressWarnings("static-method")
174     private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
175
176         svcLogic.setStatus(OUTCOME_FAILURE);
177         svcLogic.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(code));
178         svcLogic.setAttribute("org.onap.appc.adapter.ansible.message", message);
179
180         throw new SvcLogicException("Ansible Adapter Error = " + message);
181     }
182
183
184     /**
185      * initialize the  Ansible adapter based on default and over-ride configuration data
186      */
187     private void initialize() {
188
189         configuration = ConfigurationFactory.getConfiguration();
190         Properties props = configuration.getProperties();
191
192         // Create the message processor instance
193         messageProcessor = new AnsibleMessageParser();
194
195         // Create the http client instance
196         // type of client is extracted from the property file parameter
197         // org.onap.appc.adapter.ansible.clientType
198         // It can be :
199         //     1. TRUST_ALL  (trust all SSL certs). To be used ONLY in dev
200         //     2. TRUST_CERT (trust only those whose certificates have been stored in the trustStore file)
201         //     3. DEFAULT    (trust only well known certificates). This is standard behaviour to which it will
202         //     revert. To be used in PROD
203
204         try {
205             String clientType = props.getProperty("org.onap.appc.adapter.ansible.clientType");
206             logger.info("Ansible http client type set to " + clientType);
207
208             if (clientType.equals("TRUST_ALL")) {
209                 logger.info("Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
210                 http_client = new ConnectionBuilder(1);
211             } else if (clientType.equals("TRUST_CERT")) {
212                 // set path to keystore file
213                 String trustStoreFile = props.getProperty("org.onap.appc.adapter.ansible.trustStore");
214                 String key = props.getProperty("org.onap.appc.adapter.ansible.trustStore.trustPasswd");
215                 char[] trustStorePasswd = key.toCharArray();
216                 String trustStoreType = "JKS";
217                 logger.info("Creating http client with trustmanager from " + trustStoreFile);
218                 http_client = new ConnectionBuilder(trustStoreFile, trustStorePasswd);
219             } else {
220                 logger.info("Creating http client with default behaviour");
221                 http_client = new ConnectionBuilder(0);
222             }
223         } catch (Exception e) {
224             logger.error("Error Initializing Ansible Adapter due to Unknown Exception: reason = " + e.getMessage());
225         }
226
227         logger.info("Intitialized Ansible Adapter");
228
229     }
230
231
232     /**
233      * set the certificate file if not a trusted/known CA
234      **/
235     private void setCertFile(String CertFile) {
236         this.certFile = CertFile;
237     }
238
239
240     // Public Method to post request to execute playbook. Posts the following back
241     // to Svc context memory
242     //  org.onap.appc.adapter.ansible.req.code : 100 if successful
243     //  org.onap.appc.adapter.ansible.req.messge : any message
244     //  org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request
245
246     public void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
247
248         String PlaybookName = "";
249         String payload = "";
250         String AgentUrl = "";
251         String User = "";
252         String Password = "";
253         String Id = "";
254
255         JSONObject JsonPayload;
256
257         try {
258             // create json object to send request
259             JsonPayload = messageProcessor.reqMessage(params);
260
261             AgentUrl = (String) JsonPayload.remove("AgentUrl");
262             User = (String) JsonPayload.remove("User");
263             Password = (String) JsonPayload.remove("Password");
264             Id = (String) JsonPayload.getString("Id");
265             payload = JsonPayload.toString();
266             logger.info("Updated Payload  = " + payload);
267         } catch (APPCException e) {
268             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = " + e.getMessage());
269         } catch (JSONException e) {
270             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid JSON block. Reason = " + e.getMessage());
271         } catch (NumberFormatException e) {
272             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid parameter values. Reason = " + e.getMessage());
273         }
274
275
276         int code = -1;
277         String message = "";
278
279         try {
280
281             // post the test request
282             //---------------------------------------
283             logger.info("Posting request = " + payload + " to url = " + AgentUrl);
284             AnsibleResult testresult = postExecRequest(AgentUrl, payload, User, Password);
285
286
287             // Process if HTTP was successfull
288             if (testresult.getStatusCode() == 200) {
289                 testresult = messageProcessor.parsePostResponse(testresult.getStatusMessage());
290             } else {
291                 doFailure(ctx, testresult.getStatusCode(), "Error posting request. Reason = " + testresult.getStatusMessage());
292             }
293
294             code = testresult.getStatusCode();
295             message = testresult.getStatusMessage();
296
297
298             // Check status of test request returned by Agent
299             //-----------------------------------------------
300             if (code == AnsibleResultCodes.PENDING.getValue()) {
301                 logger.info(String.format("Submission of Test %s successful.", PlaybookName));
302                 // test request accepted. We are in asynchronous case
303             } else {
304                 doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
305             }
306         } catch (APPCException e) {
307             doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
308         }
309
310
311         ctx.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(code));
312         ctx.setAttribute("org.onap.appc.adapter.ansible.message", message);
313         ctx.setAttribute("org.onap.appc.adapter.ansible.Id", Id);
314
315     }
316
317
318     // Public method to query status of a specific request
319     // It blocks till the Ansible Server responds or the session times out
320
321     public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
322
323
324         // Get uri
325         String ReqUri = "";
326
327         try {
328             ReqUri = messageProcessor.reqUriResult(params);
329             System.out.println("Got uri = " + ReqUri);
330         } catch (APPCException e) {
331             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to missing parameters. Reason = " + e.getMessage());
332             return;
333         } catch (NumberFormatException e) {
334             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to invalid parameters value. Reason = " + e.getMessage());
335             return;
336         }
337
338         int code = -1;
339         String message = "";
340         String results = "";
341
342         try {
343             // Try to  retreive the test results (modify the url for that)
344             AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
345             code = testresult.getStatusCode();
346             message = testresult.getStatusMessage();
347
348             if (code == 200) {
349                 logger.info("Parsing response from Server = " + message);
350                 // Valid HTTP. process the Ansible message
351                 testresult = messageProcessor.parseGetResponse(message);
352                 code = testresult.getStatusCode();
353                 message = testresult.getStatusMessage();
354                 results = testresult.getResults();
355
356             }
357
358             logger.info("Request response = " + message);
359
360         } catch (APPCException e) {
361             doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving result : " + e.getMessage());
362             return;
363         }
364
365         // We were able to get and process the results. Determine if playbook succeeded
366
367         if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()) {
368             message = String.format("Ansible Request  %s finished with Result = %s, Message = %s", params.get("Id"), OUTCOME_SUCCESS, message);
369             logger.info(message);
370         } else {
371             logger.info(String.format("Ansible Request  %s finished with Result %s, Message = %s", params.get("Id"), OUTCOME_FAILURE, message));
372             ctx.setAttribute("org.onap.appc.adapter.ansible.results", results);
373             doFailure(ctx, code, message);
374             return;
375         }
376
377
378         ctx.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(400));
379         ctx.setAttribute("org.onap.appc.adapter.ansible.message", message);
380         ctx.setAttribute("org.onap.appc.adapter.ansible.results", results);
381         ctx.setStatus(OUTCOME_SUCCESS);
382     }
383
384
385     // Public method to get logs  from plyabook execution for a  specifcic request
386     // It blocks till the Ansible Server responds or the session times out
387     // very similar to reqExecResult
388     // logs are returned in the DG context variable org.onap.appc.adapter.ansible.log
389
390     public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
391
392
393         // Get uri
394         String ReqUri = "";
395         try {
396             ReqUri = messageProcessor.reqUriLog(params);
397             logger.info("Retreiving results from " + ReqUri);
398         } catch (Exception e) {
399             doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
400         }
401
402         int code = -1;
403         String message = "";
404         float Duration = -1;
405
406         try {
407             // Try to  retreive the test results (modify the url for that)
408             AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
409             code = testresult.getStatusCode();
410             message = testresult.getStatusMessage();
411
412             logger.info("Request output = " + message);
413
414         } catch (Exception e) {
415             doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving output : " + e.getMessage());
416         }
417
418         ctx.setAttribute("org.onap.appc.adapter.ansible.log", message);
419         ctx.setStatus(OUTCOME_SUCCESS);
420     }
421
422
423     /**
424      * Method that posts the request
425      **/
426
427     private AnsibleResult postExecRequest(String AgentUrl, String Payload, String User, String Password) {
428
429         String reqOutput = "UNKNOWN";
430         int reqStatus = -1;
431
432         AnsibleResult testresult;
433
434         if (!testMode) {
435             http_client.setHttpContext(User, Password);
436             testresult = http_client.Post(AgentUrl, Payload);
437         } else {
438             testresult = testServer.Post(AgentUrl, Payload);
439         }
440
441         return testresult;
442     }
443
444
445     /* 
446        Method to query Ansible server
447
448     */
449     private AnsibleResult queryServer(String AgentUrl, String User, String Password) {
450
451         String testOutput = "UNKNOWN";
452         int testStatus = -1;
453         AnsibleResult testresult;
454
455         logger.info("Querying url = " + AgentUrl);
456
457         if (!testMode) {
458             testresult = http_client.Get(AgentUrl);
459         } else {
460             testresult = testServer.Get(AgentUrl);
461         }
462
463         return testresult;
464
465     }
466
467
468 }