Merge conflicts: add edges from flowentry to ports and switches
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 3eec2ec..257add8 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -50,6 +50,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;
 
@@ -64,12 +65,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;
@@ -144,8 +146,6 @@
 		    return;
 		}
 
-		ITopoRouteService topoRouteService =
-		    context.getServiceImpl(ITopoRouteService.class);
 		if (topoRouteService == null) {
 		    log.debug("Topology Route Service not found");
 		    return;
@@ -312,7 +312,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
@@ -339,6 +339,7 @@
 		    if (mySwitch == null)
 			continue;		// Shouldn't happen
 
+		    // TODO: PAVPAVPAV: FROM HERE...
 		    //
 		    // Create the Open Flow Flow Modification Entry to push
 		    //
@@ -446,6 +447,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.
 		}
 
 		//
@@ -542,6 +546,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;
     }
@@ -551,6 +556,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),
@@ -1175,4 +1181,396 @@
 
 	return flowPath;
     }
+
+    /**
+     * Add and maintain a shortest-path flow.
+     *
+     * NOTE: The Flow Path 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.
+     * @param flowId the return-by-reference Flow ID as assigned internally.
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean addAndMaintainShortestPathFlow(FlowPath flowPath,
+						  FlowId flowId) {
+	//
+	// Do the shortest path computation
+	//
+	DataPath dataPath =
+	    topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
+					     flowPath.dataPath().dstPort());
+	if (dataPath == null)
+	    return false;
+
+	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 resultFlowPath = new FlowPath();
+	resultFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
+	resultFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
+	resultFlowPath.setDataPath(dataPath);
+
+	boolean returnValue = addFlow(resultFlowPath, flowId);
+
+	// TODO: Mark the flow for maintenance purpose
+
+	return (returnValue);
+    }
+
+    /**
+     * 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) {
+	// 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.
+     */
+    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.
+     */
+    public Iterable<FlowPath> getFlows(IPortObject port) {
+	// TODO: We need it now: Pankaj
+	return null;
+    }
+
+    /**
+     * Reconcile all flows on inactive port (src port of link which might be
+     * broken).
+     *
+     * TODO: We need it now: Pavlin
+     *
+     * @param src_port the port that has become inactive.
+     */
+    public void reconcileFlows(IPortObject src_port) {
+	// TODO: We need it now: Pavlin
+
+	// TODO: It should call installFlowEntry() as appropriate.
+    }
+
+    /**
+     * 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) {
+	// 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).
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     *
+     * TODO: We need it now: Pavlin
+     * - Remove only for local switches
+     * - It will call the removeRemoteFlowEntry() for remote switches.
+     * - To be called by reconcileFlow()
+     *
+     * @param entry the flow entry to remove.
+     */
+    public void removeFlowEntry(FlowEntry entry) {
+	// TODO: We need it now: Pavlin
+	//  - Remove only for local switches
+	//  - It will call the removeRemoteFlowEntry() for remote switches.
+	//  - To be called by reconcileFlow()
+    }
+
+    /**
+     * 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 entry the flow entry to install.
+     * @return true on success, otherwise false.
+     */
+    public boolean installRemoteFlowEntry(FlowEntry entry) {
+	// 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.
+     *
+     * 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 entry the flow entry to remove.
+     */
+    public void removeRemoteFlowEntry(FlowEntry entry) {
+	// 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.
+    }
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 41c0f57..45cdde0 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -82,4 +82,21 @@
      * @return the Flow Paths if found, otherwise null.
      */
     ArrayList<FlowPath> getAllFlows();
+
+    /**
+     * Add and maintain a shortest-path flow.
+     *
+     * NOTE: The Flow Path 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.
+     * @param flowId the return-by-reference Flow ID as assigned internally.
+     * @return true on success, otherwise false.
+     */
+    public boolean addAndMaintainShortestPathFlow(FlowPath flowPath,
+						  FlowId flowId);
 }
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..e00a4b5
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddShortestPathFlowResource.java
@@ -0,0 +1,63 @@
+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) {
+	    if (flowService.addAndMaintainShortestPathFlow(flowPath, result)
+		!= true) {
+		result = new FlowId();		// Error: Return empty Flow Id
+	    }
+	}
+
+        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/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..f2f9d49 100644
--- a/src/main/java/net/onrc/onos/flow/IFlowManager.java
+++ b/src/main/java/net/onrc/onos/flow/IFlowManager.java
@@ -1,53 +1,136 @@
 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
-	 */
-    public Iterable<FlowEntry> getFlowEntries(FlowPath flow);
-    /*
-     * install a flow entry on switch
+    /**
+     * 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 installFlowEntry(FlowEntry entry);
-    /*
-     * remove a flowEntry from switch
+    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 port (src port of link which might be
+     * broken).
+     *
+     * TODO: We need it now: Pavlin
+     *
+     * @param src_port the port that has become inactive.
+     */
+    public void reconcileFlows(IPortObject src_port);
+
+    /**
+     * 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.
+     *
+     * TODO: We need it now: Pavlin
+     *
+     * @param src_port the source port.
+     * @param dest_port the destination port.
+     * @return the computed shortest path between the source and the
+     * destination ports.
+     */
+    public FlowPath computeFlowPath(IPortObject src_port,
+				    IPortObject dest_port);
+
+    /**
+     * Get all Flow Entries of a Flow.
+     *
+     * TODO: We need it now: Pavlin
+     *
+     * @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 a switch.
+     *
+     * TODO: We need it now: Pavlin
+     * - Install only for local switches
+     * - It will call the installRemoteFlowEntry() for remote switches.
+     * - To be called by reconcileFlow()
+     *
+     * @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 boolean installFlowEntry(Map<Long, IOFSwitch> mySwitches,
+				    FlowEntry flowEntry);
+
+    /**
+     * Remove a Flow Entry from a switch.
+     *
+     * TODO: We need it now: Pavlin
+     * - Remove only for local switches
+     * - It will call the removeRemoteFlowEntry() for remote switches.
+     * - To be called by reconcileFlow()
+     *
+     * @param entry the flow entry to remove.
      */
     public void removeFlowEntry(FlowEntry entry);
-    /*
-     * install flow entry on remote controller
+
+    /**
+     * 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 entry 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 entry);
+
+    /**
+     * Remove 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 entry the flow entry to remove.
      */
-    public void removeFlowEntry(String ctrlId, FlowEntry entry);        
+    public void removeRemoteFlowEntry(FlowEntry entry);        
 }
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 556b433..e4a2684 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -16,10 +16,22 @@
 RestIP="localhost"
 RestPort=8080
 #DBName="onos-network-map"
-controllers=["onosdevb1", "onosdevb2", "onosdevb3", "onosdevb4"]
-#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"]
-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"]
+
+## 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
 
@@ -75,10 +87,6 @@
 
   return response
 
-## PROXY API (allows development where the webui is served from someplace other than the controller)##
-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
 
 @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):