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