2 * ============LICENSE_START=======================================================
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
23 package org.onap.appc.workflow.impl;
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;
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;
54 import java.io.ObjectInputStream;
55 import java.io.IOException;
60 import java.text.SimpleDateFormat;
61 import java.util.Arrays;
62 import java.util.Enumeration;
64 import java.util.Properties;
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();
73 private WorkflowResolver workflowResolver = new WorkflowResolver(
74 configuration.getIntegerProperty("org.onap.appc.workflow.resolver.refresh_interval", 300)
77 public void setWorkflowResolver(WorkflowResolver workflowResolver){
78 this.workflowResolver = workflowResolver;
81 public void setSvcLogicServiceRef(SvcLogicService svcLogic) {
82 this.svcLogic = svcLogic;
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.
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
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());
106 WorkflowKey workflowKey =
107 workflowResolver.resolve(workflowRequest.getRequestContext().getAction().name(),
108 workflowRequest.getVnfContext().getType(), null,
109 workflowRequest.getRequestContext().getCommonHeader().getApiVer());
111 Properties workflowParams = new Properties();
112 String actionProperty;
113 String requestIdProperty;
115 if (!workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")) {
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.
123 populateDGContext(workflowParams, workflowRequest);
126 configuration.getProperty("org.onap.appc.workflow.action", String.valueOf(Constants.ACTION));
128 configuration.getProperty("org.onap.appc.workflow.request.id",
129 String.valueOf(Constants.REQUEST_ID));
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);
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());
153 Object payloadJson = workflowRequest.getRequestContext().getPayload();
154 if (payloadJson != null) {
156 Map<String, String> payloadProperties = ObjectMapper.map(payloadJson);
157 workflowParams.putAll(payloadProperties);
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;
171 logger.debug("DG parameters " + actionProperty + ":"
172 + workflowRequest.getRequestContext().getAction().name() + ", "
173 + requestIdProperty + ":"
174 + workflowRequest.getRequestContext().getCommonHeader().getRequestId() + ", "
175 + vfIdProperty + ":" + workflowRequest.getVnfContext().getId());
177 logger.debug("Starting DG Execution for request "
178 + workflowRequest.getRequestContext().getCommonHeader().getRequestId());
180 if (workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")) {
181 workflowParams.put("isBwcMode", "true");
183 workflowParams.put("isBwcMode", "false");
186 SVCLogicServiceExecute(workflowKey, workflowRequest.getRequestContext(), workflowParams,
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());
195 logger.trace("Exiting from executeWorkflow with (workflowResponse = "
196 + ObjectUtils.toString(workflowResponse.getResponseContext().getStatus().getMessage()) + ")");
197 return workflowResponse;
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() : "");
239 * Check if workflow (DG) exists in database
241 * @param workflowQueryParams workflow request with command and vnf Type
242 * @return True if workflow exists else False.
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()));
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);
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()));
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));
276 } catch (RuntimeException e) {
277 logger.error("Error querying workflow from database" + e.getMessage());
279 } catch (SvcLogicException e) {
280 logger.error("Error querying workflow from database" + e.getMessage());
281 throw new RuntimeException(e);
283 logger.trace("Exiting workflowExists");
284 return workflowExistsOutput;
288 private boolean isDGExists(WorkflowKey workflowKey) throws SvcLogicException {
289 return svcLogic.hasGraph(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync");
293 private void restartDbLibProvider() {
295 Bundle bundle = FrameworkUtil.getBundle(org.onap.ccsdk.sli.core.dblib.DbLibService.class);
299 //Thread.sleep(5000);
300 } catch (org.osgi.framework.BundleException be) {
301 logger.error("Error restarting db lib" + be.toString());
302 } /*catch (InterruptedException e) {
306 private SvcLogicGraph fetchGraph(String module, String rpc, String version, String mode, StringBuilder sbError)
308 DbLibService dbLibSvc = null;
310 ServiceReference sref = null;
311 BundleContext bctx = null;
313 Bundle bundle = FrameworkUtil.getBundle(WorkFlowManagerImpl.class);
315 if (bundle != null) {
316 bctx = bundle.getBundleContext();
319 sref = bctx.getServiceReference("org.onap.ccsdk.sli.core.dblib.DbLibService");
323 logger.warn("Could not find service reference for DBLIB service");
325 dbLibSvc = (DbLibService) bctx.getService(sref);
326 if (dbLibSvc == null) {
327 logger.warn("Could not find service reference for DBLIB service");
331 if (dbLibSvc == null)
334 logger.info("Retrieving graph(new)");
336 SvcLogicGraph retval = null;
337 CachedRowSet results = null;
339 String fetchVersionGraphSql = "SELECT graph FROM SVC_LOGIC"
340 + " WHERE module = ? AND rpc = ? AND mode = ? AND version = ?";
342 String fetchActiveGraphSql = "SELECT graph FROM SVC_LOGIC"
343 + " WHERE module = ? AND rpc = ? AND mode = ? AND active = 'Y'";
346 String fetchGraphStmt;
348 ArrayList<String> params = new ArrayList<>();
353 if (version == null) {
354 fetchGraphStmt = fetchActiveGraphSql;
357 fetchGraphStmt = fetchVersionGraphSql;
360 StringBuilder sqlBuilder = new StringBuilder(fetchGraphStmt);
362 results = dbLibSvc.getData(sqlBuilder.toString(), new ArrayList(params), "sdnctl");
364 if (results.next()) {
366 ObjectInputStream gStream = new ObjectInputStream(results.getBinaryStream("graph"));
368 Object graphObj = gStream.readObject();
371 if (graphObj instanceof SvcLogicGraph) {
372 retval = (SvcLogicGraph) graphObj;
374 logger.error("invalid type for graph " + graphObj.getClass().getName());
381 } catch (SQLException e) {
382 logger.error("query " + sqlBuilder + " :: " + e.getMessage() + " Will retry");
383 //sbError.append(DRIVER_ERROR);
385 } catch (IOException e) {
386 logger.error("IOException " + " :: " + e.getMessage() + " Will retry");
387 //sbError.append(DRIVER_ERROR);
389 } catch (Exception e) {
390 logger.error("Exception " + " :: " + e.getMessage() + " Will retry");
391 sbError.append(DRIVER_ERROR);
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");
406 StringBuilder sbError = new StringBuilder();
407 SvcLogicGraph graph = fetchGraph(module, rpc, version, mode, sbError);
408 if (sbError.toString().equals(DRIVER_ERROR))
410 restartDbLibProvider();
411 sbError = new StringBuilder();
412 graph = fetchGraph(module, rpc, version, mode, sbError);
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 + "]");
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());
429 private void SVCLogicServiceExecute(WorkflowKey workflowKey, RequestContext requestContext,
430 Properties workflowParams, WorkflowResponse workflowResponse) {
431 logger.trace("Entering SVCLogicServiceExecute");
433 Properties respProps = null;
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);
443 if (logger.isDebugEnabled()) {
444 stk = Arrays.toString(e.getStackTrace());
445 logger.debug("Error while executing DG " + e.getMessage() + stk);
447 logger.error("Error in DG", e.getMessage() + stk, e);
450 if (respProps != null) {
451 if (!requestContext.getCommonHeader().getApiVer().startsWith("1.")) {
452 fillResponseContextByOutputFieldsFromDgContext(workflowResponse.getResponseContext(), respProps);
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);
463 setWorkFlowResponseStatus(workflowResponse.getResponseContext(), commonStatus, specificStatusMessage,
466 logger.debug("DG Execution Status: " + commonStatus);
469 logger.trace("Exiting from SVCLogicServiceExecute");
473 * Filling response context by output.* fields from DG context. Works only for 2.* API version
475 * @param responseContext response context which you need to fill
476 * @param respProps DG context in a properties format
478 private void fillResponseContextByOutputFieldsFromDgContext(ResponseContext responseContext,
479 Properties respProps) {
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.")) {
487 if (key.equalsIgnoreCase("output.payload")) {
488 responseContext.setPayload(respProps.getProperty(key));
490 responseContext.addKeyValueToAdditionalContext(key, respProps.getProperty(key));
498 * Filling responseContext status code and message according to response messages and codes from DG.
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
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);
512 fillStatus(400, commonStatus, responseContext);
515 if (specificStatusCode != 0) {
516 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
518 fillStatus(401, specificStatusMessage, responseContext);
524 * filling responseContext by status code and status message
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
530 private void fillStatus(int code, String message, ResponseContext responseContext) {
531 responseContext.getStatus().setCode(code);
532 responseContext.getStatus().setMessage(message);