ONOS-7963 workflow invocation trigger on ONOS event
Change-Id: Ie9014bfa77f5514c3580042f9ff2add8e42f25f6
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java
index 5fb8943..30e50db 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/AbstractWorkflow.java
@@ -57,4 +57,5 @@
.data(data)
.build();
}
+
}
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java
index 4b54cf3..389352c 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/ContextEventMapStore.java
@@ -33,11 +33,21 @@
* @param eventType the class name of event
* @param eventHintSet Set of event hint string value of the event
* @param contextName workflow context name
- * @param programCounterString the program counter of workflow
+ * @param programCounter the program counter of workflow
* @throws WorkflowException workflow exception
*/
void registerEventMap(String eventType, Set<String> eventHintSet,
- String contextName, String programCounterString) throws WorkflowException;
+ String contextName, ProgramCounter programCounter) throws WorkflowException;
+
+ /**
+ * Registers workflow trigger flag.
+ * @param eventType the class name of event
+ * @param eventHintSet Set of event hint string value of the event
+ * @param contextName workflow context name
+ * @throws WorkflowException workflow exception
+ */
+ void registerTriggerFlag(String eventType, Set<String> eventHintSet,
+ String contextName) throws WorkflowException;
/**
* Unregisters workflow context event mapping.
@@ -52,10 +62,10 @@
* Returns workflow context event mapping.
* @param eventType the class name of event
* @param eventHint vent hint string value of the event
- * @return Map of workflow context and value (program counter)
+ * @return Map of workflow context and value (WorkflowEventMetaData)
* @throws WorkflowException workflow exception
*/
- Map<String, String> getEventMapByHint(String eventType,
+ Map<String, WorkflowEventMetaData> getEventMapByHint(String eventType,
String eventHint) throws WorkflowException;
/**
@@ -66,12 +76,23 @@
boolean isEventMapPresent(String contextName);
/**
+ * Returns true or false depending on trigger flag for the workflow.
+ * @param eventType the class name of event
+ * @param eventHint vent hint string value of the event
+ * @param contextName name of workflow context
+ * @return Boolean true or false depending on trigger flag for the workflow
+ * @throws WorkflowException workflow exception
+ */
+ boolean isTriggerSet(String eventType, String eventHint,
+ String contextName) throws WorkflowException;
+
+ /**
* Returns child nodes on document tree path.
* @param path document tree path including eventType and Hint
* @return children under document tree path
* @throws WorkflowException workflow exception
*/
- Map<String, Versioned<String>> getChildren(String path) throws WorkflowException;
+ Map<String, Versioned<WorkflowEventMetaData>> getChildren(String path) throws WorkflowException;
/**
* Returns document path.
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java
index 8f5d3c7..977a978 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DefaultWorkflowContext.java
@@ -92,6 +92,12 @@
private transient WorkplaceStore workplaceStore;
/**
+ * Service reference for eventMap store.
+ */
+ private transient ContextEventMapStore eventMapStore;
+
+
+ /**
* Constructor of DefaultWorkflowContext.
* @param builder default workflow context builder
*/
@@ -214,6 +220,11 @@
}
@Override
+ public void registerTriggerEvent(Class<? extends Event> event, Set<String> eventHintSet) throws WorkflowException {
+ eventMapStore.registerTriggerFlag(event.getName(), eventHintSet, this.name());
+ }
+
+ @Override
public void setWorkflowExecutionService(WorkflowExecutionService workflowExecutionService) {
this.workflowExecutionService = workflowExecutionService;
}
@@ -243,6 +254,17 @@
return workplaceStore;
}
+
+ @Override
+ public void setEventMapStore(ContextEventMapStore contextEventMapStore) {
+ this.eventMapStore = contextEventMapStore;
+ }
+
+ @Override
+ public ContextEventMapStore eventMapStore() {
+ return eventMapStore;
+ }
+
public <T> T getService(Class<T> serviceClass) throws WorkflowException {
T service;
try {
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 5819f11..f167b94 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
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import static org.onosproject.workflow.api.CheckCondition.check;
@@ -55,6 +56,8 @@
private static JsonDataModelInjector dataModelInjector = new JsonDataModelInjector();
private static StaticDataModelInjector staticDataModelInjector = new StaticDataModelInjector();
+ private Optional<String> triggerWorkletClassName = Optional.empty();
+
/**
* Constructor of ImmutableListWorkflow.
*
@@ -62,12 +65,18 @@
*/
private ImmutableListWorkflow(Builder builder) {
super(builder.id);
+ triggerWorkletClassName = builder.triggerWorkletClassName;
this.initWorkletType = builder.initWorkletType;
program = ImmutableList.copyOf(builder.workletDescList);
attributes = ImmutableSet.copyOf(builder.attributes);
}
@Override
+ public Optional<String> getTriggerWorkletClassName() {
+ return triggerWorkletClassName;
+ }
+
+ @Override
public Worklet init(WorkflowContext context) throws WorkflowException {
if (Objects.isNull(initWorkletType)) {
return null;
@@ -209,6 +218,12 @@
}
@Override
+ public Worklet getTriggerWorkletInstance(String workletType) throws WorkflowException {
+ return getWorkletInstance(workletType);
+ }
+
+
+ @Override
public WorkletDescription getWorkletDesc(ProgramCounter pc) {
WorkletDescription workletDescription = program.get(pc.workletIndex());
@@ -297,6 +312,7 @@
public static class Builder {
private URI id;
+ private Optional<String> triggerWorkletClassName = Optional.empty();
private String initWorkletType;
private final List<WorkletDescription> workletDescList = Lists.newArrayList();
private final Set<WorkflowAttribute> attributes = Sets.newHashSet();
@@ -314,6 +330,17 @@
}
/**
+ * Sets trigger flag of immutable list workflow.
+ *
+ * @param triggerWorkletClassName name of trigger worklet class
+ * @return builder
+ */
+ public Builder trigger(String triggerWorkletClassName) {
+ this.triggerWorkletClassName = Optional.of(triggerWorkletClassName);
+ return this;
+ }
+
+ /**
* Sets init worklet class name of immutable list workflow.
*
* @param workletClassName class name of worklet
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TriggerWorklet.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TriggerWorklet.java
new file mode 100644
index 0000000..3931693
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/TriggerWorklet.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.onosproject.event.Event;
+
+/**
+ * Abstract class for Trigger worklet.
+ */
+public abstract class TriggerWorklet implements Worklet {
+
+ @Override
+ public String tag() {
+ return this.getClass().getName();
+ }
+
+ /**
+ * Registers trigger event for a workflow under the workflow context.
+ * @param context workflow context
+ * @throws WorkflowException workflow exception
+ */
+ public abstract void register(WorkflowContext context) throws WorkflowException;
+
+ /**
+ * Validates trigger event for a given workflow context.
+ * @param context workflow context
+ * @param event trigger event
+ * @return true/false based on trigger event's validity
+ * @throws WorkflowException workflow exception
+ */
+ public abstract boolean isTriggerValid(WorkflowContext context, Event event) throws WorkflowException;
+
+ @Override
+ public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException {
+ throw new WorkflowException("(" + tag() + ").isCompleted should not be called");
+ }
+
+ @Override
+ public boolean isNext(WorkflowContext context) throws WorkflowException {
+ throw new WorkflowException("(" + tag() + ").isNext should not be called");
+ }
+
+ @Override
+ public void timeout(WorkflowContext context) throws WorkflowException {
+ throw new WorkflowException("(" + tag() + ").timeout should not be called");
+ }
+
+ @Override
+ public void process(WorkflowContext context) throws WorkflowException {
+ throw new WorkflowException("(" + tag() + ").process should not be called");
+ }
+}
+
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WfTriggerEventTask.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WfTriggerEventTask.java
new file mode 100644
index 0000000..8febd38
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WfTriggerEventTask.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2018-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.google.common.base.MoreObjects;
+import org.onosproject.event.Event;
+
+import java.util.Objects;
+
+import static org.onosproject.workflow.api.CheckCondition.check;
+
+/**
+ * Class for WorkFlow Trigger event task.
+ */
+public final class WfTriggerEventTask extends HandlerTask {
+
+ /**
+ * Event triggering event task.
+ */
+ private final Event event;
+
+ /**
+ * Constructor of event task.
+ * @param builder builder of event task
+ */
+ private WfTriggerEventTask(Builder builder) {
+ super(builder);
+ this.event = builder.event;
+ }
+
+
+ /**
+ * Gets event of event task.
+ * @return event triggering event task
+ */
+ public Event event() {
+ return event;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.toString());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof EventTask)) {
+ return false;
+ }
+ return Objects.equals(this.event(),
+ ((WfTriggerEventTask) obj).event());
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("context", context())
+ .add("event", event())
+ .toString();
+ }
+
+ /**
+ * Gets a instance of builder.
+ * @return instance of builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of WfTriggerEventTask.
+ */
+ public static class Builder extends HandlerTask.Builder {
+
+ /**
+ * Event triggering event task.
+ */
+ private Event event;
+
+
+ /**
+ * Sets event.
+ * @param event event triggering event task
+ * @return Builder of WfTriggerEventTask
+ */
+ public Builder event(Event event) {
+ this.event = event;
+ return this;
+ }
+
+ @Override
+ public Builder context(WorkflowContext context) {
+ super.context(context);
+ return this;
+ }
+
+ /**
+ * Builds WfTriggerEventTask.
+ * @return instance of WfTriggerEventTask
+ * @throws WorkflowException workflow exception
+ */
+ public WfTriggerEventTask build() throws WorkflowException {
+ check(context != null, "context is invalid");
+ check(event != null, "event is invalid");
+ return new WfTriggerEventTask(this);
+ }
+ }
+}
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 3849afe..01fb92d 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
@@ -17,6 +17,7 @@
import java.net.URI;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
/**
@@ -102,10 +103,25 @@
List<ProgramCounter> getProgram();
/**
+ * Returns worklet instance with given worklet Name.
+ * @param workletType worklet name
+ * @return Worklet
+ * @throws WorkflowException workflow exception
+ */
+
+ Worklet getTriggerWorkletInstance(String workletType) throws WorkflowException;
+
+ /**
* Returns worklet description.
* @param pc program counter
* @return worklet description list
*/
WorkletDescription getWorkletDesc(ProgramCounter pc);
-}
+ /**
+ * Returns trigger worklet class name if any.
+ * @return trigger worklet class name
+ */
+ Optional<String> getTriggerWorkletClassName();
+
+ }
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java
index 836524d..2398b09 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowContext.java
@@ -144,6 +144,29 @@
public abstract long completionEventTimeout();
/**
+ * Sets event map store.
+ * @param contextEventMapStore event map store
+ */
+ public abstract void setEventMapStore(ContextEventMapStore contextEventMapStore);
+
+ /**
+ * Returns the event map store.
+ * @return ContextEventMapStore
+ */
+ public abstract ContextEventMapStore eventMapStore();
+
+
+ /**
+ * Registers an trigger event which has 'eventHint'.
+ * If the event happens, Workflow would be resatrted.
+ * @param event event when trigger happens
+ * @param eventHintSet Set of hint for the event
+ * @throws WorkflowException exception in case any
+ */
+ public abstract void registerTriggerEvent(Class<? extends Event> event,
+ Set<String> eventHintSet) throws WorkflowException;
+
+ /**
* Sets workflow service.
* @param workflowExecutionService workflow service
*/
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowEventMetaData.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowEventMetaData.java
new file mode 100644
index 0000000..24a485c
--- /dev/null
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowEventMetaData.java
@@ -0,0 +1,90 @@
+/*
+ * 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.google.common.base.MoreObjects;
+
+/**
+ * A class representing meta data for workflow event.
+ */
+public class WorkflowEventMetaData {
+
+ private boolean isTriggerSet = false;
+ private ProgramCounter programCounter;
+
+ /**
+ * Constructor of workflow event meta data.
+ * @param isTriggerSet trigger event set for the the workflow
+ * @param programCounter program counter representing worklet type for registered event
+ */
+ public WorkflowEventMetaData(boolean isTriggerSet, ProgramCounter programCounter) {
+ this.isTriggerSet = isTriggerSet;
+ this.programCounter = programCounter;
+ }
+
+ /**
+ * Copy constructor of workflow event meta data.
+ * @param workflowEventMetaData object of WorkflowEventMetaData
+ */
+ public WorkflowEventMetaData(WorkflowEventMetaData workflowEventMetaData) {
+ this.isTriggerSet = workflowEventMetaData.getTriggerFlag();
+ this.programCounter = workflowEventMetaData.getProgramCounter().clone();
+ }
+
+ /**
+ * Returns program counter value related to worflow event.
+ * @return programCounter
+ */
+ public ProgramCounter getProgramCounter() {
+ return programCounter;
+ }
+
+ /**
+ * Returns trigger flag for the workflow.
+ * @return triggerFlag
+ */
+ public boolean getTriggerFlag() {
+ return isTriggerSet;
+ }
+
+
+ /**
+ * Sets true or false for triggerFlag of the workflow.
+ * @param triggerFlag flag to indicate trigger event set for the workflow
+ */
+ public void setTriggerFlag(boolean triggerFlag) {
+ this.isTriggerSet = triggerFlag;
+ }
+
+ /**
+ * Sets program counter representing worklet type for registered event of the workflow.
+ * @param programCounter program counter representing worklet type for registered event
+ */
+ public void setProgramCounterString(ProgramCounter programCounter) {
+ this.programCounter = programCounter;
+ }
+
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("trigger-flag", getTriggerFlag())
+ .add("program-counter", getProgramCounter())
+ .toString();
+ }
+
+}
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java
index c5e00a7..3a41a60 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/WorkflowExecutionService.java
@@ -49,9 +49,9 @@
* @param eventType event type (class name of event)
* @param eventHintSet Set of event hint value
* @param contextName workflow context name to be called by this event map
- * @param programCounterString worklet type to be called by this event map
+ * @param programCounter worklet type to be called by this event map
* @throws WorkflowException workflow exception
*/
void registerEventMap(Class<? extends Event> eventType, Set<String> eventHintSet,
- String contextName, String programCounterString) throws WorkflowException;
+ String contextName, ProgramCounter programCounter) throws WorkflowException;
}
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java
index 8cd352e..947eff3 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/DistributedContextEventMapTreeStore.java
@@ -21,6 +21,8 @@
import com.google.common.collect.Maps;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.workflow.api.ProgramCounter;
+import org.onosproject.workflow.api.WorkflowEventMetaData;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -64,10 +66,12 @@
private ApplicationId appId;
- private AsyncDocumentTree<String> eventMapTree;
+ private AsyncDocumentTree<WorkflowEventMetaData> eventMapTree;
private EventuallyConsistentMap<String, Set<String>> hintSetPerCxtMap;
+ private static final ProgramCounter INVALID_PC = ProgramCounter.valueOf("INVALID_WORKLET", 0);
+
@Activate
public void activate() {
@@ -77,9 +81,12 @@
KryoNamespace eventMapNamespace = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
+ .register(ProgramCounter.class)
+ .register(WorkflowEventMetaData.class)
+ .register(Set.class)
.build();
- eventMapTree = storageService.<String>documentTreeBuilder()
+ eventMapTree = storageService.<WorkflowEventMetaData>documentTreeBuilder()
.withSerializer(Serializer.using(eventMapNamespace))
.withName("context-event-map-store")
.withOrdering(Ordering.INSERTION)
@@ -103,25 +110,49 @@
@Override
public void registerEventMap(String eventType, Set<String> eventHintSet,
- String contextName, String programCounterString) throws WorkflowException {
+ String contextName, ProgramCounter programCounter) throws WorkflowException {
for (String eventHint : eventHintSet) {
//Insert in eventCxtPerHintMap
DocumentPath dpathForCxt = DocumentPath.from(Lists.newArrayList(
"root", eventType, eventHint, contextName));
- String currentWorkletType = completeVersioned(eventMapTree.get(dpathForCxt));
- if (currentWorkletType == null) {
- complete(eventMapTree.createRecursive(dpathForCxt, programCounterString));
+ WorkflowEventMetaData workflowEventMetaData = completeVersioned(eventMapTree.get(dpathForCxt));
+ if (workflowEventMetaData == null) {
+ workflowEventMetaData = new WorkflowEventMetaData(false, programCounter);
+ complete(eventMapTree.createRecursive(dpathForCxt, workflowEventMetaData));
} else {
- complete(eventMapTree.replace(dpathForCxt, programCounterString, currentWorkletType));
+ WorkflowEventMetaData updatedWorkflowEventMetaData =
+ new WorkflowEventMetaData(workflowEventMetaData.getTriggerFlag(), programCounter);
+ complete(eventMapTree.replace(dpathForCxt, updatedWorkflowEventMetaData, workflowEventMetaData));
}
log.trace("RegisterEventMap for eventType:{}, eventSet:{}, contextName:{}, pc:{}",
- eventType, eventHintSet, contextName, programCounterString);
+ eventType, eventHintSet, contextName, programCounter.toString());
}
hintSetPerCxtMap.put(contextName, eventHintSet);
log.trace("RegisterEventMap in hintSetPerCxt for " +
"eventType:{}, eventSet:{}, contextName:{}, pc:{}",
- eventType, eventHintSet, contextName, programCounterString);
+ eventType, eventHintSet, contextName, programCounter.toString());
+ }
+
+ @Override
+ public void registerTriggerFlag(String eventType, Set<String> eventHintSet,
+ String contextName) throws WorkflowException {
+ for (String eventHint : eventHintSet) {
+ //Insert in eventCxtPerHintMap
+ DocumentPath dpathForCxt = DocumentPath.from(Lists.newArrayList(
+ "root", eventType, eventHint, contextName));
+ WorkflowEventMetaData workflowEventMetaData = completeVersioned(eventMapTree.get(dpathForCxt));
+ if (workflowEventMetaData == null) {
+ workflowEventMetaData = new WorkflowEventMetaData(true, INVALID_PC);
+ complete(eventMapTree.createRecursive(dpathForCxt, workflowEventMetaData));
+ } else {
+ WorkflowEventMetaData updatedWorkflowEventMetaData =
+ new WorkflowEventMetaData(true, workflowEventMetaData.getProgramCounter());
+ complete(eventMapTree.replace(dpathForCxt, updatedWorkflowEventMetaData, workflowEventMetaData));
+ }
+ log.trace("RegisterTriggerFlag for eventType:{}, eventSet:{}, contextName:{}",
+ eventType, eventHintSet, contextName);
+ }
}
@Override
@@ -129,28 +160,39 @@
throws WorkflowException {
Set<String> hints = hintSetPerCxtMap.get(contextName);
+
+ if (Objects.isNull(hints)) {
+ return;
+ }
for (String eventHint : hints) {
//Remove from eventCxtPerHintMap
- complete(eventMapTree.removeNode(DocumentPath.from(Lists.newArrayList(
- "root", eventType, eventHint, contextName))));
- log.trace("UnregisterEventMap from eventCxtPerHintMap for eventType:{}, eventSet:{}, contextName:{}",
- eventType, eventHint, contextName);
+ DocumentPath dpathForCxt = DocumentPath.from(Lists.newArrayList(
+ "root", eventType, eventHint, contextName));
+ WorkflowEventMetaData workflowEventMetaData = completeVersioned(eventMapTree.get(dpathForCxt));
+ if (Objects.nonNull(workflowEventMetaData)) {
+ WorkflowEventMetaData updatedWorkflowEventMetaData = new WorkflowEventMetaData(workflowEventMetaData);
+ updatedWorkflowEventMetaData.setProgramCounterString(INVALID_PC);
+ complete(eventMapTree.replace(dpathForCxt, updatedWorkflowEventMetaData, workflowEventMetaData));
+ log.trace("UnregisterEventMap from eventCxtPerHintMap for eventType:{}, eventSet:{}, contextName:{}",
+ eventType, eventHint, contextName);
+ }
}
hintSetPerCxtMap.remove(contextName);
}
@Override
- public Map<String, String> getEventMapByHint(String eventType, String eventHint) throws WorkflowException {
+ public Map<String, WorkflowEventMetaData> getEventMapByHint(String eventType,
+ String eventHint) throws WorkflowException {
DocumentPath path = DocumentPath.from(
Lists.newArrayList("root", eventType, eventHint));
- Map<String, Versioned<String>> contexts = complete(eventMapTree.getChildren(path));
- Map<String, String> eventMap = Maps.newHashMap();
+ Map<String, Versioned<WorkflowEventMetaData>> contexts = complete(eventMapTree.getChildren(path));
+ Map<String, WorkflowEventMetaData> eventMap = Maps.newHashMap();
if (Objects.isNull(contexts)) {
return eventMap;
}
- for (Map.Entry<String, Versioned<String>> entry : contexts.entrySet()) {
+ for (Map.Entry<String, Versioned<WorkflowEventMetaData>> entry : contexts.entrySet()) {
eventMap.put(entry.getKey(), entry.getValue().value());
}
log.trace("getEventMapByHint returns eventMap {} ", eventMap);
@@ -170,11 +212,25 @@
}
}
+ @Override
+ public boolean isTriggerSet(String eventType, String eventHint,
+ String contextName) throws WorkflowException {
+
+ //Remove from eventCxtPerHintMap
+ DocumentPath dpathForCxt = DocumentPath.from(Lists.newArrayList(
+ "root", eventType, eventHint, contextName));
+ WorkflowEventMetaData workflowEventMetaData = completeVersioned(eventMapTree.get(dpathForCxt));
+ if (Objects.nonNull(workflowEventMetaData)) {
+ return workflowEventMetaData.getTriggerFlag();
+ } else {
+ return false;
+ }
+ }
@Override
- public Map<String, Versioned<String>> getChildren(String path) throws WorkflowException {
+ public Map<String, Versioned<WorkflowEventMetaData>> getChildren(String path) throws WorkflowException {
DocumentPath dpath = DocumentPath.from(path);
- Map<String, Versioned<String>> entries = complete(eventMapTree.getChildren(dpath));
+ Map<String, Versioned<WorkflowEventMetaData>> entries = complete(eventMapTree.getChildren(dpath));
return entries;
}
@@ -188,11 +244,11 @@
public ObjectNode asJsonTree() throws WorkflowException {
DocumentPath rootPath = DocumentPath.from(Lists.newArrayList("root"));
- Map<String, Versioned<String>> eventmap = complete(eventMapTree.getChildren(rootPath));
+ Map<String, Versioned<WorkflowEventMetaData>> eventmap = complete(eventMapTree.getChildren(rootPath));
ObjectNode rootNode = JsonNodeFactory.instance.objectNode();
- for (Map.Entry<String, Versioned<String>> eventTypeEntry : eventmap.entrySet()) {
+ for (Map.Entry<String, Versioned<WorkflowEventMetaData>> eventTypeEntry : eventmap.entrySet()) {
String eventType = eventTypeEntry.getKey();
@@ -200,9 +256,9 @@
rootNode.put(eventType, eventTypeNode);
DocumentPath eventTypePath = DocumentPath.from(Lists.newArrayList("root", eventType));
- Map<String, Versioned<String>> hintmap = complete(eventMapTree.getChildren(eventTypePath));
+ Map<String, Versioned<WorkflowEventMetaData>> hintmap = complete(eventMapTree.getChildren(eventTypePath));
- for (Map.Entry<String, Versioned<String>> hintEntry : hintmap.entrySet()) {
+ for (Map.Entry<String, Versioned<WorkflowEventMetaData>> hintEntry : hintmap.entrySet()) {
String hint = hintEntry.getKey();
@@ -210,10 +266,10 @@
eventTypeNode.put(hint, hintNode);
DocumentPath hintPath = DocumentPath.from(Lists.newArrayList("root", eventType, hint));
- Map<String, Versioned<String>> contextmap = complete(eventMapTree.getChildren(hintPath));
+ Map<String, Versioned<WorkflowEventMetaData>> contextmap = complete(eventMapTree.getChildren(hintPath));
- for (Map.Entry<String, Versioned<String>> ctxtEntry : contextmap.entrySet()) {
- hintNode.put(ctxtEntry.getKey(), ctxtEntry.getValue().value());
+ for (Map.Entry<String, Versioned<WorkflowEventMetaData>> ctxtEntry : contextmap.entrySet()) {
+ hintNode.put(ctxtEntry.getKey(), ctxtEntry.getValue().value().toString());
}
}
}
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 167e1c4..68c556e 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
@@ -32,6 +32,8 @@
import org.onosproject.workflow.api.EventTimeoutTask;
import org.onosproject.workflow.api.TimeoutTask;
import org.onosproject.workflow.api.TimerChain;
+import org.onosproject.workflow.api.TriggerWorklet;
+import org.onosproject.workflow.api.WorkflowEventMetaData;
import org.onosproject.workflow.api.Worklet;
import org.onosproject.workflow.api.Workflow;
import org.onosproject.workflow.api.WorkflowContext;
@@ -220,6 +222,7 @@
initWorkletExecution(latestContext);
workplaceStore.commitContext(latestContext.name(), latestContext, true);
+
}
@Override
@@ -229,7 +232,7 @@
return;
}
- Map<String, String> eventMap;
+ Map<String, WorkflowEventMetaData> eventMap;
String eventHint;
try {
@@ -238,6 +241,7 @@
log.error("Exception: ", e);
return;
}
+
if (eventHint == null) {
// do nothing
log.error("Invalid eventHint, event: {}", event);
@@ -246,59 +250,87 @@
try {
eventMap = eventMapStore.getEventMapByHint(event.getClass().getName(), eventHint);
- } catch (WorkflowException e) {
- log.error("Exception: ", e);
- return;
- }
-
- if (Objects.isNull(eventMap) || eventMap.isEmpty()) {
- // do nothing;
- log.debug("Invalid eventMap, event: {}", event);
- return;
- }
-
- for (Map.Entry<String, String> entry : eventMap.entrySet()) {
- String contextName = entry.getKey();
- String strProgramCounter = entry.getValue();
- ProgramCounter pc;
- try {
- pc = ProgramCounter.valueOf(strProgramCounter);
- } catch (IllegalArgumentException e) {
- log.error("Exception: ", e);
+ if (Objects.isNull(eventMap) || eventMap.isEmpty()) {
+ // do nothing;
+ log.debug("Invalid eventMap, event: {}", event);
return;
}
- WorkflowContext context = workplaceStore.getContext(contextName);
- if (Objects.isNull(context)) {
- log.info("Invalid context: {}, event: {}", contextName, event);
- continue;
- }
- EventTask eventtask = null;
- try {
- eventtask = EventTask.builder()
- .event(event)
- .eventHint(eventHint)
- .context(context)
- .programCounter(pc)
- .build();
- } catch (WorkflowException e) {
- log.error("Exception: ", e);
+ for (Map.Entry<String, WorkflowEventMetaData> entry : eventMap.entrySet()) {
+ String contextName = entry.getKey();
+ ProgramCounter pc = ProgramCounter.valueOf("INVALID_WORKLET", 0);
+ WorkflowContext context = null;
+
+ context = workplaceStore.getContext(contextName);
+
+ if (Objects.isNull(context)) {
+ log.info("Invalid context: {}, event: {}", contextName, event);
+ continue;
+ }
+
+ EventTask eventtask = null;
+ if (eventMapStore.isTriggerSet(event.getClass().getName(), eventHint, contextName)) {
+ try {
+ eventtask = EventTask.builder()
+ .event(event)
+ .eventHint(eventHint)
+ .context(context)
+ .programCounter(pc)
+ .build();
+ } catch (WorkflowException e) {
+ log.error("Exception: ", e);
+ }
+
+ log.debug("eventtaskAccumulator.add: task: {}", eventtask);
+ if (!Objects.isNull(eventtask)) {
+ eventtaskAccumulator.add(eventtask);
+ }
+ }
+ /*Both type of event is being scheduled here if applicable.
+ If worfklow trigger event is set but may not be a valid one for current event type,
+ then normal worklet event should be processed if applicable. But validity of workflow
+ trigger event would be checked later, so as of now both kind of event would be scheduled.
+ later while trigger event processing, its validity is found to be true, then worklet events
+ would be unregistered and eventually these events wont be processed.*/
+ if (eventMapStore.isEventMapPresent(contextName)) {
+ try {
+ pc = entry.getValue().getProgramCounter();
+ } catch (IllegalArgumentException e) {
+ log.error("Exception: ", e);
+ continue;
+ }
+ try {
+ eventtask = EventTask.builder()
+ .event(event)
+ .eventHint(eventHint)
+ .context(context)
+ .programCounter(pc)
+ .build();
+ } catch (WorkflowException e) {
+ log.error("Exception: ", e);
+ }
+
+ log.debug("eventtaskAccumulator.add: task: {}", eventtask);
+ if (!Objects.isNull(eventtask)) {
+ eventtaskAccumulator.add(eventtask);
+ }
+
+ }
}
- log.debug("eventtaskAccumulator.add: task: {}", eventtask);
- if (!Objects.isNull(eventtask)) {
- eventtaskAccumulator.add(eventtask);
- }
+ } catch (WorkflowException we) {
+ log.error("Exception {} occured in fetching contexts for trigger event {}", we, event);
}
}
@Override
public void registerEventMap(Class<? extends Event> eventType, Set<String> eventHintSet,
- String contextName, String programCounterString) throws WorkflowException {
- eventMapStore.registerEventMap(eventType.getName(), eventHintSet, contextName, programCounterString);
+ String contextName, ProgramCounter programCounter) throws WorkflowException {
+ eventMapStore.registerEventMap(eventType.getName(), eventHintSet, contextName, programCounter);
for (String eventHint : eventHintSet) {
for (int i = 0; i < MAX_REGISTER_EVENTMAP_WAITS; i++) {
- Map<String, String> eventMap = eventMapStore.getEventMapByHint(eventType.getName(), eventHint);
+ Map<String, WorkflowEventMetaData> eventMap =
+ eventMapStore.getEventMapByHint(eventType.getName(), eventHint);
if (eventMap != null && eventMap.containsKey(contextName)) {
break;
}
@@ -418,6 +450,7 @@
context.setWorkflowExecutionService(this);
context.setWorkflowStore(workflowStore);
context.setWorkplaceStore(workplaceStore);
+ context.setEventMapStore(eventMapStore);
context.waitCompletion(null, null, null, 0L);
context.setTriggerNext(false);
}
@@ -453,14 +486,39 @@
*/
private EventTask execEventTask(EventTask task) {
- if (!eventMapStore.isEventMapPresent(task.context().name())) {
- log.trace("EventMap doesnt exist for taskcontext:{}", task.context().name());
+ WorkflowContext context = (WorkflowContext) (task.context());
+ String cxtName = context.name();
+ try {
+ if (eventMapStore.isTriggerSet(task.event().getClass().getName(), task.eventHint(), cxtName)) {
+ WorkflowContext workflowContext = workplaceStore.getContext(cxtName);
+ Workflow workflow = workflowStore.get(workflowContext.workflowId());
+ String triggerWorkletName = workflow.getTriggerWorkletClassName().get();
+ Worklet worklet = workflow.getTriggerWorkletInstance(triggerWorkletName);
+ if (worklet instanceof TriggerWorklet) {
+ if (((TriggerWorklet) worklet).isTriggerValid(workflowContext, task.event())) {
+ if (Objects.nonNull(workflowContext.completionEventType())) {
+ eventMapStore.unregisterEventMap(workflowContext.completionEventType().getName(),
+ workflowContext.name());
+ }
+ initWorkletExecution(workflowContext);
+ workflowContext.setCurrent(ProgramCounter.INIT_PC);
+ workplaceStore.commitContext(cxtName, workflowContext, true);
+ }
+ }
+ }
+
+ } catch (WorkflowException we) {
+ log.error("Error Occurred in validating trigger for eventType {} eventHint {} context name {}",
+ task.eventType(), task.eventHint(), cxtName);
+ }
+
+ if (!eventMapStore.isEventMapPresent(cxtName)) {
+ log.trace("EventMap doesnt exist for taskcontext:{}", cxtName);
return task;
}
log.debug("execEventTask- task: {}, hash: {}", task, stringHash(task.context().distributor()));
- WorkflowContext context = (WorkflowContext) (task.context());
Workflow workflow = workflowStore.get(context.workflowId());
if (workflow == null) {
log.error("Invalid workflow {}", context.workflowId());
@@ -803,7 +861,6 @@
log.info("{} worklet.process(done): {}", latestContext.name(), worklet.tag());
log.trace("{} context: {}", latestContext.name(), latestContext);
-
if (latestContext.completionEventType() != null) {
if (latestContext.completionEventGenerator() == null) {
String msg = String.format("Invalid exepecting event(%s), generator(%s)",
@@ -813,8 +870,7 @@
}
registerEventMap(latestContext.completionEventType(), latestContext.completionEventHints(),
- latestContext.name(), pc.toString());
-
+ latestContext.name(), pc);
latestContext.completionEventGenerator().apply();
if (latestContext.completionEventTimeout() != 0L) {
@@ -846,7 +902,6 @@
latestContext.setCurrent(workflow.increased(pc));
}
}
-
workplaceStore.commitContext(latestContext.name(), latestContext, latestContext.triggerNext());
} catch (WorkflowException e) {
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java
index 219436c..7bfe01b 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkplaceWorkflow.java
@@ -23,8 +23,10 @@
import org.onosproject.workflow.api.DefaultWorkflowContext;
import org.onosproject.workflow.api.DefaultWorkplace;
import org.onosproject.workflow.api.ImmutableListWorkflow;
+import org.onosproject.workflow.api.JsonDataModelInjector;
import org.onosproject.workflow.api.JsonDataModelTree;
import org.onosproject.workflow.api.SystemWorkflowContext;
+import org.onosproject.workflow.api.TriggerWorklet;
import org.onosproject.workflow.api.Workflow;
import org.onosproject.workflow.api.WorkflowAttribute;
import org.onosproject.workflow.api.WorkflowContext;
@@ -35,6 +37,7 @@
import org.onosproject.workflow.api.WorkflowException;
import org.onosproject.workflow.api.WorkflowExecutionService;
import org.onosproject.workflow.api.WorkflowStore;
+import org.onosproject.workflow.api.Worklet;
import org.onosproject.workflow.api.Workplace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -214,6 +217,17 @@
WorkflowContext buildingContext = workflow.buildContext(workplace, subTree);
log.info("registerContext {}", buildingContext.name());
context.workplaceStore().registerContext(buildingContext.name(), buildingContext);
+
+ if (workflow.getTriggerWorkletClassName().isPresent()) {
+ String triggerWorkletName = workflow.getTriggerWorkletClassName().get();
+ Worklet worklet = workflow.getTriggerWorkletInstance(triggerWorkletName);
+ if (worklet instanceof TriggerWorklet) {
+ buildingContext.setEventMapStore(context.eventMapStore());
+ JsonDataModelInjector dataModelInjector = new JsonDataModelInjector();
+ dataModelInjector.inject(worklet, buildingContext);
+ ((TriggerWorklet) worklet).register(buildingContext);
+ }
+ }
submitTrue(context);
context.completed();
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 b397db5..14eb59d 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
@@ -106,6 +106,29 @@
workflowStore.register(workflow);
+ // registering new workflow definition
+ uri = URI.create("of-overlay.workflow-nova-with-trigger-to-restart");
+ workflow = ImmutableListWorkflow.builder()
+ .id(uri)
+ //.attribute(WorkflowAttribute.REMOVE_AFTER_COMPLETE)
+ .chain(Ovs.CreateOvsdbDevice.class.getName())
+ .chain(Ovs.UpdateOvsVersion.class.getName())
+ .chain(Ovs.UpdateBridgeId.class.getName())
+ .chain(DefaultWorkletDescription.builder().name(Ovs.CreateBridge.class.getName())
+ .staticDataModel(BRIDGE_NAME, "br-int")
+ .build())
+ .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())
+ .trigger(Ovs.TrigerWorkflowAtDeviceReboot.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()
@@ -188,6 +211,7 @@
String hint = event.subject().id().toString();
log.debug("hint: {}", hint);
return hint;
+
}
)
);
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 923173c..0506793 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
@@ -57,6 +57,7 @@
import org.onosproject.ovsdb.controller.OvsdbNodeId;
import org.onosproject.workflow.api.AbstractWorklet;
import org.onosproject.workflow.api.JsonDataModel;
+import org.onosproject.workflow.api.TriggerWorklet;
import org.onosproject.workflow.api.WorkflowContext;
import org.onosproject.workflow.api.WorkflowException;
import org.onosproject.workflow.api.StaticDataModel;
@@ -67,6 +68,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Objects;
@@ -1312,5 +1314,40 @@
}
}
}
+
+ /**
+ * Work-let class for trigger event registration and validation.
+ */
+ public static class TrigerWorkflowAtDeviceReboot extends TriggerWorklet {
+
+ @JsonDataModel(path = MODEL_MGMT_IP)
+ String strMgmtIp;
+
+ String strOfDevIdUnderlay;
+
+ @Override
+ public void register(WorkflowContext context) throws WorkflowException {
+ DeviceId brphyDevId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_UNDERLAY_NOVA);
+ Set<String> eventHintSet = new HashSet<>();
+ eventHintSet.add(brphyDevId.toString());
+ context.registerTriggerEvent(DeviceEvent.class, eventHintSet);
+ }
+
+ @Override
+ public boolean isTriggerValid(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_AVAILABILITY_CHANGED:
+ return !context.getService(DeviceService.class).isAvailable(device.id());
+ default:
+ return false;
+ }
+ }
+ }
}
diff --git a/apps/workflow/ofoverlay/test-cfg/network-cfg-cr-trigger-event-wf.json b/apps/workflow/ofoverlay/test-cfg/network-cfg-cr-trigger-event-wf.json
new file mode 100755
index 0000000..f837445
--- /dev/null
+++ b/apps/workflow/ofoverlay/test-cfg/network-cfg-cr-trigger-event-wf.json
@@ -0,0 +1,41 @@
+{
+ "apps": {
+ "org.onosproject.workflow": {
+ "workflow" : {
+ "rpc" : [
+ {
+ "op" : "workflow.invoke",
+ "params" : {
+ "workplace" : "Nova-000",
+ "id" : "of-overlay.workflow-nova-with-trigger-to-restart",
+ "data" : {
+
+ "mgmtIp" : "192.168.10.8",
+ "ovsdbPort" : 6641,
+
+ "sshAccessInfo" : {
+ "remoteIp" : "192.168.10.8",
+ "port" : 22,
+ "user" : "root",
+ "password" : "iloveyou",
+ "keyfile" : "~/.ssh/id_rsa"
+ },
+
+ "ovsDatapathType" : "system",
+ "physicalPorts" : [ "nova0_1" ],
+ "vtepIp" : "120.0.0.200/24",
+
+ "annotations" : {
+ "rackId" : 1,
+ "rackPosition" : 3
+ }
+
+ }
+ },
+ "id" : "00001@10.0.0.1"
+ }
+ ]
+ }
+ }
+ }
+}