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;
31 import org.onap.appc.configuration.Configuration;
32 import org.onap.appc.configuration.ConfigurationFactory;
33 import org.onap.appc.exceptions.APPCException;
35 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
36 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
38 import org.json.JSONObject;
39 import org.json.JSONException;
41 import org.onap.appc.adapter.ansible.AnsibleAdapter;
43 import org.onap.appc.adapter.ansible.model.AnsibleResult;
44 import org.onap.appc.adapter.ansible.model.AnsibleMessageParser;
45 import org.onap.appc.adapter.ansible.model.AnsibleResultCodes;
46 import org.onap.appc.adapter.ansible.model.AnsibleServerEmulator;
48 import com.att.eelf.configuration.EELFLogger;
49 import com.att.eelf.configuration.EELFManager;
54 * This class implements the {@link AnsibleAdapter} interface. This interface
55 * defines the behaviors that our service provides.
57 public class AnsibleAdapterImpl implements AnsibleAdapter {
60 * The constant used to define the adapter name in the mapped diagnostic
65 @SuppressWarnings("nls")
66 public static final String MDC_ADAPTER = "Ansible Adapter";
69 * The constant used to define the service name in the mapped diagnostic
72 @SuppressWarnings("nls")
73 public static final String MDC_SERVICE = "service";
76 * The constant for the status code for a failed outcome
78 @SuppressWarnings("nls")
79 public static final String OUTCOME_FAILURE = "failure";
82 * The constant for the status code for a successful outcome
84 @SuppressWarnings("nls")
85 public static final String OUTCOME_SUCCESS = "success";
90 private static final String ADAPTER_NAME = "Ansible Adapter";
94 * The logger to be used
96 private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
99 * A reference to the adapter configuration object.
101 private Configuration configuration;
105 * can Specify a X509 certificate file for use if required ...
106 * Must be initialized with setCertFile
108 private String certFile = "";
114 ConnectionBuilder http_client;
117 * Ansible API Message Handlers
119 private AnsibleMessageParser messageProcessor;
122 * indicator whether in test mode
124 private boolean testMode = false;
127 * server emulator object to be used if in test mode
129 private AnsibleServerEmulator testServer;
132 * This default constructor is used as a work around because the activator
133 * wasnt getting called
135 public AnsibleAdapterImpl() {
141 * @param props not used
143 public AnsibleAdapterImpl(Properties props) {
149 * Used for jUnit test and testing interface
151 public AnsibleAdapterImpl(boolean Mode) {
153 testServer = new AnsibleServerEmulator();
154 messageProcessor = new AnsibleMessageParser();
158 * Returns the symbolic name of the adapter
160 * @return The adapter name
161 * @see org.onap.appc.adapter.rest.AnsibleAdapter#getAdapterName()
164 public String getAdapterName() {
170 * @param rc Method posts info to Context memory in case of an error
171 * and throws a SvcLogicException causing SLI to register this as a failure
173 @SuppressWarnings("static-method")
174 private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
176 svcLogic.setStatus(OUTCOME_FAILURE);
177 svcLogic.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(code));
178 svcLogic.setAttribute("org.onap.appc.adapter.ansible.message", message);
180 throw new SvcLogicException("Ansible Adapter Error = " + message);
185 * initialize the Ansible adapter based on default and over-ride configuration data
187 private void initialize() {
189 configuration = ConfigurationFactory.getConfiguration();
190 Properties props = configuration.getProperties();
192 // Create the message processor instance
193 messageProcessor = new AnsibleMessageParser();
195 // Create the http client instance
196 // type of client is extracted from the property file parameter
197 // org.onap.appc.adapter.ansible.clientType
199 // 1. TRUST_ALL (trust all SSL certs). To be used ONLY in dev
200 // 2. TRUST_CERT (trust only those whose certificates have been stored in the trustStore file)
201 // 3. DEFAULT (trust only well known certificates). This is standard behaviour to which it will
202 // revert. To be used in PROD
205 String clientType = props.getProperty("org.onap.appc.adapter.ansible.clientType");
206 logger.info("Ansible http client type set to " + clientType);
208 if (clientType.equals("TRUST_ALL")) {
209 logger.info("Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
210 http_client = new ConnectionBuilder(1);
211 } else if (clientType.equals("TRUST_CERT")) {
212 // set path to keystore file
213 String trustStoreFile = props.getProperty("org.onap.appc.adapter.ansible.trustStore");
214 String key = props.getProperty("org.onap.appc.adapter.ansible.trustStore.trustPasswd");
215 char[] trustStorePasswd = key.toCharArray();
216 String trustStoreType = "JKS";
217 logger.info("Creating http client with trustmanager from " + trustStoreFile);
218 http_client = new ConnectionBuilder(trustStoreFile, trustStorePasswd);
220 logger.info("Creating http client with default behaviour");
221 http_client = new ConnectionBuilder(0);
223 } catch (Exception e) {
224 logger.error("Error Initializing Ansible Adapter due to Unknown Exception: reason = " + e.getMessage());
227 logger.info("Intitialized Ansible Adapter");
233 * set the certificate file if not a trusted/known CA
235 private void setCertFile(String CertFile) {
236 this.certFile = CertFile;
240 // Public Method to post request to execute playbook. Posts the following back
241 // to Svc context memory
242 // org.onap.appc.adapter.ansible.req.code : 100 if successful
243 // org.onap.appc.adapter.ansible.req.messge : any message
244 // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request
246 public void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
248 String PlaybookName = "";
250 String AgentUrl = "";
252 String Password = "";
255 JSONObject JsonPayload;
258 // create json object to send request
259 JsonPayload = messageProcessor.reqMessage(params);
261 AgentUrl = (String) JsonPayload.remove("AgentUrl");
262 User = (String) JsonPayload.remove("User");
263 Password = (String) JsonPayload.remove("Password");
264 Id = (String) JsonPayload.getString("Id");
265 payload = JsonPayload.toString();
266 logger.info("Updated Payload = " + payload);
267 } catch (APPCException e) {
268 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = " + e.getMessage());
269 } catch (JSONException e) {
270 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid JSON block. Reason = " + e.getMessage());
271 } catch (NumberFormatException e) {
272 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid parameter values. Reason = " + e.getMessage());
281 // post the test request
282 //---------------------------------------
283 logger.info("Posting request = " + payload + " to url = " + AgentUrl);
284 AnsibleResult testresult = postExecRequest(AgentUrl, payload, User, Password);
287 // Process if HTTP was successfull
288 if (testresult.getStatusCode() == 200) {
289 testresult = messageProcessor.parsePostResponse(testresult.getStatusMessage());
291 doFailure(ctx, testresult.getStatusCode(), "Error posting request. Reason = " + testresult.getStatusMessage());
294 code = testresult.getStatusCode();
295 message = testresult.getStatusMessage();
298 // Check status of test request returned by Agent
299 //-----------------------------------------------
300 if (code == AnsibleResultCodes.PENDING.getValue()) {
301 logger.info(String.format("Submission of Test %s successful.", PlaybookName));
302 // test request accepted. We are in asynchronous case
304 doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
306 } catch (APPCException e) {
307 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
311 ctx.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(code));
312 ctx.setAttribute("org.onap.appc.adapter.ansible.message", message);
313 ctx.setAttribute("org.onap.appc.adapter.ansible.Id", Id);
318 // Public method to query status of a specific request
319 // It blocks till the Ansible Server responds or the session times out
321 public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
328 ReqUri = messageProcessor.reqUriResult(params);
329 System.out.println("Got uri = " + ReqUri);
330 } catch (APPCException e) {
331 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to missing parameters. Reason = " + e.getMessage());
333 } catch (NumberFormatException e) {
334 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to invalid parameters value. Reason = " + e.getMessage());
343 // Try to retreive the test results (modify the url for that)
344 AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
345 code = testresult.getStatusCode();
346 message = testresult.getStatusMessage();
349 logger.info("Parsing response from Server = " + message);
350 // Valid HTTP. process the Ansible message
351 testresult = messageProcessor.parseGetResponse(message);
352 code = testresult.getStatusCode();
353 message = testresult.getStatusMessage();
354 results = testresult.getResults();
358 logger.info("Request response = " + message);
360 } catch (APPCException e) {
361 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving result : " + e.getMessage());
365 // We were able to get and process the results. Determine if playbook succeeded
367 if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()) {
368 message = String.format("Ansible Request %s finished with Result = %s, Message = %s", params.get("Id"), OUTCOME_SUCCESS, message);
369 logger.info(message);
371 logger.info(String.format("Ansible Request %s finished with Result %s, Message = %s", params.get("Id"), OUTCOME_FAILURE, message));
372 ctx.setAttribute("org.onap.appc.adapter.ansible.results", results);
373 doFailure(ctx, code, message);
378 ctx.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(400));
379 ctx.setAttribute("org.onap.appc.adapter.ansible.message", message);
380 ctx.setAttribute("org.onap.appc.adapter.ansible.results", results);
381 ctx.setStatus(OUTCOME_SUCCESS);
385 // Public method to get logs from plyabook execution for a specifcic request
386 // It blocks till the Ansible Server responds or the session times out
387 // very similar to reqExecResult
388 // logs are returned in the DG context variable org.onap.appc.adapter.ansible.log
390 public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
396 ReqUri = messageProcessor.reqUriLog(params);
397 logger.info("Retreiving results from " + ReqUri);
398 } catch (Exception e) {
399 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
407 // Try to retreive the test results (modify the url for that)
408 AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
409 code = testresult.getStatusCode();
410 message = testresult.getStatusMessage();
412 logger.info("Request output = " + message);
414 } catch (Exception e) {
415 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving output : " + e.getMessage());
418 ctx.setAttribute("org.onap.appc.adapter.ansible.log", message);
419 ctx.setStatus(OUTCOME_SUCCESS);
424 * Method that posts the request
427 private AnsibleResult postExecRequest(String AgentUrl, String Payload, String User, String Password) {
429 String reqOutput = "UNKNOWN";
432 AnsibleResult testresult;
435 http_client.setHttpContext(User, Password);
436 testresult = http_client.Post(AgentUrl, Payload);
438 testresult = testServer.Post(AgentUrl, Payload);
446 Method to query Ansible server
449 private AnsibleResult queryServer(String AgentUrl, String User, String Password) {
451 String testOutput = "UNKNOWN";
453 AnsibleResult testresult;
455 logger.info("Querying url = " + AgentUrl);
458 testresult = http_client.Get(AgentUrl);
460 testresult = testServer.Get(AgentUrl);