000415c93181064ad0f1951c00071971baa85ff7
[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  * Modifications (C) 2019 Ericsson
10  * =============================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  * 
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  * 
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * 
23  * ============LICENSE_END=========================================================
24  */
25
26 package org.onap.appc.workflow.impl;
27
28 import org.apache.commons.lang.ObjectUtils;
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             WorkflowKey workflowKey = workflowResolver.resolve(workflowRequest.getRequestContext().getAction().name(), workflowRequest.getVnfContext().getType(), null,workflowRequest.getRequestContext().getCommonHeader().getApiVer());
89
90             Properties workflowParams = new Properties();
91             String actionProperty;
92             String requestIdProperty;
93             String vfIdProperty;
94             if(!workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")){
95                 /*
96                 The following method call (populateDGContext) populates DG context with the
97                 request parameters to maintain backward compatibility with old DGs,
98                 we are not altering the old way of passing (org.onap.appc.vnfId and so on..)
99                 This is still a temporary solution, the end solution should be agreed with
100                 all stakeholders and implemented.
101                 */
102                 populateDGContext(workflowParams, workflowRequest);
103             } else {
104                 actionProperty = configuration.getProperty("org.onap.appc.workflow.action", String.valueOf(Constants.ACTION));
105                 requestIdProperty = configuration.getProperty("org.onap.appc.workflow.request.id", String.valueOf(Constants.REQUEST_ID));
106                 vfIdProperty = configuration.getProperty("org.onap.appc.workflow.vfid", String.valueOf(Constants.VF_ID));
107                 String vfTypeProperty = configuration.getProperty("org.onap.appc.workflow.vftype", String.valueOf(Constants.VF_TYPE));
108                 String apiVerProperty = configuration.getProperty("org.onap.appc.workflow.apiVersion", String.valueOf(Constants.API_VERSION));
109                 String originatorIdProperty = configuration.getProperty("org.onap.appc.workflow.originatorId", Constants.ORIGINATOR_ID);
110                 String subRequestId = configuration.getProperty("org.onap.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                         logger.debug("DG properties: " + workflowParams);
127                     } catch (Exception e) {
128                         logger.error("Error parsing payload json string", e);
129                         Properties workflowPrp = new Properties();
130                         workflowPrp.setProperty("error-message", "Error parsing payload json string");
131                         fillStatus(501, "Error parsing payload json string: " + e.getMessage(), workflowRequest.getResponseContext());
132                         logger.trace("Exiting from executeWorkflow with (workflowResponse = " + ObjectUtils.toString(workflowResponse) + ")");
133                         return workflowResponse;
134                     }
135                 }
136                 logger.debug("DG parameters "+ actionProperty +":"+ workflowRequest.getRequestContext().getAction().name() + ", "+
137                         requestIdProperty +":"+ workflowRequest.getRequestContext().getCommonHeader().getRequestId() + ", " +
138                         vfIdProperty + ":" + workflowRequest.getVnfContext().getId());
139
140                 logger.debug("Starting DG Execution for request "+workflowRequest.getRequestContext().getCommonHeader().getRequestId());
141             }
142             if (workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")){
143                 workflowParams.put("isBwcMode", "true");
144             } else {
145                 workflowParams.put("isBwcMode", "false");
146             }
147
148             SVCLogicServiceExecute(workflowKey, workflowRequest.getRequestContext(), workflowParams , workflowResponse);
149             logger.trace("Completed DG Execution for Request id: " + workflowRequest.getRequestContext().getCommonHeader().getRequestId()
150                     + "with response code: " + workflowResponse.getResponseContext().getStatus().getCode());
151         }catch (Exception e){
152             logger.error("Error Executing DG " + e.getMessage(), e);
153             fillStatus(501, "Error Executing DG "+ e.getMessage(), workflowRequest.getResponseContext());
154         }
155         logger.trace("Exiting from executeWorkflow with (workflowResponse = " +
156                 ObjectUtils.toString(workflowResponse.getResponseContext().getStatus().getMessage()) + ")");
157         return workflowResponse;
158     }
159
160     private void populateDGContext(Properties workflowParams, WorkflowRequest workflowRequest) {
161         workflowParams.put("input.common-header.timestamp", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(workflowRequest.getRequestContext().getCommonHeader().getTimeStamp()));
162         workflowParams.put("input.common-header.api-ver", workflowRequest.getRequestContext().getCommonHeader().getApiVer());
163         workflowParams.put("input.common-header.request-id", workflowRequest.getRequestContext().getCommonHeader().getRequestId());
164         workflowParams.put("input.common-header.originator-id", workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
165         workflowParams.put("input.common-header.sub-request-id", workflowRequest.getRequestContext().getCommonHeader().getSubRequestId() != null ?
166                 workflowRequest.getRequestContext().getCommonHeader().getSubRequestId() : "");
167         workflowParams.put("input.action", workflowRequest.getRequestContext().getAction().toString());
168         workflowParams.put("input.payload", null != workflowRequest.getRequestContext().getPayload() ?
169                 workflowRequest.getRequestContext().getPayload() : "");
170         workflowParams.put("input.action-identifiers.vnf-id", workflowRequest.getVnfContext().getId());
171         workflowParams.put("input.action-identifiers.vnfc-name", workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName() != null ?
172                 workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName() : "");
173         workflowParams.put("input.action-identifiers.service-instance-id", workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId() !=null ?
174                 workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId() : "");
175         workflowParams.put("input.action-identifiers.vserver-id", workflowRequest.getRequestContext().getActionIdentifiers().getVserverId() !=null ?
176                 workflowRequest.getRequestContext().getActionIdentifiers().getVserverId() : "");
177         workflowParams.put("input.action-identifiers.vf-module-id",workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId() !=null ?
178                 workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId() : "");
179         final Map<String, String> additionalContext;
180         if ((additionalContext = workflowRequest.getRequestContext().getAdditionalContext()) != null) {
181             for (Map.Entry<String, String> entry : additionalContext.entrySet()) {
182                 workflowParams.put("input." + entry.getKey(), null != entry.getValue() ? entry.getValue() : "");
183     }
184         }
185     }
186
187     /**
188      * Check if workflow (DG) exists in database
189      *
190      * @param workflowQueryParams workflow request with command and vnf Type
191      * @return True if workflow exists else False.
192      */
193     @Override
194     public WorkflowExistsOutput workflowExists(WorkflowRequest workflowQueryParams) {
195         WorkflowExistsOutput workflowExistsOutput = new WorkflowExistsOutput(false,false);
196         logger.trace("Entering to workflowExists with WorkflowRequest = " + ObjectUtils.toString(workflowQueryParams.toString()));
197
198         try {
199             WorkflowKey workflowKey = workflowResolver.resolve(
200                     workflowQueryParams.getRequestContext().getAction().name(),
201                     workflowQueryParams.getVnfContext().getType(),
202                     workflowQueryParams.getVnfContext().getVersion(),
203                     workflowQueryParams.getRequestContext().getCommonHeader().getApiVer());
204             if (workflowKey != null) {
205                 workflowExistsOutput.setMappingExist(true);
206                 workflowExistsOutput.setWorkflowModule(workflowKey.module());
207                 workflowExistsOutput.setWorkflowName(workflowKey.name());
208                 workflowExistsOutput.setWorkflowVersion(workflowKey.version());
209                 if (isDGExists(workflowKey)) {
210                     workflowExistsOutput.setDgExist(true);
211                 }else{
212                     logger.warn(
213                             String.format("SLI doesn't have DG for resolved mapping entry:  DG module - '%s', DG name - '%s', DG version - '%s'",
214                                     workflowKey.module(), workflowKey.name(), workflowKey.version()));
215                 }
216             }else{
217                 logger.warn(
218                         String.format("Unable to resolve recipe matching action '%s', VNF type '%s' and VNF version '%s'",
219                                 workflowQueryParams.getRequestContext().getAction().name(), workflowQueryParams.getVnfContext().getType(), null));
220             }
221         } catch (RuntimeException e) {
222             logger.error("Error querying workflow from database"+e.getMessage());
223             throw e;
224         }catch (SvcLogicException e) {
225             logger.error("Error querying workflow from database"+e.getMessage());
226             throw new RuntimeException(e);
227         }
228         logger.trace("Exiting workflowExists");
229         return workflowExistsOutput;
230     }
231
232
233     private boolean isDGExists(WorkflowKey workflowKey) throws SvcLogicException {
234         return svcLogic.hasGraph(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync");
235     }
236
237     private void SVCLogicServiceExecute(WorkflowKey workflowKey, RequestContext requestContext, Properties workflowParams, WorkflowResponse workflowResponse) {
238         logger.trace("Entering SVCLogicServiceExecute");
239
240         Properties respProps = null;
241
242         try {
243             respProps = svcLogic.execute(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync", workflowParams);
244         } catch (Exception e) {
245             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), "failure", "Unexpected SLI Adapter failure", 200);
246             if (logger.isDebugEnabled()) {
247                 logger.debug("Error while executing DG " + e.getMessage() + e.getStackTrace());
248                 logger.error("Error in DG", e.getMessage() + Arrays.toString(e.getStackTrace()), e);
249             }
250         }
251
252         if (respProps != null) {
253             if (!requestContext.getCommonHeader().getApiVer().startsWith("1.")) {
254                 fillResponseContextByOutputFieldsFromDgContext(workflowResponse.getResponseContext(), respProps);
255             }
256
257             final String commonStatus = respProps.getProperty(Constants.DG_ATTRIBUTE_STATUS);
258             final String specificStatusMessage = respProps.getProperty(Constants.DG_OUTPUT_STATUS_MESSAGE);
259             String dgOutputStatusCode = respProps.getProperty(Constants.DG_OUTPUT_STATUS_CODE);
260             int specificStatusCode = 0;
261             if (dgOutputStatusCode != null) {
262                 specificStatusCode = Integer.parseInt(dgOutputStatusCode);
263             }
264
265             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), commonStatus, specificStatusMessage, specificStatusCode);
266
267             logger.debug("DG Execution Status: " + commonStatus);
268         }
269
270         logger.trace("Exiting from SVCLogicServiceExecute");
271     }
272
273     /**
274      * Filling response context by output.* fields from DG context. Works only for 2.* API version
275      *
276      * @param responseContext response context which you need to fill
277      * @param respProps DG context in a properties format
278      */
279     private void fillResponseContextByOutputFieldsFromDgContext(ResponseContext responseContext, Properties respProps) {
280
281         Enumeration<?> e = respProps.propertyNames();
282         while (e.hasMoreElements()){
283             String key = (String) e.nextElement();
284             if (key.startsWith("output.")){
285                 if (!key.startsWith("output.common-header.") && !key.startsWith("output.status.")){
286
287                     if (key.equalsIgnoreCase("output.payload")){
288                         responseContext.setPayload(respProps.getProperty(key));
289                     } else {
290                         responseContext.addKeyValueToAdditionalContext(key, respProps.getProperty(key));
291         }
292                 }
293             }
294         }
295     }
296
297     /**
298      * Filling responceContext status code amd message according to responce messages and codes from DG.
299      *
300      * @param responseContext response cotext
301      * @param commonStatus common status message from DG ("success" or "failure")
302      * @param specificStatusMessage specific status message from specific DG node
303      * @param specificStatusCode specific status code from specific DG node
304      */
305     private void setWorkFlowResponseStatus(ResponseContext responseContext, String commonStatus, String specificStatusMessage, int specificStatusCode) {
306         if (null == specificStatusMessage) { specificStatusMessage = ""; }
307         if (commonStatus.equalsIgnoreCase(Constants.DG_STATUS_SUCCESS)){
308             if (specificStatusCode != 0 ){
309                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
310             } else {
311                 fillStatus(400, commonStatus, responseContext);
312             }
313         } else {
314             if (specificStatusCode != 0){
315                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
316             } else {
317                 fillStatus(401, specificStatusMessage, responseContext);
318             }
319         }
320     }
321
322     /**
323      * filling responseContext by status code and status message
324      *
325      * @param code 3-digit status code
326      * @param message explanation of a status code
327      * @param responceContext response context which will be store status code and status message
328      */
329     private void fillStatus(int code, String message, ResponseContext responceContext) {
330         responceContext.getStatus().setCode(code);
331         responceContext.getStatus().setMessage(message);
332     }
333 }