2 * ============LICENSE_START=======================================================
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
25 package org.onap.appc.adapter.ansible.model;
28 * This module imples the APP-C/Ansible Server interface
29 * based on the REST API specifications
34 import com.google.common.base.Strings;
36 import org.json.JSONObject;
37 import org.json.JSONArray;
38 import org.json.JSONException;
39 import org.onap.appc.exceptions.APPCException;
42 * Class that validates and constructs requests sent/received from
45 public class AnsibleMessageParser {
47 private static final String STATUS_MESSAGE_KEY = "StatusMessage";
48 private static final String STATUS_CODE_KEY = "StatusCode";
50 private static final String PLAYBOOK_NAME_KEY = "PlaybookName";
51 private static final String AGENT_URL_KEY = "AgentUrl";
52 private static final String PASS_KEY = "Password";
53 private static final String USER_KEY = "User";
54 private static final String ID_KEY = "Id";
56 private static final String LOCAL_PARAMETERS_OPT_KEY = "LocalParameters";
57 private static final String FILE_PARAMETERS_OPT_KEY = "FileParameters";
58 private static final String ENV_PARAMETERS_OPT_KEY = "EnvParameters";
59 private static final String NODE_LIST_OPT_KEY = "NodeList";
60 private static final String TIMEOUT_OPT_KEY = "Timeout";
61 private static final String VERSION_OPT_KEY = "Version";
62 private static final String ACTION_OPT_KEY = "Action";
65 * Accepts a map of strings and
66 * a) validates if all parameters are appropriate (else, throws an exception) and
67 * b) if correct returns a JSON object with appropriate key-value pairs to send to the server.
69 * Mandatory parameters, that must be in the supplied information to the Ansible Adapter
70 * 1. URL to connect to
71 * 2. credentials for URL (assume username password for now)
75 public JSONObject reqMessage(Map<String, String> params) throws APPCException {
76 final String[] mandatoryTestParams = { AGENT_URL_KEY, PLAYBOOK_NAME_KEY, USER_KEY, PASS_KEY };
77 final String[] optionalTestParams = { ENV_PARAMETERS_OPT_KEY, NODE_LIST_OPT_KEY, LOCAL_PARAMETERS_OPT_KEY,
78 TIMEOUT_OPT_KEY, VERSION_OPT_KEY, FILE_PARAMETERS_OPT_KEY, ACTION_OPT_KEY };
80 JSONObject jsonPayload = new JSONObject();
82 for (String key : mandatoryTestParams) {
83 throwIfMissingMandatoryParam(params, key);
84 jsonPayload.put(key, params.get(key));
87 parseOptionalParams(params, optionalTestParams, jsonPayload);
89 // Generate a unique uuid for the test
90 String reqId = UUID.randomUUID().toString();
91 jsonPayload.put(ID_KEY, reqId);
97 * Method that validates that the Map has enough information
98 * to query Ansible server for a result. If so, it returns
99 * the appropriate url, else an empty string.
101 public String reqUriResult(Map<String, String> params) throws APPCException {
103 final String[] mandatoryTestParams = { AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY };
105 for (String key : mandatoryTestParams) {
106 throwIfMissingMandatoryParam(params, key);
108 return params.get(AGENT_URL_KEY) + "?Id=" + params.get(ID_KEY) + "&Type=GetResult";
112 * Method that validates that the Map has enough information
113 * to query Ansible server for logs. If so, it populates the
114 * appropriate returns the appropriate url, else an empty string.
116 public String reqUriOutput(Map<String, String> params) throws APPCException {
118 final String[] mandatoryTestParams = { AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY };
120 for (String mandatoryParam : mandatoryTestParams) {
121 throwIfMissingMandatoryParam(params, mandatoryParam);
123 return params.get(AGENT_URL_KEY) + "?Id=" + params.get(ID_KEY) + "&Type=GetOutput";
127 * Method that validates that the Map has enough information
128 * to query Ansible server for logs. If so, it populates the appropriate
129 * returns the appropriate url, else an empty string.
131 public String reqUriLog(Map<String, String> params) throws APPCException {
133 final String[] mandatoryTestParams = { AGENT_URL_KEY, ID_KEY, USER_KEY, PASS_KEY };
135 for (String mandatoryParam : mandatoryTestParams) {
136 throwIfMissingMandatoryParam(params, mandatoryParam);
138 return params.get(AGENT_URL_KEY) + "?Id=" + params.get(ID_KEY) + "&Type=GetLog";
142 * This method parses response from the Ansible Server when we do a post
143 * and returns an AnsibleResult object.
145 public AnsibleResult parsePostResponse(String input) throws APPCException {
146 AnsibleResult ansibleResult;
148 JSONObject postResponse = new JSONObject(input);
150 int code = postResponse.getInt(STATUS_CODE_KEY);
151 String msg = postResponse.getString(STATUS_MESSAGE_KEY);
153 int initResponseValue = AnsibleResultCodes.INITRESPONSE.getValue();
154 boolean validCode = AnsibleResultCodes.CODE.checkValidCode(initResponseValue, code);
156 throw new APPCException("Invalid InitResponse code = " + code + " received. MUST be one of " + AnsibleResultCodes.CODE.getValidCodes(initResponseValue));
159 ansibleResult = new AnsibleResult(code, msg);
161 } catch (JSONException e) {
162 ansibleResult = new AnsibleResult(600, "Error parsing response = " + input + ". Error = " + e.getMessage());
164 return ansibleResult;
168 * This method parses response from an Ansible server when we do a GET for a result
169 * and returns an AnsibleResult object.
171 public AnsibleResult parseGetResponse(String input) throws APPCException {
173 AnsibleResult ansibleResult = new AnsibleResult();
174 int finalCode = AnsibleResultCodes.FINAL_SUCCESS.getValue();
177 JSONObject postResponse = new JSONObject(input);
179 int codeStatus = postResponse.getInt(STATUS_CODE_KEY);
180 String messageStatus = postResponse.getString(STATUS_MESSAGE_KEY);
182 boolean valCode = AnsibleResultCodes.CODE.checkValidCode(AnsibleResultCodes.FINALRESPONSE.getValue(), codeStatus);
185 throw new APPCException("Invalid FinalResponse code = " + codeStatus + " received. MUST be one of " + AnsibleResultCodes.CODE.getValidCodes(AnsibleResultCodes.FINALRESPONSE.getValue()));
188 ansibleResult.setStatusCode(codeStatus);
189 ansibleResult.setStatusMessage(messageStatus);
190 System.out.println("Received response with code = " + Integer.toString(codeStatus) + " Message = " + messageStatus);
192 if(! postResponse.isNull("Results")){
194 // Results are available. process them
195 // Results is a dictionary of the form
196 // {host :{status:s, group:g, message:m, hostname:h}, ...}
197 System.out.println("Processing results in response");
198 JSONObject results = postResponse.getJSONObject("Results");
199 System.out.println("Get JSON dictionary from Results ..");
200 Iterator<String> hosts = results.keys();
201 System.out.println("Iterating through hosts");
203 while(hosts.hasNext()){
204 String host = hosts.next();
205 System.out.println("Processing host = " + host);
208 JSONObject hostResponse = results.getJSONObject(host);
209 int subCode = hostResponse.getInt(STATUS_CODE_KEY);
210 String message = hostResponse.getString(STATUS_MESSAGE_KEY);
212 System.out.println("Code = " + Integer.toString(subCode) + " Message = " + message);
214 if(subCode != 200 || ! message.equals("SUCCESS")){
215 finalCode = AnsibleResultCodes.REQ_FAILURE.getValue();
218 catch(JSONException e){
219 ansibleResult.setStatusCode(AnsibleResultCodes.INVALID_RESPONSE.getValue());
220 ansibleResult.setStatusMessage(String.format("Error processing response message = %s from host %s", results.getString(host), host));
225 ansibleResult.setStatusCode(finalCode);
227 // We return entire Results object as message
228 ansibleResult.setResults(results.toString());
232 ansibleResult.setStatusCode(AnsibleResultCodes.INVALID_RESPONSE.getValue());
233 ansibleResult.setStatusMessage("Results not found in GET for response");
238 catch(JSONException e){
239 ansibleResult = new AnsibleResult(AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error parsing response = " + input + ". Error = " + e.getMessage(), "");
243 return ansibleResult;
246 private void parseOptionalParams(Map<String, String> params, String[] optionalTestParams, JSONObject jsonPayload) {
248 Set<String> optionalParamsSet = new HashSet<>();
249 Collections.addAll(optionalParamsSet, optionalTestParams);
253 .filter(entry -> optionalParamsSet.contains(entry.getKey()))
254 .filter(entry -> !Strings.isNullOrEmpty(entry.getValue()))
255 .forEach(entry -> parseOptionalParam(entry, jsonPayload));
258 private void parseOptionalParam(Map.Entry<String, String> params, JSONObject jsonPayload) {
259 String key = params.getKey();
260 String payload = params.getValue();
263 case TIMEOUT_OPT_KEY:
264 int timeout = Integer.parseInt(payload);
266 throw new NumberFormatException(" : specified negative integer for timeout = " + payload);
268 jsonPayload.put(key, payload);
271 case VERSION_OPT_KEY:
272 jsonPayload.put(key, payload);
275 case LOCAL_PARAMETERS_OPT_KEY:
276 case ENV_PARAMETERS_OPT_KEY:
277 JSONObject paramsJson = new JSONObject(payload);
278 jsonPayload.put(key, paramsJson);
281 case NODE_LIST_OPT_KEY:
282 JSONArray paramsArray = new JSONArray(payload);
283 jsonPayload.put(key, paramsArray);
286 case FILE_PARAMETERS_OPT_KEY:
287 // Files may have strings with newlines. Escape them as appropriate
288 String formattedPayload = payload.replace("\n", "\\n").replace("\r", "\\r");
289 JSONObject fileParams = new JSONObject(formattedPayload);
290 jsonPayload.put(key, fileParams);
298 private void throwIfMissingMandatoryParam(Map<String, String> params, String key) throws APPCException {
299 if (!params.containsKey(key)) {
300 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));
302 if (Strings.isNullOrEmpty(params.get(key))) {
303 throw new APPCException(String.format("Ansible: Mandatory AnsibleAdapter key %s not found in parameters provided by calling agent !", key));