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