Merge remote-tracking branch 'upstream/master'
diff --git a/cassandra.titan b/cassandra.titan
index ef6f3ae..2f34fb3 100644
--- a/cassandra.titan
+++ b/cassandra.titan
@@ -1,3 +1,6 @@
 storage.backend=cassandra
 storage.hostname=localhost
 storage.keyspace=onos
+storage.connection-pool-size=4096
+storage.replication-factor=3
+storage.read-consistency-level=1
diff --git a/scripts/link.sh b/scripts/link.sh
index d87464b..dc202e7 100755
--- a/scripts/link.sh
+++ b/scripts/link.sh
@@ -1,4 +1,5 @@
 #! /bin/bash
+
 controller=`hostname`
 switches=`ifconfig -a | grep sw |grep -v eth | awk '{print $1}'`
 
@@ -20,21 +21,26 @@
 for s in $switches; do
     dpid=`sudo ovs-ofctl  show  $s |grep dpid | awk '{print $4}'`
     if [  "x$dpid" == "x$src_dpid" ]; then
-        intfs=`sudo ovs-ofctl show $s |grep addr | awk '{print $1}' | sed 's/.*(//g' | sed 's/):$//g'`
-	intf_list=()
-	for i in $intfs; do
-	  intf_list+=($i)
-        done
-	intf=${intf_list[$src_port]}
-        if [ x$cmd == "xup" ]; then
-	    echo "sudo ifconfig ${intf}  up"
-	    sudo ifconfig ${intf}  up
-        elif [ x$cmd == "xdown" ]; then
-	    echo "sudo ifconfig ${intf}  down"
-	    sudo ifconfig ${intf}  down
-        else
-	    echo "sudo ifconfig ${intf}"
-	    sudo ifconfig ${intf} 
+
+#       intf=`sudo ovs-ofctl show $s |grep addr | awk -v p=$src_port 'BEGIN {pat="^ "p"\("}
+#	$0 ~ pat {w=match ($0, /\(.*\)/); if (w) print substr($0, RSTART+1, RLENGTH-2)}'`
+
+        sudo ovs-ofctl show $s |grep addr | sed 's/[\(\)]/,/g'>/tmp/baz.out
+	intf=`cat /tmp/baz.out | awk -v p=$src_port 'BEGIN {pat="^ "p","}
+	$0 ~ pat {w=match($0, /,.*,/); if (w) print substr($0, RSTART+1, RLENGTH-2)}'`
+
+	if [ x$intf != "x" ]; then
+	        if [ x$cmd == "xup" ]; then
+		    echo "sudo ifconfig ${intf}  up"
+		    sudo ifconfig ${intf}  up
+       		elif [ x$cmd == "xdown" ]; then
+		    echo "sudo ifconfig ${intf}  down"
+		    sudo ifconfig ${intf}  down
+	        else
+		    echo "sudo ifconfig ${intf}"
+		    sudo ifconfig ${intf} 
+		fi
+		break
         fi
     fi
 done
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index e58b19f..18da3dd 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -54,6 +54,10 @@
 		@JsonIgnore
 		@GremlinGroovy("_().out('on').out('host')")
 		public Iterable<IDeviceObject> getDevices();
+		
+		@JsonIgnore
+		@Incidence(label="switch",direction = Direction.IN)
+		public Iterable<IFlowEntry> getFlowEntries();
 	}
 	
 	public interface IPortObject extends IBaseObject{
@@ -94,6 +98,14 @@
 		@Adjacency(label="host")
 		public void removeDevice(final IDeviceObject device);
 		
+		@JsonIgnore
+		@Incidence(label="inport",direction = Direction.IN)
+		public Iterable<IFlowEntry> getInFlowEntries();
+		
+		@JsonIgnore
+		@Incidence(label="outport",direction = Direction.IN)
+		public Iterable<IFlowEntry> getOutFlowEntries();
+		
 //		@JsonIgnore
 //		@Adjacency(label="link")
 //		public Iterable<ILinkObject> getLinks();
@@ -187,6 +199,10 @@
 
 		@Adjacency(label="flow", direction=Direction.IN)
 		public void removeFlowEntry(final IFlowEntry flowEntry);
+		
+		@JsonIgnore
+		@GremlinGroovy("_().in('flow').out('switch')")
+		public Iterable<IDeviceObject> getSwitches();
 	}
 
 public interface IFlowEntry extends IBaseObject {
@@ -267,5 +283,29 @@
 
 		@Property("actionOutput")
 		public void setActionOutput(Short actionOutput);
+
+		@Adjacency(label="flow")
+		public IFlowPath getFlow();
+
+		@Adjacency(label="flow")
+		public void setFlow(IFlowPath flow);
+
+		@Adjacency(label="switch")
+		public ISwitchObject getSwitch();
+
+		@Adjacency(label="switch")
+		public void setSwitch(ISwitchObject sw);
+
+		@Adjacency(label="inport")
+		public IPortObject getInPort();
+
+		@Adjacency(label="inport")
+		public void setInPort(IPortObject port);
+
+		@Adjacency(label="outport")
+		public IPortObject getOutPort();
+
+		@Adjacency(label="outport")
+		public void setOutPort(IPortObject port);
 	}
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 405af1a..2b23e18 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -5,6 +5,7 @@
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -22,6 +23,8 @@
 import net.floodlightcontroller.core.INetMapStorage;
 import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
 import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
+import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
@@ -48,6 +51,7 @@
 import net.floodlightcontroller.util.OFMessageDamper;
 import net.floodlightcontroller.util.Port;
 import net.floodlightcontroller.util.SwitchPort;
+import net.onrc.onos.flow.IFlowManager;
 import net.onrc.onos.util.GraphDBConnection;
 import net.onrc.onos.util.GraphDBConnection.Transaction;
 
@@ -62,12 +66,13 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
+public class FlowManager implements IFloodlightModule, IFlowService, IFlowManager, INetMapStorage {
 
     public GraphDBConnection conn;
 
     protected IRestApiService restApi;
     protected IFloodlightProviderService floodlightProvider;
+    protected ITopoRouteService topoRouteService;
     protected FloodlightModuleContext context;
 
     protected OFMessageDamper messageDamper;
@@ -142,8 +147,6 @@
 		    return;
 		}
 
-		ITopoRouteService topoRouteService =
-		    context.getServiceImpl(ITopoRouteService.class);
 		if (topoRouteService == null) {
 		    log.debug("Topology Route Service not found");
 		    return;
@@ -310,7 +313,7 @@
 		//
 		// Process my Flow Entries
 		//
-		Boolean processed_measurement_flow = false;
+		boolean processed_measurement_flow = false;
 		for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
 		    IFlowEntry flowEntryObj = entry.getValue();
 		    // Code for measurement purpose
@@ -337,6 +340,7 @@
 		    if (mySwitch == null)
 			continue;		// Shouldn't happen
 
+		    // TODO: PAVPAVPAV: FROM HERE...
 		    //
 		    // Create the Open Flow Flow Modification Entry to push
 		    //
@@ -444,6 +448,9 @@
 		    } catch (IOException e) {
 			log.error("Failure writing flow mod from network map", e);
 		    }
+		    // TODO: XXX: PAVPAVPAV: TO HERE.
+		    // TODO: XXX: PAVPAVPAV: Update the flowEntryObj
+		    // to "FE_SWITCH_UPDATED" in the new (refactored) code.
 		}
 
 		//
@@ -540,6 +547,7 @@
 	Collection<Class<? extends IFloodlightService>> l =
 	    new ArrayList<Class<? extends IFloodlightService>>();
 	l.add(IFloodlightProviderService.class);
+	l.add(ITopoRouteService.class);
 	l.add(IRestApiService.class);
         return l;
     }
@@ -549,6 +557,7 @@
 	throws FloodlightModuleException {
 	this.context = context;
 	floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+	topoRouteService = context.getServiceImpl(ITopoRouteService.class);
 	restApi = context.getServiceImpl(IRestApiService.class);
 	messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
 					    EnumSet.of(OFType.FLOW_MOD),
@@ -687,7 +696,11 @@
 	    flowEntryObj.setType("flow_entry");
 
 	    // 
-	    // Set the Flow Entry attributes:
+	    // Set the Flow Entry Edges and attributes:
+	    // - Switch edge
+	    // - InPort edge
+	    // - OutPort edge
+	    //
 	    // - flowEntry.flowEntryMatch()
 	    // - flowEntry.flowEntryActions()
 	    // - flowEntry.dpid()
@@ -702,23 +715,42 @@
 	    // - flowEntry.matchDstMac()
 	    // - flowEntry.actionOutput()
 	    //
+	    ISwitchObject sw =
+		conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
 	    flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
-	    if (flowEntry.flowEntryMatch().matchInPort())
-		flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
-	    if (flowEntry.flowEntryMatch().matchEthernetFrameType())
-		flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
-	    if (flowEntry.flowEntryMatch().matchSrcIPv4Net())
-		flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
-	    if (flowEntry.flowEntryMatch().matchDstIPv4Net())
-		flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
-	    if (flowEntry.flowEntryMatch().matchSrcMac())
-		flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
-	    if (flowEntry.flowEntryMatch().matchDstMac())
-		flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
+	    flowEntryObj.setSwitch(sw);
+	    if (flowEntry.flowEntryMatch().matchInPort()) {
+	    	IPortObject inport =
+		    conn.utils().searchPort(conn, flowEntry.dpid().toString(),
+					    flowEntry.flowEntryMatch().inPort().value());
+	    	flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
+	    	flowEntryObj.setInPort(inport);
+	    }
+	    if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
+	    	flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
+	    }
+	    if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
+	    	flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
+	    }
+	    if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
+	    	flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
+	    }
+	    if (flowEntry.flowEntryMatch().matchSrcMac()) {
+	    	flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
+	    }
+	    if (flowEntry.flowEntryMatch().matchDstMac()) {
+	    	flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
+	    }
 
 	    for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
-		if (fa.actionOutput() != null)
+	    	if (fa.actionOutput() != null) {
+		    IPortObject outport =
+			conn.utils().searchPort(conn,
+						flowEntry.dpid().toString(),
+						fa.actionOutput().port().value());
 		    flowEntryObj.setActionOutput(fa.actionOutput().port().value());
+		    flowEntryObj.setOutPort(outport);
+	    	}
 	    }
 	    // TODO: Hacks with hard-coded state names!
 	    if (found)
@@ -727,18 +759,16 @@
 		flowEntryObj.setUserState("FE_USER_ADD");
 	    flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
 	    //
-	    // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
-	    // and FlowEntryErrorState.
+	    // TODO: Take care of the FlowEntryErrorState.
 	    //
 
 	    // Flow Entries edges:
 	    //   Flow
-	    //   NextFE
-	    //   InPort
-	    //   OutPort
-	    //   Switch
-	    if (! found)
+	    //   NextFE (TODO)
+	    if (! found) {
 		flowObj.addFlowEntry(flowEntryObj);
+		flowEntryObj.setFlow(flowObj);
+	    }
 	}
 	conn.endTx(Transaction.COMMIT);
 
@@ -1092,12 +1122,29 @@
 	//
 	// Extract the Flow state
 	//
-	flowPath.setFlowId(new FlowId(flowObj.getFlowId()));
-	flowPath.setInstallerId(new CallerId(flowObj.getInstallerId()));
-	flowPath.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
-	flowPath.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
-	flowPath.dataPath().dstPort().setDpid(new Dpid(flowObj.getDstSwitch()));
-	flowPath.dataPath().dstPort().setPort(new Port(flowObj.getDstPort()));
+	String flowIdStr = flowObj.getFlowId();
+	String installerIdStr = flowObj.getInstallerId();
+	String srcSwitchStr = flowObj.getSrcSwitch();
+	Short srcPortStr = flowObj.getSrcPort();
+	String dstSwitchStr = flowObj.getDstSwitch();
+	Short dstPortStr = flowObj.getDstPort();
+
+	if ((flowIdStr == null) ||
+	    (installerIdStr == null) ||
+	    (srcSwitchStr == null) ||
+	    (srcPortStr == null) ||
+	    (dstSwitchStr == null) ||
+	    (dstPortStr == null)) {
+	    // TODO: A work-around, becauuse of some bogus database objects
+	    return null;
+	}
+
+	flowPath.setFlowId(new FlowId(flowIdStr));
+	flowPath.setInstallerId(new CallerId(installerIdStr));
+	flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
+	flowPath.dataPath().srcPort().setPort(new Port(srcPortStr));
+	flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
+	flowPath.dataPath().dstPort().setPort(new Port(dstPortStr));
 
 	//
 	// Extract all Flow Entries
@@ -1105,8 +1152,20 @@
 	Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
 	for (IFlowEntry flowEntryObj : flowEntries) {
 	    FlowEntry flowEntry = new FlowEntry();
-	    flowEntry.setFlowEntryId(new FlowEntryId(flowEntryObj.getFlowEntryId()));
-	    flowEntry.setDpid(new Dpid(flowEntryObj.getSwitchDpid()));
+	    String flowEntryIdStr = flowEntryObj.getFlowEntryId();
+	    String switchDpidStr = flowEntryObj.getSwitchDpid();
+	    String userState = flowEntryObj.getUserState();
+	    String switchState = flowEntryObj.getSwitchState();
+
+	    if ((flowEntryIdStr == null) ||
+		(switchDpidStr == null) ||
+		(userState == null) ||
+		(switchState == null)) {
+		// TODO: A work-around, becauuse of some bogus database objects
+		continue;
+	    }
+	    flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
+	    flowEntry.setDpid(new Dpid(switchDpidStr));
 
 	    //
 	    // Extract the match conditions
@@ -1143,10 +1202,7 @@
 		actions.add(action);
 	    }
 	    flowEntry.setFlowEntryActions(actions);
-
-	    String userState = flowEntryObj.getUserState();
 	    flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
-	    String switchState = flowEntryObj.getSwitchState();
 	    flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
 	    //
 	    // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
@@ -1157,4 +1213,472 @@
 
 	return flowPath;
     }
+
+    /**
+     * Add and maintain a shortest-path flow.
+     *
+     * NOTE: The Flow Path argument does NOT contain all flow entries.
+     * Instead, it contains a single dummy flow entry that is used to
+     * store the matching condition(s).
+     * That entry is replaced by the appropriate entries from the
+     * internally performed shortest-path computation.
+     *
+     * @param flowPath the Flow Path with the endpoints and the match
+     * conditions to install.
+     * @return the added shortest-path flow on success, otherwise null.
+     */
+    @Override
+    public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
+	//
+	// Do the shortest path computation
+	//
+	DataPath dataPath =
+	    topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
+					     flowPath.dataPath().dstPort());
+	if (dataPath == null)
+	    return null;
+
+	FlowEntryMatch userFlowEntryMatch = null;
+	if (! flowPath.dataPath().flowEntries().isEmpty()) {
+	    userFlowEntryMatch = flowPath.dataPath().flowEntries().get(0).flowEntryMatch();
+	}
+
+	//
+	// Set the incoming port matching and the outgoing port output
+	// actions for each flow entry.
+	//
+	for (FlowEntry flowEntry : dataPath.flowEntries()) {
+	    // Set the incoming port matching
+	    FlowEntryMatch flowEntryMatch = null;
+	    if (userFlowEntryMatch != null)
+		flowEntryMatch = new FlowEntryMatch(userFlowEntryMatch);
+	    else
+		flowEntryMatch = new FlowEntryMatch();
+	    flowEntry.setFlowEntryMatch(flowEntryMatch);
+	    flowEntryMatch.enableInPort(flowEntry.inPort());
+
+	    // Set the outgoing port output action
+	    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
+	    if (flowEntryActions == null) {
+		flowEntryActions = new ArrayList<FlowEntryAction>();
+		flowEntry.setFlowEntryActions(flowEntryActions);
+	    }
+	    FlowEntryAction flowEntryAction = new FlowEntryAction();
+	    flowEntryAction.setActionOutput(flowEntry.outPort());
+	    flowEntryActions.add(flowEntryAction);
+	}
+
+	//
+	// Prepare the computed Flow Path
+	//
+	FlowPath computedFlowPath = new FlowPath();
+	computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
+	computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
+	computedFlowPath.setDataPath(dataPath);
+
+	FlowId flowId = new FlowId();
+	if (! addFlow(computedFlowPath, flowId))
+	    return null;
+
+	// TODO: Mark the flow for maintenance purpose
+
+	return (computedFlowPath);
+    }
+
+    /**
+     * Create a Flow from port to port.
+     *
+     * TODO: We don't need it for now.
+     *
+     * @param src_port the source port.
+     * @param dest_port the destination port.
+     */
+    @Override
+    public void createFlow(IPortObject src_port, IPortObject dest_port) {
+	// TODO: We don't need it for now.
+    }
+
+    /**
+     * Get all Flows matching a source and a destination port.
+     *
+     * TODO: Pankaj might be implementing it later.
+     *
+     * @param src_port the source port to match.
+     * @param dest_port the destination port to match.
+     * @return all flows matching the source and the destination port.
+     */
+    @Override
+    public Iterable<FlowPath> getFlows(IPortObject src_port,
+				       IPortObject dest_port) {
+	// TODO: Pankaj might be implementing it later.
+	return null;
+    }
+
+    /**
+     * Get all Flows going out from a port.
+     *
+     * TODO: We need it now: Pankaj
+     *
+     * @param port the port to match.
+     * @return the list of flows that are going out from the port.
+     */
+    @Override
+    public Iterable<FlowPath> getFlows(IPortObject port) {
+	// TODO: We need it now: Pankaj
+	return null;
+    }
+
+    /**
+     * Reconcile all flows on inactive switch port.
+     *
+     * @param portObject the port that has become inactive.
+     */
+    @Override
+    public void reconcileFlows(IPortObject portObject) {
+	Iterable<IFlowEntry> inFlowEntries = portObject.getInFlowEntries();
+	Iterable<IFlowEntry> outFlowEntries = portObject.getOutFlowEntries();
+
+	//
+	// Collect all affected Flow IDs from the affected flow entries
+	//
+	HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
+	for (IFlowEntry flowEntryObj: inFlowEntries) {
+	    IFlowPath flowObj = flowEntryObj.getFlow();
+	    if (flowObj != null)
+		flowObjSet.add(flowObj);
+	}
+	for (IFlowEntry flowEntryObj: outFlowEntries) {
+	    IFlowPath flowObj = flowEntryObj.getFlow();
+	    if (flowObj != null)
+		flowObjSet.add(flowObj);
+	}
+	// conn.endTx(Transaction.COMMIT);
+
+	//
+	// Remove the old Flow Entries, and add the new Flow Entries
+	//
+	Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+	for (IFlowPath flowObj : flowObjSet) {
+	    FlowPath flowPath = extractFlowPath(flowObj);
+	    if (flowPath == null)
+		continue;
+
+	    //
+	    // Remove my Flow Entries from the Network MAP
+	    //
+	    Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+	    for (IFlowEntry flowEntryObj : flowEntries) {
+		String dpidStr = flowEntryObj.getSwitchDpid();
+		if (dpidStr == null)
+		    continue;
+		Dpid dpid = new Dpid(dpidStr);
+		IOFSwitch mySwitch = mySwitches.get(dpid.value());
+		if (mySwitch == null)
+		    continue;		// Ignore the entry: not my switch
+		flowObj.removeFlowEntry(flowEntryObj);
+		conn.utils().removeFlowEntry(conn, flowEntryObj);
+	    }
+
+	    //
+	    // Delete the flow entries from the switches
+	    //
+	    for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+		flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+		installFlowEntry(mySwitches, flowEntry);
+	    }
+
+	    //
+	    // Calculate the new shortest path and install it in the
+	    // Network MAP.
+	    //
+	    FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
+	    if (addedFlowPath == null) {
+		log.error("Cannot add Shortest Path Flow from {} to {}",
+			  flowPath.dataPath().srcPort().toString(),
+			  flowPath.dataPath().dstPort().toString());
+		continue;
+	    }
+
+	    //
+	    // Add the flow entries to the switches
+	    //
+	    for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
+		flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
+		installFlowEntry(mySwitches, flowEntry);
+	    }
+	}
+    }
+
+    /**
+     * Reconcile all flows between a source and a destination port.
+     *
+     * TODO: We don't need it for now.
+     *
+     * @param src_port the source port.
+     * @param dest_port the destination port.
+     */
+    @Override
+    public void reconcileFlow(IPortObject src_port, IPortObject dest_port) {
+	// TODO: We don't need it for now.
+    }
+
+    /**
+     * Compute the shortest path between a source and a destination ports.
+     *
+     * @param src_port the source port.
+     * @param dest_port the destination port.
+     * @return the computed shortest path between the source and the
+     * destination ports. The flow entries in the path itself would
+     * contain the incoming port matching and the outgoing port output
+     * actions set. However, the path itself will NOT have the Flow ID,
+     * Installer ID, and any additional matching conditions for the
+     * flow entries (e.g., source or destination MAC address, etc).
+     */
+    @Override
+    public FlowPath computeFlowPath(IPortObject src_port,
+				    IPortObject dest_port) {
+	//
+	// Prepare the arguments
+	//
+	String dpidStr = src_port.getSwitch().getDPID();
+	Dpid srcDpid = new Dpid(dpidStr);
+	Port srcPort = new Port(src_port.getNumber());
+
+	dpidStr = dest_port.getSwitch().getDPID();
+	Dpid dstDpid = new Dpid(dpidStr);
+	Port dstPort = new Port(dest_port.getNumber());
+
+	SwitchPort src = new SwitchPort(srcDpid, srcPort);
+	SwitchPort dst = new SwitchPort(dstDpid, dstPort);
+
+	//
+	// Do the shortest path computation
+	//
+	DataPath dataPath = topoRouteService.getShortestPath(src, dst);
+	if (dataPath == null)
+	    return null;
+
+	//
+	// Set the incoming port matching and the outgoing port output
+	// actions for each flow entry.
+	//
+	for (FlowEntry flowEntry : dataPath.flowEntries()) {
+	    // Set the incoming port matching
+	    FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
+	    if (flowEntryMatch == null) {
+		flowEntryMatch = new FlowEntryMatch();
+		flowEntry.setFlowEntryMatch(flowEntryMatch);
+	    }
+	    flowEntryMatch.enableInPort(flowEntry.inPort());
+
+	    // Set the outgoing port output action
+	    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
+	    if (flowEntryActions == null) {
+		flowEntryActions = new ArrayList<FlowEntryAction>();
+		flowEntry.setFlowEntryActions(flowEntryActions);
+	    }
+	    FlowEntryAction flowEntryAction = new FlowEntryAction();
+	    flowEntryAction.setActionOutput(flowEntry.outPort());
+	    flowEntryActions.add(flowEntryAction);
+	}
+
+	//
+	// Prepare the return result
+	//
+	FlowPath flowPath = new FlowPath();
+	flowPath.setDataPath(dataPath);
+
+	return flowPath;
+    }
+
+    /**
+     * Get all Flow Entries of a Flow.
+     *
+     * @param flow the flow whose flow entries should be returned.
+     * @return the flow entries of the flow.
+     */
+    @Override
+    public Iterable<FlowEntry> getFlowEntries(FlowPath flow) {
+	return flow.dataPath().flowEntries();
+    }
+
+    /**
+     * Install a Flow Entry on a switch.
+     *
+     * @param mySwitches the DPID-to-Switch mapping for the switches
+     * controlled by this controller.
+     * @param flowEntry the flow entry to install.
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean installFlowEntry(Map<Long, IOFSwitch> mySwitches,
+				    FlowEntry flowEntry) {
+	IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+	if (mySwitch == null) {
+	    // Not my switch
+	    return (installRemoteFlowEntry(flowEntry));
+	}
+
+	//
+	// Create the OpenFlow Flow Modification Entry to push
+	//
+	OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
+	    .getMessage(OFType.FLOW_MOD);
+	long cookie = flowEntry.flowEntryId().value();
+
+	short flowModCommand = OFFlowMod.OFPFC_ADD;
+	if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
+	    flowModCommand = OFFlowMod.OFPFC_ADD;
+	} else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
+	    flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+	} else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
+	    flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+	} else {
+	    // Unknown user state. Ignore the entry
+	    log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
+		      flowEntry.flowEntryId().toString(),
+		      flowEntry.flowEntryUserState());
+	    return false;
+	}
+
+	//
+	// Fetch the match conditions
+	//
+	OFMatch match = new OFMatch();
+	match.setWildcards(OFMatch.OFPFW_ALL);
+	Port matchInPort = flowEntry.flowEntryMatch().inPort();
+	if (matchInPort != null) {
+	    match.setInputPort(matchInPort.value());
+	    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
+	}
+	Short matchEthernetFrameType =
+	    flowEntry.flowEntryMatch().ethernetFrameType();
+	if (matchEthernetFrameType != null) {
+	    match.setDataLayerType(matchEthernetFrameType);
+	    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+	}
+	IPv4Net matchSrcIPv4Net = flowEntry.flowEntryMatch().srcIPv4Net();
+	if (matchSrcIPv4Net != null) {
+	    match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
+	}
+	IPv4Net matchDstIPv4Net = flowEntry.flowEntryMatch().dstIPv4Net();
+	if (matchDstIPv4Net != null) {
+	    match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
+	}
+	MACAddress matchSrcMac = flowEntry.flowEntryMatch().srcMac();
+	if (matchSrcMac != null) {
+	    match.setDataLayerSource(matchSrcMac.toString());
+	    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+	}
+	MACAddress matchDstMac = flowEntry.flowEntryMatch().dstMac();
+	if (matchDstMac != null) {
+	    match.setDataLayerDestination(matchDstMac.toString());
+	    match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+	}
+
+	//
+	// Fetch the actions
+	//
+	// TODO: For now we support only the "OUTPUT" actions.
+	//
+	fm.setOutPort(OFPort.OFPP_NONE.getValue());
+	List<OFAction> actions = new ArrayList<OFAction>();
+	ArrayList<FlowEntryAction> flowEntryActions =
+	    flowEntry.flowEntryActions();
+	for (FlowEntryAction flowEntryAction : flowEntryActions) {
+	    FlowEntryAction.ActionOutput actionOutput =
+		flowEntryAction.actionOutput();
+	    if (actionOutput != null) {
+		short actionOutputPort = actionOutput.port().value();
+		OFActionOutput action = new OFActionOutput();
+		// XXX: The max length is hard-coded for now
+		action.setMaxLength((short)0xffff);
+		action.setPort(actionOutputPort);
+		actions.add(action);
+		if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
+		    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
+		    fm.setOutPort(actionOutputPort);
+		}
+	    }
+	}
+
+	fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+	    .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+	    .setPriority(PRIORITY_DEFAULT)
+	    .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+	    .setCookie(cookie)
+	    .setCommand(flowModCommand)
+	    .setMatch(match)
+	    .setActions(actions)
+	    .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+
+	//
+	// TODO: Set the following flag
+	// fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+	// See method ForwardingBase::pushRoute()
+	//
+
+	//
+	// Write the message to the switch
+	//
+	try {
+	    messageDamper.write(mySwitch, fm, null);
+	    mySwitch.flush();
+	} catch (IOException e) {
+	    log.error("Failure writing flow mod from network map", e);
+	    return false;
+	}
+	return true;
+    }
+
+    /**
+     * Remove a Flow Entry from a switch.
+     *
+     * @param mySwitches the DPID-to-Switch mapping for the switches
+     * controlled by this controller.
+     * @param flowEntry the flow entry to remove.
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean removeFlowEntry(Map<Long, IOFSwitch> mySwitches,
+				    FlowEntry flowEntry) {
+	//
+	// The installFlowEntry() method implements both installation
+	// and removal of flow entries.
+	//
+	return (installFlowEntry(mySwitches, flowEntry));
+    }
+
+    /**
+     * Install a Flow Entry on a remote controller.
+     *
+     * TODO: We need it now: Jono
+     * - For now it will make a REST call to the remote controller.
+     * - Internally, it needs to know the name of the remote controller.
+     *
+     * @param flowEntry the flow entry to install.
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean installRemoteFlowEntry(FlowEntry flowEntry) {
+	// TODO: We need it now: Jono
+	//  - For now it will make a REST call to the remote controller.
+	//  - Internally, it needs to know the name of the remote controller.
+	return true;
+    }
+
+    /**
+     * Remove a flow entry on a remote controller.
+     *
+     * @param flowEntry the flow entry to remove.
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean removeRemoteFlowEntry(FlowEntry flowEntry) {
+	//
+	// The installRemoteFlowEntry() method implements both installation
+	// and removal of flow entries.
+	//
+	return (installRemoteFlowEntry(flowEntry));
+    }
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 41c0f57..b6df1e2 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -82,4 +82,19 @@
      * @return the Flow Paths if found, otherwise null.
      */
     ArrayList<FlowPath> getAllFlows();
+
+    /**
+     * Add and maintain a shortest-path flow.
+     *
+     * NOTE: The Flow Path argument does NOT contain all flow entries.
+     * Instead, it contains a single dummy flow entry that is used to
+     * store the matching condition(s).
+     * That entry is replaced by the appropriate entries from the
+     * internally performed shortest-path computation.
+     *
+     * @param flowPath the Flow Path with the endpoints and the match
+     * conditions to install.
+     * @return the added shortest-path flow on success, otherwise null.
+     */
+    public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/AddShortestPathFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/AddShortestPathFlowResource.java
new file mode 100644
index 0000000..3c5bcd8
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddShortestPathFlowResource.java
@@ -0,0 +1,65 @@
+package net.floodlightcontroller.flowcache.web;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+import net.floodlightcontroller.util.FlowPath;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.restlet.resource.Post;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AddShortestPathFlowResource extends ServerResource {
+
+    protected static Logger log = LoggerFactory.getLogger(AddShortestPathFlowResource.class);
+
+    @Post("json")
+    public FlowId store(String flowJson) {
+	FlowId result = new FlowId();
+
+        IFlowService flowService =
+                (IFlowService)getContext().getAttributes().
+                get(IFlowService.class.getCanonicalName());
+
+        if (flowService == null) {
+	    log.debug("ONOS Flow Service not found");
+            return result;
+	}
+
+	//
+	// Extract the arguments
+	// NOTE: The "flow" is specified in JSON format.
+	//
+	ObjectMapper mapper = new ObjectMapper();
+	String flowPathStr = flowJson;
+	FlowPath flowPath = null;
+	log.debug("Add Shortest Path Flow Path: " + flowPathStr);
+	try {
+	    flowPath = mapper.readValue(flowPathStr, FlowPath.class);
+	} catch (JsonGenerationException e) {
+	    e.printStackTrace();
+	} catch (JsonMappingException e) {
+	    e.printStackTrace();
+	} catch (IOException e) {
+	    e.printStackTrace();
+	}
+
+	// Process the request
+	if (flowPath != null) {
+	    FlowPath addedFlowPath =
+		flowService.addAndMaintainShortestPathFlow(flowPath);
+	    if (addedFlowPath == null)
+		result = new FlowId();		// Error: Return empty Flow Id
+	    else
+		result = addedFlowPath.flowId();
+	}
+
+        return result;
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 19f9e14..962dbbb 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -14,6 +14,7 @@
     public Restlet getRestlet(Context context) {
         Router router = new Router(context);
         router.attach("/add/json", AddFlowResource.class);
+        router.attach("/add-shortest-path/json", AddShortestPathFlowResource.class);
         router.attach("/clear/{flow-id}/json", ClearFlowResource.class);
         router.attach("/delete/{flow-id}/json", DeleteFlowResource.class);
         router.attach("/get/{flow-id}/json", GetFlowByIdResource.class);
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntry.java b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
index 56a1631..2e61636 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntry.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
@@ -23,7 +23,7 @@
  * support multiple in-ports and multiple out-ports.
  */
 public class FlowEntry {
-	private FlowId flowId;                  //FlowID of flowEntry
+    private FlowId flowId;			// FlowID of flowEntry
     private FlowEntryId flowEntryId;		// The Flow Entry ID
     private FlowEntryMatch flowEntryMatch;	// The Flow Entry Match
     private ArrayList<FlowEntryAction> flowEntryActions; // The Flow Entry Actions
@@ -127,6 +127,22 @@
     }
 
     /**
+     * Get the Flow ID.
+     * @return the Flow ID.
+     */
+    @JsonIgnore
+    public FlowId getFlowId() { return flowId; }
+
+    /**
+     * Set the Flow ID.
+     *
+     * @param flowId the Flow ID to set.
+     */
+    public void setFlowId(FlowId flowId) {
+	this.flowId = flowId;
+    }
+
+    /**
      * Get the Flow Entry ID.
      *
      * @return the Flow Entry ID.
@@ -329,19 +345,4 @@
 
 	return ret;
     }
-
-	/**
-	 * @return the flowId
-	 */
-    @JsonIgnore
-	public FlowId getFlowId() {
-		return flowId;
-	}
-
-	/**
-	 * @param flowId the flowId to set
-	 */
-	public void setFlowId(FlowId flowId) {
-		this.flowId = flowId;
-	}
 }
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
index 64527c5..6c8e71e 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
@@ -27,6 +27,8 @@
 
 	/**
 	 * Constructor for a given value to match.
+	 *
+	 * @param value the value to match.
 	 */
 	public Field(T value) {
 	    this.value = value;
@@ -88,6 +90,38 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public FlowEntryMatch(FlowEntryMatch other) {
+	if ((other.inPort != null) && other.inPort.enabled())
+	    this.enableInPort(other.inPort.value());
+	if ((other.srcMac != null) && other.srcMac.enabled())
+	    this.enableSrcMac(other.srcMac.value());
+	if ((other.dstMac != null) && other.dstMac.enabled())
+	    this.enableDstMac(other.dstMac.value());
+	if ((other.vlanId != null) && other.vlanId.enabled())
+	    this.enableVlanId(other.vlanId.value());
+	if ((other.vlanPriority != null) && other.vlanPriority.enabled())
+	    this.enableVlanPriority(other.vlanPriority.value());
+	if ((other.ethernetFrameType != null) && other.ethernetFrameType.enabled())
+	    this.enableEthernetFrameType(other.ethernetFrameType.value());
+	if ((other.ipToS != null) && other.ipToS.enabled())
+	    this.enableIpToS(other.ipToS.value());
+	if ((other.ipProto != null) && other.ipProto.enabled())
+	    this.enableIpProto(other.ipProto.value());
+	if ((other.srcIPv4Net != null) && other.srcIPv4Net.enabled())
+	    this.enableSrcIPv4Net(other.srcIPv4Net.value());
+	if ((other.dstIPv4Net != null) && other.dstIPv4Net.enabled())
+	    this.enableDstIPv4Net(other.dstIPv4Net.value());
+	if ((other.srcTcpUdpPort != null) && other.srcTcpUdpPort.enabled())
+	    this.enableSrcTcpUdpPort(other.srcTcpUdpPort.value());
+	if ((other.dstTcpUdpPort != null) && other.dstTcpUdpPort.enabled())
+	    this.enableDstTcpUdpPort(other.dstTcpUdpPort.value());
+    }
+
+    /**
      * Get the matching input switch port.
      *
      * @return the matching input switch port.
diff --git a/src/main/java/net/onrc/onos/flow/IFlowManager.java b/src/main/java/net/onrc/onos/flow/IFlowManager.java
index da6448c..8394046 100644
--- a/src/main/java/net/onrc/onos/flow/IFlowManager.java
+++ b/src/main/java/net/onrc/onos/flow/IFlowManager.java
@@ -1,53 +1,124 @@
 package net.onrc.onos.flow;
 
+import java.util.Map;
+
+import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
 import net.floodlightcontroller.util.FlowEntry;
 import net.floodlightcontroller.util.FlowPath;
 
 public interface IFlowManager {
-	
-	/*
-	 * Generic create Flow from port to port
-	 */
-	public void createFlow(IPortObject src_port, IPortObject dest_port);
-	/*
-	 * get Flows matching a src_port & dest_port
-	 */
-	public Iterable<FlowPath> getFlows(IPortObject src_port, IPortObject dest_port);
-	/*
-	 * get all Flows going out from port
-	 */
-	public Iterable<FlowPath> getFlows(IPortObject port);
-	/*
-	 * Reconcile all flows on inactive port (src port of link which might be broken)
-	 */
-	public void reconcileFlows(IPortObject src_port);
-	/*
-	 * Reconcile flow based on flow
-	 */
-	public void reconcileFlow(IPortObject src_port, IPortObject dest_port);
-	/*
-	 * compute a flow path using src/dest port
-	 */
-	public FlowPath computeFlowPath(IPortObject src_port, IPortObject dest_port);
-	/*
-	 * Get all FlowEntries of a Flow
-	 */
+    /**
+     * Create a Flow from port to port.
+     *
+     * TODO: We don't need it for now.
+     *
+     * @param src_port the source port.
+     * @param dest_port the destination port.
+     */
+    public void createFlow(IPortObject src_port, IPortObject dest_port);
+
+    /**
+     * Get all Flows matching a source and a destination port.
+     *
+     * TODO: Pankaj might be implementing it later.
+     *
+     * @param src_port the source port to match.
+     * @param dest_port the destination port to match.
+     * @return all flows matching the source and the destination port.
+     */
+    public Iterable<FlowPath> getFlows(IPortObject src_port,
+				       IPortObject dest_port);
+
+    /**
+     * Get all Flows going out from a port.
+     *
+     * TODO: We need it now: Pankaj
+     *
+     * @param port the port to match.
+     * @return the list of flows that are going out from the port.
+     */
+    public Iterable<FlowPath> getFlows(IPortObject port);
+
+    /**
+     * Reconcile all flows on inactive switch port.
+     *
+     * @param portObject the port that has become inactive.
+     */
+    public void reconcileFlows(IPortObject portObject);
+
+    /**
+     * Reconcile all flows between a source and a destination port.
+     *
+     * TODO: We don't need it for now.
+     *
+     * @param src_port the source port.
+     * @param dest_port the destination port.
+     */
+    public void reconcileFlow(IPortObject src_port, IPortObject dest_port);
+
+    /**
+     * Compute the shortest path between a source and a destination ports.
+     *
+     * @param src_port the source port.
+     * @param dest_port the destination port.
+     * @return the computed shortest path between the source and the
+     * destination ports. The flow entries in the path itself would
+     * contain the incoming port matching and the outgoing port output
+     * actions set. However, the path itself will NOT have the Flow ID,
+     * Installer ID, and any additional matching conditions for the
+     * flow entries (e.g., source or destination MAC address, etc).
+     */
+    public FlowPath computeFlowPath(IPortObject src_port,
+				    IPortObject dest_port);
+
+    /**
+     * Get all Flow Entries of a Flow.
+     *
+     * @param flow the flow whose flow entries should be returned.
+     * @return the flow entries of the flow.
+     */
     public Iterable<FlowEntry> getFlowEntries(FlowPath flow);
-    /*
-     * install a flow entry on switch
+
+    /**
+     * Install a Flow Entry on a switch.
+     *
+     * @param mySwitches the DPID-to-Switch mapping for the switches
+     * controlled by this controller.
+     * @param flowEntry the flow entry to install.
+     * @return true on success, otherwise false.
      */
-    public void installFlowEntry(FlowEntry entry);
-    /*
-     * remove a flowEntry from switch
+    public boolean installFlowEntry(Map<Long, IOFSwitch> mySwitches,
+				    FlowEntry flowEntry);
+
+    /**
+     * Remove a Flow Entry from a switch.
+     *
+     * @param mySwitches the DPID-to-Switch mapping for the switches
+     * controlled by this controller.
+     * @param flowEntry the flow entry to remove.
+     * @return true on success, otherwise false.
      */
-    public void removeFlowEntry(FlowEntry entry);
-    /*
-     * install flow entry on remote controller
+    public boolean removeFlowEntry(Map<Long, IOFSwitch> mySwitches,
+				   FlowEntry flowEntry);
+
+    /**
+     * Install a Flow Entry on a remote controller.
+     *
+     * TODO: We need it now: Jono
+     * - For now it will make a REST call to the remote controller.
+     * - Internally, it needs to know the name of the remote controller.
+     *
+     * @param flowEntry the flow entry to install.
+     * @return true on success, otherwise false.
      */
-    public void installFlowEntry(String ctrlId, FlowEntry entry);
-    /*
-     * remove flow entry on remote controller
+    public boolean installRemoteFlowEntry(FlowEntry flowEntry);
+
+    /**
+     * Remove a flow entry on a remote controller.
+     *
+     * @param flowEntry the flow entry to remove.
+     * @return true on success, otherwise false.
      */
-    public void removeFlowEntry(String ctrlId, FlowEntry entry);        
+    public boolean removeRemoteFlowEntry(FlowEntry flowEntry);
 }
diff --git a/web/ons-demo/data/configuration.json.dev b/web/ons-demo/data/configuration.json.dev
new file mode 100644
index 0000000..90cad6a
--- /dev/null
+++ b/web/ons-demo/data/configuration.json.dev
@@ -0,0 +1,40 @@
+{
+	"core": [
+		"00:00:00:00:00:00:01:01",
+		"00:00:00:00:00:00:01:02",
+		"00:00:00:00:00:00:01:03",
+		"00:00:00:00:00:00:01:04",
+		"00:00:00:00:00:00:01:05",
+		"00:00:00:00:00:00:01:06"
+	],
+	"aggregation": [
+		"00:00:00:00:00:00:02:01",
+		"00:00:00:00:00:00:03:01",
+		"00:00:00:00:00:00:04:01",
+		"00:00:00:00:00:00:05:01",
+		"00:00:00:00:00:00:06:01",
+		"00:00:00:00:00:00:07:01",
+		"00:00:00:00:00:00:08:01"
+	],
+	"association": {
+		"00:00:00:00:00:00:01:01": [
+			"00:00:00:00:00:00:03:01"
+		],
+		"00:00:00:00:00:00:01:02": [
+			"00:00:00:00:00:00:02:01"
+		],
+		"00:00:00:00:00:00:01:03": [
+			"00:00:00:00:00:00:07:01"
+		],
+		"00:00:00:00:00:00:01:04": [
+			"00:00:00:00:00:00:04:01",
+			"00:00:00:00:00:00:05:01"
+		],
+		"00:00:00:00:00:00:01:05": [
+			"00:00:00:00:00:00:06:01"
+		],
+		"00:00:00:00:00:00:01:06": [
+			"00:00:00:00:00:00:08:01"
+		]
+	}
+}
\ No newline at end of file
diff --git a/web/ons-demo/data/controllers.json.dev b/web/ons-demo/data/controllers.json.dev
new file mode 100644
index 0000000..4abcf5a
--- /dev/null
+++ b/web/ons-demo/data/controllers.json.dev
@@ -0,0 +1,10 @@
+[
+	"onosdevb1",
+	"onosdevb2",
+	"onosdevb3",
+	"onosdevb4",
+	"onosdevb5",
+	"onosdevb6",
+	"onosdevb7",
+	"onosdevb8"
+]
\ No newline at end of file
diff --git a/web/topology_rest.py b/web/topology_rest.py
index 8a94ef9..40e36ae 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -16,9 +16,22 @@
 RestIP="localhost"
 RestPort=8080
 #DBName="onos-network-map"
-#controllers=["onosgui1", "onosgui2", "onosgui3", "onosgui4"]
+
+## Uncomment the desired block based on your testbed environment
+
+# Settings for running on production
 controllers=["onosgui1", "onosgui2", "onosgui3", "onosgui4", "onosgui5", "onosgui6", "onosgui7", "onosgui8"]
 core_switches=["00:00:00:00:ba:5e:ba:11", "00:00:00:00:00:00:ba:12", "00:00:20:4e:7f:51:8a:35", "00:00:00:00:ba:5e:ba:13", "00:00:00:08:a2:08:f9:01", "00:00:00:16:97:08:9a:46"]
+ONOS_GUI3_HOST="http://gui3.onlab.us:8080"
+ONOS_GUI3_CONTROL_HOST="http://gui3.onlab.us:8081"
+
+# Settings for running on dev testbed. Replace dev
+#controllers=["onosdevb1", "onosdevb2", "onosdevb3", "onosdevb4"]
+#core_switches=["00:00:00:00:00:00:01:01", "00:00:00:00:00:00:01:02", "00:00:00:00:00:00:01:03", "00:00:00:00:00:00:01:04", "00:00:00:00:00:00:01:05", "00:00:00:00:00:00:01:06"]
+#ONOS_GUI3_HOST="http://devb-gui.onlab.us:8080"
+#ONOS_GUI3_CONTROL_HOST="http://devb-gui.onlab.us:8080"
+
+ONOS_LOCAL_HOST="http://localhost:8080" ;# for Amazon EC2
 
 nr_flow=0
 
@@ -74,10 +87,6 @@
 
   return response
 
-## PROXY API (allows development where the webui is served from someplace other than the controller)##
-ONOS_GUI3_HOST="http://gui3.onlab.us:8080"
-ONOS_GUI3_CONTROL_HOST="http://gui3.onlab.us:8081"
-ONOS_LOCAL_HOST="http://localhost:8080" ;# for Amazon EC2
 
 @app.route("/proxy/gui/link/<cmd>/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>")
 def proxy_link_change(cmd, src_dpid, src_port, dst_dpid, dst_port):
@@ -686,9 +695,11 @@
   stop_onos="ssh -i ~/.ssh/onlabkey.pem %s ONOS/start-onos.sh stop" % (controller_name)
 
   if cmd == "up":
+    print start_onos
     result=os.popen(start_onos).read()
     ret = "controller %s is up" % (controller_name)
   elif cmd == "down":
+    print stop_onos
     result=os.popen(stop_onos).read()
     ret = "controller %s is down" % (controller_name)
 
@@ -698,8 +709,9 @@
 def switch_status_change(cmd, dpid):
   r = re.compile(':')
   dpid = re.sub(r, '', dpid)
-  cmd_string="ssh -i ~/.ssh/onlabkey.pem onosgui1 'cd ONOS/scripts; ./switch.sh %s %s'" % (dpid, cmd)
-  get_status="ssh -i ~/.ssh/onlabkey.pem onosgui1 'cd ONOS/scripts; ./switch.sh %s'" % (dpid)
+  host=controllers[0]
+  cmd_string="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./switch.sh %s %s'" % (host, dpid, cmd)
+  get_status="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./switch.sh %s'" % (host, dpid)
   print "cmd_string"
 
   if cmd =="up" or cmd=="down":
@@ -709,12 +721,40 @@
 
   return result
 
-#* Link Up/Down
+#* Link Up
 #http://localhost:9000/gui/link/up/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>
-#http://localhost:9000/gui/link/down/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>
+@app.route("/gui/link/up/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>")
+def link_up(src_dpid, src_port, dst_dpid, dst_port):
 
+  cmd = 'up'
+  result=""
+
+  for dpid in (src_dpid, dst_dpid): 
+    if dpid in core_switches:
+      host = controllers[0]
+      src_ports = [1, 2, 3, 4, 5]
+    else:
+      hostid=int(src_dpid.split(':')[-2])
+      host = controllers[hostid-1]
+      if hostid == 2 :
+        src_ports = [51]
+      else :
+        src_ports = [26]
+
+    for port in src_ports :
+      cmd_string="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./link.sh %s %s %s'" % (host, dpid, port, cmd)
+      print cmd_string
+      res=os.popen(cmd_string).read()
+      result = result + ' ' + res
+
+  return result
+
+
+#* Link Down
+#http://localhost:9000/gui/link/down/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>
 @app.route("/gui/link/<cmd>/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>")
-def link_change(cmd, src_dpid, src_port, dst_dpid, dst_port):
+def link_down(cmd, src_dpid, src_port, dst_dpid, dst_port):
+
   if src_dpid in core_switches:
     host = controllers[0]
   else:
@@ -724,8 +764,7 @@
   cmd_string="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./link.sh %s %s %s'" % (host, src_dpid, src_port, cmd)
   print cmd_string
 
-  if cmd =="up" or cmd=="down":
-    result=os.popen(cmd_string).read()
+  result=os.popen(cmd_string).read()
 
   return result
 
@@ -758,10 +797,10 @@
   return errcode
 
 #* Start Iperf Througput
-#http://localhost:9000/gui/iperf/start/<flow_id>
-@app.route("/gui/iperf/start/<flow_id>")
-def iperf_start(flow_id):
-  command = "iperf -xCMSV -t30 -i1 -u -c 127.0.0.1 > iperf_%s.out &" % (flow_id)
+#http://localhost:9000/gui/iperf/start/<flow_id>/<duration>
+@app.route("/gui/iperf/start/<flow_id>/<duration>")
+def iperf_start(flow_id,duration):
+  command = "iperf -xCMSV -t%d -i 0.5 -y c -u -c 127.0.0.1 > iperf_%s.out 2>/dev/null &" % (duration, flow_id)
   print command
   errcode = os.popen(command).read()
   return errcode