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.impl;
28 import java.util.Properties;
29 import org.apache.commons.lang.StringUtils;
30 import org.json.JSONException;
31 import org.json.JSONObject;
32 import org.onap.appc.adapter.ansible.AnsibleAdapter;
33 import org.onap.appc.adapter.ansible.model.AnsibleMessageParser;
34 import org.onap.appc.adapter.ansible.model.AnsibleResult;
35 import org.onap.appc.adapter.ansible.model.AnsibleResultCodes;
36 import org.onap.appc.adapter.ansible.model.AnsibleServerEmulator;
37 import org.onap.appc.configuration.Configuration;
38 import org.onap.appc.configuration.ConfigurationFactory;
39 import org.onap.appc.exceptions.APPCException;
40 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
41 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
42 import com.att.eelf.configuration.EELFLogger;
43 import com.att.eelf.configuration.EELFManager;
46 * This class implements the {@link AnsibleAdapter} interface. This interface defines the behaviors
47 * that our service provides.
49 public class AnsibleAdapterImpl implements AnsibleAdapter {
52 * The constant used to define the adapter name in the mapped diagnostic context
54 @SuppressWarnings("nls")
55 public static final String MDC_ADAPTER = "Ansible Adapter";
58 * The constant used to define the service name in the mapped diagnostic context
60 @SuppressWarnings("nls")
61 public static final String MDC_SERVICE = "service";
64 * The constant for the status code for a failed outcome
66 @SuppressWarnings("nls")
67 public static final String OUTCOME_FAILURE = "failure";
70 * The constant for the status code for a successful outcome
72 @SuppressWarnings("nls")
73 public static final String OUTCOME_SUCCESS = "success";
78 private static final String ADAPTER_NAME = "Ansible Adapter";
80 private static final String RESULT_CODE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.result.code";
81 private static final String MESSAGE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.message";
82 private static final String RESULTS_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.results";
83 private static final String ID_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.Id";
84 private static final String LOG_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.log";
86 private static final String CLIENT_TYPE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.clientType";
87 private static final String TRUSTSTORE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore";
88 private static final String TRUSTPASSWD_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore.trustPasswd";
90 private static final String PASSWORD = "Password";
93 * The logger to be used
95 private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
98 * A reference to the adapter configuration object.
100 private Configuration configuration;
105 private ConnectionBuilder httpClient;
108 * Ansible API Message Handlers
110 private AnsibleMessageParser messageProcessor;
113 * indicator whether in test mode
115 private boolean testMode = false;
118 * server emulator object to be used if in test mode
120 private AnsibleServerEmulator testServer;
123 * This default constructor is used as a work around because the activator wasnt getting called
125 public AnsibleAdapterImpl() {
130 * Used for jUnit test and testing interface
132 public AnsibleAdapterImpl(boolean mode) {
134 testServer = new AnsibleServerEmulator();
135 messageProcessor = new AnsibleMessageParser();
139 * Returns the symbolic name of the adapter
141 * @return The adapter name
142 * @see org.onap.appc.adapter.rest.AnsibleAdapter#getAdapterName()
145 public String getAdapterName() {
150 * @param rc Method posts info to Context memory in case of an error and throws a
151 * SvcLogicException causing SLI to register this as a failure
153 @SuppressWarnings("static-method")
154 private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
156 svcLogic.setStatus(OUTCOME_FAILURE);
157 svcLogic.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
158 svcLogic.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
160 throw new SvcLogicException("Ansible Adapter Error = " + message);
164 * initialize the Ansible adapter based on default and over-ride configuration data
166 private void initialize() {
168 configuration = ConfigurationFactory.getConfiguration();
169 Properties props = configuration.getProperties();
171 // Create the message processor instance
172 messageProcessor = new AnsibleMessageParser();
174 // Create the http client instance
175 // type of client is extracted from the property file parameter
176 // org.onap.appc.adapter.ansible.clientType
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
184 String clientType = props.getProperty(CLIENT_TYPE_PROPERTY_NAME);
185 logger.info("Ansible http client type set to " + clientType);
187 if ("TRUST_ALL".equals(clientType)) {
189 "Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
190 httpClient = new ConnectionBuilder(1);
191 } else if ("TRUST_CERT".equals(clientType)) {
192 // set path to keystore file
193 String trustStoreFile = props.getProperty(TRUSTSTORE_PROPERTY_NAME);
194 String key = props.getProperty(TRUSTPASSWD_PROPERTY_NAME);
195 char[] trustStorePasswd = key.toCharArray();
196 logger.info("Creating http client with trustmanager from " + trustStoreFile);
197 httpClient = new ConnectionBuilder(trustStoreFile, trustStorePasswd);
199 logger.info("Creating http client with default behaviour");
200 httpClient = new ConnectionBuilder(0);
202 } catch (Exception e) {
203 logger.error("Error Initializing Ansible Adapter due to Unknown Exception", e);
206 logger.info("Intitialized Ansible Adapter");
209 // Public Method to post request to execute playbook. Posts the following back
210 // to Svc context memory
211 // org.onap.appc.adapter.ansible.req.code : 100 if successful
212 // org.onap.appc.adapter.ansible.req.messge : any message
213 // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request
215 public void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
217 String playbookName = StringUtils.EMPTY;
218 String payload = StringUtils.EMPTY;
219 String agentUrl = StringUtils.EMPTY;
220 String user = StringUtils.EMPTY;
221 String password = StringUtils.EMPTY;
222 String id = StringUtils.EMPTY;
224 JSONObject jsonPayload;
227 // create json object to send request
228 jsonPayload = messageProcessor.reqMessage(params);
230 agentUrl = (String) jsonPayload.remove("AgentUrl");
231 user = (String) jsonPayload.remove("User");
232 password = (String) jsonPayload.remove(PASSWORD);
233 id = jsonPayload.getString("Id");
234 payload = jsonPayload.toString();
235 logger.info("Updated Payload = " + payload);
236 } catch (APPCException e) {
237 logger.error("APPCException caught", e);
238 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
239 "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = "
241 } catch (JSONException e) {
242 logger.error("JSONException caught", e);
243 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
244 "Error constructing request for execution of playbook due to invalid JSON block. Reason = "
246 } catch (NumberFormatException e) {
247 logger.error("NumberFormateException caught", e);
248 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
249 "Error constructing request for execution of playbook due to invalid parameter values. Reason = "
254 String message = StringUtils.EMPTY;
257 // post the test request
258 logger.info("Posting request = " + payload + " to url = " + agentUrl);
259 AnsibleResult testresult = postExecRequest(agentUrl, payload, user, password);
261 // Process if HTTP was successful
262 if (testresult.getStatusCode() == 200) {
263 testresult = messageProcessor.parsePostResponse(testresult.getStatusMessage());
265 doFailure(ctx, testresult.getStatusCode(),
266 "Error posting request. Reason = " + testresult.getStatusMessage());
269 code = testresult.getStatusCode();
270 message = testresult.getStatusMessage();
272 // Check status of test request returned by Agent
273 if (code == AnsibleResultCodes.PENDING.getValue()) {
274 logger.info(String.format("Submission of Test %s successful.", playbookName));
275 // test request accepted. We are in asynchronous case
277 doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
279 } catch (APPCException e) {
280 logger.error("APPCException caught", e);
281 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
282 "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
285 ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
286 ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
287 ctx.setAttribute(ID_ATTRIBUTE_NAME, id);
291 * Public method to query status of a specific request It blocks till the Ansible Server
292 * responds or the session times out (non-Javadoc)
294 * @see org.onap.appc.adapter.ansible.AnsibleAdapter#reqExecResult(java.util.Map,
295 * org.onap.ccsdk.sli.core.sli.SvcLogicContext)
298 public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
301 String reqUri = StringUtils.EMPTY;
304 reqUri = messageProcessor.reqUriResult(params);
305 System.out.println("Got uri = " + reqUri);
306 } catch (APPCException e) {
307 logger.error("APPCException caught", e);
308 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
309 "Error constructing request to retreive result due to missing parameters. Reason = "
312 } catch (NumberFormatException e) {
313 logger.error("NumberFormatException caught", e);
314 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
315 "Error constructing request to retreive result due to invalid parameters value. Reason = "
321 String message = StringUtils.EMPTY;
322 String results = StringUtils.EMPTY;
325 // Try to retrieve the test results (modify the URL for that)
326 AnsibleResult testresult = queryServer(reqUri, params.get("User"), params.get(PASSWORD));
327 code = testresult.getStatusCode();
328 message = testresult.getStatusMessage();
331 logger.info("Parsing response from Server = " + message);
332 // Valid HTTP. process the Ansible message
333 testresult = messageProcessor.parseGetResponse(message);
334 code = testresult.getStatusCode();
335 message = testresult.getStatusMessage();
336 results = testresult.getResults();
340 logger.info("Request response = " + message);
341 } catch (APPCException e) {
342 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
343 "Exception encountered retreiving result : " + e.getMessage());
347 // We were able to get and process the results. Determine if playbook succeeded
349 if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()) {
350 message = String.format("Ansible Request %s finished with Result = %s, Message = %s", params.get("Id"),
351 OUTCOME_SUCCESS, message);
352 logger.info(message);
354 logger.info(String.format("Ansible Request %s finished with Result %s, Message = %s", params.get("Id"),
355 OUTCOME_FAILURE, message));
356 ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
357 doFailure(ctx, code, message);
361 ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(400));
362 ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
363 ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
364 ctx.setStatus(OUTCOME_SUCCESS);
368 * Public method to get logs from playbook execution for a specific request
370 * It blocks till the Ansible Server responds or the session times out very similar to
371 * reqExecResult logs are returned in the DG context variable org.onap.appc.adapter.ansible.log
374 public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
376 String reqUri = StringUtils.EMPTY;
378 reqUri = messageProcessor.reqUriLog(params);
379 logger.info("Retreiving results from " + reqUri);
380 } catch (Exception e) {
381 logger.error("Exception caught", e);
382 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
385 String message = StringUtils.EMPTY;
387 // Try to retrieve the test results (modify the url for that)
388 AnsibleResult testresult = queryServer(reqUri, params.get("User"), params.get(PASSWORD));
389 message = testresult.getStatusMessage();
390 logger.info("Request output = " + message);
391 ctx.setAttribute(LOG_ATTRIBUTE_NAME, message);
392 ctx.setStatus(OUTCOME_SUCCESS);
393 } catch (Exception e) {
394 logger.error("Exception caught", e);
395 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
396 "Exception encountered retreiving output : " + e.getMessage());
401 * Method that posts the request
403 private AnsibleResult postExecRequest(String agentUrl, String payload, String User, String password) {
405 AnsibleResult testresult;
408 httpClient.setHttpContext(User, password);
409 testresult = httpClient.post(agentUrl, payload);
411 testresult = testServer.Post(agentUrl, payload);
417 * Method to query Ansible server
419 private AnsibleResult queryServer(String agentUrl, String user, String password) {
421 AnsibleResult testresult;
423 logger.info("Querying url = " + agentUrl);
426 testresult = httpClient.get(agentUrl);
428 testresult = testServer.Get(agentUrl);