Merge changes I675fbfe2,I33acebec into dev/ramcloud-new-datamodel
* changes:
WIP: Adding PathInstallRuntime for installing flow entries using FlowPusher
WIP: Initial implementation of runtime #2: take PathIntents and build a plan
diff --git a/src/main/java/net/onrc/onos/intent/Action.java b/src/main/java/net/onrc/onos/intent/Action.java
new file mode 100644
index 0000000..2f109d1
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/Action.java
@@ -0,0 +1,14 @@
+package net.onrc.onos.intent;
+
+import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+
+/**
+ *
+ * @author Brian O'Connor <bocon@onlab.us>
+ *
+ */
+
+public abstract class Action {
+
+ public abstract FlowEntryAction getFlowEntryAction();
+}
diff --git a/src/main/java/net/onrc/onos/intent/FlowEntry.java b/src/main/java/net/onrc/onos/intent/FlowEntry.java
new file mode 100644
index 0000000..14d1038
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/FlowEntry.java
@@ -0,0 +1,52 @@
+package net.onrc.onos.intent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.networkgraph.Port;
+import net.onrc.onos.ofcontroller.networkgraph.Switch;
+import net.onrc.onos.ofcontroller.util.Dpid;
+import net.onrc.onos.ofcontroller.util.FlowEntryActions;
+import net.onrc.onos.ofcontroller.util.FlowEntryId;
+
+/**
+ *
+ * @author Brian O'Connor <bocon@onlab.us>
+ *
+ */
+
+public class FlowEntry {
+ protected Switch sw;
+ protected Match match;
+ protected Set<Action> actions;
+
+ public FlowEntry(Switch sw, Port srcPort, Port dstPort,
+ MACAddress srcMac, MACAddress dstMac) {
+ this.sw = sw;
+ this.match = new Match(sw, srcPort, srcMac, dstMac);
+ this.actions = new HashSet<Action>();
+ this.actions.add(new ForwardAction(dstPort));
+ }
+
+ public String toString() {
+ return match + "->" + actions;
+ }
+
+ public Switch getSwitch() {
+ return sw;
+ }
+
+ public net.onrc.onos.ofcontroller.util.FlowEntry getFlowEntry() {
+ net.onrc.onos.ofcontroller.util.FlowEntry entry = new net.onrc.onos.ofcontroller.util.FlowEntry();
+ entry.setDpid(new Dpid(sw.getDpid()));
+ entry.setFlowEntryId(new FlowEntryId(0)); // all zero for now
+ entry.setFlowEntryMatch(match.getFlowEntryMatch());
+ FlowEntryActions flowEntryActions = new FlowEntryActions();
+ for(Action action : actions) {
+ flowEntryActions.addAction(action.getFlowEntryAction());
+ }
+ entry.setFlowEntryActions(flowEntryActions);
+ return entry;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/intent/ForwardAction.java b/src/main/java/net/onrc/onos/intent/ForwardAction.java
new file mode 100644
index 0000000..c6769b8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/ForwardAction.java
@@ -0,0 +1,30 @@
+package net.onrc.onos.intent;
+
+import net.onrc.onos.ofcontroller.networkgraph.Port;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+
+/**
+ *
+ * @author Brian O'Connor <bocon@onlab.us>
+ *
+ */
+
+class ForwardAction extends Action {
+ protected Port dstPort;
+
+ public ForwardAction(Port dstPort) {
+ this.dstPort = dstPort;
+ }
+
+ public String toString() {
+ return dstPort.toString();
+ }
+
+ @Override
+ public FlowEntryAction getFlowEntryAction() {
+ FlowEntryAction action = new FlowEntryAction();
+ action.setActionOutput(new net.onrc.onos.ofcontroller.util.Port(dstPort.getNumber().shortValue()));
+ return action;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/intent/Match.java b/src/main/java/net/onrc/onos/intent/Match.java
new file mode 100644
index 0000000..3f95ce4
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/Match.java
@@ -0,0 +1,53 @@
+package net.onrc.onos.intent;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.networkgraph.Port;
+import net.onrc.onos.ofcontroller.networkgraph.Switch;
+import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
+
+/**
+ *
+ * @author Brian O'Connor <bocon@onlab.us>
+ *
+ */
+
+public class Match {
+ protected Switch sw;
+ protected MACAddress srcMac;
+ protected MACAddress dstMac;
+ protected Port srcPort;
+
+ public Match(Switch sw, Port srcPort,
+ MACAddress srcMac, MACAddress dstMac) {
+ this.sw = sw;
+ this.srcPort = srcPort;
+ this.srcMac = srcMac;
+ this.dstMac = dstMac;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof Match) {
+ Match other = (Match) obj;
+ return this.sw == other.sw &&
+ this.srcMac == other.srcMac &&
+ this.dstMac == other.dstMac &&
+ this.srcPort == other.srcPort;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public FlowEntryMatch getFlowEntryMatch(){
+ FlowEntryMatch match = new FlowEntryMatch();
+ match.enableSrcMac(srcMac);
+ match.enableDstMac(dstMac);
+ match.enableInPort(new net.onrc.onos.ofcontroller.util.Port(srcPort.getNumber().shortValue()));
+ return match;
+ }
+
+ public String toString() {
+ return "(" + srcPort + "," + srcMac + "," + dstMac + ")";
+ }
+}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/IntentRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/IntentRuntime.java
new file mode 100644
index 0000000..567986e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/runtime/IntentRuntime.java
@@ -0,0 +1,55 @@
+package net.onrc.onos.intent.runtime;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
+
+public class IntentRuntime implements IFloodlightModule {
+ protected volatile IFloodlightProviderService floodlightProvider;
+ protected volatile INetworkGraphService networkGraph;
+ protected volatile IRestApiService restApi;
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ networkGraph = context.getServiceImpl(INetworkGraphService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ restApi.addRestletRoutable(new IntentWebRoutable());
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IFloodlightProviderService.class);
+ l.add(INetworkGraphService.class);
+ l.add(IRestApiService.class);
+ return l;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/IntentWebRoutable.java b/src/main/java/net/onrc/onos/intent/runtime/IntentWebRoutable.java
new file mode 100644
index 0000000..533b55b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/runtime/IntentWebRoutable.java
@@ -0,0 +1,23 @@
+package net.onrc.onos.intent.runtime;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+public class IntentWebRoutable implements RestletRoutable {
+
+ @Override
+ public Restlet getRestlet(Context context) {
+ Router router = new Router(context);
+ // TODO: add routes
+ return router;
+ }
+
+ @Override
+ public String basePath() {
+ return "/wm/onos/intent";
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathInstallRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PathInstallRuntime.java
new file mode 100644
index 0000000..0b74cfc
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathInstallRuntime.java
@@ -0,0 +1,48 @@
+package net.onrc.onos.intent.runtime;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.onrc.onos.intent.FlowEntry;
+import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
+import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
+import net.onrc.onos.ofcontroller.util.Pair;
+
+/**
+ *
+ * @author Brian O'Connor <bocon@onlab.us>
+ *
+ */
+
+public class PathInstallRuntime {
+ NetworkGraph graph;
+ IFlowPusherService pusher;
+ IFloodlightProviderService provider;
+ protected List<Set<FlowEntry>> plan;
+
+ public PathInstallRuntime(NetworkGraph graph) {
+ this.graph = graph;
+ }
+
+ public void installPlan(List<Set<FlowEntry>> plan) {
+ this.plan = plan;
+ Map<Long,IOFSwitch> switches = provider.getSwitches();
+ for(Set<FlowEntry> phase : plan) {
+ Set<Pair<IOFSwitch, net.onrc.onos.ofcontroller.util.FlowEntry>> entries
+ = new HashSet<>();
+ // convert flow entries and create pairs
+ for(FlowEntry entry : phase) {
+ entries.add(new Pair<>(switches.get(entry.getSwitch().getDpid()),
+ entry.getFlowEntry()));
+ }
+ // push flow entries to switches
+ pusher.pushFlowEntries(entries);
+ // TODO: wait for confirmation messages before proceeding
+ }
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
new file mode 100644
index 0000000..9a46530
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
@@ -0,0 +1,108 @@
+package net.onrc.onos.intent.runtime;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.intent.FlowEntry;
+import net.onrc.onos.intent.Intent;
+import net.onrc.onos.intent.PathIntent;
+import net.onrc.onos.intent.PathIntents;
+import net.onrc.onos.intent.ShortestPathIntent;
+import net.onrc.onos.ofcontroller.networkgraph.Link;
+import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
+import net.onrc.onos.ofcontroller.networkgraph.Port;
+import net.onrc.onos.ofcontroller.networkgraph.Switch;
+
+/**
+ *
+ * @author Brian O'Connor <bocon@onlab.us>
+ *
+ */
+
+public class PlanCalcRuntime {
+ NetworkGraph graph;
+ protected PathIntents intents;
+ protected Set<Collection<FlowEntry>> flowEntries;
+ protected List<Set<FlowEntry>> plan;
+
+ public PlanCalcRuntime(NetworkGraph graph) {
+ this.graph = graph;
+ this.flowEntries = new HashSet<>();
+ this.plan = new ArrayList<>();
+ }
+
+ public void addIntents(PathIntents intents) {
+ this.intents = intents;
+ computeFlowEntries();
+ constructPlan();
+ }
+
+ public List<Set<FlowEntry>> getPlan() {
+ return plan;
+ }
+
+ public void computeFlowEntries() {
+ for(PathIntent intent : intents.getIntents()) {
+ Intent parent = intent.getParentIntent();
+ Port srcPort, dstPort, lastDstPort = null;
+ MACAddress srcMac, dstMac;
+ if(parent instanceof ShortestPathIntent) {
+ ShortestPathIntent pathIntent = (ShortestPathIntent) parent;
+ Switch srcSwitch = graph.getSwitch(pathIntent.getSrcSwitchDpid());
+ srcPort = srcSwitch.getPort(pathIntent.getSrcPortNumber());
+ srcMac = MACAddress.valueOf(pathIntent.getSrcMac());
+ dstMac = MACAddress.valueOf(pathIntent.getDstMac());
+ Switch dstSwitch = graph.getSwitch(pathIntent.getDstSwitchDpid());
+ lastDstPort = dstSwitch.getPort(pathIntent.getDstPortNumber());
+ }
+ else {
+ // TODO: log this error
+ continue;
+ }
+ List<FlowEntry> entries = new ArrayList<>();
+ for(Link link : intent.getPath(graph)) {
+ Switch sw = link.getSourceSwitch();
+ dstPort = link.getSourcePort();
+ FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac);
+ entries.add(fe);
+ srcPort = link.getDestinationPort();
+ }
+ if(lastDstPort != null) {
+ Switch sw = lastDstPort.getSwitch();
+ dstPort = lastDstPort;
+ FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac);
+ entries.add(fe);
+ }
+ // install flow entries in reverse order
+ Collections.reverse(entries);
+ flowEntries.add(entries);
+ }
+ }
+
+ public void constructPlan() {
+ Map<FlowEntry, Integer> map = new HashMap<>();
+ for(Collection<FlowEntry> c : flowEntries) {
+ for(FlowEntry e: c) {
+ Integer i = map.get(e);
+ if(i == null) {
+ map.put(e, 1);
+ }
+ else {
+ i += 1;
+ }
+
+ }
+ }
+
+ // really simple first iteration of plan
+ //TODO: optimize the map in phases
+ plan.add(map.keySet());
+ }
+}
diff --git a/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java b/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
index e2a9a64..d31b9b6 100644
--- a/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
+++ b/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
@@ -56,9 +56,14 @@
// compile high-level intents into low-level intents (calculate paths)
PathCalcRuntime runtime1 = new PathCalcRuntime(g);
runtime1.addInputIntents(intents);
+
+ // compile low-level intents into flow entry installation plan
+ PlanCalcRuntime runtime2 = new PlanCalcRuntime(g);
+ runtime2.addIntents(runtime1.getOutputIntents());
// show results
showResult(runtime1.getOutputIntents());
+ System.out.println(runtime2.getPlan());
}
@Test
@@ -75,8 +80,13 @@
PathCalcRuntime runtime1 = new PathCalcRuntime(g);
runtime1.addInputIntents(intents);
+ // compile low-level intents into flow entry installation plan
+ PlanCalcRuntime runtime2 = new PlanCalcRuntime(g);
+ runtime2.addIntents(runtime1.getOutputIntents());
+
// show results
showResult(runtime1.getOutputIntents());
+ System.out.println(runtime2.getPlan());
}
@Test
@@ -93,7 +103,12 @@
PathCalcRuntime runtime1 = new PathCalcRuntime(g);
runtime1.addInputIntents(intents);
+ // compile low-level intents into flow entry installation plan
+ PlanCalcRuntime runtime2 = new PlanCalcRuntime(g);
+ runtime2.addIntents(runtime1.getOutputIntents());
+
// show results
showResult(runtime1.getOutputIntents());
+ System.out.println(runtime2.getPlan());
}
}