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