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