workflow app fixes

Change-Id: Ia3b06b7947671e4e38ca37eb832e6a969ed1e6af
This commit is contained in:
mohamedrahilr 2019-02-27 19:48:25 +05:30 committed by Jaegon Kim
parent 65cb23d676
commit 63a921cb33
6 changed files with 281 additions and 16 deletions

View File

@ -55,6 +55,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Constructor of ImmutableListWorkflow.
*
* @param builder builder of ImmutableListWorkflow
*/
private ImmutableListWorkflow(Builder builder) {
@ -148,7 +149,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
WorkflowStore store;
try {
store = DefaultServiceDirectory.getService(WorkflowStore.class);
store = DefaultServiceDirectory.getService(WorkflowStore.class);
} catch (ServiceNotFoundException e) {
throw new WorkflowException(e);
}
@ -179,8 +180,14 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
return ImmutableSet.copyOf(attributes);
}
@Override
public List<String> getWorkletTypeList() {
return ImmutableList.copyOf(workletTypeList);
}
/**
* Gets index of class in worklet type list.
*
* @param aClass class to get index
* @return index of class in worklet type list
*/
@ -195,6 +202,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Checks whether class is allowed class or not.
*
* @param clazz class to check
* @return Check result
*/
@ -245,6 +253,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Gets a instance of builder.
*
* @return instance of builder
*/
public static Builder builder() {
@ -263,6 +272,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Sets id of immutable list workflow.
*
* @param uri id of immutable list workflow
* @return builder
*/
@ -274,6 +284,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Sets init worklet class name of immutable list workflow.
*
* @param workletClassName class name of worklet
* @return builder
*/
@ -284,6 +295,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Chains worklet class name of immutable list workflow.
*
* @param workletClassName class name of worklet
* @return builder
*/
@ -294,6 +306,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Adds workflow attribute.
*
* @param attribute workflow attribute to be added
* @return builder
*/
@ -304,6 +317,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow {
/**
* Builds ImmutableListWorkflow.
*
* @return instance of ImmutableListWorkflow
*/
public ImmutableListWorkflow build() {

View File

@ -16,6 +16,7 @@
package org.onosproject.workflow.api;
import java.net.URI;
import java.util.List;
import java.util.Set;
/**
@ -84,4 +85,10 @@ public interface Workflow {
* @return attributes
*/
Set<WorkflowAttribute> attributes();
/**
* Returns worklet type list.
* @return worklet type
*/
List<String> getWorkletTypeList();
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2019-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.workflow.api;
import java.util.Map;
/**
* Workflow DataModel exception class.
*/
public class WorkflowDataModelException extends WorkflowException {
private String workflowName;
private Map<String, Map<String, String>> errorListMap;
/**
* Constructor for Workflow DataModel Exception.
*
* @param msg exception message
*/
public WorkflowDataModelException(String msg) {
super(msg);
}
/**
* Constructor for Workflow DataModel Exception.
*
* @param msg exception message
* @param workflowName workflow name
* @param errorListMap throwable to deliver
*/
public WorkflowDataModelException(String msg, String workflowName, Map<String, Map<String, String>> errorListMap) {
super(msg);
this.workflowName = workflowName;
this.errorListMap = errorListMap;
}
@Override
public String toString() {
return "WorkflowDataModelException{" +
"workflowName='" + workflowName + '\'' +
", errorListMap=" + errorListMap.toString() +
'}';
}
}

View File

@ -37,15 +37,16 @@ import java.util.Objects;
public class WorkFlowTestCommand extends AbstractShellCommand {
static final String INVOKE_SAMPLE = "invoke-sample";
static final String EXCEPTION_SAMPLE = "exception-sample";
@Argument(index = 0, name = "test-name",
description = "Test name (" + INVOKE_SAMPLE + ")",
description = "Test name (" + INVOKE_SAMPLE + " | " + EXCEPTION_SAMPLE + ")",
required = true)
@Completion(WorkFlowTestCompleter.class)
private String testName = null;
@Argument(index = 1, name = "arg1",
description = "number of test for " + INVOKE_SAMPLE,
description = "number of test for (" + INVOKE_SAMPLE + " | " + EXCEPTION_SAMPLE + ")",
required = false)
private String arg1 = null;
@ -76,6 +77,26 @@ public class WorkFlowTestCommand extends AbstractShellCommand {
invokeSampleTest(num);
break;
case EXCEPTION_SAMPLE:
if (Objects.isNull(arg1)) {
error("arg1 is required for test " + EXCEPTION_SAMPLE);
return;
}
int count;
try {
count = Integer.parseInt(arg1);
} catch (NumberFormatException e) {
error("arg1 should be an integer value");
return;
} catch (Exception e) {
error(e.getMessage() + ", trace: " + Arrays.asList(e.getStackTrace()));
return;
}
invokeExceptionTest(count);
break;
default:
print("Unsupported test-name: " + testName);
}
@ -83,6 +104,7 @@ public class WorkFlowTestCommand extends AbstractShellCommand {
/**
* Workflow invoke test_name.
*
* @param num the arg1 of workflow to test_name
*/
private void invokeSampleTest(int num) {
@ -94,9 +116,23 @@ public class WorkFlowTestCommand extends AbstractShellCommand {
}
}
/**
* Workflow datatype exception test.
*
* @param num the number of workflow to test
*/
private void invokeExceptionTest(int num) {
for (int i = 0; i <= num; i++) {
String wpName = "test-" + i;
invoke("sample.workflow-3", wpName);
}
}
/**
* Invokes workflow.
* @param workflowId workflow id
*
* @param workflowId workflow id
* @param workplaceName workplace name
*/
private void invoke(String workflowId, String workplaceName) {

View File

@ -15,22 +15,27 @@
*/
package org.onosproject.workflow.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.workflow.api.DefaultWorkplace;
import org.onosproject.workflow.api.JsonDataModelTree;
import org.onosproject.workflow.api.Workflow;
import org.onosproject.workflow.api.WorkflowContext;
import org.onosproject.workflow.api.WorkflowDescription;
import org.onosproject.workflow.api.WorkflowException;
import org.onosproject.workflow.api.WorkflowService;
import org.onosproject.workflow.api.WorkflowExecutionService;
import org.onosproject.workflow.api.WorkflowStore;
import org.onosproject.workflow.api.Workplace;
import org.onosproject.workflow.api.WorkplaceDescription;
import org.onosproject.workflow.api.WorkplaceStore;
import org.onosproject.workflow.api.WorkflowStore;
import org.onosproject.workflow.api.WorkplaceDescription;
import org.onosproject.workflow.api.WorkflowException;
import org.onosproject.workflow.api.DefaultWorkplace;
import org.onosproject.workflow.api.JsonDataModelTree;
import org.onosproject.workflow.api.WorkflowDescription;
import org.onosproject.workflow.api.Workplace;
import org.onosproject.workflow.api.WorkflowDataModelException;
import org.onosproject.workflow.api.Workflow;
import org.onosproject.workflow.api.Worklet;
import org.onosproject.workflow.api.WorkflowContext;
import org.onosproject.workflow.api.JsonDataModel;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@ -38,8 +43,15 @@ import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.slf4j.LoggerFactory.getLogger;
@ -118,21 +130,121 @@ public class WorkflowManager implements WorkflowService {
@Override
public void invokeWorkflow(JsonNode worklowDescJson) throws WorkflowException {
log.info("invokeWorkflow: {}", worklowDescJson);
Workplace workplace = workplaceStore.getWorkplace(Workplace.SYSTEM_WORKPLACE);
if (Objects.isNull(workplace)) {
throw new WorkflowException("Invalid system workplace");
}
Workflow workflow = workflowStore.get(URI.create(WorkplaceWorkflow.WF_CREATE_WORKFLOW));
Workflow workflow = workflowStore.get(URI.create(worklowDescJson.get("id").asText()));
if (Objects.isNull(workflow)) {
throw new WorkflowException("Invalid Workflow");
}
if (!checkWorkflowSchema(workflow, worklowDescJson)) {
throw new WorkflowException("Invalid Workflow " + worklowDescJson.get("id").asText());
}
Workflow wfCreationWf = workflowStore.get(URI.create(WorkplaceWorkflow.WF_CREATE_WORKFLOW));
if (Objects.isNull(wfCreationWf)) {
throw new WorkflowException("Invalid workflow " + WorkplaceWorkflow.WF_CREATE_WORKFLOW);
}
WorkflowContext context = workflow.buildSystemContext(workplace, new JsonDataModelTree(worklowDescJson));
WorkflowContext context = wfCreationWf.buildSystemContext(workplace, new JsonDataModelTree(worklowDescJson));
workflowExecutionService.execInitWorklet(context);
}
/**
* Checks if the type of worklet is same as that of wfdesc Json.
*
* @param workflow workflow
* @param jsonNode jsonNode
* @throws WorkflowException workflow exception
*/
private boolean checkWorkflowSchema(Workflow workflow, JsonNode jsonNode) throws WorkflowException {
Map<String, Map<String, String>> workletDataTypeMap = new HashMap<>();
for (String workletType : workflow.getWorkletTypeList()) {
Map<String, String> jsonDataModelMap = new HashMap<>();
if (Objects.equals(workletType, Worklet.Common.INIT.tag())
|| (Objects.equals(workletType, Worklet.Common.COMPLETED.tag()))) {
continue;
}
Worklet worklet = workflow.getWorkletInstance(workletType);
Class cls = worklet.getClass();
for (Field field : cls.getDeclaredFields()) {
if (field.isSynthetic()) {
continue;
}
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof JsonDataModel) {
JsonDataModel jsonDataModel = (JsonDataModel) annotation;
Matcher matcher = Pattern.compile("(\\w+)").matcher(jsonDataModel.path());
if (!matcher.find()) {
throw new WorkflowException("Invalid Json Data Model Path");
}
String path = matcher.group(1);
if (checkJsonNodeDataType(jsonNode, field, path)) {
jsonDataModelMap.put(path, field.getType().getName());
}
}
}
}
if (!jsonDataModelMap.isEmpty()) {
workletDataTypeMap.put(worklet.tag(), jsonDataModelMap);
}
}
if (!workletDataTypeMap.isEmpty()) {
throw new WorkflowDataModelException("invalid workflow ", workflow.id().toString(), workletDataTypeMap);
}
return true;
}
private boolean checkJsonNodeDataType(JsonNode jsonNode, Field field, String path) throws WorkflowException {
if (!Objects.nonNull(jsonNode.get("data")) && !Objects.nonNull(jsonNode.get("data").get(path))) {
throw new WorkflowException("Invalid Json");
}
JsonNodeType jsonNodeType = jsonNode.get("data").get(path).getNodeType();
if (jsonNodeType != null) {
switch (jsonNodeType) {
case NUMBER:
if (!(field.getType().isAssignableFrom(Integer.class))) {
return true;
}
break;
case STRING:
if (!(field.getType().isAssignableFrom(String.class))) {
return true;
}
break;
case OBJECT:
if (!(field.getType().isAssignableFrom(Objects.class))) {
return true;
}
break;
case BOOLEAN:
if (!(field.getType().isAssignableFrom(Boolean.class))) {
return true;
}
break;
case ARRAY:
if (!(field.getType().isAssignableFrom(Arrays.class))) {
return true;
}
break;
default:
return true;
}
} else {
return false;
}
return false;
}
@Override
public void terminateWorkflow(WorkflowDescription wfDesc) throws WorkflowException {
log.info("terminateWorkflow: {}", wfDesc);

View File

@ -116,6 +116,15 @@ public class SampleWorkflow {
.chain(SampleWorklet5.class.getName())
.build();
workflowStore.register(workflow);
// registering new workflow definition
uri = URI.create("sample.workflow-3");
workflow = ImmutableListWorkflow.builder()
.id(uri)
.chain(SampleWorklet6.class.getName())
.build();
workflowStore.register(workflow);
}
/**
@ -308,4 +317,29 @@ public class SampleWorkflow {
}
}
/**
* Class for sample worklet-6 to test workflow datamodel exception.
*/
public static class SampleWorklet6 extends AbsSampleWorklet {
@JsonDataModel(path = MODEL_COUNT)
String str;
@Override
public void process(WorkflowContext context) throws WorkflowException {
ObjectNode node = getDataModel(context);
node.put("work6", "done");
log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName());
sleep(10);
context.completed();
}
@Override
public boolean isNext(WorkflowContext context) throws WorkflowException {
ObjectNode node = allocOrGetModel(context);
log.info("workflow-isNext {}-{}", context.workplaceName(), this.getClass().getSimpleName());
sleep(10);
return !node.has("work6");
}
}
}