Change code in appc dispatcher for new LCMs in R6
[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-2019 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  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.appc.workflow.impl;
24
25 import org.apache.commons.lang.ObjectUtils;
26 import org.apache.commons.lang3.StringUtils;
27 import org.onap.appc.common.constant.Constants;
28 import org.onap.appc.configuration.Configuration;
29 import org.onap.appc.configuration.ConfigurationFactory;
30 import org.onap.appc.domainmodel.lcm.RequestContext;
31 import org.onap.appc.domainmodel.lcm.ResponseContext;
32 import org.onap.appc.util.ObjectMapper;
33 import org.onap.appc.workflow.WorkFlowManager;
34 import org.onap.appc.workflow.objects.WorkflowExistsOutput;
35 import org.onap.appc.workflow.objects.WorkflowRequest;
36 import org.onap.appc.workflow.objects.WorkflowResponse;
37 import com.att.eelf.configuration.EELFLogger;
38 import com.att.eelf.configuration.EELFManager;
39 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
40 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
41 import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService;
42
43 import org.osgi.framework.Bundle;
44 import org.osgi.framework.BundleContext;
45 import org.osgi.framework.FrameworkUtil;
46 import org.osgi.framework.ServiceReference;
47 import java.util.ArrayList;
48 import java.util.List;
49 import org.onap.ccsdk.sli.core.dblib.DbLibService;
50 import org.onap.ccsdk.sli.core.sli.SvcLogicGraph;
51 import javax.sql.rowset.CachedRowSet;
52 import java.sql.SQLException;
53 import java.sql.Blob;
54 import java.io.ObjectInputStream;
55 import java.io.IOException;
56 import org.slf4j.MDC;
57
58
59
60 import java.text.SimpleDateFormat;
61 import java.util.Arrays;
62 import java.util.Enumeration;
63 import java.util.Map;
64 import java.util.Properties;
65
66
67 public class WorkFlowManagerImpl implements WorkFlowManager{
68     private static final String DRIVER_ERROR = "DriverLoadError";
69     private SvcLogicService svcLogic = null;
70     private final EELFLogger logger = EELFManager.getInstance().getLogger(WorkFlowManagerImpl.class);
71     private final Configuration configuration = ConfigurationFactory.getConfiguration();
72
73     private  WorkflowResolver workflowResolver = new WorkflowResolver(
74             configuration.getIntegerProperty("org.onap.appc.workflow.resolver.refresh_interval", 300)
75     );
76
77     public void setWorkflowResolver(WorkflowResolver workflowResolver){
78         this.workflowResolver = workflowResolver;
79     }
80
81     public void setSvcLogicServiceRef(SvcLogicService svcLogic) {
82         this.svcLogic = svcLogic;
83     }
84
85     /**
86      * Execute workflow and return response.
87      * This method execute workflow with following steps.
88      * Retrieve workflow(DG) details - module, version and mode from database
89      * based on command and vnf Type from incoming request.
90      * Execute workflow (DG) using SVC Logic Service reference
91      * Return response of workflow (DG) to caller.
92      *
93      * @param workflowRequest workflow execution request which contains vnfType,
94      * command, requestId, targetId, payload and (optional) confID;
95      * @return Workflow Response which contains execution status and payload from DG if any
96      */
97
98     @Override
99     public WorkflowResponse executeWorkflow(WorkflowRequest workflowRequest) {
100         logger.trace("Entering to executeWorkflow with WorkflowRequest = "
101                 + ObjectUtils.toString(workflowRequest.toString()));
102         WorkflowResponse workflowResponse = new WorkflowResponse();
103         workflowResponse.setResponseContext(workflowRequest.getResponseContext());
104
105         try {
106             WorkflowKey workflowKey =
107                     workflowResolver.resolve(workflowRequest.getRequestContext().getAction().name(),
108                         workflowRequest.getVnfContext().getType(), null,
109                         workflowRequest.getRequestContext().getCommonHeader().getApiVer());
110
111             Properties workflowParams = new Properties();
112             String actionProperty;
113             String requestIdProperty;
114             String vfIdProperty;
115             if (!workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")) {
116                 /*
117                 The following method call (populateDGContext) populates DG context with the
118                 request parameters to maintain backward compatibility with old DGs,
119                 we are not altering the old way of passing (org.onap.appc.vnfId and so on..)
120                 This is still a temporary solution, the end solution should be agreed with
121                 all stakeholders and implemented.
122              */
123                 populateDGContext(workflowParams, workflowRequest);
124             } else {
125                 actionProperty =
126                         configuration.getProperty("org.onap.appc.workflow.action", String.valueOf(Constants.ACTION));
127                 requestIdProperty =
128                         configuration.getProperty("org.onap.appc.workflow.request.id",
129                         String.valueOf(Constants.REQUEST_ID));
130                 vfIdProperty =
131                         configuration.getProperty("org.onap.appc.workflow.vfid", String.valueOf(Constants.VF_ID));
132                 String vfTypeProperty =
133                         configuration.getProperty("org.onap.appc.workflow.vftype", String.valueOf(Constants.VF_TYPE));
134                 String apiVerProperty =
135                         configuration.getProperty("org.onap.appc.workflow.apiVersion",
136                         String.valueOf(Constants.API_VERSION));
137                 String originatorIdProperty =
138                         configuration.getProperty("org.onap.appc.workflow.originatorId", Constants.ORIGINATOR_ID);
139                 String subRequestId =
140                         configuration.getProperty("org.onap.appc.workflow.subRequestId", Constants.SUB_REQUEST_ID);
141
142                 workflowParams.put(actionProperty, workflowRequest.getRequestContext().getAction().name());
143                 workflowParams.put(requestIdProperty,
144                         workflowRequest.getRequestContext().getCommonHeader().getRequestId());
145                 workflowParams.put(vfIdProperty, workflowRequest.getVnfContext().getId());
146                 workflowParams.put(vfTypeProperty, workflowRequest.getVnfContext().getType());
147                 workflowParams.put(apiVerProperty, workflowRequest.getRequestContext().getCommonHeader().getApiVer());
148                 workflowParams.put(originatorIdProperty,
149                         workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
150                 workflowParams.put(subRequestId,
151                         workflowRequest.getRequestContext().getCommonHeader().getSubRequestId());
152
153                 Object payloadJson = workflowRequest.getRequestContext().getPayload();
154                 if (payloadJson != null) {
155                     try {
156                         Map<String, String> payloadProperties = ObjectMapper.map(payloadJson);
157                         workflowParams.putAll(payloadProperties);
158
159                         logger.debug("DG properties: " + workflowParams);
160                     } catch (Exception e) {
161                         logger.error("Error parsing payload json string", e);
162                         Properties workflowPrp = new Properties();
163                         workflowPrp.setProperty("error-message", "Error parsing payload json string");
164                         fillStatus(501, "Error parsing payload json string: " + e.getMessage(),
165                                 workflowRequest.getResponseContext());
166                         logger.trace("Exiting from executeWorkflow with (workflowResponse = "
167                                 + ObjectUtils.toString(workflowResponse) + ")");
168                         return workflowResponse;
169                     }
170                 }
171                 logger.debug("DG parameters " + actionProperty + ":"
172                         + workflowRequest.getRequestContext().getAction().name() + ", "
173                         + requestIdProperty + ":"
174                         + workflowRequest.getRequestContext().getCommonHeader().getRequestId() + ", "
175                         + vfIdProperty + ":" + workflowRequest.getVnfContext().getId());
176
177                 logger.debug("Starting DG Execution for request "
178                         + workflowRequest.getRequestContext().getCommonHeader().getRequestId());
179             }
180             if (workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")) {
181                 workflowParams.put("isBwcMode", "true");
182             } else {
183                 workflowParams.put("isBwcMode", "false");
184             }
185
186             SVCLogicServiceExecute(workflowKey, workflowRequest.getRequestContext(), workflowParams,
187                     workflowResponse);
188             logger.trace("Completed DG Execution for Request id: "
189                     + workflowRequest.getRequestContext().getCommonHeader().getRequestId() + " with response code: "
190                     + workflowResponse.getResponseContext().getStatus().getCode());
191         } catch (Exception e) {
192             logger.error("Error Executing DG " + e.getMessage(), e);
193             fillStatus(501, "Error Executing DG " + e.getMessage(), workflowRequest.getResponseContext());
194         }
195         logger.trace("Exiting from executeWorkflow with (workflowResponse = "
196                 + ObjectUtils.toString(workflowResponse.getResponseContext().getStatus().getMessage()) + ")");
197         return workflowResponse;
198     }
199
200     private void populateDGContext(Properties workflowParams, WorkflowRequest workflowRequest) {
201         workflowParams.put("input.common-header.timestamp",
202                 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
203                     .format(workflowRequest.getRequestContext().getCommonHeader().getTimeStamp()));
204         workflowParams.put("input.common-header.api-ver",
205                 workflowRequest.getRequestContext().getCommonHeader().getApiVer());
206         workflowParams.put("input.common-header.request-id",
207                 workflowRequest.getRequestContext().getCommonHeader().getRequestId());
208         workflowParams.put("input.common-header.originator-id",
209                 workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
210         workflowParams.put("input.common-header.sub-request-id",
211                 workflowRequest.getRequestContext().getCommonHeader().getSubRequestId() != null ?
212                 workflowRequest.getRequestContext().getCommonHeader().getSubRequestId() : "");
213         workflowParams.put("input.action", workflowRequest.getRequestContext().getAction().toString());
214         workflowParams.put("input.payload",
215                 null != workflowRequest.getRequestContext().getPayload() ?
216                 workflowRequest.getRequestContext().getPayload() : "");
217         workflowParams.put("input.action-identifiers.vnf-id", workflowRequest.getVnfContext().getId());
218         workflowParams.put("input.action-identifiers.vnfc-name",
219                 workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName() != null ?
220                 workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName() : "");
221         workflowParams.put("input.action-identifiers.service-instance-id",
222                 workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId() != null ?
223                 workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId() : "");
224         workflowParams.put("input.action-identifiers.vserver-id",
225                 workflowRequest.getRequestContext().getActionIdentifiers().getVserverId() != null ?
226                 workflowRequest.getRequestContext().getActionIdentifiers().getVserverId() : "");
227         workflowParams.put("input.action-identifiers.vf-module-id",
228                 workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId() != null ?
229                 workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId() : "");
230         final Map<String, String> additionalContext;
231         if ((additionalContext = workflowRequest.getRequestContext().getAdditionalContext()) != null) {
232             for (Map.Entry<String, String> entry : additionalContext.entrySet()) {
233                 workflowParams.put("input." + entry.getKey(), null != entry.getValue() ? entry.getValue() : "");
234     }
235         }
236     }
237
238     /**
239      * Check if workflow (DG) exists in database
240      *
241      * @param workflowQueryParams workflow request with command and vnf Type
242      * @return True if workflow exists else False.
243      */
244     @Override
245     public WorkflowExistsOutput workflowExists(WorkflowRequest workflowQueryParams) {
246         WorkflowExistsOutput workflowExistsOutput = new WorkflowExistsOutput(false, false);
247         logger.trace("Entering to workflowExists with WorkflowRequest = "
248                 + ObjectUtils.toString(workflowQueryParams.toString()));
249
250         try {
251             WorkflowKey workflowKey = workflowResolver.resolve(
252                     workflowQueryParams.getRequestContext().getAction().name(),
253                     workflowQueryParams.getVnfContext().getType(),
254                     workflowQueryParams.getVnfContext().getVersion(),
255                     workflowQueryParams.getRequestContext().getCommonHeader().getApiVer());
256             if (workflowKey != null) {
257                 workflowExistsOutput.setMappingExist(true);
258                 workflowExistsOutput.setWorkflowModule(workflowKey.module());
259                 workflowExistsOutput.setWorkflowName(workflowKey.name());
260                 workflowExistsOutput.setWorkflowVersion(workflowKey.version());
261                 if (isDGExists(workflowKey)) {
262                     workflowExistsOutput.setDgExist(true);
263                 } else {
264                     logger.warn(
265                             String.format("SLI doesn't have DG for resolved mapping entry:  "
266                                 + "DG module - '%s', DG name - '%s', DG version - '%s'",
267                                 workflowKey.module(), workflowKey.name(), workflowKey.version()));
268                 }
269             } else {
270                 logger.warn(
271                         String.format("Unable to resolve recipe matching action '%s', VNF type '%s'"
272                             + " and VNF version '%s'",
273                             workflowQueryParams.getRequestContext().getAction().name(),
274                             workflowQueryParams.getVnfContext().getType(), null));
275             }
276         } catch (RuntimeException e) {
277             logger.error("Error querying workflow from database" + e.getMessage());
278             throw e;
279         } catch (SvcLogicException e) {
280             logger.error("Error querying workflow from database" + e.getMessage());
281             throw new RuntimeException(e);
282         }
283         logger.trace("Exiting workflowExists");
284         return workflowExistsOutput;
285     }
286
287
288     private boolean isDGExists(WorkflowKey workflowKey) throws SvcLogicException {
289         return svcLogic.hasGraph(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync");
290     }
291
292
293     private void restartDbLibProvider() {
294
295         Bundle bundle = FrameworkUtil.getBundle(org.onap.ccsdk.sli.core.dblib.DbLibService.class);
296         try {
297             bundle.stop();
298             bundle.start();
299             //Thread.sleep(5000);
300         } catch (org.osgi.framework.BundleException be) {
301             logger.error("Error restarting db lib" + be.toString());
302         } /*catch (InterruptedException e) {
303         }*/
304     }
305
306     private SvcLogicGraph fetchGraph(String module, String rpc, String version, String mode, StringBuilder sbError)
307     {
308         DbLibService dbLibSvc = null;
309
310         ServiceReference sref = null;
311         BundleContext bctx = null;
312
313         Bundle bundle = FrameworkUtil.getBundle(WorkFlowManagerImpl.class);
314
315         if (bundle != null) {
316             bctx = bundle.getBundleContext();
317
318             if (bctx != null) {
319                 sref = bctx.getServiceReference("org.onap.ccsdk.sli.core.dblib.DbLibService");
320             }
321
322             if (sref == null) {
323                 logger.warn("Could not find service reference for DBLIB service");
324             } else {
325                 dbLibSvc = (DbLibService) bctx.getService(sref);
326                 if (dbLibSvc == null) {
327                     logger.warn("Could not find service reference for DBLIB service");
328                 }
329             }
330         }
331         if (dbLibSvc == null)
332             return null;
333         else
334             logger.info("Retrieving graph(new)");
335
336         SvcLogicGraph retval = null;
337         CachedRowSet results = null;
338
339         String fetchVersionGraphSql = "SELECT graph FROM SVC_LOGIC"
340                 + " WHERE module = ? AND rpc = ? AND mode = ? AND version = ?";
341
342         String fetchActiveGraphSql = "SELECT graph FROM SVC_LOGIC"
343                 + " WHERE module = ? AND rpc = ? AND mode = ? AND active = 'Y'";
344
345
346         String fetchGraphStmt;
347
348         ArrayList<String> params = new ArrayList<>();
349         params.add(module);
350         params.add(rpc);
351         params.add(mode);
352
353         if (version == null) {
354             fetchGraphStmt = fetchActiveGraphSql;
355         } else {
356             params.add(version);
357             fetchGraphStmt = fetchVersionGraphSql;
358         }
359
360         StringBuilder sqlBuilder = new StringBuilder(fetchGraphStmt);
361         try {
362             results = dbLibSvc.getData(sqlBuilder.toString(), new ArrayList(params), "sdnctl");
363
364             if (results.next()) {
365
366                 ObjectInputStream gStream = new ObjectInputStream(results.getBinaryStream("graph"));
367
368                 Object graphObj = gStream.readObject();
369                 gStream.close();
370
371                 if (graphObj instanceof SvcLogicGraph) {
372                     retval = (SvcLogicGraph) graphObj;
373                 } else {
374                     logger.error("invalid type for graph " + graphObj.getClass().getName());
375                     return null;
376                 }
377
378             } else {
379                 return null;
380             }
381         } catch (SQLException e) {
382             logger.error("query " + sqlBuilder + " :: " + e.getMessage() + " Will retry");
383             //sbError.append(DRIVER_ERROR);
384             return null;
385         } catch (IOException e) {
386             logger.error("IOException "  + " :: " + e.getMessage() + " Will retry");
387             //sbError.append(DRIVER_ERROR);
388             return null;
389         } catch (Exception e) {
390             logger.error("Exception "  + " :: " + e.getMessage() + " Will retry");
391             sbError.append(DRIVER_ERROR);
392             return null;
393         }
394
395
396         return retval;
397     }
398
399     protected Properties workflowExecute(String module, String rpc, String version, String mode, Properties props)
400             throws SvcLogicException {
401         logger.info("Fetching service logic from data store");
402         //logger.info("Trial Restart ");
403         //restartDbLibProvider();
404         //logger.info("Trial Restart End");
405
406         StringBuilder sbError = new StringBuilder();
407         SvcLogicGraph graph = fetchGraph(module, rpc, version, mode, sbError);
408         if (sbError.toString().equals(DRIVER_ERROR))
409         {
410             restartDbLibProvider();
411             sbError = new StringBuilder();
412             graph = fetchGraph(module, rpc, version, mode, sbError);
413         }
414         if (graph == null) {
415             Properties retProps = new Properties();
416             retProps.setProperty("error-code", "401");
417             retProps.setProperty("error-message",
418                     "No service logic found for [" + module + "," + rpc + "," + version + "," + mode + "]");
419             return (retProps);
420         }
421
422         SvcLogicContext ctx = new SvcLogicContext(props);
423         ctx.setAttribute("currentGraph", graph.toString());
424         ctx.setAttribute("X-ONAP-RequestID", MDC.get("X-ONAP-RequestID"));
425         svcLogic.execute(graph, ctx);
426         return (ctx.toProperties());
427     }
428
429     private void SVCLogicServiceExecute(WorkflowKey workflowKey, RequestContext requestContext,
430             Properties workflowParams, WorkflowResponse workflowResponse) {
431         logger.trace("Entering SVCLogicServiceExecute");
432
433         Properties respProps = null;
434
435         try {
436             respProps = workflowExecute(workflowKey.module(), workflowKey.name(), workflowKey.version(),
437                     "sync", workflowParams);
438         } catch (Exception e) {
439             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), "failure",
440                     "Unexpected SLI Adapter failure", 200);
441
442             String stk = "";
443             if (logger.isDebugEnabled()) {
444                 stk = Arrays.toString(e.getStackTrace());
445                 logger.debug("Error while executing DG " + e.getMessage() + stk);
446             }
447             logger.error("Error in DG", e.getMessage() + stk, e);
448         }
449
450         if (respProps != null) {
451             if (!requestContext.getCommonHeader().getApiVer().startsWith("1.")) {
452                 fillResponseContextByOutputFieldsFromDgContext(workflowResponse.getResponseContext(), respProps);
453             }
454
455             final String commonStatus = respProps.getProperty(Constants.DG_ATTRIBUTE_STATUS);
456             final String specificStatusMessage = respProps.getProperty(Constants.DG_OUTPUT_STATUS_MESSAGE);
457             String dgOutputStatusCode = respProps.getProperty(Constants.DG_OUTPUT_STATUS_CODE);
458             int specificStatusCode = 0;
459             if (dgOutputStatusCode != null) {
460                 specificStatusCode = Integer.parseInt(dgOutputStatusCode);
461             }
462
463             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), commonStatus, specificStatusMessage,
464                     specificStatusCode);
465
466             logger.debug("DG Execution Status: " + commonStatus);
467         }
468
469         logger.trace("Exiting from SVCLogicServiceExecute");
470     }
471
472     /**
473      * Filling response context by output.* fields from DG context. Works only for 2.* API version
474      *
475      * @param responseContext response context which you need to fill
476      * @param respProps DG context in a properties format
477      */
478     private void fillResponseContextByOutputFieldsFromDgContext(ResponseContext responseContext,
479             Properties respProps) {
480
481         Enumeration<?> e = respProps.propertyNames();
482         while (e.hasMoreElements()) {
483             String key = (String) e.nextElement();
484             if (key.startsWith("output.")) {
485                 if (!key.startsWith("output.common-header.") && !key.startsWith("output.status.")) {
486
487                     if (key.equalsIgnoreCase("output.payload")) {
488                         responseContext.setPayload(respProps.getProperty(key));
489                     } else {
490                         responseContext.addKeyValueToAdditionalContext(key, respProps.getProperty(key));
491                     }
492                 }
493             }
494         }
495     }
496
497     /**
498      * Filling responseContext status code and message according to response messages and codes from DG.
499      *
500      * @param responseContext response cotext
501      * @param commonStatus common status message from DG ("success" or "failure")
502      * @param specificStatusMessage specific status message from specific DG node
503      * @param specificStatusCode specific status code from specific DG node
504      */
505     private void setWorkFlowResponseStatus(ResponseContext responseContext, String commonStatus,
506             String specificStatusMessage, int specificStatusCode) {
507         if (null == specificStatusMessage) { specificStatusMessage = ""; }
508         if (commonStatus.equalsIgnoreCase(Constants.DG_STATUS_SUCCESS)) {
509             if (specificStatusCode != 0) {
510                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
511             } else {
512                 fillStatus(400, commonStatus, responseContext);
513             }
514         } else {
515             if (specificStatusCode != 0) {
516                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
517             } else {
518                 fillStatus(401, specificStatusMessage, responseContext);
519             }
520         }
521     }
522
523     /**
524      * filling responseContext by status code and status message
525      *
526      * @param code 3-digit status code
527      * @param message explanation of a status code
528      * @param responseContext response context which will be store status code and status message
529      */
530     private void fillStatus(int code, String message, ResponseContext responseContext) {
531         responseContext.getStatus().setCode(code);
532         responseContext.getStatus().setMessage(message);
533     }
534
535
536 }