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