2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright (C) 2017 Amdocs
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
20 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 package org.openecomp.appc.adapter.ansible.impl;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Properties;
32 import java.util.regex.Pattern;
35 import org.openecomp.appc.Constants;
36 import org.openecomp.appc.exceptions.APPCException;
38 import org.openecomp.appc.configuration.Configuration;
39 import org.openecomp.appc.configuration.ConfigurationFactory;
40 import org.openecomp.appc.exceptions.APPCException;
41 import org.openecomp.appc.i18n.Msg;
42 import org.openecomp.appc.pool.Pool;
43 import org.openecomp.appc.pool.PoolExtensionException;
44 import org.openecomp.appc.util.StructuredPropertyHelper;
45 import org.openecomp.appc.util.StructuredPropertyHelper.Node;
47 import org.openecomp.sdnc.sli.SvcLogicContext;
48 import org.openecomp.sdnc.sli.SvcLogicException;
53 import org.json.JSONObject;
54 import org.json.JSONArray;
55 import org.json.JSONException;
58 import com.google.common.base.Strings;
59 //import com.google.gson.Gson;
60 //import com.google.gson.GsonBuilder;
62 import org.openecomp.appc.adapter.ansible.AnsibleAdapter;
64 import org.openecomp.appc.adapter.ansible.model.AnsibleResult;
65 import org.openecomp.appc.adapter.ansible.model.AnsibleMessageParser;
66 import org.openecomp.appc.adapter.ansible.model.AnsibleResultCodes;
67 import org.openecomp.appc.adapter.ansible.model.AnsibleServerEmulator;
69 import com.att.eelf.configuration.EELFLogger;
70 import com.att.eelf.configuration.EELFManager;
71 import com.att.eelf.i18n.EELFResourceManager;
72 import static com.att.eelf.configuration.Configuration.*;
76 * This class implements the {@link AnsibleAdapter} interface. This interface
77 * defines the behaviors that our service provides.
80 public class AnsibleAdapterImpl implements AnsibleAdapter {
83 * The constant used to define the adapter name in the mapped diagnostic
88 @SuppressWarnings("nls")
89 public static final String MDC_ADAPTER = "Ansible Adapter";
92 * The constant used to define the service name in the mapped diagnostic
95 @SuppressWarnings("nls")
96 public static final String MDC_SERVICE = "service";
99 * The constant for the status code for a failed outcome
101 @SuppressWarnings("nls")
102 public static final String OUTCOME_FAILURE = "failure";
105 * The constant for the status code for a successful outcome
107 @SuppressWarnings("nls")
108 public static final String OUTCOME_SUCCESS = "success";
113 private static final String ADAPTER_NAME = "Ansible Adapter";
117 * The logger to be used
119 private static final EELFLogger logger = EELFManager.getInstance().getLogger(AnsibleAdapterImpl.class);
122 * A reference to the adapter configuration object.
124 private Configuration configuration;;
126 /** can Specify a X509 certificate file for use if required ...
127 Must be initialized with setCertFile
129 private String certFile = "";
135 ConnectionBuilder http_client ;
138 * Ansible API Message Handlers
140 private AnsibleMessageParser messageProcessor;
143 indicator whether in test mode
145 private boolean testMode = false;
148 server emulator object to be used if in test mode
150 private AnsibleServerEmulator testServer;
153 * This default constructor is used as a work around because the activator
154 * wasnt getting called
156 public AnsibleAdapterImpl() {
165 public AnsibleAdapterImpl(Properties props) {
172 Used for jUnit test and testing interface
174 public AnsibleAdapterImpl(boolean Mode){
176 testServer = new AnsibleServerEmulator();
177 messageProcessor = new AnsibleMessageParser();
181 * Returns the symbolic name of the adapter
183 * @return The adapter name
184 * @see org.openecomp.appc.adapter.rest.AnsibleAdapter#getAdapterName()
187 public String getAdapterName() {
195 * Method posts info to Context memory in case of an error
196 * and throws a SvcLogicException causing SLI to register this as a failure
198 @SuppressWarnings("static-method")
199 private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException {
201 svcLogic.setStatus(OUTCOME_FAILURE);
202 svcLogic.setAttribute("org.openecomp.appc.adapter.ansible.result.code",Integer.toString(code));
203 svcLogic.setAttribute("org.openecomp.appc.adapter.ansible.message",message);
205 throw new SvcLogicException("Ansible Adapter Error = " + message );
210 * initialize the Ansible adapter based on default and over-ride configuration data
212 private void initialize() {
214 configuration = ConfigurationFactory.getConfiguration();
215 Properties props = configuration.getProperties();
217 // Create the message processor instance
218 messageProcessor = new AnsibleMessageParser();
220 // Create the http client instance
221 // type of client is extracted from the property file parameter
222 // org.openecomp.appc.adapter.ansible.clientType
224 // 1. TRUST_ALL (trust all SSL certs). To be used ONLY in dev
225 // 2. TRUST_CERT (trust only those whose certificates have been stored in the trustStore file)
226 // 3. DEFAULT (trust only well known certificates). This is standard behaviour to which it will
227 // revert. To be used in PROD
230 String clientType = props.getProperty("org.openecomp.appc.adapter.ansible.clientType");
231 logger.info("Ansible http client type set to " + clientType);
233 if (clientType.equals("TRUST_ALL")){
234 logger.info("Creating http client to trust ALL ssl certificates. WARNING. This should be done only in dev environments");
235 http_client = new ConnectionBuilder(1);
237 else if (clientType.equals("TRUST_CERT")){
238 // set path to keystore file
239 String trustStoreFile = props.getProperty("org.openecomp.appc.adapter.ansible.trustStore");
240 String key = props.getProperty("org.openecomp.appc.adapter.ansible.trustStore.trustPasswd");
241 char [] trustStorePasswd = key.toCharArray();
242 String trustStoreType = "JKS";
243 logger.info("Creating http client with trustmanager from " + trustStoreFile);
244 http_client = new ConnectionBuilder(trustStoreFile, trustStorePasswd);
247 logger.info("Creating http client with default behaviour");
248 http_client = new ConnectionBuilder(0);
252 logger.error("Error Initializing Ansible Adapter due to Unknown Exception: reason = " + e.getMessage());
255 logger.info("Intitialized Ansible Adapter");
260 /** set the certificate file if not a trusted/known CA **/
261 private void setCertFile(String CertFile){
262 this.certFile = CertFile;
267 // Public Method to post request to execute playbook. Posts the following back
268 // to Svc context memory
269 // org.openecomp.appc.adapter.ansible.req.code : 100 if successful
270 // org.openecomp.appc.adapter.ansible.req.messge : any message
271 // org.openecomp.appc.adapter.ansible.req.Id : a unique uuid to reference the request
273 public void reqExec(Map <String, String> params, SvcLogicContext ctx) throws SvcLogicException {
275 String PlaybookName = "";
277 String AgentUrl = "";
279 String Password = "";
282 JSONObject JsonPayload;
285 // create json object to send request
286 JsonPayload = messageProcessor.ReqMessage(params);
288 AgentUrl = (String) JsonPayload.remove("AgentUrl");
289 User = (String) JsonPayload.remove("User");
290 Password = (String) JsonPayload.remove("Password");
291 Id = (String)JsonPayload.getString("Id");
292 payload = JsonPayload.toString();
293 logger.info("Updated Payload = " + payload);
295 catch(APPCException e){
296 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to missing mandatory parameters. Reason = " + e.getMessage());
298 catch(JSONException e){
299 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid JSON block. Reason = " + e.getMessage());
301 catch(NumberFormatException e){
302 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request for execution of playbook due to invalid parameter values. Reason = " + e.getMessage());
312 // post the test request
313 //---------------------------------------
314 logger.info("Posting request = " + payload + " to url = " + AgentUrl );
315 AnsibleResult testresult = postExecRequest(AgentUrl, payload, User, Password);
318 // Process if HTTP was successfull
319 if(testresult.getStatusCode() == 200){
320 testresult = messageProcessor.parsePostResponse(testresult.getStatusMessage());
323 doFailure(ctx, testresult.getStatusCode(), "Error posting request. Reason = " + testresult.getStatusMessage());
327 code = testresult.getStatusCode();
328 message = testresult.getStatusMessage();
331 // Check status of test request returned by Agent
332 //-----------------------------------------------
333 if (code == AnsibleResultCodes.PENDING.getValue()){
334 logger.info(String.format("Submission of Test %s successful.", PlaybookName));
335 // test request accepted. We are in asynchronous case
338 doFailure(ctx, code, "Request for execution of playbook rejected. Reason = " + message);
342 catch(APPCException e){
343 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered when posting request for execution of playbook. Reason = " + e.getMessage());
347 ctx.setAttribute("org.openecomp.appc.adapter.ansible.result.code", Integer.toString(code));
348 ctx.setAttribute("org.openecomp.appc.adapter.ansible.message", message );
349 ctx.setAttribute("org.openecomp.appc.adapter.ansible.Id", Id);
354 // Public method to query status of a specific request
355 // It blocks till the Ansible Server responds or the session times out
357 public void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
364 ReqUri = messageProcessor.ReqUri_Result(params);
365 System.out.println("Got uri = " + ReqUri);
367 catch(APPCException e){
368 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to missing parameters. Reason = " + e.getMessage());
371 catch(NumberFormatException e){
372 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), "Error constructing request to retreive result due to invalid parameters value. Reason = " + e.getMessage());
381 // Try to retreive the test results (modify the url for that)
382 AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
383 code = testresult.getStatusCode();
384 message = testresult.getStatusMessage();
387 logger.info("Parsing response from Server = " + message);
388 // Valid HTTP. process the Ansible message
389 testresult = messageProcessor.parseGetResponse(message);
390 code = testresult.getStatusCode();
391 message = testresult.getStatusMessage();
392 results = testresult.getResults();
396 logger.info("Request response = " + message);
399 catch (APPCException e){
400 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving result : " + e.getMessage());
404 // We were able to get and process the results. Determine if playbook succeeded
406 if (code == AnsibleResultCodes.FINAL_SUCCESS.getValue()){
407 message = String.format("Ansible Request %s finished with Result = %s, Message = %s", params.get("Id"), OUTCOME_SUCCESS, message);
408 logger.info(message);
411 logger.info(String.format("Ansible Request %s finished with Result %s, Message = %s", params.get("Id"), OUTCOME_FAILURE, message));
412 ctx.setAttribute("org.openecomp.appc.adapter.ansible.results", results);
413 doFailure(ctx, code, message );
418 ctx.setAttribute("org.openecomp.appc.adapter.ansible.result.code", Integer.toString(400));
419 ctx.setAttribute("org.openecomp.appc.adapter.ansible.message",message);
420 ctx.setAttribute("org.openecomp.appc.adapter.ansible.results", results);
421 ctx.setStatus(OUTCOME_SUCCESS);
425 // Public method to get logs from plyabook execution for a specifcic request
426 // It blocks till the Ansible Server responds or the session times out
427 // very similar to reqExecResult
428 // logs are returned in the DG context variable org.openecomp.appc.adapter.ansible.log
430 public void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException{
436 ReqUri = messageProcessor.ReqUri_Log(params);
437 logger.info("Retreiving results from " + ReqUri);
440 doFailure(ctx, AnsibleResultCodes.INVALID_PAYLOAD.getValue(), e.getMessage());
448 // Try to retreive the test results (modify the url for that)
449 AnsibleResult testresult = queryServer(ReqUri, params.get("User"), params.get("Password"));
450 code = testresult.getStatusCode();
451 message = testresult.getStatusMessage();
453 logger.info("Request output = " + message);
457 doFailure(ctx, AnsibleResultCodes.UNKNOWN_EXCEPTION.getValue(), "Exception encountered retreiving output : " + e.getMessage());
460 ctx.setAttribute("org.openecomp.appc.adapter.ansible.log",message);
461 ctx.setStatus(OUTCOME_SUCCESS);
469 * Method that posts the request
472 private AnsibleResult postExecRequest(String AgentUrl, String Payload, String User, String Password) {
474 String reqOutput = "UNKNOWN";
477 AnsibleResult testresult;
480 http_client.setHttpContext(User, Password);
481 testresult = http_client.Post(AgentUrl, Payload);
484 testresult = testServer.Post(AgentUrl, Payload);
492 Method to query Ansible server
495 private AnsibleResult queryServer(String AgentUrl, String User, String Password) {
497 String testOutput = "UNKNOWN";
499 AnsibleResult testresult;
501 logger.info("Querying url = " + AgentUrl);
504 testresult = http_client.Get(AgentUrl);
507 testresult = testServer.Get(AgentUrl);