2  * ============LICENSE_START=======================================================
 
   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
 
  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.
 
  21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
 
  22  * ============LICENSE_END=========================================================
 
  25 package org.onap.appc.workflow.impl;
 
  27 import org.apache.commons.lang.ObjectUtils;
 
  28 import org.apache.commons.lang3.StringUtils;
 
  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;
 
  44 import java.text.SimpleDateFormat;
 
  45 import java.util.Arrays;
 
  46 import java.util.Enumeration;
 
  48 import java.util.Properties;
 
  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();
 
  56     private  WorkflowResolver workflowResolver = new WorkflowResolver(
 
  57             configuration.getIntegerProperty("org.onap.appc.workflow.resolver.refresh_interval", 300)
 
  60     public void setWorkflowResolver(WorkflowResolver workflowResolver){
 
  61         this.workflowResolver=workflowResolver;
 
  64     public void setSvcLogicServiceRef(SvcLogicService svcLogic) {
 
  65         this.svcLogic = svcLogic;
 
  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.
 
  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
 
  80     public WorkflowResponse executeWorkflow(WorkflowRequest workflowRequest) {
 
  81         if (logger.isTraceEnabled()) {
 
  82             logger.trace("Entering to executeWorkflow with WorkflowRequest = "+ ObjectUtils.toString(workflowRequest.toString()));
 
  84         WorkflowResponse workflowResponse = new WorkflowResponse();
 
  85         workflowResponse.setResponseContext(workflowRequest.getResponseContext());
 
  90             WorkflowKey workflowKey = workflowResolver.resolve(workflowRequest.getRequestContext().getAction().name(), workflowRequest.getVnfContext().getType(), null,workflowRequest.getRequestContext().getCommonHeader().getApiVer());
 
  92             Properties workflowParams = new Properties();
 
  93             String actionProperty;
 
  94             String requestIdProperty;
 
  96             if(!workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")){
 
  98                 The following method call (populateDGContext) populates DG context with the
 
  99                 request parameters to maintain backward compatibility with old DGs,
 
 100                  we are not altering the old way of passing (org.onap.appc.vnfId and so on..)
 
 101                 This is still a temporary solution, the end solution should be agreed with
 
 102                 all stakeholders and implemented.
 
 104                 populateDGContext(workflowParams,workflowRequest);
 
 106                 actionProperty = configuration.getProperty("org.onap.appc.workflow.action", String.valueOf(Constants.ACTION));
 
 107                 requestIdProperty = configuration.getProperty("org.onap.appc.workflow.request.id", String.valueOf(Constants.REQUEST_ID));
 
 108                 vfIdProperty = configuration.getProperty("org.onap.appc.workflow.vfid", String.valueOf(Constants.VF_ID));
 
 109                 String vfTypeProperty = configuration.getProperty("org.onap.appc.workflow.vftype", String.valueOf(Constants.VF_TYPE));
 
 110                 String apiVerProperty = configuration.getProperty("org.onap.appc.workflow.apiVersion", String.valueOf(Constants.API_VERSION));
 
 111                 String originatorIdProperty = configuration.getProperty("org.onap.appc.workflow.originatorId",Constants.ORIGINATOR_ID);
 
 112                 String subRequestId = configuration.getProperty("org.onap.appc.workflow.subRequestId",Constants.SUB_REQUEST_ID);
 
 114                 workflowParams.put(actionProperty,workflowRequest.getRequestContext().getAction().name());
 
 115                 workflowParams.put(requestIdProperty, workflowRequest.getRequestContext().getCommonHeader().getRequestId());
 
 116                 workflowParams.put(vfIdProperty, workflowRequest.getVnfContext().getId());
 
 117                 workflowParams.put(vfTypeProperty,workflowRequest.getVnfContext().getType());
 
 118                 workflowParams.put(apiVerProperty,workflowRequest.getRequestContext().getCommonHeader().getApiVer());
 
 119                 workflowParams.put(originatorIdProperty,workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
 
 120                 workflowParams.put(subRequestId,workflowRequest.getRequestContext().getCommonHeader().getSubRequestId());
 
 122                 Object payloadJson = workflowRequest.getRequestContext().getPayload();
 
 123                 if(payloadJson!=null) {
 
 125                         Map<String, String> payloadProperties = ObjectMapper.map(payloadJson);
 
 126                         workflowParams.putAll(payloadProperties);
 
 128                         if (logger.isDebugEnabled()) {
 
 129                             logger.debug("DG properties: " + workflowParams);
 
 131                     } catch (Exception e) {
 
 132                         logger.error("Error parsing payload json string", e);
 
 133                         Properties workflowPrp = new Properties();
 
 134                         workflowPrp.setProperty("error-message", "Error parsing payload json string");
 
 135                         fillStatus(501, "Error parsing payload json string: "+e.getMessage(), workflowRequest.getResponseContext());
 
 136                         if (logger.isTraceEnabled()) {
 
 137                             logger.trace("Exiting from executeWorkflow with (workflowResponse = "+ObjectUtils.toString(workflowResponse)+")");
 
 139                         return workflowResponse;
 
 142                 if (logger.isDebugEnabled()) {
 
 143                     logger.debug("DG parameters "+ actionProperty +":"+ workflowRequest.getRequestContext().getAction().name()+", "+
 
 144                             requestIdProperty +":"+ workflowRequest.getRequestContext().getCommonHeader().getRequestId()+", "+
 
 145                             vfIdProperty +":"+ workflowRequest.getVnfContext().getId());
 
 147                     logger.debug("Starting DG Execution for request "+workflowRequest.getRequestContext().getCommonHeader().getRequestId());
 
 150             if (workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")){
 
 151                 workflowParams.put("isBwcMode","true");
 
 153                 workflowParams.put("isBwcMode", "false");
 
 156             SVCLogicServiceExecute(workflowKey, workflowRequest.getRequestContext(), workflowParams , workflowResponse);
 
 157             if (logger.isTraceEnabled()) {
 
 158                 logger.trace("Completed DG Execution for Request id: " + workflowRequest.getRequestContext().getCommonHeader().getRequestId() + "with response code: " + workflowResponse.getResponseContext().getStatus().getCode());
 
 160         }catch (Exception e){
 
 161             logger.error("Error Executing DG " +e.getMessage(),e);
 
 162             fillStatus(501, "Error Executing DG "+e.getMessage(), workflowRequest.getResponseContext());
 
 164         if (logger.isTraceEnabled()) {
 
 165             logger.trace("Exiting from executeWorkflow with (workflowResponse = "+ ObjectUtils.toString(workflowResponse.getResponseContext().getStatus().getMessage())+")");
 
 167         return workflowResponse;
 
 170     private void populateDGContext(Properties workflowParams, WorkflowRequest workflowRequest) {
 
 171         workflowParams.put("input.common-header.timestamp",new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(workflowRequest.getRequestContext().getCommonHeader().getTimeStamp()));
 
 172         workflowParams.put("input.common-header.api-ver",workflowRequest.getRequestContext().getCommonHeader().getApiVer());
 
 173         workflowParams.put("input.common-header.request-id",workflowRequest.getRequestContext().getCommonHeader().getRequestId());
 
 174         workflowParams.put("input.common-header.originator-id",workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
 
 175         workflowParams.put("input.common-header.sub-request-id",workflowRequest.getRequestContext().getCommonHeader().getSubRequestId()!=null ? workflowRequest.getRequestContext().getCommonHeader().getSubRequestId():"");
 
 176         workflowParams.put("input.action",workflowRequest.getRequestContext().getAction().toString());
 
 177         workflowParams.put("input.payload",null != workflowRequest.getRequestContext().getPayload() ? workflowRequest.getRequestContext().getPayload() : "");
 
 178         workflowParams.put("input.action-identifiers.vnf-id",workflowRequest.getVnfContext().getId());
 
 179         workflowParams.put("input.action-identifiers.vnfc-name",workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName():"");
 
 180         workflowParams.put("input.action-identifiers.service-instance-id",workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId():"");
 
 181         workflowParams.put("input.action-identifiers.vserver-id",workflowRequest.getRequestContext().getActionIdentifiers().getVserverId()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getVserverId():"");
 
 182         workflowParams.put("input.action-identifiers.vf-module-id",workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId()!=null?workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId():"");
 
 183         final Map<String, String> additionalContext;
 
 184         if ((additionalContext = workflowRequest.getRequestContext().getAdditionalContext())!=null) {
 
 185             for (Map.Entry<String, String> entry : additionalContext.entrySet()) {
 
 186                 workflowParams.put("input." + entry.getKey(), null != entry.getValue() ? entry.getValue() : "");
 
 192      * Check if workflow (DG) exists in database
 
 194      * @param workflowQueryParams workflow request with command and vnf Type
 
 195      * @return True if workflow exists else False.
 
 198     public WorkflowExistsOutput workflowExists(WorkflowRequest workflowQueryParams) {
 
 199         WorkflowExistsOutput workflowExistsOutput = new WorkflowExistsOutput(false,false);
 
 200         if (logger.isTraceEnabled()) {
 
 201             logger.trace("Entering to workflowExists with WorkflowRequest = "+ObjectUtils.toString(workflowQueryParams.toString()));
 
 205             WorkflowKey workflowKey = workflowResolver.resolve(
 
 206                     workflowQueryParams.getRequestContext().getAction().name(),
 
 207                     workflowQueryParams.getVnfContext().getType(),
 
 208                     workflowQueryParams.getVnfContext().getVersion(),
 
 209                     workflowQueryParams.getRequestContext().getCommonHeader().getApiVer());
 
 210             if (workflowKey != null) {
 
 211                 workflowExistsOutput.setMappingExist(true);
 
 212                 workflowExistsOutput.setWorkflowModule(workflowKey.module());
 
 213                 workflowExistsOutput.setWorkflowName(workflowKey.name());
 
 214                 workflowExistsOutput.setWorkflowVersion(workflowKey.version());
 
 215                 if (isDGExists(workflowKey)) {
 
 216                     workflowExistsOutput.setDgExist(true);
 
 219                             String.format("SLI doesn't have DG for resolved mapping entry:  DG module - '%s', DG name - '%s', DG version - '%s'",
 
 220                                     workflowKey.module(), workflowKey.name(), workflowKey.version()));
 
 224                         String.format("Unable to resolve recipe matching action '%s', VNF type '%s' and VNF version '%s'",
 
 225                                 workflowQueryParams.getRequestContext().getAction().name(), workflowQueryParams.getVnfContext().getType(), null));
 
 227         } catch (RuntimeException e) {
 
 228             logger.error("Error querying workflow from database"+e.getMessage());
 
 230         }catch (SvcLogicException e) {
 
 231             logger.error("Error querying workflow from database"+e.getMessage());
 
 232             throw new RuntimeException(e);
 
 234         if (logger.isTraceEnabled()) {
 
 235             logger.trace("Exiting workflowExists");
 
 237         return workflowExistsOutput;
 
 241     private boolean isDGExists(WorkflowKey workflowKey) throws SvcLogicException {
 
 242         return svcLogic.hasGraph(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync");
 
 245     private void SVCLogicServiceExecute(WorkflowKey workflowKey, RequestContext requestContext, Properties workflowParams, WorkflowResponse workflowResponse) {
 
 246         if (logger.isTraceEnabled()) {
 
 247             logger.trace("Entering SVCLogicServiceExecute");
 
 250         Properties respProps = null;
 
 253             respProps = svcLogic.execute(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync", workflowParams);
 
 254         } catch (Exception e) {
 
 255             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), "failure", "Unexpected SLI Adapter failure", 200);
 
 257             if (logger.isDebugEnabled()) {
 
 258                 logger.debug("Error while executing DG " + e.getMessage() + e.getStackTrace());
 
 260             logger.error("Error in DG", e.getMessage()+ Arrays.toString(e.getStackTrace()),e);
 
 263         if (respProps != null) {
 
 264             if (!requestContext.getCommonHeader().getApiVer().startsWith("1.")) {
 
 265                 fillResponseContextByOutputFieldsFromDgContext(workflowResponse.getResponseContext(), respProps);
 
 268             final String commonStatus = respProps.getProperty(Constants.DG_ATTRIBUTE_STATUS);
 
 269             final String specificStatusMessage = respProps.getProperty(Constants.DG_OUTPUT_STATUS_MESSAGE);
 
 270             String dgOutputStatusCode = respProps.getProperty(Constants.DG_OUTPUT_STATUS_CODE);
 
 271             int specificStatusCode = 0;
 
 272             if (dgOutputStatusCode != null) {
 
 273                 specificStatusCode = Integer.parseInt(dgOutputStatusCode);
 
 276             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), commonStatus, specificStatusMessage, specificStatusCode);
 
 278             if (logger.isDebugEnabled()) {
 
 279                 logger.debug("DG Execution Status: " + commonStatus);
 
 283         if (logger.isTraceEnabled()) {
 
 284             logger.trace("Exiting from SVCLogicServiceExecute");
 
 289      * Filling response context by output.* fields from DG context. Works only for 2.* API version
 
 291      * @param responseContext response context which you need to fill
 
 292      * @param respProps DG context in a properties format
 
 294     private void fillResponseContextByOutputFieldsFromDgContext(ResponseContext responseContext, Properties respProps) {
 
 296         Enumeration<?> e = respProps.propertyNames();
 
 297         while (e.hasMoreElements()){
 
 298             String key = (String) e.nextElement();
 
 299             if (key.startsWith("output.")){
 
 300                 if (!key.startsWith("output.common-header.") && !key.startsWith("output.status.")){
 
 302                     if (key.equalsIgnoreCase("output.payload")){
 
 303                         responseContext.setPayload(respProps.getProperty(key));
 
 305                         responseContext.addKeyValueToAdditionalContext(key, respProps.getProperty(key));
 
 313      * Filling responceContext status code amd message according to responce messages and codes from DG.
 
 315      * @param responseContext response cotext
 
 316      * @param commonStatus common status message from DG ("success" or "failure")
 
 317      * @param specificStatusMessage specific status message from specific DG node
 
 318      * @param specificStatusCode specific status code from specific DG node
 
 320     private void setWorkFlowResponseStatus(ResponseContext responseContext, String commonStatus, String specificStatusMessage, int specificStatusCode) {
 
 321         if (null == specificStatusMessage) { specificStatusMessage = ""; }
 
 322         if (commonStatus.equalsIgnoreCase(Constants.DG_STATUS_SUCCESS)){
 
 323             if (specificStatusCode != 0 ){
 
 324                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
 
 326                 fillStatus(400, commonStatus, responseContext);
 
 329             if (specificStatusCode != 0){
 
 330                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
 
 332                 fillStatus(401, specificStatusMessage, responseContext);
 
 338      * filling responseContext by status code and status message
 
 340      * @param code 3-digit status code
 
 341      * @param message explanation of a status code
 
 342      * @param responceContext response context which will be store status code and status message
 
 344     private void fillStatus(int code, String message, ResponseContext responceContext) {
 
 345         responceContext.getStatus().setCode(code);
 
 346         responceContext.getStatus().setMessage(message);