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());
 	}
 }