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;
32 import org.onap.appc.configuration.Configuration;
33 import org.onap.appc.configuration.ConfigurationFactory;
34 import org.onap.appc.exceptions.APPCException;
36 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
37 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
41 import org.json.JSONObject;
42 import org.json.JSONArray;
43 import org.json.JSONException;
47 import org.onap.appc.adapter.ansible.AnsibleAdapter;
49 import org.onap.appc.adapter.ansible.model.AnsibleResult;
50 import org.onap.appc.adapter.ansible.model.AnsibleMessageParser;
51 import org.onap.appc.adapter.ansible.model.AnsibleResultCodes;
52 import org.onap.appc.adapter.ansible.model.AnsibleServerEmulator;
54 import com.att.eelf.configuration.EELFLogger;
55 import com.att.eelf.configuration.EELFManager;
56 import com.att.eelf.i18n.EELFResourceManager;
57 import static com.att.eelf.configuration.Configuration.*;
61 * This class implements the {@link AnsibleAdapter} interface. This interface
62 * defines the behaviors that our service provides.
65 public class AnsibleAdapterImpl implements AnsibleAdapter {
68 * The constant used to define the adapter name in the mapped diagnostic
73 @SuppressWarnings("nls")
74 public static final String MDC_ADAPTER = "Ansible Adapter";
77 * The constant used to define the service name in the mapped diagnostic
80 @SuppressWarnings("nls")
81 public static final String MDC_SERVICE = "service";
84 * The constant for the status code for a failed outcome
86 @SuppressWarnings("nls")
87 public static final String OUTCOME_FAILURE = "failure";
90 * The constant for the status code for a successful outcome
92 @SuppressWarnings("nls")
93 public static final String OUTCOME_SUCCESS = "success";
98 private static final String ADAPTER_NAME = "Ansible Adapter";
102 * The logger to be used
104 private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
107 * A reference to the adapter configuration object.
109 private Configuration configuration;;
111 /** can Specify a X509 certificate file for use if required ...
112 Must be initialized with setCertFile
114 private String certFile = "";
120 ConnectionBuilder http_client ;
123 * Ansible API Message Handlers
125 private AnsibleMessageParser messageProcessor;
128 indicator whether in test mode
130 private boolean testMode = false;
133 server emulator object to be used if in test mode
135 private AnsibleServerEmulator testServer;
138 * This default constructor is used as a work around because the activator
139 * wasnt getting called
141 public AnsibleAdapterImpl() {
150 public AnsibleAdapterImpl(Properties props) {
157 Used for jUnit test and testing interface
159 public AnsibleAdapterImpl(boolean Mode){
161 testServer = new AnsibleServerEmulator();
162 messageProcessor = new AnsibleMessageParser();
166 * Returns the symbolic name of the adapter
168 * @return The adapter name
169 * @see org.onap.appc.adapter.rest.AnsibleAdapter#getAdapterName()
172 public String getAdapterName() {
180 * Method posts info to Context memory in case of an error
181 * and throws a SvcLogicException causing SLI to register this as a failure
183 @SuppressWarnings("static-method")
184 private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
186 svcLogic.setStatus(OUTCOME_FAILURE);
187 svcLogic.setAttribute("org.onap.appc.adapter.ansible.result.code",Integer.toString(code));
188 svcLogic.setAttribute("org.onap.appc.adapter.ansible.message",message);
190 throw new SvcLogicException("Ansible Adapter Error = " + message );
195 * initialize the Ansible adapter based on default and over-ride configuration data
197 private void initialize() {
199 configuration = ConfigurationFactory.getConfiguration();
200 Properties props = configuration.getProperties();
202 // Create the message processor instance
203 messageProcessor = new AnsibleMessageParser();
205 // Create the http client instance
206 // type of client is extracted from the property file parameter
207 // org.onap.appc.adapter.ansible.clientType
209 // 1. TRUST_ALL (trust all SSL certs). To be used ONLY in dev
210 // 2. TRUST_CERT (trust only those whose certificates have been stored in the trustStore file)
211 // 3. DEFAULT (trust only well known certificates). This is standard behaviour to which it will
212 // revert. To be used in PROD
215 String clientType = props.getProperty("org.onap.appc.adapter.ansible.clientType");
216 logger.info("Ansible http client type set to " + clientType);
218 if (clientType.equals("TRUST_ALL")){
219 logger.info("Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
220 http_client = new ConnectionBuilder(1);
222 else if (clientType.equals("TRUST_CERT")){
223 // set path to keystore file
224 String trustStoreFile = props.getProperty("org.onap.appc.adapter.ansible.trustStore");
225 String key = props.getProperty("org.onap.appc.adapter.ansible.trustStore.trustPasswd");
226 char [] trustStorePasswd = key.toCharArray();
227 String trustStoreType = "JKS";
228 logger.info("Creating http client with trustmanager from " + trustStoreFile);
229 http_client = new ConnectionBuilder(trustStoreFile, trustStorePasswd);
232 logger.info("Creating http client with default behaviour");
233 http_client = new ConnectionBuilder(0);
237 logger.error("Error Initializing Ansible Adapter due to Unknown Exception: reason = " + e.getMessage());
240 logger.info("Intitialized Ansible Adapter");
245 /** set the certificate file if not a trusted/known CA **/
246 private void setCertFile(String CertFile){
247 this.certFile = CertFile;
252 // Public Method to post request to execute playbook. Posts the following back
253 // to Svc context memory
254 // org.onap.appc.adapter.ansible.req.code : 100 if successful
255 // org.onap.appc.adapter.ansible.req.messge : any message
256 // org.onap.appc.adapter.ansible.req.Id : a unique uuid to reference the request
258 public void reqExec(Map <String, String> params, SvcLogicContext ctx) throws SvcLogicException {
260 String PlaybookName = "";
262 String AgentUrl = "";
264 String Password = "";
267 JSONObject JsonPayload;
270 // create json object to send request
271 JsonPayload = messageProcessor.ReqMessage(params);
273 AgentUrl = (String) JsonPayload.remove("AgentUrl");
274 User = (String) JsonPayload.remove("User");
275 Password = (String) JsonPayload.remove("Password");
276 Id = (String)JsonPayload.getString("Id");
277 payload = JsonPayload.toString();
278 logger.info("Updated Payload = " + payload);
280 catch(APPCException e){
281 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = " + e.getMessage());
283 catch(JSONException e){
284 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid JSON block. Reason = " + e.getMessage());
286 catch(NumberFormatException e){
287 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid parameter values. Reason = " + e.getMessage());
297 // post the test request
298 //---------------------------------------
299 logger.info("Posting request = " + payload + " to url = " + AgentUrl );
300 AnsibleResult testresult = postExecRequest(AgentUrl, payload, User, Password);
303 // Process if HTTP was successfull
304 if(testresult.getStatusCode() == 200){
305 testresult = messageProcessor.parsePostResponse(testresult.getStatusMessage());
308 doFailure(ctx, testresult.getStatusCode(), "Error posting request. Reason = " + testresult.getStatusMessage());
312 code = testresult.getStatusCode();
313 message = testresult.getStatusMessage();
316 // Check status of test request returned by Agent
317 //-----------------------------------------------
318 if (code == AnsibleResultCodes.PENDING.getValue()){
319 logger.info(String.format("Submission of Test %s successful.", PlaybookName));
320 // test request accepted. We are in asynchronous case
323 doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
327 catch(APPCException e){
328 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
332 ctx.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(code));
333 ctx.setAttribute("org.onap.appc.adapter.ansible.message", message );
334 ctx.setAttribute("org.onap.appc.adapter.ansible.Id", Id);
339 // Public method to query status of a specific request
340 // It blocks till the Ansible Server responds or the session times out
342 public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
349 ReqUri = messageProcessor.ReqUri_Result(params);
350 System.out.println("Got uri = " + ReqUri);
352 catch(APPCException e){
353 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to missing parameters. Reason = " + e.getMessage());
356 catch(NumberFormatException e){
357 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to invalid parameters value. Reason = " + e.getMessage());
366 // Try to retreive the test results (modify the url for that)
367 AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
368 code = testresult.getStatusCode();
369 message = testresult.getStatusMessage();
372 logger.info("Parsing response from Server = " + message);
373 // Valid HTTP. process the Ansible message
374 testresult = messageProcessor.parseGetResponse(message);
375 code = testresult.getStatusCode();
376 message = testresult.getStatusMessage();
377 results = testresult.getResults();
381 logger.info("Request response = " + message);
384 catch (APPCException e){
385 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving result : " + e.getMessage());
389 // We were able to get and process the results. Determine if playbook succeeded
391 if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()){
392 message = String.format("Ansible Request %s finished with Result = %s, Message = %s", params.get("Id"), OUTCOME_SUCCESS, message);
393 logger.info(message);
396 logger.info(String.format("Ansible Request %s finished with Result %s, Message = %s", params.get("Id"), OUTCOME_FAILURE, message));
397 ctx.setAttribute("org.onap.appc.adapter.ansible.results", results);
398 doFailure(ctx, code, message );
403 ctx.setAttribute("org.onap.appc.adapter.ansible.result.code", Integer.toString(400));
404 ctx.setAttribute("org.onap.appc.adapter.ansible.message",message);
405 ctx.setAttribute("org.onap.appc.adapter.ansible.results", results);
406 ctx.setStatus(OUTCOME_SUCCESS);
410 // Public method to get logs from plyabook execution for a specifcic request
411 // It blocks till the Ansible Server responds or the session times out
412 // very similar to reqExecResult
413 // logs are returned in the DG context variable org.onap.appc.adapter.ansible.log
415 public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException{
421 ReqUri = messageProcessor.ReqUri_Log(params);
422 logger.info("Retreiving results from " + ReqUri);
425 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
433 // Try to retreive the test results (modify the url for that)
434 AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
435 code = testresult.getStatusCode();
436 message = testresult.getStatusMessage();
438 logger.info("Request output = " + message);
442 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving output : " + e.getMessage());
445 ctx.setAttribute("org.onap.appc.adapter.ansible.log",message);
446 ctx.setStatus(OUTCOME_SUCCESS);
454 * Method that posts the request
457 private AnsibleResult postExecRequest(String AgentUrl, String Payload, String User, String Password) {
459 String reqOutput = "UNKNOWN";
462 AnsibleResult testresult;
465 http_client.setHttpContext(User, Password);
466 testresult = http_client.Post(AgentUrl, Payload);
469 testresult = testServer.Post(AgentUrl, Payload);
477 Method to query Ansible server
480 private AnsibleResult queryServer(String AgentUrl, String User, String Password) {
482 String testOutput = "UNKNOWN";
484 AnsibleResult testresult;
486 logger.info("Querying url = " + AgentUrl);
489 testresult = http_client.Get(AgentUrl);
492 testresult = testServer.Get(AgentUrl);