[feature/APPC-6]
[appc.git] / appc-adapters / appc-ansible-adapter / appc-ansible-adapter-bundle / src / main / java / org / openecomp / appc / adapter / ansible / model / AnsibleMessageParser.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22
23 package org.openecomp.appc.adapter.ansible.model;
24
25 /**
26  * This module imples the APP-C/Ansible Server interface
27  * based on the REST API specifications
28  */
29
30 import java.lang.NumberFormatException ;
31 import java.util.*;
32 import com.google.common.base.Strings;
33
34 import org.json.JSONObject;
35 import org.json.JSONArray;
36 import org.json.JSONException;
37 import org.openecomp.appc.exceptions.APPCException;
38 import org.openecomp.appc.adapter.ansible.model.AnsibleResult;
39
40
41 /**
42  * Class that validates and constructs requests sent/received from 
43  * Ansible Server
44  *
45  */
46 public class AnsibleMessageParser {
47
48
49
50
51     // Accepts a map of strings and
52     // a) validates if all parameters are appropriate (else, throws an exception)
53     // and b) if correct returns a JSON object with appropriate key-value
54     // pairs to send to the server. 
55     public JSONObject ReqMessage(Map <String, String> params) throws APPCException, NumberFormatException, JSONException{
56
57         // Mandatory  parameters, that must be in the supplied information to the Ansible Adapter
58         // 1. URL to connect to
59         // 2. credentials for URL (assume username password for now)
60         // 3. Playbook name
61         String[] mandatoryTestParams = {"AgentUrl", "PlaybookName", "User", "Password"};
62
63         // Optional testService parameters that may be provided in the request
64         String[] optionalTestParams = {"EnvParameters", "NodeList", "LocalParameters", "Timeout", "Version", "FileParameters", "Action"};
65
66         JSONObject JsonPayload = new JSONObject();
67         String payload = "";
68         JSONObject paramsJson;
69
70   
71         // Verify all the mandatory parameters are there 
72         for (String key: mandatoryTestParams){
73             if (! params.containsKey(key)){
74                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));
75             }
76             payload = params.get(key);
77             if (Strings.isNullOrEmpty(payload)){
78                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key % value is Null or Emtpy", key));
79             }
80             
81             JsonPayload.put(key, payload);
82         }
83
84         // Iterate through optional parameters
85         // If null or empty omit it 
86         for (String key : optionalTestParams){
87             if (params.containsKey(key)){
88                 payload = params.get(key);
89                 if(!Strings.isNullOrEmpty(payload)){
90                     
91                     // different cases require different treatment
92                     switch (key){
93                     case "Timeout": 
94                         int Timeout = Integer.parseInt(payload);
95                         if (Timeout < 0){
96                             throw new NumberFormatException(" : specified negative integer for timeout = " +  payload);
97                         }
98                         JsonPayload.put(key, payload);
99                         break;
100
101                     case "Version": 
102                         JsonPayload.put(key, payload);
103                         break;
104
105                     case "LocalParameters":  
106                         paramsJson = new JSONObject(payload);
107                         JsonPayload.put(key, paramsJson);
108                         break;
109                         
110                     case "EnvParameters":  
111                         paramsJson = new JSONObject(payload);
112                         JsonPayload.put(key, paramsJson);
113                         break;
114                         
115                     case "NodeList":  
116                         JSONArray paramsArray = new JSONArray(payload);
117                         JsonPayload.put(key, paramsArray);
118                         break;
119                         
120                     case "FileParameters":
121                         // Files may have strings with newlines. Escape them as appropriate
122                         String formattedPayload = payload.replace("\n", "\\n").replace("\r", "\\r");
123                         JSONObject fileParams = new JSONObject(formattedPayload);
124                         JsonPayload.put(key, fileParams);
125                         break;
126                         
127                     }
128                 }
129             }
130         }
131         
132
133         // Generate a unique uuid for the test
134         String ReqId = UUID.randomUUID().toString();
135         JsonPayload.put("Id", ReqId);
136
137         return JsonPayload;
138         
139     }
140
141
142
143     // method that validates that the Map has  enough information
144     // to query Ansible server for a result . If so, it
145     // returns the appropriate url, else an empty string
146     public String ReqUri_Result(Map <String, String> params) throws APPCException, NumberFormatException{
147         
148         // Mandatory  parameters, that must be in the request
149         String[] mandatoryTestParams = {"AgentUrl", "Id", "User", "Password" };
150         
151         // Verify all the mandatory parameters are there
152         String payload = "";
153         String Uri = "";
154         
155         for (String key: mandatoryTestParams){
156             if (! params.containsKey(key)){
157                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));                
158             }
159
160             payload = params.get(key);
161             if (Strings.isNullOrEmpty(payload)){
162                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));                
163             }
164
165         }
166
167         Uri = params.get("AgentUrl") + "?Id=" + params.get("Id") + "&Type=GetResult";
168
169         return Uri;
170       
171     }
172
173
174
175     // method that validates that the Map has  enough information
176     // to query Ansible server for logs. If so, it populates the appropriate
177     // returns the appropriate url, else an empty string
178     public String ReqUri_Output(Map <String, String> params) throws APPCException, NumberFormatException{
179         
180         
181         // Mandatory  parameters, that must be in the request
182         String[] mandatoryTestParams = {"AgentUrl", "Id", "User", "Password" };
183         
184         // Verify all the mandatory parameters are there
185         String payload = "";
186         String Uri = "";
187         
188         for (String key: mandatoryTestParams){
189             if (! params.containsKey(key)){
190                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));                
191             }
192             payload = params.get(key);
193             if (Strings.isNullOrEmpty(payload)){
194                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));                
195             }
196
197         }
198
199         Uri = params.get("AgentUrl") + "?Id=" + params.get("Id") + "&Type=GetOutput";
200         return Uri;
201       
202     }
203
204     // method that validates that the Map has  enough information
205     // to query Ansible server for logs. If so, it populates the appropriate
206     // returns the appropriate url, else an empty string
207     public String ReqUri_Log(Map <String, String> params) throws APPCException, NumberFormatException{
208         
209         
210         // Mandatory  parameters, that must be in the request
211         String[] mandatoryTestParams = {"AgentUrl", "Id", "User", "Password" };
212         
213         // Verify all the mandatory parameters are there
214         String payload = "";
215         String Uri = "";
216         
217         for (String key: mandatoryTestParams){
218             if (! params.containsKey(key)){
219                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));                
220             }
221             payload = params.get(key);
222             if (Strings.isNullOrEmpty(payload)){
223                 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));                
224             }
225
226         }
227
228         Uri = params.get("AgentUrl") + "?Id=" + params.get("Id") + "&Type=GetLog";
229         return Uri;
230       
231     }
232
233    
234     /** 
235         This method parses response from the 
236         Ansible Server when we do a post 
237         and returns an AnsibleResult object
238     **/
239     
240     public AnsibleResult  parsePostResponse(String Input) throws APPCException{
241
242         AnsibleResult ansibleResult = new AnsibleResult();
243         
244         try{
245             //Jsonify it
246             JSONObject  postResponse = new JSONObject(Input);
247                 
248             // Mandatory keys required are StatusCode and StatusMessage
249             int Code = postResponse.getInt("StatusCode");
250             String Message = postResponse.getString("StatusMessage");
251
252             
253             // Status code must must be either 100 (accepted) or 101 (rejected)
254             boolean valCode = AnsibleResultCodes.CODE.checkValidCode(AnsibleResultCodes.INITRESPONSE.getValue(), Code);
255             if(!valCode){
256                 throw new APPCException("Invalid InitResponse code  = " + Code + " received. MUST be one of " + AnsibleResultCodes.CODE.getValidCodes(AnsibleResultCodes.INITRESPONSE.getValue()) );
257             }
258             
259             ansibleResult.setStatusCode(Code);
260             ansibleResult.setStatusMessage(Message);
261
262         }
263         catch(JSONException e){
264             ansibleResult = new AnsibleResult(600, "Error parsing response = " + Input + ". Error = " + e.getMessage(), "");
265         }
266
267         
268         return ansibleResult;
269     }
270
271
272     /** This method  parses response from an Ansible server when we do a GET for a result 
273         and returns an AnsibleResult object
274      **/
275     public AnsibleResult  parseGetResponse(String Input) throws APPCException {
276
277         AnsibleResult ansibleResult = new AnsibleResult();
278         int FinalCode = AnsibleResultCodes.FINAL_SUCCESS.getValue();
279
280
281         try{
282             
283             //Jsonify it
284             JSONObject  postResponse = new JSONObject(Input);
285             
286             // Mandatory keys required are Status and Message
287             int Code = postResponse.getInt("StatusCode");
288             String Message = postResponse.getString("StatusMessage");
289             
290             // Status code must be valid
291             // Status code must must be either 100 (accepted) or 101 (rejected)
292             boolean valCode = AnsibleResultCodes.CODE.checkValidCode(AnsibleResultCodes.FINALRESPONSE.getValue(), Code);
293             
294             if(!valCode){
295                 throw new APPCException("Invalid FinalResponse code  = " + Code + " received. MUST be one of " + AnsibleResultCodes.CODE.getValidCodes(AnsibleResultCodes.FINALRESPONSE.getValue()));
296             }
297
298             
299             ansibleResult.setStatusCode(Code);
300             ansibleResult.setStatusMessage(Message);
301             System.out.println("Received response with code = " + Integer.toString(Code) + " Message = " + Message);
302
303             if(! postResponse.isNull("Results")){
304
305                 // Results are available. process them 
306                 // Results is a dictionary of the form
307                 // {host :{status:s, group:g, message:m, hostname:h}, ...}
308                 System.out.println("Processing results in response");
309                 JSONObject results = postResponse.getJSONObject("Results");
310                 System.out.println("Get JSON dictionary from Results ..");
311                 Iterator<String> hosts = results.keys();
312                 System.out.println("Iterating through hosts");
313                 
314                 while(hosts.hasNext()){
315                     String host = hosts.next();
316                     System.out.println("Processing host = " + host);
317                     
318                     try{
319                         JSONObject host_response = results.getJSONObject(host);
320                         int subCode = host_response.getInt("StatusCode");
321                         String message = host_response.getString("StatusMessage");
322
323                         System.out.println("Code = " + Integer.toString(subCode) + " Message = " + message);
324                         
325                         if(subCode != 200 || ! message.equals("SUCCESS")){
326                             FinalCode = AnsibleResultCodes.REQ_FAILURE.getValue();
327                         }
328                     }
329                     catch(JSONException e){
330                         ansibleResult.setStatusCode(AnsibleResultCodes.INVALID_RESPONSE.getValue());
331                         ansibleResult.setStatusMessage(String.format("Error processing response message = %s from host %s", results.getString(host), host));
332                         break;
333                     }
334                 }
335
336                 ansibleResult.setStatusCode(FinalCode);
337
338                 // We return entire Results object as message
339                 ansibleResult.setResults(results.toString());
340
341             }
342             else{
343                 ansibleResult.setStatusCode(AnsibleResultCodes.INVALID_RESPONSE.getValue());
344                 ansibleResult.setStatusMessage("Results not found in GET for response");
345             }
346             
347             
348         }
349         catch(JSONException e){
350             ansibleResult = new AnsibleResult(AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error parsing response = " + Input + ". Error = " + e.getMessage(), "");
351         }
352
353
354         return ansibleResult;
355     }
356
357
358
359 };    
360             
361             
362