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 {
53 * The constant used to define the service name in the mapped diagnostic context
55 @SuppressWarnings("nls")
56 public static final String MDC_SERVICE = "service";
59 * The constant for the status code for a failed outcome
61 @SuppressWarnings("nls")
62 public static final String OUTCOME_FAILURE = "failure";
65 * The constant for the status code for a successful outcome
67 @SuppressWarnings("nls")
68 public static final String OUTCOME_SUCCESS = "success";
73 private static final String ADAPTER_NAME = "Ansible Adapter";
74 private static final String APPC_EXCEPTION_CAUGHT = "APPCException caught";
76 private static final String RESULT_CODE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.result.code";
77 private static final String MESSAGE_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.message";
78 private static final String RESULTS_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.results";
79 private static final String ID_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.Id";
80 private static final String LOG_ATTRIBUTE_NAME = "org.onap.appc.adapter.ansible.log";
82 private static final String CLIENT_TYPE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.clientType";
83 private static final String TRUSTSTORE_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore";
84 private static final String TRUSTPASSWD_PROPERTY_NAME = "org.onap.appc.adapter.ansible.trustStore.trustPasswd";
86 private static final String PASSWORD = "Password";
89 * The logger to be used
91 private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
94 * A reference to the adapter configuration object.
96 private Configuration configuration;
101 private ConnectionBuilder httpClient;
104 * Ansible API Message Handlers
106 private AnsibleMessageParser messageProcessor;
109 * indicator whether in test mode
111 private boolean testMode = false;
114 * server emulator object to be used if in test mode
116 private AnsibleServerEmulator testServer;
119 * This default constructor is used as a work around because the activator wasn't getting called
121 public AnsibleAdapterImpl() {
126 * Used for jUnit test and testing interface
128 public AnsibleAdapterImpl(boolean mode) {
130 testServer = new AnsibleServerEmulator();
131 messageProcessor = new AnsibleMessageParser();
135 * Returns the symbolic name of the adapter
137 * @return The adapter name
138 * @see org.onap.appc.adapter.rest.AnsibleAdapter#getAdapterName()
141 public String getAdapterName() {
146 * @param rc Method posts info to Context memory in case of an error and throws a
147 * SvcLogicException causing SLI to register this as a failure
149 @SuppressWarnings("static-method")
150 private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
152 svcLogic.setStatus(OUTCOME_FAILURE);
153 svcLogic.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
154 svcLogic.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
156 throw new SvcLogicException("Ansible Adapter Error = " + message);
160 * initialize the Ansible adapter based on default and over-ride configuration data
162 private void initialize() {
164 configuration = ConfigurationFactory.getConfiguration();
165 Properties props = configuration.getProperties();
167 // Create the message processor instance
168 messageProcessor = new AnsibleMessageParser();
170 // Create the http client instance
171 // type of client is extracted from the property file parameter
172 // org.onap.appc.adapter.ansible.clientType
174 // 1. TRUST_ALL (trust all SSL certs). To be used ONLY in dev
175 // 2. TRUST_CERT (trust only those whose certificates have been stored in the trustStore file)
176 // 3. DEFAULT (trust only well known certificates). This is standard behavior to which it will
177 // revert. To be used in PROD
180 String clientType = props.getProperty(CLIENT_TYPE_PROPERTY_NAME);
181 logger.info("Ansible http client type set to " + clientType);
183 if ("TRUST_ALL".equals(clientType)) {
185 "Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
186 httpClient = new ConnectionBuilder(1);
187 } else if ("TRUST_CERT".equals(clientType)) {
188 // set path to keystore file
189 String trustStoreFile = props.getProperty(TRUSTSTORE_PROPERTY_NAME);
190 String key = props.getProperty(TRUSTPASSWD_PROPERTY_NAME);
191 char[] trustStorePasswd = key.toCharArray();
192 logger.info("Creating http client with trustmanager from " + trustStoreFile);
193 httpClient = new ConnectionBuilder(trustStoreFile, trustStorePasswd);
195 logger.info("Creating http client with default behaviour");
196 httpClient = new ConnectionBuilder(0);
198 } catch (Exception e) {
199 logger.error("Error Initializing Ansible Adapter due to Unknown Exception", e);
202 logger.info("Initialized Ansible Adapter");
205 // Public Method to post request to execute playbook. Posts the following back
206 // to Svc context memory
207 // org.onap.appc.adapter.ansible.req.code : 100 if successful
208 // org.onap.appc.adapter.ansible.req.messge : any message
209 // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request
211 public void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
213 String playbookName = StringUtils.EMPTY;
214 String payload = StringUtils.EMPTY;
215 String agentUrl = StringUtils.EMPTY;
216 String user = StringUtils.EMPTY;
217 String password = StringUtils.EMPTY;
218 String id = StringUtils.EMPTY;
220 JSONObject jsonPayload;
223 // create json object to send request
224 jsonPayload = messageProcessor.reqMessage(params);
226 agentUrl = (String) jsonPayload.remove("AgentUrl");
227 user = (String) jsonPayload.remove("User");
228 password = (String) jsonPayload.remove(PASSWORD);
229 id = jsonPayload.getString("Id");
230 payload = jsonPayload.toString();
231 logger.info("Updated Payload = " + payload);
232 } catch (APPCException e) {
233 logger.error(APPC_EXCEPTION_CAUGHT, e);
234 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
235 "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = "
237 } catch (JSONException e) {
238 logger.error("JSONException caught", e);
239 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
240 "Error constructing request for execution of playbook due to invalid JSON block. Reason = "
242 } catch (NumberFormatException e) {
243 logger.error("NumberFormatException caught", e);
244 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
245 "Error constructing request for execution of playbook due to invalid parameter values. Reason = "
250 String message = StringUtils.EMPTY;
253 // post the test request
254 logger.info("Posting request = " + payload + " to url = " + agentUrl);
255 AnsibleResult testResult = postExecRequest(agentUrl, payload, user, password);
257 // Process if HTTP was successful
258 if (testResult.getStatusCode() == 200) {
259 testResult = messageProcessor.parsePostResponse(testResult.getStatusMessage());
261 doFailure(ctx, testResult.getStatusCode(),
262 "Error posting request. Reason = " + testResult.getStatusMessage());
265 code = testResult.getStatusCode();
266 message = testResult.getStatusMessage();
268 // Check status of test request returned by Agent
269 if (code == AnsibleResultCodes.PENDING.getValue()) {
270 logger.info(String.format("Submission of Test %s successful.", playbookName));
271 // test request accepted. We are in asynchronous case
273 doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
275 } catch (APPCException e) {
276 logger.error(APPC_EXCEPTION_CAUGHT, e);
277 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
278 "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
281 ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(code));
282 ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
283 ctx.setAttribute(ID_ATTRIBUTE_NAME, id);
287 * Public method to query status of a specific request It blocks till the Ansible Server
288 * responds or the session times out (non-Javadoc)
290 * @see org.onap.appc.adapter.ansible.AnsibleAdapter#reqExecResult(java.util.Map,
291 * org.onap.ccsdk.sli.core.sli.SvcLogicContext)
294 public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
297 String reqUri = StringUtils.EMPTY;
300 reqUri = messageProcessor.reqUriResult(params);
301 logger.info("Got uri ", reqUri );
302 } catch (APPCException e) {
303 logger.error(APPC_EXCEPTION_CAUGHT, e);
304 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
305 "Error constructing request to retrieve result due to missing parameters. Reason = "
308 } catch (NumberFormatException e) {
309 logger.error("NumberFormatException caught", e);
310 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(),
311 "Error constructing request to retrieve result due to invalid parameters value. Reason = "
317 String message = StringUtils.EMPTY;
318 String results = StringUtils.EMPTY;
321 // Try to retrieve the test results (modify the URL for that)
322 AnsibleResult testResult = queryServer(reqUri, params.get("User"), params.get(PASSWORD));
323 code = testResult.getStatusCode();
324 message = testResult.getStatusMessage();
327 logger.info("Parsing response from Server = " + message);
328 // Valid HTTP. process the Ansible message
329 testResult = messageProcessor.parseGetResponse(message);
330 code = testResult.getStatusCode();
331 message = testResult.getStatusMessage();
332 results = testResult.getResults();
335 logger.info("Request response = " + message);
336 } catch (APPCException e) {
337 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
338 "Exception encountered retrieving result : " + e.getMessage());
342 // We were able to get and process the results. Determine if playbook succeeded
344 if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()) {
345 message = String.format("Ansible Request %s finished with Result = %s, Message = %s", params.get("Id"),
346 OUTCOME_SUCCESS, message);
347 logger.info(message);
349 logger.info(String.format("Ansible Request %s finished with Result %s, Message = %s", params.get("Id"),
350 OUTCOME_FAILURE, message));
351 ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
352 doFailure(ctx, code, message);
356 ctx.setAttribute(RESULT_CODE_ATTRIBUTE_NAME, Integer.toString(400));
357 ctx.setAttribute(MESSAGE_ATTRIBUTE_NAME, message);
358 ctx.setAttribute(RESULTS_ATTRIBUTE_NAME, results);
359 ctx.setStatus(OUTCOME_SUCCESS);
363 * Public method to get logs from playbook execution for a specific request
365 * It blocks till the Ansible Server responds or the session times out very similar to
366 * reqExecResult logs are returned in the DG context variable org.onap.appc.adapter.ansible.log
369 public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
371 String reqUri = StringUtils.EMPTY;
373 reqUri = messageProcessor.reqUriLog(params);
374 logger.info("Retrieving results from " + reqUri);
375 } catch (Exception e) {
376 logger.error("Exception caught", e);
377 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
380 String message = StringUtils.EMPTY;
382 // Try to retrieve the test results (modify the url for that)
383 AnsibleResult testResult = queryServer(reqUri, params.get("User"), params.get(PASSWORD));
384 message = testResult.getStatusMessage();
385 logger.info("Request output = " + message);
386 ctx.setAttribute(LOG_ATTRIBUTE_NAME, message);
387 ctx.setStatus(OUTCOME_SUCCESS);
388 } catch (Exception e) {
389 logger.error("Exception caught", e);
390 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(),
391 "Exception encountered retreiving output : " + e.getMessage());
396 * Method that posts the request
398 private AnsibleResult postExecRequest(String agentUrl, String payload, String user, String password) {
400 AnsibleResult testResult;
403 httpClient.setHttpContext(user, password);
404 testResult = httpClient.post(agentUrl, payload);
406 testResult = testServer.Post(agentUrl, payload);
412 * Method to query Ansible server
414 private AnsibleResult queryServer(String agentUrl, String user, String password) {
416 AnsibleResult testResult;
418 logger.info("Querying url = " + agentUrl);
421 testResult = httpClient.get(agentUrl);
423 testResult = testServer.Get(agentUrl);