diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkletDescription.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkletDescription.java new file mode 100644 index 0000000000..82bb3f8178 --- /dev/null +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkletDescription.java @@ -0,0 +1,170 @@ +/* + * 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 com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.MoreObjects; +import org.slf4j.Logger; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Class for default worklet description. + */ +public final class DefaultWorkletDescription implements WorkletDescription { + + protected static final Logger log = getLogger(DefaultWorkletDescription.class); + + /** + * worklet Name. + */ + private String tag; + + /** + * worklet data model. + */ + private JsonDataModelTree data; + + /** + * Constructor of worklet description. + * + * @param builder worklet description builder + */ + public DefaultWorkletDescription(DefaultWorkletDescription.Builder builder) { + this.tag = builder.tag; + this.data = builder.data; + } + + public DefaultWorkletDescription(String tag) { + this.tag = tag; + } + + @Override + public String tag() { + return this.tag; + } + + @Override + public JsonDataModelTree data() { + return this.data; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("tag", tag()) + .add("data", data()) + .toString(); + } + + /** + * Gets builder instance. + * + * @return builder instance + */ + public static DefaultWorkletDescription.Builder builder() { + return new DefaultWorkletDescription.Builder(); + } + + /** + * Builder for worklet description. + */ + public static class Builder { + + /** + * worklet name. + */ + private String tag; + + /** + * static data model tree. + */ + JsonDataModelTree data = new JsonDataModelTree(); + + /** + * Sets worklet name. + * + * @param tag worklet name + * @return builder + */ + public DefaultWorkletDescription.Builder name(String tag) { + this.tag = tag; + return this; + } + + + public DefaultWorkletDescription.Builder staticDataModel(String path, String value) throws WorkflowException { + + data.setAt(path, value); + + return this; + } + + public DefaultWorkletDescription.Builder staticDataModel(String path, Integer value) throws WorkflowException { + + data.setAt(path, value); + + return this; + } + + public DefaultWorkletDescription.Builder staticDataModel(String path, Boolean value) throws WorkflowException { + + data.setAt(path, value); + + return this; + } + + public DefaultWorkletDescription.Builder staticDataModel(String path, JsonNode value) throws WorkflowException { + + data.setAt(path, value); + + return this; + } + + public DefaultWorkletDescription.Builder staticDataModel(String path, ArrayNode value) + throws WorkflowException { + + data.setAt(path, value); + + return this; + } + + public DefaultWorkletDescription.Builder staticDataModel(String path, ObjectNode value) + throws WorkflowException { + + data.setAt(path, value); + + return this; + } + + + /** + * Builds worklet description from builder. + * + * @return instance of worklet description + * @throws WorkflowException workflow exception + */ + public DefaultWorkletDescription build() { + + return new DefaultWorkletDescription(this); + } + + + } +} diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java index 3e54c94ae2..f4d94708de 100644 --- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ImmutableListWorkflow.java @@ -27,6 +27,7 @@ import java.lang.reflect.Modifier; import java.net.URI; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import static org.onosproject.workflow.api.CheckCondition.check; @@ -42,9 +43,9 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { private String initWorkletType; /** - * List of worklet. + * List of worklet description. */ - private List workletTypeList; + private List workletDescList; /** * Set of workflow attributes. @@ -52,6 +53,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { private Set attributes; private static JsonDataModelInjector dataModelInjector = new JsonDataModelInjector(); + private static StaticDataModelInjector staticDataModelInjector = new StaticDataModelInjector(); /** * Constructor of ImmutableListWorkflow. @@ -61,7 +63,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { private ImmutableListWorkflow(Builder builder) { super(builder.id); this.initWorkletType = builder.initWorkletType; - workletTypeList = ImmutableList.copyOf(builder.workletTypeList); + workletDescList = ImmutableList.copyOf(builder.workletDescList); attributes = ImmutableSet.copyOf(builder.attributes); } @@ -84,12 +86,11 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { ProgramCounter pc = current.clone(); - for (int i = current.workletIndex(); i < workletTypeList.size(); pc = increased(pc), i++) { + for (int i = current.workletIndex(); i < workletDescList.size(); pc = increased(pc), i++) { if (cnt++ > Worklet.MAX_WORKS) { throw new WorkflowException("Maximum worklet execution exceeded"); } - if (pc.isCompleted()) { return pc; } @@ -119,6 +120,12 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { } else { // isNext is read only. It does not perform 'inhale'. dataModelInjector.inject(worklet, context); + WorkletDescription workletDesc = getWorkletDesc(pc); + if (Objects.nonNull(workletDesc)) { + if (!(workletDesc.tag().equals("INIT") || workletDesc.tag().equals("COMPLETED"))) { + staticDataModelInjector.inject(worklet, workletDesc); + } + } if (worklet.isNext(context)) { return pc; } @@ -132,18 +139,18 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { public ProgramCounter increased(ProgramCounter pc) throws WorkflowException { int increaedIndex = pc.workletIndex() + 1; - if (increaedIndex >= workletTypeList.size()) { + if (increaedIndex >= workletDescList.size()) { throw new WorkflowException("Out of bound in program counter(" + pc + ")"); } - String workletType = workletTypeList.get(increaedIndex); - return ProgramCounter.valueOf(workletType, increaedIndex); + WorkletDescription workletDesc = workletDescList.get(increaedIndex); + return ProgramCounter.valueOf(workletDesc.tag(), increaedIndex); } @Override public Worklet getWorkletInstance(ProgramCounter pc) throws WorkflowException { - return getWorkletInstance(workletTypeList.get(pc.workletIndex())); + return getWorkletInstance(workletDescList.get(pc.workletIndex()).tag()); } @Override @@ -191,10 +198,21 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { } @Override - public List getWorkletTypeList() { - return ImmutableList.copyOf(workletTypeList); + public List getWorkletDescList() { + return ImmutableList.copyOf(workletDescList); } + @Override + public WorkletDescription getWorkletDesc(ProgramCounter pc) { + Optional workletDescription = workletDescList.stream().filter(a -> Objects.equals(a.tag(), + workletDescList.get(pc.workletIndex()).tag())).findAny(); + if (workletDescription.isPresent()) { + return workletDescription.get(); + } + return null; + } + + /** * Gets index of class in worklet type list. * @@ -202,8 +220,8 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { * @return index of class in worklet type list */ private int getClassIndex(Class aClass) { - for (int i = 0; i < workletTypeList.size(); i++) { - if (Objects.equals(aClass.getName(), workletTypeList.get(i))) { + for (int i = 0; i < workletDescList.size(); i++) { + if (Objects.equals(aClass.getName(), workletDescList.get(i))) { return i; } } @@ -247,7 +265,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { } return Objects.equals(this.id(), ((ImmutableListWorkflow) obj).id()) && Objects.equals(this.initWorkletType, ((ImmutableListWorkflow) obj).initWorkletType) - && Objects.equals(this.workletTypeList, ((ImmutableListWorkflow) obj).workletTypeList) + && Objects.equals(this.workletDescList, ((ImmutableListWorkflow) obj).workletDescList) && Objects.equals(this.attributes, ((ImmutableListWorkflow) obj).attributes); } @@ -256,7 +274,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { return MoreObjects.toStringHelper(getClass()) .add("id", id()) .add("initWorklet", initWorkletType) - .add("workList", workletTypeList) + .add("workList", workletDescList) .add("attributes", attributes) .toString(); } @@ -277,7 +295,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { private URI id; private String initWorkletType; - private final List workletTypeList = Lists.newArrayList(); + private final List workletDescList = Lists.newArrayList(); private final Set attributes = Sets.newHashSet(); /** @@ -288,7 +306,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { */ public Builder id(URI uri) { this.id = uri; - workletTypeList.add(Worklet.Common.INIT.tag()); + workletDescList.add(new DefaultWorkletDescription(Worklet.Common.INIT.tag())); return this; } @@ -310,7 +328,7 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { * @return builder */ public Builder chain(String workletClassName) { - workletTypeList.add(workletClassName); + workletDescList.add(new DefaultWorkletDescription(workletClassName)); return this; } @@ -331,8 +349,13 @@ public final class ImmutableListWorkflow extends AbstractWorkflow { * @return instance of ImmutableListWorkflow */ public ImmutableListWorkflow build() { - workletTypeList.add(Worklet.Common.COMPLETED.tag()); + workletDescList.add(new DefaultWorkletDescription(Worklet.Common.COMPLETED.tag())); return new ImmutableListWorkflow(this); } + + public Builder chain(DefaultWorkletDescription workletDesc) { + workletDescList.add(workletDesc); + return this; + } } } diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java index 7ee1be7fae..aec19ae9c2 100644 --- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java @@ -56,6 +56,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Constructor of JsonDataModelTree. + * * @param root root node of json data model tree */ public JsonDataModelTree(JsonNode root) { @@ -169,7 +170,8 @@ public final class JsonDataModelTree implements DataModelTree { /** * Allocates json data model tree on json pointer path with specific leaf type. - * @param ptr json pointer to allocate + * + * @param ptr json pointer to allocate * @param leaftype type of leaf node * @return json data model tree * @throws WorkflowException workflow exception @@ -194,6 +196,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets root json node. + * * @return root json node * @throws WorkflowException workflow exception */ @@ -203,6 +206,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets root json node as ObjectNode (MAP type). + * * @return root json node as ObjectNode * @throws WorkflowException workflow exception */ @@ -212,6 +216,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets root json node as ArrayNode (Array type). + * * @return root json node as ArrayNode * @throws WorkflowException workflow exception */ @@ -221,6 +226,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets json node on specific path. + * * @param path path of json node * @return json node on specific path * @throws WorkflowException workflow exception @@ -232,6 +238,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets json node on specific json pointer. + * * @param ptr json pointer * @return json node on specific json pointer. * @throws WorkflowException workflow exception @@ -246,6 +253,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets json node on specific path as ObjectNode. + * * @param path path of json node * @return ObjectNode type json node on specific path * @throws WorkflowException workflow exception @@ -257,6 +265,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets json node on specific json pointer as ObjectNode. + * * @param ptr json pointer * @return ObjectNode type json node on specific json pointer. * @throws WorkflowException workflow exception @@ -277,6 +286,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets json node on specific path as ArrayNode. + * * @param path path of json node * @return ArrayNode type json node on specific path * @throws WorkflowException workflow exception @@ -288,6 +298,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets json node on specific json pointer as ArrayNode. + * * @param ptr json pointer * @return ArrayNode type json node on specific json pointer. * @throws WorkflowException workflow exception @@ -308,6 +319,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets text node on specific path. + * * @param path path of json node * @return text on specific path * @throws WorkflowException workflow exception @@ -319,6 +331,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets text on specific json pointer. + * * @param ptr json pointer * @return text on specific json pointer * @throws WorkflowException workflow exception @@ -339,6 +352,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets integer node on specific path. + * * @param path path of json node * @return integer on specific path * @throws WorkflowException workflow exception @@ -350,6 +364,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets integer on specific json pointer. + * * @param ptr json pointer * @return integer on specific json pointer * @throws WorkflowException workflow exception @@ -370,6 +385,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets boolean on specific path. + * * @param path path of json node * @return boolean on specific path * @throws WorkflowException workflow exception @@ -381,6 +397,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets boolean on specific json pointer. + * * @param ptr json pointer * @return boolean on specific json pointer * @throws WorkflowException workflow exception @@ -401,6 +418,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Sets text on specific json path. + * * @param path json path * @param text text to set * @throws WorkflowException workflow exception @@ -412,18 +430,21 @@ public final class JsonDataModelTree implements DataModelTree { /** * Sets text on the specific json pointer. - * @param ptr json pointer + * + * @param ptr json pointer * @param text text to set * @throws WorkflowException workflow exception */ public void setAt(JsonPointer ptr, String text) throws WorkflowException { TextNode textNode = TextNode.valueOf(text); + attach(ptr, textNode); } /** * Sets boolean on specific json path. - * @param path json path + * + * @param path json path * @param isTrue boolean to set * @throws WorkflowException workflow exception */ @@ -432,9 +453,84 @@ public final class JsonDataModelTree implements DataModelTree { setAt(ptr, isTrue); } + /** + * Sets text on the specific json pointer. + * + * @param ptr json pointer + * @param jsonNode jsonNode to set + * @throws WorkflowException workflow exception + */ + public void setAt(JsonPointer ptr, JsonNode jsonNode) throws WorkflowException { + JsonNode node = jsonNode; + attach(ptr, node); + } + + /** + * Sets boolean on specific json path. + * + * @param path json path + * @param jsonNode jsonNode to set + * @throws WorkflowException workflow exception + */ + public void setAt(String path, JsonNode jsonNode) throws WorkflowException { + JsonPointer ptr = JsonPointer.compile(path); + setAt(ptr, jsonNode); + } + + + /** + * Sets text on the specific json pointer. + * + * @param ptr json pointer + * @param arrayNode arrayNode to set + * @throws WorkflowException workflow exception + */ + public void setAt(JsonPointer ptr, ArrayNode arrayNode) throws WorkflowException { + ArrayNode node = arrayNode; + attach(ptr, node); + } + + /** + * Sets boolean on specific json path. + * + * @param path json path + * @param arrayNode arrayNode to set + * @throws WorkflowException workflow exception + */ + public void setAt(String path, ArrayNode arrayNode) throws WorkflowException { + JsonPointer ptr = JsonPointer.compile(path); + setAt(ptr, arrayNode); + } + + /** + * Sets text on the specific json pointer. + * + * @param ptr json pointer + * @param objectNode objectNode to set + * @throws WorkflowException workflow exception + */ + public void setAt(JsonPointer ptr, ObjectNode objectNode) throws WorkflowException { + ObjectNode node = objectNode; + attach(ptr, node); + } + + /** + * Sets boolean on specific json path. + * + * @param path json path + * @param objectNode objectNode to set + * @throws WorkflowException workflow exception + */ + public void setAt(String path, ObjectNode objectNode) throws WorkflowException { + JsonPointer ptr = JsonPointer.compile(path); + setAt(ptr, objectNode); + } + + /** * Sets boolean on the specific json pointer. - * @param ptr json pointer + * + * @param ptr json pointer * @param isTrue boolean to set * @throws WorkflowException workflow exception */ @@ -445,7 +541,8 @@ public final class JsonDataModelTree implements DataModelTree { /** * Sets integer on specific json path. - * @param path json path + * + * @param path json path * @param number number to set * @throws WorkflowException workflow exception */ @@ -456,7 +553,8 @@ public final class JsonDataModelTree implements DataModelTree { /** * Sets integer on the specific json pointer. - * @param ptr json pointer + * + * @param ptr json pointer * @param number number to set * @throws WorkflowException workflow exception */ @@ -467,8 +565,9 @@ public final class JsonDataModelTree implements DataModelTree { /** * Allocates json data model tree on json pointer path with specific leaf type. - * @param node current json node in the json tree path - * @param ptr json pointer + * + * @param node current json node in the json tree path + * @param ptr json pointer * @param leaftype leaf type to be allocated * @return allocated json node * @throws WorkflowException workflow exception @@ -510,6 +609,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Creating empty json node. + * * @param type json node type to create * @return created json node * @throws WorkflowException workflow exception @@ -528,6 +628,7 @@ public final class JsonDataModelTree implements DataModelTree { /** * Gets the pretty json formatted string of this json data model tree. + * * @return pretty json formatted string of this json data model tree */ public String formattedRootString() { diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/StaticDataModel.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/StaticDataModel.java new file mode 100644 index 0000000000..91e83822f6 --- /dev/null +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/StaticDataModel.java @@ -0,0 +1,45 @@ +/* + * 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.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) + +/** + * Annotation for injecting static data model on work-let execution context. + */ +public @interface StaticDataModel { + + /** + * Path of data model. + * + * @return path of data model + */ + String path() default "/"; + + /** + * Representing whether this data model is optional or not. + * + * @return optional or not + */ + boolean optional() default false; +} diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/StaticDataModelInjector.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/StaticDataModelInjector.java new file mode 100644 index 0000000000..92d36649c4 --- /dev/null +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/StaticDataModelInjector.java @@ -0,0 +1,317 @@ +/* + * 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 com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.List; +import java.util.Objects; +import java.util.ArrayList; +import java.util.HashMap; + +public class StaticDataModelInjector { + + private static final Logger log = LoggerFactory.getLogger(StaticDataModelInjector.class); + + /** + * Injects data model to work-let. + * + * @param worklet work-let to be injected + * @param workletDescription worklet description + * @throws WorkflowException workflow exception + */ + public void inject(Worklet worklet, WorkletDescription workletDescription) throws WorkflowException { + + handle(worklet, workletDescription, this::injectModel); + } + + private void handle(Worklet worklet, WorkletDescription workletDescription, DataModelFieldBehavior func) + throws WorkflowException { + Class cl = worklet.getClass(); + List fields = getInheritedFields(cl); + if (Objects.isNull(fields)) { + log.error("Invalid fields on {}", cl); + return; + } + + for (Field field : fields) { + Annotation[] annotations = field.getAnnotations(); + if (Objects.isNull(annotations)) { + continue; + } + for (Annotation annotation : annotations) { + if (!(annotation instanceof StaticDataModel)) { + continue; + } + StaticDataModel model = (StaticDataModel) annotation; + func.apply(worklet, workletDescription, field, model); + } + } + } + + private static List getInheritedFields(Class type) { + List fields = new ArrayList(); + + Class cl = type; + while (cl != null && cl != Object.class) { + for (Field field : cl.getDeclaredFields()) { + if (!field.isSynthetic()) { + fields.add(field); + } + } + cl = cl.getSuperclass(); + } + return fields; + } + + /** + * Functional interface for json data model annotated field behavior. + */ + @FunctionalInterface + public interface DataModelFieldBehavior { + void apply(Worklet worklet, WorkletDescription workletDescription, Field field, StaticDataModel model) + throws WorkflowException; + } + + private static Map injectTypeMap = new HashMap<>(); + + static { + injectTypeMap.put(String.class, StaticDataModelInjector::injectText); + injectTypeMap.put(Integer.class, StaticDataModelInjector::injectInteger); + injectTypeMap.put(Boolean.class, StaticDataModelInjector::injectBoolean); + injectTypeMap.put(JsonNode.class, StaticDataModelInjector::injectJsonNode); + injectTypeMap.put(ArrayNode.class, StaticDataModelInjector::injectArrayNode); + injectTypeMap.put(ObjectNode.class, StaticDataModelInjector::injectObjectNode); + } + + /** + * Injects data model on the filed of work-let. + * + * @param worklet work-let + * @param workletDescription worklet description + * @param field the field of work-let + * @param model data model for the field + * @throws WorkflowException workflow exception + */ + private void injectModel(Worklet worklet, WorkletDescription workletDescription, Field field, StaticDataModel model) + throws WorkflowException { + + DataModelFieldBehavior behavior = injectTypeMap.get(field.getType()); + if (Objects.isNull(behavior)) { + throw new WorkflowException("Not supported type(" + field.getType() + ")"); + } + behavior.apply(worklet, workletDescription, field, model); + } + + /** + * Injects text data model on the filed of work-let. + * + * @param worklet work-let + * @param workletDescription worklet description + * @param field the field of work-let + * @param model text data model for the field + * @throws WorkflowException workflow exception + */ + private static void injectText(Worklet worklet, WorkletDescription workletDescription, Field field, + StaticDataModel model) throws WorkflowException { + + String text = ((JsonDataModelTree) workletDescription.data()).textAt(model.path()); + if (Objects.isNull(text)) { + if (model.optional()) { + return; + } + throw new WorkflowException("Invalid array node data model on (" + model.path() + ")"); + } + + + if (!(Objects.equals(field.getType(), String.class))) { + throw new WorkflowException("Target field (" + field + ") is not String"); + } + + try { + field.setAccessible(true); + field.set(worklet, text); + } catch (IllegalAccessException e) { + throw new WorkflowException(e); + } + } + + /** + * Injects integer data model on the filed of work-let. + * + * @param worklet work-let + * @param workletDescription worklet description + * @param field the field of work-let + * @param model integer data model for the field + * @throws WorkflowException workflow exception + */ + private static void injectInteger(Worklet worklet, WorkletDescription workletDescription, Field field, + StaticDataModel model) throws WorkflowException { + + Integer number = ((JsonDataModelTree) workletDescription.data()).intAt(model.path()); + if (Objects.isNull(number)) { + if (model.optional()) { + return; + } + throw new WorkflowException("Invalid array node data model on (" + model.path() + ")"); + } + if (!(Objects.equals(field.getType(), Integer.class))) { + throw new WorkflowException("Target field (" + field + ") is not Integer"); + } + + try { + field.setAccessible(true); + field.set(worklet, number); + } catch (IllegalAccessException e) { + throw new WorkflowException(e); + } + } + + + /** + * Injects boolean data model on the filed of work-let. + * + * @param worklet work-let + * @param workletDescription worklet description + * @param field the field of work-let + * @param model boolean data model for the field + * @throws WorkflowException workflow exception + */ + private static void injectBoolean(Worklet worklet, WorkletDescription workletDescription, Field field, + StaticDataModel model) throws WorkflowException { + + Boolean bool = ((JsonDataModelTree) workletDescription.data()).booleanAt(model.path()); + if (Objects.isNull(bool)) { + if (model.optional()) { + return; + } + throw new WorkflowException("Invalid boolean data model on (" + model.path() + ")"); + } + + if (!(Objects.equals(field.getType(), Boolean.class))) { + throw new WorkflowException("Target field (" + field + ") is not Boolean"); + } + + try { + field.setAccessible(true); + field.set(worklet, bool); + } catch (IllegalAccessException e) { + throw new WorkflowException(e); + } + } + + /** + * Injects json node data model on the filed of work-let. + * + * @param worklet work-let + * @param workletDescription worklet description + * @param field the field of work-let + * @param model json node data model for the field + * @throws WorkflowException workflow exception + */ + private static void injectJsonNode(Worklet worklet, WorkletDescription workletDescription, Field field, + StaticDataModel model) throws WorkflowException { + + JsonNode jsonNode = ((JsonDataModelTree) workletDescription.data()).nodeAt(model.path()); + if (Objects.isNull(jsonNode)) { + if (model.optional()) { + return; + } + throw new WorkflowException("Invalid json node data model on (" + model.path() + ")"); + } + + if (!(Objects.equals(field.getType(), JsonNode.class))) { + throw new WorkflowException("Target field (" + field + ") is not JsonNode"); + } + + try { + field.setAccessible(true); + field.set(worklet, jsonNode); + } catch (IllegalAccessException e) { + throw new WorkflowException(e); + } + } + + /** + * Injects json array node data model on the filed of work-let. + * + * @param worklet work-let + * @param workletDescription worklet description + * @param field the field of work-let + * @param model json array node data model for the field + * @throws WorkflowException workflow exception + */ + private static void injectArrayNode(Worklet worklet, WorkletDescription workletDescription, Field field, + StaticDataModel model) throws WorkflowException { + + ArrayNode arrayNode = ((JsonDataModelTree) workletDescription.data()).arrayAt(model.path()); + if (Objects.isNull(arrayNode)) { + if (model.optional()) { + return; + } + throw new WorkflowException("Invalid array node data model on (" + model.path() + ")"); + } + + if (!(Objects.equals(field.getType(), ArrayNode.class))) { + throw new WorkflowException("Target field (" + field + ") is not ArrayNode"); + } + + try { + field.setAccessible(true); + field.set(worklet, arrayNode); + } catch (IllegalAccessException e) { + throw new WorkflowException(e); + } + } + + /** + * Injects json object node data model on the filed of work-let. + * + * @param worklet work-let + * @param workletDescription worklet description + * @param field the field of work-let + * @param model json object node data model for the field + * @throws WorkflowException workflow exception + */ + private static void injectObjectNode(Worklet worklet, WorkletDescription workletDescription, Field field, + StaticDataModel model) throws WorkflowException { + + ObjectNode objNode = ((JsonDataModelTree) workletDescription.data()).objectAt(model.path()); + if (Objects.isNull(objNode)) { + if (model.optional()) { + return; + } + throw new WorkflowException("Invalid object node data model on (" + model.path() + ")"); + } + + if (!(Objects.equals(field.getType(), ObjectNode.class))) { + throw new WorkflowException("Target field (" + field + ") is not ObjectNode"); + } + + try { + field.setAccessible(true); + field.set(worklet, objNode); + } catch (IllegalAccessException e) { + throw new WorkflowException(e); + } + } +} diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java index 1404f9b662..5073105b61 100644 --- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/Workflow.java @@ -26,12 +26,14 @@ public interface Workflow { /** * Id of workflow. + * * @return id */ URI id(); /** * Returns init worklet. + * * @param context workflow context * @return init worklet * @throws WorkflowException workflow exception @@ -40,6 +42,7 @@ public interface Workflow { /** * Returns next program counter. + * * @param context workflow context * @return next program counter * @throws WorkflowException workflow exception @@ -48,6 +51,7 @@ public interface Workflow { /** * Gets increased program coounter. + * * @param pc program counter * @return increased program counter * @throws WorkflowException workflow exception @@ -56,6 +60,7 @@ public interface Workflow { /** * Returns instance of worklet. + * * @param pc program counter * @return instance of worklet * @throws WorkflowException workflow exception @@ -64,6 +69,7 @@ public interface Workflow { /** * Returns instance of worklet. + * * @param workletType class name of worklet * @return instance of worklet * @throws WorkflowException workflow exception @@ -72,8 +78,9 @@ public interface Workflow { /** * Builds workflow context. + * * @param workplace workplace of system workflow - * @param data data model of system workflow context + * @param data data model of system workflow context * @return workflow context * @throws WorkflowException workflow exception */ @@ -81,8 +88,9 @@ public interface Workflow { /** * Builds system workflow context. + * * @param workplace workplace of system workflow - * @param data data model of system workflow context + * @param data data model of system workflow context * @return system workflow context * @throws WorkflowException workflow exception */ @@ -90,13 +98,23 @@ public interface Workflow { /** * Returns workflow attributes. + * * @return attributes */ Set attributes(); /** - * Returns worklet type list. - * @return worklet type + * Returns worklet desc list. + * + * @return worklet description list */ - List getWorkletTypeList(); + List getWorkletDescList(); + + /** + * Returns worklet description. + * @param pc program counter + * @return worklet description list + */ + WorkletDescription getWorkletDesc(ProgramCounter pc); + } diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowStore.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowStore.java index ac0de4d3cb..a427493538 100644 --- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowStore.java +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowStore.java @@ -25,18 +25,21 @@ public interface WorkflowStore { /** * Registers workflow. + * * @param workflow registering workflow */ void register(Workflow workflow); /** * Unregisters workflow. + * * @param id id of workflow */ void unregister(URI id); /** * Gets workflow. + * * @param id id of workflow * @return workflow */ @@ -44,27 +47,32 @@ public interface WorkflowStore { /** * Gets all workflow. + * * @return collection of workflow */ Collection getAll(); /** * Registers local class loader. + * * @param loader class loader */ void registerLocal(ClassLoader loader); /** * Unregisters local class loader. + * * @param loader class loader */ void unregisterLocal(ClassLoader loader); /** * Gets class from registered class loaders. + * * @param name name of class * @return class * @throws ClassNotFoundException class not found exception */ Class getClass(String name) throws ClassNotFoundException; + } diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkletDescription.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkletDescription.java new file mode 100644 index 0000000000..71ddbe5fb1 --- /dev/null +++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkletDescription.java @@ -0,0 +1,36 @@ +/* + * 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; + + +public interface WorkletDescription { + + /** + * Gets worklet name. + * + * @return worklet name + */ + String tag(); + + /** + * Gets worklet data model. + * + * @return worklet data model + */ + JsonDataModelTree data(); + + +} diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java index 3069be6335..c0a418c46a 100644 --- a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java +++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java @@ -122,8 +122,8 @@ public class WorkFlowTestCommand extends AbstractShellCommand { for (int i = 0; i <= num; i++) { String wpName = "test_name-" + i; invoke("sample.workflow-0", wpName); - invoke("sample.workflow-1", wpName); - invoke("sample.workflow-2", wpName); + // invoke("sample.workflow-1", wpName); + // invoke("sample.workflow-2", wpName); } } diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/ECWorkFlowStore.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/ECWorkFlowStore.java index fcb17c954f..725c84e1db 100644 --- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/ECWorkFlowStore.java +++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/ECWorkFlowStore.java @@ -19,6 +19,15 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import org.onlab.util.KryoNamespace; +import org.onosproject.store.service.EventuallyConsistentMap; +import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.WallClockTimestamp; +import org.onosproject.workflow.api.AbstractWorkflow; +import org.onosproject.workflow.api.ImmutableListWorkflow; +import org.onosproject.workflow.api.Workflow; +import org.onosproject.workflow.api.WorkflowAttribute; +import org.onosproject.workflow.api.WorkflowStore; +import org.onosproject.workflow.api.WorkletDescription; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -27,18 +36,10 @@ import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.Logger; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; -import org.onosproject.workflow.api.AbstractWorkflow; -import org.onosproject.workflow.api.ImmutableListWorkflow; -import org.onosproject.workflow.api.Workflow; -import org.onosproject.workflow.api.WorkflowAttribute; -import org.onosproject.workflow.api.WorkflowStore; import org.onosproject.net.group.GroupEvent; import org.onosproject.net.group.GroupStoreDelegate; import org.onosproject.store.AbstractStore; import org.onosproject.store.serializers.KryoNamespaces; -import org.onosproject.store.service.EventuallyConsistentMap; -import org.onosproject.store.service.StorageService; -import org.onosproject.store.service.WallClockTimestamp; import java.net.URI; import java.util.Collection; @@ -51,7 +52,7 @@ import static org.slf4j.LoggerFactory.getLogger; @Component(immediate = true, service = WorkflowStore.class) public class ECWorkFlowStore - extends AbstractStore implements WorkflowStore { + extends AbstractStore implements WorkflowStore { private final Logger log = getLogger(getClass()); @@ -77,6 +78,7 @@ public class ECWorkFlowStore .register(Workflow.class) .register(AbstractWorkflow.class) .register(ImmutableListWorkflow.class) + .register(WorkletDescription.class) .register(List.class) .register(ImmutableList.class) .register(Class.class) @@ -101,7 +103,6 @@ public class ECWorkFlowStore @Deactivate public void deactivate() { workflowStore.destroy(); - log.info("Stopped"); } diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java index 5337c59850..167e1c4d54 100644 --- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java +++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkFlowEngine.java @@ -49,6 +49,8 @@ import org.onosproject.workflow.api.Workplace; import org.onosproject.workflow.api.WorkplaceStore; import org.onosproject.workflow.api.WorkplaceStoreDelegate; import org.onosproject.workflow.api.WorkflowExecutionService; +import org.onosproject.workflow.api.WorkletDescription; +import org.onosproject.workflow.api.StaticDataModelInjector; import org.onosproject.event.AbstractListenerManager; import org.onosproject.event.Event; import org.onosproject.net.intent.WorkPartitionService; @@ -126,6 +128,7 @@ public class WorkFlowEngine extends AbstractListenerManager> futures = operations.stream() .map( x -> CompletableFuture.completedFuture(x) - .thenApplyAsync(WorkFlowEngine.this::execWorkflow, workflowExecutor) - .exceptionally(e -> null) + .thenApplyAsync(WorkFlowEngine.this::execWorkflow, workflowExecutor) + .exceptionally(e -> null) ) .collect(Collectors.toList()); @@ -699,6 +725,7 @@ public class WorkFlowEngine extends AbstractListenerManager errors = new ArrayList<>(); - for (String workletType : workflow.getWorkletTypeList()) { + for (WorkletDescription workletType : workflow.getWorkletDescList()) { - Worklet worklet = workflow.getWorkletInstance(workletType); + Worklet worklet = workflow.getWorkletInstance(workletType.tag()); if (Worklet.Common.COMPLETED.equals(worklet) || Worklet.Common.INIT.equals(worklet)) { continue; } @@ -203,7 +204,8 @@ public class WorkflowManager implements WorkflowService { String path = matcher.group(1); WorkletDataModelFieldDesc desc = - new WorkletDataModelFieldDesc(workletType, path, field.getType(), jsonDataModel.optional()); + new WorkletDataModelFieldDesc(workletType.tag(), path, field.getType(), + jsonDataModel.optional()); WorkletDataModelFieldDesc existing = descMap.get(path); @@ -296,9 +298,9 @@ public class WorkflowManager implements WorkflowService { throw new WorkflowDataModelException(workflow.id(), worklowDescJson, errors); } - for (String workletType : workflow.getWorkletTypeList()) { + for (WorkletDescription workletType : workflow.getWorkletDescList()) { - Worklet worklet = workflow.getWorkletInstance(workletType); + Worklet worklet = workflow.getWorkletInstance(workletType.tag()); if (Worklet.Common.COMPLETED.equals(worklet) || Worklet.Common.INIT.equals(worklet)) { continue; } diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java index 8652c397e3..529bfb0500 100644 --- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java +++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java @@ -29,6 +29,8 @@ import org.onosproject.workflow.api.WorkflowContext; import org.onosproject.workflow.api.WorkflowException; import org.onosproject.workflow.api.WorkflowService; import org.onosproject.workflow.api.WorkflowStore; +import org.onosproject.workflow.api.StaticDataModel; +import org.onosproject.workflow.api.DefaultWorkletDescription; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -64,8 +66,7 @@ public class SampleWorkflow { try { registerWorkflows(); } catch (WorkflowException e) { - log.error("exception: " + e); - e.printStackTrace(); + log.error("invalid workflow"); } } @@ -76,14 +77,17 @@ public class SampleWorkflow { /** * Registers example workflows. + * + * @throws WorkflowException wfex */ - private void registerWorkflows() throws WorkflowException { + public void registerWorkflows() throws WorkflowException { // registering class-loader workflowStore.registerLocal(this.getClass().getClassLoader()); // registering new workflow definition URI uri = URI.create("sample.workflow-0"); - Workflow workflow = ImmutableListWorkflow.builder() + Workflow workflow = null; + workflow = ImmutableListWorkflow.builder() .id(uri) .chain(SampleWorklet1.class.getName()) .chain(SampleWorklet2.class.getName()) @@ -125,6 +129,16 @@ public class SampleWorkflow { .build(); workflowService.register(workflow); + // registering new workflow definition + uri = URI.create("sample.workflow-static-datamodel"); + workflow = ImmutableListWorkflow.builder() + .id(uri) + .chain(DefaultWorkletDescription.builder() + .name(SampleWorklet6.class.getName()) + .staticDataModel("/sample", "value") + .build()) + .build(); + workflowService.register(workflow); } /** @@ -142,8 +156,10 @@ public class SampleWorkflow { } + /** * Allocates or gets data model. + * * @param context workflow context * @return json object node * @throws WorkflowException workflow exception @@ -161,6 +177,7 @@ public class SampleWorkflow { /** * Gets data model. + * * @param context workflow context * @return json object node * @throws WorkflowException workflow exception @@ -172,6 +189,7 @@ public class SampleWorkflow { /** * Sleeps for 'ms' milli seconds. + * * @param ms milli seconds to sleep */ protected void sleep(long ms) { @@ -198,7 +216,6 @@ public class SampleWorkflow { node.put("work1", "done"); log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName()); sleep(30); - context.completed(); //Complete the job of worklet in the process } @@ -226,6 +243,7 @@ public class SampleWorkflow { node.put("work2", "done"); log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName()); sleep(50); + intCount++; context.waitFor(50L); //Timeout will happen after 50 milli seconds. @@ -243,6 +261,7 @@ public class SampleWorkflow { sleep(50); return !node.has("work2"); } + } public static class SampleWorklet3 extends AbsSampleWorklet { @@ -342,4 +361,32 @@ public class SampleWorkflow { return !node.has("work6"); } } + + /** + * Class for sample worklet-7 to test workflow datamodel exception. + */ + public static class SampleWorklet7 extends AbsSampleWorklet { + + @StaticDataModel(path = "/sample") + String value; + + @Override + public void process(WorkflowContext context) throws WorkflowException { + ObjectNode node = getDataModel(context); + node.put("work7", "done"); + log.info("inside worklet - static data model {}", value); + 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("work7"); + } + } + } diff --git a/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/OfOverlayWorkflow.java b/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/OfOverlayWorkflow.java index cbddfc728a..2267337b8f 100644 --- a/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/OfOverlayWorkflow.java +++ b/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/OfOverlayWorkflow.java @@ -21,6 +21,8 @@ import org.onosproject.workflow.api.Workflow; import org.onosproject.workflow.api.WorkflowExecutionService; import org.onosproject.workflow.api.WorkflowStore; import org.onosproject.workflow.api.WorkplaceStore; +import org.onosproject.workflow.api.DefaultWorkletDescription; +import org.onosproject.workflow.api.WorkflowException; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -57,6 +59,8 @@ public class OfOverlayWorkflow { private ScheduledExecutorService eventMapTriggerExecutor; + private static final String BRIDGE_NAME = "/bridgeName"; + @Activate public void activate() { log.info("Activated"); @@ -77,108 +81,117 @@ public class OfOverlayWorkflow { * Registers workflows. */ private void registerWorkflows() { - // registering class-loader - workflowStore.registerLocal(this.getClass().getClassLoader()); + try { + // registering class-loader + workflowStore.registerLocal(this.getClass().getClassLoader()); - // registering new workflow definition - URI uri = URI.create("of-overlay.workflow-nova"); - Workflow workflow = ImmutableListWorkflow.builder() - .id(uri) - //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) - .chain(Ovs.CreateOvsdbDevice.class.getName()) - .chain(Ovs.UpdateOvsVersion.class.getName()) - .chain(Ovs.UpdateOverlayBridgeId.class.getName()) - .chain(Ovs.CreateOverlayBridge.class.getName()) - .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) - .chain(Ovs.CreateUnderlayBridge.class.getName()) - .chain(Ovs.CreateOverlayBridgeVxlanPort.class.getName()) - .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) - .chain(Ovs.ConfigureUnderlayBridgeLocalIp.class.getName()) - .build(); - workflowStore.register(workflow); + // registering new workflow definition + URI uri = URI.create("of-overlay.workflow-nova"); + Workflow workflow = ImmutableListWorkflow.builder() + .id(uri) + //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) + .chain(Ovs.CreateOvsdbDevice.class.getName()) + .chain(Ovs.UpdateOvsVersion.class.getName()) + .chain(Ovs.UpdateOverlayBridgeId.class.getName()) + .chain(DefaultWorkletDescription.builder().name(Ovs.CreateBridge.class.getName()) + .staticDataModel(BRIDGE_NAME, "br-int") + .build()) + .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) + .chain(DefaultWorkletDescription.builder().name(Ovs.CreateBridge.class.getName()) + .staticDataModel(BRIDGE_NAME, "br-phy") + .build()) + .chain(Ovs.CreateOverlayBridgeVxlanPort.class.getName()) + .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) + .chain(Ovs.ConfigureUnderlayBridgeLocalIp.class.getName()) + .build(); - // registering new workflow definition based on multi-event handling - uri = URI.create("of-overlay.workflow-nova-multiEvent-test"); - workflow = ImmutableListWorkflow.builder() - .id(uri) - //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) - .chain(Ovs.CreateOvsdbDevice.class.getName()) - .chain(Ovs.UpdateOvsVersion.class.getName()) - .chain(Ovs.UpdateOverlayBridgeId.class.getName()) - .chain(Ovs.CreateOverlayBridgeMultiEvent.class.getName()) - .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) - .chain(Ovs.CreateUnderlayBridge.class.getName()) - .chain(Ovs.CreateOverlayBridgeVxlanPort.class.getName()) - .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) - .chain(Ovs.ConfigureUnderlayBridgeLocalIp.class.getName()) - .build(); - workflowStore.register(workflow); + workflowStore.register(workflow); - uri = URI.create("of-overlay.clean-workflow-nova"); - workflow = ImmutableListWorkflow.builder() - .id(uri) - //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) - .chain(Ovs.DeleteOverlayBridgeConfig.class.getName()) - .chain(Ovs.RemoveOverlayBridgeOfDevice.class.getName()) - .chain(Ovs.DeleteUnderlayBridgeConfig.class.getName()) - .chain(Ovs.RemoveUnderlayBridgeOfDevice.class.getName()) - .chain(Ovs.RemoveOvsdbDevice.class.getName()) - .build(); - workflowStore.register(workflow); + // registering new workflow definition based on multi-event handling + uri = URI.create("of-overlay.workflow-nova-multiEvent-test"); + workflow = ImmutableListWorkflow.builder() + .id(uri) + //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) + .chain(Ovs.CreateOvsdbDevice.class.getName()) + .chain(Ovs.UpdateOvsVersion.class.getName()) + .chain(Ovs.UpdateOverlayBridgeId.class.getName()) + .chain(Ovs.CreateOverlayBridgeMultiEvent.class.getName()) + .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) + .chain(Ovs.CreateUnderlayBridge.class.getName()) + .chain(Ovs.CreateOverlayBridgeVxlanPort.class.getName()) + .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) + .chain(Ovs.ConfigureUnderlayBridgeLocalIp.class.getName()) + .build(); + workflowStore.register(workflow); - uri = URI.create("of-overlay.clean-workflow-nova-waitAll-Bridge-Del"); - workflow = ImmutableListWorkflow.builder() - .id(uri) - //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) - .chain(Ovs.DeleteOverlayBridgeConfig.class.getName()) - .chain(Ovs.DeleteUnderlayBridgeConfig.class.getName()) - .chain(Ovs.RemoveBridgeOfDevice.class.getName()) - .chain(Ovs.RemoveOvsdbDevice.class.getName()) - .build(); - workflowStore.register(workflow); + uri = URI.create("of-overlay.clean-workflow-nova"); + workflow = ImmutableListWorkflow.builder() + .id(uri) + //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) + .chain(Ovs.DeleteOverlayBridgeConfig.class.getName()) + .chain(Ovs.RemoveOverlayBridgeOfDevice.class.getName()) + .chain(Ovs.DeleteUnderlayBridgeConfig.class.getName()) + .chain(Ovs.RemoveUnderlayBridgeOfDevice.class.getName()) + .chain(Ovs.RemoveOvsdbDevice.class.getName()) + .build(); + workflowStore.register(workflow); - uri = URI.create("of-overlay.workflow-ovs-leaf"); - workflow = ImmutableListWorkflow.builder() - .id(uri) - .chain(Ovs.CreateOvsdbDevice.class.getName()) - .chain(Ovs.UpdateOvsVersion.class.getName()) - .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) - .chain(Ovs.CreateUnderlayBridge.class.getName()) - .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) - .build(); - workflowStore.register(workflow); + uri = URI.create("of-overlay.clean-workflow-nova-waitAll-Bridge-Del"); + workflow = ImmutableListWorkflow.builder() + .id(uri) + //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE) + .chain(Ovs.DeleteOverlayBridgeConfig.class.getName()) + .chain(Ovs.DeleteUnderlayBridgeConfig.class.getName()) + .chain(Ovs.RemoveBridgeOfDevice.class.getName()) + .chain(Ovs.RemoveOvsdbDevice.class.getName()) + .build(); + workflowStore.register(workflow); - uri = URI.create("of-overlay.workflow-ovs-spine"); - workflow = ImmutableListWorkflow.builder() - .id(uri) - .chain(Ovs.CreateOvsdbDevice.class.getName()) - .chain(Ovs.UpdateOvsVersion.class.getName()) - .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) - .chain(Ovs.CreateUnderlayBridge.class.getName()) - .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) - .build(); - workflowStore.register(workflow); + uri = URI.create("of-overlay.workflow-ovs-leaf"); + workflow = ImmutableListWorkflow.builder() + .id(uri) + .chain(Ovs.CreateOvsdbDevice.class.getName()) + .chain(Ovs.UpdateOvsVersion.class.getName()) + .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) + .chain(Ovs.CreateUnderlayBridge.class.getName()) + .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) + .build(); + workflowStore.register(workflow); - deviceService.addListener( - event -> { - // trigger EventTask for DeviceEvent - eventMapTriggerExecutor.submit( - () -> workflowExecutionService.eventMapTrigger( - event, - // event hint supplier - (ev) -> { - if (ev == null || ev.subject() == null) { - return null; + uri = URI.create("of-overlay.workflow-ovs-spine"); + workflow = ImmutableListWorkflow.builder() + .id(uri) + .chain(Ovs.CreateOvsdbDevice.class.getName()) + .chain(Ovs.UpdateOvsVersion.class.getName()) + .chain(Ovs.UpdateUnderlayBridgeId.class.getName()) + .chain(Ovs.CreateUnderlayBridge.class.getName()) + .chain(Ovs.AddPhysicalPortsOnUnderlayBridge.class.getName()) + .build(); + workflowStore.register(workflow); + + deviceService.addListener( + event -> { + // trigger EventTask for DeviceEvent + eventMapTriggerExecutor.submit( + () -> workflowExecutionService.eventMapTrigger( + event, + // event hint supplier + (ev) -> { + if (ev == null || ev.subject() == null) { + return null; + } + String hint = event.subject().id().toString(); + log.debug("hint: {}", hint); + return hint; } - String hint = event.subject().id().toString(); - log.debug("hint: {}", hint); - return hint; - } - ) - ); - } - ); + ) + ); + } + ); + } catch (WorkflowException e) { + e.printStackTrace(); + } } } diff --git a/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/Ovs.java b/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/Ovs.java index ad56d52943..ccbc182403 100644 --- a/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/Ovs.java +++ b/apps/workflow/ofoverlay/app/src/main/java/org/onosproject/ofoverlay/impl/Ovs.java @@ -59,6 +59,7 @@ import org.onosproject.workflow.api.AbstractWorklet; import org.onosproject.workflow.api.JsonDataModel; import org.onosproject.workflow.api.WorkflowContext; import org.onosproject.workflow.api.WorkflowException; +import org.onosproject.workflow.api.StaticDataModel; import org.onosproject.workflow.model.accessinfo.SshAccessInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,6 +84,7 @@ public final class Ovs { private static final Logger log = LoggerFactory.getLogger(Ovs.class); private static final String MODEL_MGMT_IP = "/mgmtIp"; + private static final String BRIDGE_NAME = "/bridgeName"; private static final String MODEL_OVSDB_PORT = "/ovsdbPort"; private static final String MODEL_OVS_VERSION = "/ovsVersion"; private static final String MODEL_OVS_DATAPATH_TYPE = "/ovsDatapathType"; @@ -535,6 +537,108 @@ public final class Ovs { } } + + public static class CreateBridge extends AbstractWorklet { + + @StaticDataModel(path = BRIDGE_NAME) + String bridgeName; + + @JsonDataModel(path = MODEL_MGMT_IP) + String strMgmtIp; + + @JsonDataModel(path = MODEL_OVSDB_PORT) + Integer intOvsdbPort; + + @JsonDataModel(path = MODEL_OVS_DATAPATH_TYPE) + String strOvsDatapath; + + @JsonDataModel(path = MODEL_OF_DEVID_OVERLAY_BRIDGE, optional = true) + String strOfDevId; + + @Override + public boolean isNext(WorkflowContext context) throws WorkflowException { + + check(strOfDevId != null, "invalid strOfDevIdUnderlay"); + return !OvsUtil.isAvailableBridge(context, DeviceId.deviceId(strOfDevId)); + } + + @Override + public void process(WorkflowContext context) throws WorkflowException { + + check(strOfDevId != null, "invalid strOfDevIdOverlay"); + BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); + List ofControllers = OvsUtil.getOpenflowControllerInfoList(context); + DeviceId ofDeviceId = DeviceId.deviceId(strOfDevId); + + if (ofControllers == null || ofControllers.size() == 0) { + throw new WorkflowException("Invalid of controllers"); + } + + Optional optBd = OvsUtil.getBridgeDescription(bridgeConfig, bridgeName); + if (!optBd.isPresent()) { + + // If bridge does not exist, just creates a new bridge. + context.waitCompletion(DeviceEvent.class, ofDeviceId.toString(), + () -> OvsUtil.createBridge(bridgeConfig, + bridgeName, + OvsUtil.bridgeDatapathId(ofDeviceId), + ofControllers, + OvsUtil.buildOvsDatapathType(strOvsDatapath)), + TIMEOUT_DEVICE_CREATION_MS + ); + return; + + } else { + BridgeDescription bd = optBd.get(); + if (OvsUtil.isEqual(ofControllers, bd.controllers())) { + log.error("{} has valid controller setting({})", bridgeName, bd.controllers()); + context.completed(); + return; + } + + OvsdbClientService ovsdbClient = OvsUtil.getOvsdbClient(context, strMgmtIp, intOvsdbPort); + if (ovsdbClient == null || !ovsdbClient.isConnected()) { + throw new WorkflowException("Invalid ovsdb client for " + strMgmtIp); + } + + // If controller settings are not matched, set controller with valid controller information. + context.waitCompletion(DeviceEvent.class, ofDeviceId.toString(), + () -> ovsdbClient.setControllersWithDeviceId(bd.deviceId().get(), ofControllers), + TIMEOUT_DEVICE_CREATION_MS + ); + return; + } + } + + @Override + public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { + if (!(event instanceof DeviceEvent)) { + return false; + } + DeviceEvent deviceEvent = (DeviceEvent) event; + Device device = deviceEvent.subject(); + switch (deviceEvent.type()) { + case DEVICE_ADDED: + case DEVICE_AVAILABILITY_CHANGED: + case DEVICE_UPDATED: + return context.getService(DeviceService.class).isAvailable(device.id()); + default: + return false; + } + } + + @Override + public void timeout(WorkflowContext context) throws WorkflowException { + if (!isNext(context)) { + context.completed(); //Complete the job of worklet by timeout + } else { + super.timeout(context); + } + } + + } + + /** * Work-let class for creating overlay openflow bridge. */