Include impacted changes for APPC-346,APPC-348
[appc.git] / appc-dispatcher / appc-workflow-management / appc-workflow-management-core / src / main / java / org / onap / appc / workflow / impl / WorkFlowManagerImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
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
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
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.
20  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.appc.workflow.impl;
26
27 import org.apache.commons.lang.ObjectUtils;
28 import org.apache.commons.lang3.StringUtils;
29 import org.onap.appc.common.constant.Constants;
30 import org.onap.appc.configuration.Configuration;
31 import org.onap.appc.configuration.ConfigurationFactory;
32 import org.onap.appc.domainmodel.lcm.RequestContext;
33 import org.onap.appc.domainmodel.lcm.ResponseContext;
34 import org.onap.appc.util.ObjectMapper;
35 import org.onap.appc.workflow.WorkFlowManager;
36 import org.onap.appc.workflow.objects.WorkflowExistsOutput;
37 import org.onap.appc.workflow.objects.WorkflowRequest;
38 import org.onap.appc.workflow.objects.WorkflowResponse;
39 import com.att.eelf.configuration.EELFLogger;
40 import com.att.eelf.configuration.EELFManager;
41 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
42 import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService;
43
44 import java.text.SimpleDateFormat;
45 import java.util.Arrays;
46 import java.util.Enumeration;
47 import java.util.Map;
48 import java.util.Properties;
49
50
51 public class WorkFlowManagerImpl implements WorkFlowManager{
52     private SvcLogicService svcLogic = null;
53     private final EELFLogger logger = EELFManager.getInstance().getLogger(WorkFlowManagerImpl.class);
54     private final Configuration configuration = ConfigurationFactory.getConfiguration();
55
56     private  WorkflowResolver workflowResolver = new WorkflowResolver(
57             configuration.getIntegerProperty("org.onap.appc.workflow.resolver.refresh_interval", 300)
58     );
59
60     public void setWorkflowResolver(WorkflowResolver workflowResolver){
61         this.workflowResolver=workflowResolver;
62     }
63
64     public void setSvcLogicServiceRef(SvcLogicService svcLogic) {
65         this.svcLogic = svcLogic;
66     }
67
68     /**
69      * Execute workflow and return response.
70      * This method execute workflow with following steps.
71      * Retrieve workflow(DG) details - module, version and mode  from database based on command and vnf Type from incoming request.
72      * Execute workflow (DG) using SVC Logic Service reference
73      * Return response of workflow (DG) to caller.
74      *
75      * @param workflowRequest workflow execution request which contains vnfType, command, requestId, targetId, payload and (optional) confID;
76      * @return Workflow Response which contains execution status and payload from DG if any
77      */
78
79     @Override
80     public WorkflowResponse executeWorkflow(WorkflowRequest workflowRequest) {
81         if (logger.isTraceEnabled()) {
82             logger.trace("Entering to executeWorkflow with WorkflowRequest = "+ ObjectUtils.toString(workflowRequest.toString()));
83         }
84         WorkflowResponse workflowResponse = new WorkflowResponse();
85         workflowResponse.setResponseContext(workflowRequest.getResponseContext());
86
87         try {
88
89
90             WorkflowKey workflowKey = workflowResolver.resolve(workflowRequest.getRequestContext().getAction().name(), workflowRequest.getVnfContext().getType(), null,workflowRequest.getRequestContext().getCommonHeader().getApiVer());
91
92             Properties workflowParams = new Properties();
93             String actionProperty;
94             String requestIdProperty;
95             String vfIdProperty;
96             if(!workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")){
97                 /*
98                 The following method call (populateDGContext) populates DG context with the
99                 request parameters to maintain backward compatibility with old DGs,
100                  we are not altering the old way of passing (org.onap.appc.vnfId and so on..)
101                 This is still a temporary solution, the end solution should be agreed with
102                 all stakeholders and implemented.
103              */
104                 populateDGContext(workflowParams,workflowRequest);
105             } else {
106                 actionProperty = configuration.getProperty("org.onap.appc.workflow.action", String.valueOf(Constants.ACTION));
107                 requestIdProperty = configuration.getProperty("org.onap.appc.workflow.request.id", String.valueOf(Constants.REQUEST_ID));
108                 vfIdProperty = configuration.getProperty("org.onap.appc.workflow.vfid", String.valueOf(Constants.VF_ID));
109                 String vfTypeProperty = configuration.getProperty("org.onap.appc.workflow.vftype", String.valueOf(Constants.VF_TYPE));
110                 String apiVerProperty = configuration.getProperty("org.onap.appc.workflow.apiVersion", String.valueOf(Constants.API_VERSION));
111                 String originatorIdProperty = configuration.getProperty("org.onap.appc.workflow.originatorId",Constants.ORIGINATOR_ID);
112                 String subRequestId = configuration.getProperty("org.onap.appc.workflow.subRequestId",Constants.SUB_REQUEST_ID);
113
114                 workflowParams.put(actionProperty,workflowRequest.getRequestContext().getAction().name());
115                 workflowParams.put(requestIdProperty, workflowRequest.getRequestContext().getCommonHeader().getRequestId());
116                 workflowParams.put(vfIdProperty, workflowRequest.getVnfContext().getId());
117                 workflowParams.put(vfTypeProperty,workflowRequest.getVnfContext().getType());
118                 workflowParams.put(apiVerProperty,workflowRequest.getRequestContext().getCommonHeader().getApiVer());
119                 workflowParams.put(originatorIdProperty,workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
120                 workflowParams.put(subRequestId,workflowRequest.getRequestContext().getCommonHeader().getSubRequestId());
121
122                 Object payloadJson = workflowRequest.getRequestContext().getPayload();
123                 if(payloadJson!=null) {
124                     try {
125                         Map<String, String> payloadProperties = ObjectMapper.map(payloadJson);
126                         workflowParams.putAll(payloadProperties);
127
128                         if (logger.isDebugEnabled()) {
129                             logger.debug("DG properties: " + workflowParams);
130                         }
131                     } catch (Exception e) {
132                         logger.error("Error parsing payload json string", e);
133                         Properties workflowPrp = new Properties();
134                         workflowPrp.setProperty("error-message", "Error parsing payload json string");
135                         fillStatus(501, "Error parsing payload json string: "+e.getMessage(), workflowRequest.getResponseContext());
136                         if (logger.isTraceEnabled()) {
137                             logger.trace("Exiting from executeWorkflow with (workflowResponse = "+ObjectUtils.toString(workflowResponse)+")");
138                         }
139                         return workflowResponse;
140                     }
141                 }
142                 if (logger.isDebugEnabled()) {
143                     logger.debug("DG parameters "+ actionProperty +":"+ workflowRequest.getRequestContext().getAction().name()+", "+
144                             requestIdProperty +":"+ workflowRequest.getRequestContext().getCommonHeader().getRequestId()+", "+
145                             vfIdProperty +":"+ workflowRequest.getVnfContext().getId());
146
147                     logger.debug("Starting DG Execution for request "+workflowRequest.getRequestContext().getCommonHeader().getRequestId());
148                 }
149             }
150             if (workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")){
151                 workflowParams.put("isBwcMode","true");
152             } else {
153                 workflowParams.put("isBwcMode", "false");
154             }
155
156             SVCLogicServiceExecute(workflowKey, workflowRequest.getRequestContext(), workflowParams , workflowResponse);
157             if (logger.isTraceEnabled()) {
158                 logger.trace("Completed DG Execution for Request id: " + workflowRequest.getRequestContext().getCommonHeader().getRequestId() + "with response code: " + workflowResponse.getResponseContext().getStatus().getCode());
159             }
160         }catch (Exception e){
161             logger.error("Error Executing DG " +e.getMessage(),e);
162             fillStatus(501, "Error Executing DG "+e.getMessage(), workflowRequest.getResponseContext());
163         }
164         if (logger.isTraceEnabled()) {
165             logger.trace("Exiting from executeWorkflow with (workflowResponse = "+ ObjectUtils.toString(workflowResponse.getResponseContext().getStatus().getMessage())+")");
166         }
167         return workflowResponse;
168     }
169
170     private void populateDGContext(Properties workflowParams, WorkflowRequest workflowRequest) {
171         workflowParams.put("input.common-header.timestamp",new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(workflowRequest.getRequestContext().getCommonHeader().getTimeStamp()));
172         workflowParams.put("input.common-header.api-ver",workflowRequest.getRequestContext().getCommonHeader().getApiVer());
173         workflowParams.put("input.common-header.request-id",workflowRequest.getRequestContext().getCommonHeader().getRequestId());
174         workflowParams.put("input.common-header.originator-id",workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
175         workflowParams.put("input.common-header.sub-request-id",workflowRequest.getRequestContext().getCommonHeader().getSubRequestId()!=null ? workflowRequest.getRequestContext().getCommonHeader().getSubRequestId():"");
176         workflowParams.put("input.action",workflowRequest.getRequestContext().getAction().toString());
177         workflowParams.put("input.payload",null != workflowRequest.getRequestContext().getPayload() ? workflowRequest.getRequestContext().getPayload() : "");
178         workflowParams.put("input.action-identifiers.vnf-id",workflowRequest.getVnfContext().getId());
179         workflowParams.put("input.action-identifiers.vnfc-name",workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName():"");
180         workflowParams.put("input.action-identifiers.service-instance-id",workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId():"");
181         workflowParams.put("input.action-identifiers.vserver-id",workflowRequest.getRequestContext().getActionIdentifiers().getVserverId()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getVserverId():"");
182         workflowParams.put("input.action-identifiers.vf-module-id",workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId():"");
183         final Map<String, String> additionalContext;
184         if ((additionalContext = workflowRequest.getRequestContext().getAdditionalContext())!=null) {
185             for (Map.Entry<String, String> entry : additionalContext.entrySet()) {
186                 workflowParams.put("input." + entry.getKey(), null != entry.getValue() ? entry.getValue() : "");
187     }
188         }
189     }
190
191     /**
192      * Check if workflow (DG) exists in database
193      *
194      * @param workflowQueryParams workflow request with command and vnf Type
195      * @return True if workflow exists else False.
196      */
197     @Override
198     public WorkflowExistsOutput workflowExists(WorkflowRequest workflowQueryParams) {
199         WorkflowExistsOutput workflowExistsOutput = new WorkflowExistsOutput(false,false);
200         if (logger.isTraceEnabled()) {
201             logger.trace("Entering to workflowExists with WorkflowRequest = "+ObjectUtils.toString(workflowQueryParams.toString()));
202         }
203
204         try {
205             WorkflowKey workflowKey = workflowResolver.resolve(
206                     workflowQueryParams.getRequestContext().getAction().name(),
207                     workflowQueryParams.getVnfContext().getType(),
208                     workflowQueryParams.getVnfContext().getVersion(),
209                     workflowQueryParams.getRequestContext().getCommonHeader().getApiVer());
210             if (workflowKey != null) {
211                 workflowExistsOutput.setMappingExist(true);
212                 workflowExistsOutput.setWorkflowModule(workflowKey.module());
213                 workflowExistsOutput.setWorkflowName(workflowKey.name());
214                 workflowExistsOutput.setWorkflowVersion(workflowKey.version());
215                 if (isDGExists(workflowKey)) {
216                     workflowExistsOutput.setDgExist(true);
217                 }else{
218                     logger.warn(
219                             String.format("SLI doesn't have DG for resolved mapping entry:  DG module - '%s', DG name - '%s', DG version - '%s'",
220                                     workflowKey.module(), workflowKey.name(), workflowKey.version()));
221                 }
222             }else{
223                 logger.warn(
224                         String.format("Unable to resolve recipe matching action '%s', VNF type '%s' and VNF version '%s'",
225                                 workflowQueryParams.getRequestContext().getAction().name(), workflowQueryParams.getVnfContext().getType(), null));
226             }
227         } catch (RuntimeException e) {
228             logger.error("Error querying workflow from database"+e.getMessage());
229             throw e;
230         }catch (SvcLogicException e) {
231             logger.error("Error querying workflow from database"+e.getMessage());
232             throw new RuntimeException(e);
233         }
234         if (logger.isTraceEnabled()) {
235             logger.trace("Exiting workflowExists");
236         }
237         return workflowExistsOutput;
238     }
239
240
241     private boolean isDGExists(WorkflowKey workflowKey) throws SvcLogicException {
242         return svcLogic.hasGraph(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync");
243     }
244
245     private void SVCLogicServiceExecute(WorkflowKey workflowKey, RequestContext requestContext, Properties workflowParams, WorkflowResponse workflowResponse) {
246         if (logger.isTraceEnabled()) {
247             logger.trace("Entering SVCLogicServiceExecute");
248         }
249
250         Properties respProps = null;
251
252         try {
253             respProps = svcLogic.execute(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync", workflowParams);
254         } catch (Exception e) {
255             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), "failure", "Unexpected SLI Adapter failure", 200);
256
257             if (logger.isDebugEnabled()) {
258                 logger.debug("Error while executing DG " + e.getMessage() + e.getStackTrace());
259             }
260             logger.error("Error in DG", e.getMessage()+ Arrays.toString(e.getStackTrace()),e);
261         }
262
263         if (respProps != null) {
264             if (!requestContext.getCommonHeader().getApiVer().startsWith("1.")) {
265                 fillResponseContextByOutputFieldsFromDgContext(workflowResponse.getResponseContext(), respProps);
266             }
267
268             final String commonStatus = respProps.getProperty(Constants.DG_ATTRIBUTE_STATUS);
269             final String specificStatusMessage = respProps.getProperty(Constants.DG_OUTPUT_STATUS_MESSAGE);
270             String dgOutputStatusCode = respProps.getProperty(Constants.DG_OUTPUT_STATUS_CODE);
271             int specificStatusCode = 0;
272             if (dgOutputStatusCode != null) {
273                 specificStatusCode = Integer.parseInt(dgOutputStatusCode);
274             }
275
276             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), commonStatus, specificStatusMessage, specificStatusCode);
277
278             if (logger.isDebugEnabled()) {
279                 logger.debug("DG Execution Status: " + commonStatus);
280             }
281         }
282
283         if (logger.isTraceEnabled()) {
284             logger.trace("Exiting from SVCLogicServiceExecute");
285         }
286     }
287
288     /**
289      * Filling response context by output.* fields from DG context. Works only for 2.* API version
290      *
291      * @param responseContext response context which you need to fill
292      * @param respProps DG context in a properties format
293      */
294     private void fillResponseContextByOutputFieldsFromDgContext(ResponseContext responseContext, Properties respProps) {
295
296         Enumeration<?> e = respProps.propertyNames();
297         while (e.hasMoreElements()){
298             String key = (String) e.nextElement();
299             if (key.startsWith("output.")){
300                 if (!key.startsWith("output.common-header.") && !key.startsWith("output.status.")){
301
302                     if (key.equalsIgnoreCase("output.payload")){
303                         responseContext.setPayload(respProps.getProperty(key));
304                     } else {
305                         responseContext.addKeyValueToAdditionalContext(key, respProps.getProperty(key));
306         }
307                 }
308             }
309         }
310     }
311
312     /**
313      * Filling responceContext status code amd message according to responce messages and codes from DG.
314      *
315      * @param responseContext response cotext
316      * @param commonStatus common status message from DG ("success" or "failure")
317      * @param specificStatusMessage specific status message from specific DG node
318      * @param specificStatusCode specific status code from specific DG node
319      */
320     private void setWorkFlowResponseStatus(ResponseContext responseContext, String commonStatus, String specificStatusMessage, int specificStatusCode) {
321         if (null == specificStatusMessage) { specificStatusMessage = ""; }
322         if (commonStatus.equalsIgnoreCase(Constants.DG_STATUS_SUCCESS)){
323             if (specificStatusCode != 0 ){
324                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
325             } else {
326                 fillStatus(400, commonStatus, responseContext);
327             }
328         } else {
329             if (specificStatusCode != 0){
330                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
331             } else {
332                 fillStatus(401, specificStatusMessage, responseContext);
333             }
334         }
335     }
336
337     /**
338      * filling responseContext by status code and status message
339      *
340      * @param code 3-digit status code
341      * @param message explanation of a status code
342      * @param responceContext response context which will be store status code and status message
343      */
344     private void fillStatus(int code, String message, ResponseContext responceContext) {
345         responceContext.getStatus().setCode(code);
346         responceContext.getStatus().setMessage(message);
347     }
348
349
350 }