Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index 18da3dd..71b6593 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -191,6 +191,12 @@
 		@Property("dst_port")
 		public void setDstPort(Short dstPort);
 
+		@Property("data_path_summary")
+		public String getDataPathSummary();
+
+		@Property("data_path_summary")
+		public void setDataPathSummary(String dataPathSummary);
+
 		@Adjacency(label="flow", direction=Direction.IN)
 		public Iterable<IFlowEntry> getFlowEntries();
 
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 25cf367..34e6296 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -263,7 +263,6 @@
 		    log.debug("FloodlightProvider service not found!");
 		    return;
 		}
-
 		Map<Long, IOFSwitch> mySwitches =
 		    floodlightProvider.getSwitches();
 		Map<Long, IFlowEntry> myFlowEntries =
@@ -272,6 +271,77 @@
 		    new LinkedList<IFlowEntry>();
 
 		//
+		// Fetch and recompute the Shortest Path for those
+		// Flow Paths this controller is responsible for.
+		//
+
+		/*
+		 * TODO: For now, the computation of the reconciliation is
+		 * commented-out.
+		 */
+		/*
+		topoRouteService.prepareShortestPathTopo();
+		Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
+		HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
+		for (IFlowPath flowPathObj : allFlowPaths) {
+		    if (flowPathObj == null)
+			continue;
+		    String dataPathSummaryStr = flowPathObj.getDataPathSummary();
+		    if (dataPathSummaryStr == null)
+			continue;	// Could be invalid entry?
+		    if (dataPathSummaryStr.isEmpty())
+			continue;	// No need to maintain this flow
+
+		    // Fetch the fields needed to recompute the shortest path
+		    String flowIdStr = flowPathObj.getFlowId();
+		    String srcDpidStr = flowPathObj.getSrcSwitch();
+		    Short srcPortShort = flowPathObj.getSrcPort();
+		    String dstDpidStr = flowPathObj.getDstSwitch();
+		    Short dstPortShort = flowPathObj.getDstPort();
+		    if ((flowIdStr == null) ||
+			(srcDpidStr == null) ||
+			(srcPortShort == null) ||
+			(dstDpidStr == null) ||
+			(dstPortShort == null)) {
+			log.debug("IGNORING Flow Path entry with null fields");
+			continue;
+		    }
+
+		    FlowId flowId = new FlowId(flowIdStr);
+		    Dpid srcDpid = new Dpid(srcDpidStr);
+		    Port srcPort = new Port(srcPortShort);
+		    Dpid dstDpid = new Dpid(dstDpidStr);
+		    Port dstPort = new Port(dstPortShort);
+		    SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
+		    SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
+		    DataPath dataPath =
+			topoRouteService.getTopoShortestPath(srcSwitchPort,
+							     dstSwitchPort);
+		    String newDataPathSummaryStr = dataPath.dataPathSummary();
+		    if (dataPathSummaryStr.equals(newDataPathSummaryStr))
+			continue;	// Nothing changed
+
+		    //
+		    // Use the source DPID as a heuristic to decide
+		    // which controller is responsible for maintaining the
+		    // shortest path.
+		    // NOTE: This heuristic is error-prone: if the switch
+		    // goes away and no controller is responsible for that
+		    // switch, then the original Flow Path is not cleaned-up
+		    //
+		    IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
+		    if (mySwitch == null)
+			continue;	// Ignore: not my responsibility
+
+		    log.debug("RECONCILE: Need to Reconcile Shortest Path for FlowID {}",
+			      flowId.toString());
+		    flowObjSet.add(flowPathObj);
+		}
+		reconcileFlows(flowObjSet);
+		topoRouteService.dropShortestPathTopo();
+		*/
+
+		//
 		// Fetch all Flow Entries and select only my Flow Entries
 		// that need to be undated into the switches.
 		//
@@ -593,10 +663,13 @@
      *
      * @param flowPath the Flow Path to install.
      * @param flowId the return-by-reference Flow ID as assigned internally.
+     * @param dataPathSummaryStr the data path summary string if the added
+     * flow will be maintained internally, otherwise null.
      * @return true on success, otherwise false.
      */
     @Override
-    public boolean addFlow(FlowPath flowPath, FlowId flowId) {
+    public boolean addFlow(FlowPath flowPath, FlowId flowId,
+			   String dataPathSummaryStr) {
 	if (flowPath.flowId().value() == measurementFlowId) {
 	    modifiedMeasurementFlowTime = System.nanoTime();
 	}
@@ -654,6 +727,12 @@
 	flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
 	flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
 
+	if (dataPathSummaryStr != null) {
+	    flowObj.setDataPathSummary(dataPathSummaryStr);
+	} else {
+	    flowObj.setDataPathSummary("");
+	}
+
 	// Flow edges:
 	//   HeadFE
 
@@ -1041,14 +1120,14 @@
 		    return flowPaths;
 		}
 	
-		Collections.sort(allFlows);
+//		Collections.sort(allFlows);
 		
 		for (FlowPath flow : allFlows) {
 			
 			// start from desired flowId
-			if (flow.flowId().value() < flowId.value()) {
-				continue;
-			}
+			//if (flow.flowId().value() < flowId.value()) {
+			//	continue;
+			//}
 			
 			// Summarize by making null flow entry fields that are not relevant to report
 			for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
@@ -1125,16 +1204,16 @@
 	String flowIdStr = flowObj.getFlowId();
 	String installerIdStr = flowObj.getInstallerId();
 	String srcSwitchStr = flowObj.getSrcSwitch();
-	Short srcPortStr = flowObj.getSrcPort();
+	Short srcPortShort = flowObj.getSrcPort();
 	String dstSwitchStr = flowObj.getDstSwitch();
-	Short dstPortStr = flowObj.getDstPort();
+	Short dstPortShort = flowObj.getDstPort();
 
 	if ((flowIdStr == null) ||
 	    (installerIdStr == null) ||
 	    (srcSwitchStr == null) ||
-	    (srcPortStr == null) ||
+	    (srcPortShort == null) ||
 	    (dstSwitchStr == null) ||
-	    (dstPortStr == null)) {
+	    (dstPortShort == null)) {
 	    // TODO: A work-around, becauuse of some bogus database objects
 	    return null;
 	}
@@ -1142,9 +1221,9 @@
 	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().srcPort().setPort(new Port(srcPortShort));
 	flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
-	flowPath.dataPath().dstPort().setPort(new Port(dstPortStr));
+	flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
 
 	//
 	// Extract all Flow Entries
@@ -1229,6 +1308,8 @@
      */
     @Override
     public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
+	String dataPathSummaryStr = null;
+
 	//
 	// Do the shortest path computation
 	//
@@ -1243,6 +1324,9 @@
 	    userFlowEntryMatch = flowPath.dataPath().flowEntries().get(0).flowEntryMatch();
 	}
 
+	// Compute the Data Path summary
+	dataPathSummaryStr = dataPath.dataPathSummary();
+
 	//
 	// Set the incoming port matching and the outgoing port output
 	// actions for each flow entry.
@@ -1277,7 +1361,7 @@
 	computedFlowPath.setDataPath(dataPath);
 
 	FlowId flowId = new FlowId();
-	if (! addFlow(computedFlowPath, flowId))
+	if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
 	    return null;
 
 	// TODO: Mark the flow for maintenance purpose
@@ -1352,21 +1436,37 @@
 	    if (flowObj != null)
 		flowObjSet.add(flowObj);
 	}
-	// conn.endTx(Transaction.COMMIT);
+
+	// Reconcile the affected flows
+	reconcileFlows(flowObjSet);
+    }
+
+    /**
+     * Reconcile all flows in a set.
+     *
+     * @param flowObjSet the set of flows that need to be reconciliated.
+     */
+    public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
+	if (! flowObjSet.iterator().hasNext())
+	    return;
 
 	//
 	// Remove the old Flow Entries, and add the new Flow Entries
 	//
+
 	Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+	LinkedList<FlowPath> flowPaths = new LinkedList<FlowPath>();
 	for (IFlowPath flowObj : flowObjSet) {
 	    FlowPath flowPath = extractFlowPath(flowObj);
 	    if (flowPath == null)
 		continue;
+	    flowPaths.add(flowPath);
 
 	    //
 	    // Remove my Flow Entries from the Network MAP
 	    //
 	    Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+	    LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
 	    for (IFlowEntry flowEntryObj : flowEntries) {
 		String dpidStr = flowEntryObj.getSwitchDpid();
 		if (dpidStr == null)
@@ -1375,10 +1475,15 @@
 		IOFSwitch mySwitch = mySwitches.get(dpid.value());
 		if (mySwitch == null)
 		    continue;		// Ignore the entry: not my switch
+		deleteFlowEntries.add(flowEntryObj);
+	    }
+	    for (IFlowEntry flowEntryObj : deleteFlowEntries) {
 		flowObj.removeFlowEntry(flowEntryObj);
 		conn.utils().removeFlowEntry(conn, flowEntryObj);
 	    }
+	}
 
+	for (FlowPath flowPath : flowPaths) {
 	    //
 	    // Delete the flow entries from the switches
 	    //
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index b6df1e2..619d36b 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -20,9 +20,12 @@
      *
      * @param flowPath the Flow Path to install.
      * @param flowId the return-by-reference Flow ID as assigned internally.
+     * @param dataPathSummaryStr the data path summary string if the added
+     * flow will be maintained internally, otherwise null.
      * @return true on success, otherwise false.
      */
-    boolean addFlow(FlowPath flowPath, FlowId flowId);
+    boolean addFlow(FlowPath flowPath, FlowId flowId,
+		    String dataPathSummaryStr);
 
     /**
      * Delete a previously added flow.
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
index cdccae1..e266e2e 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
@@ -52,7 +52,7 @@
 
 	// Process the request
 	if (flowPath != null) {
-	    if (flowService.addFlow(flowPath, result) != true) {
+	    if (flowService.addFlow(flowPath, result, null) != true) {
 		result = new FlowId();		// Error: Return empty Flow Id
 	    }
 	}
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index fd7c364..ca8d92f 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -13,6 +13,7 @@
 
 import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
+import net.floodlightcontroller.core.ISwitchStorage.SwitchState;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -23,14 +24,12 @@
 import net.floodlightcontroller.util.Port;
 import net.floodlightcontroller.util.SwitchPort;
 import net.onrc.onos.util.GraphDBConnection;
+import net.onrc.onos.util.GraphDBConnection.Transaction;
 
 import org.openflow.util.HexString;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.thinkaurelius.titan.core.TitanFactory;
-import com.thinkaurelius.titan.core.TitanGraph;
-import com.thinkaurelius.titan.core.TitanTransaction;
 import com.tinkerpop.blueprints.Direction;
 import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
 import com.tinkerpop.blueprints.Vertex;
@@ -194,17 +193,15 @@
      */
     
     public void prepareShortestPathTopo() {
-	TitanGraph titanGraph = TitanFactory.open("/tmp/cassandra.titan");
-	TitanTransaction titanTransaction = titanGraph.startTransaction();
 	shortestPathTopo = new HashMap<Long, Node>();
 
 	//
 	// Fetch the relevant info from the Switch and Port vertices
 	// from the Titan Graph.
 	//
-	Iterable<Vertex> nodes = titanTransaction.getVertices("type", "switch");
-	for (Vertex nodeVertex : nodes) {
-	
+	Iterable<ISwitchObject> nodes = conn.utils().getActiveSwitches(conn);
+	for (ISwitchObject switchObj : nodes) {
+	    Vertex nodeVertex = switchObj.asVertex();
 	    //
 	    // The Switch info
 	    //
@@ -245,6 +242,11 @@
 		    // The neighbor Switch info
 		    //
 		    for (Vertex neighborVertex : neighborPortVertex.getVertices(Direction.IN, "on")) {
+			// Ignore inactive switches
+			String state = neighborVertex.getProperty("state").toString();
+			if (! state.equals(SwitchState.ACTIVE.toString()))
+			    continue;
+
 			String neighborDpid = neighborVertex.getProperty("dpid").toString();
 			long neighborId = HexString.toLong(neighborDpid);
 			Node neighbor = shortestPathTopo.get(neighborId);
@@ -257,8 +259,7 @@
 		}
 	    }
 	}
-
-	titanTransaction.stopTransaction(Conclusion.SUCCESS);
+	conn.endTx(Transaction.COMMIT);
     }
 
     /**
@@ -367,7 +368,7 @@
 	Port outPort;
 	for (Node.Link link: resultPath) {
 	    // Setup the outgoing port, and add the Flow Entry
-	    outPort = new Port(link.neighborPort);
+	    outPort = new Port(link.myPort);
 
 	    FlowEntry flowEntry = new FlowEntry();
 	    flowEntry.setDpid(new Dpid(link.me.nodeId));
@@ -409,12 +410,17 @@
 	result_data_path.setSrcPort(src);
 	result_data_path.setDstPort(dest);
 
-	TitanGraph titanGraph = TitanFactory.open("/tmp/cassandra.titan");
-	TitanTransaction titanTransaction = titanGraph.startTransaction();
-
 	String dpid_src = src.dpid().toString();
 	String dpid_dest = dest.dpid().toString();
 
+	// Get the source and destination switches
+	ISwitchObject srcSwitch =
+	    conn.utils().searchActiveSwitch(conn, dpid_src);
+	ISwitchObject destSwitch =
+	    conn.utils().searchActiveSwitch(conn, dpid_dest);
+	if (srcSwitch == null || destSwitch == null) {
+	    return null;
+	}
 
 	//
 	// Test whether we are computing a path from/to the same DPID.
@@ -426,22 +432,10 @@
 	    flowEntry.setInPort(src.port());
 	    flowEntry.setOutPort(dest.port());
 	    result_data_path.flowEntries().add(flowEntry);
-	    titanTransaction.stopTransaction(Conclusion.SUCCESS);
-	    // titanTransaction.shutdown();
+	    conn.endTx(Transaction.COMMIT);
 	    return result_data_path;
 	}
 
-
-	// Get the source vertex
-
-	ISwitchObject srcSwitch = conn.utils().searchSwitch(conn, dpid_src);
-	ISwitchObject destSwitch = conn.utils().searchSwitch(conn, dpid_dest);
-
-	if (srcSwitch == null || destSwitch == null) {
-		return null;
-	}
-
-
 	Vertex v_src = srcSwitch.asVertex();	
 	Vertex v_dest = destSwitch.asVertex();
 
@@ -464,6 +458,11 @@
 	    for (Vertex parentPort : nextVertex.getVertices(Direction.OUT, "on")) {
 		for (Vertex childPort : parentPort.getVertices(Direction.OUT, "link")) {
 		    for (Vertex child : childPort.getVertices(Direction.IN, "on")) {
+			// Ignore inactive switches
+			String state = child.getProperty("state").toString();
+			if (! state.equals(SwitchState.ACTIVE.toString()))
+			    continue;
+
 			if (! visitedSet.contains(child)) {
 			    previousVertexMap.put(parentPort, nextVertex);
 			    previousVertexMap.put(childPort, parentPort);
@@ -489,7 +488,6 @@
 	Collections.reverse(resultPath);
 
 
-
 	//
 	// Loop through the result and prepare the return result
 	// as a list of Flow Entries.
@@ -553,8 +551,7 @@
 	    result_data_path.flowEntries().add(flowEntry);
 	}
 
-	titanTransaction.stopTransaction(Conclusion.SUCCESS);
-	// titanTransaction.shutdown();
+	conn.endTx(Transaction.COMMIT);
 	if (result_data_path.flowEntries().size() > 0)
 	    return result_data_path;
 
diff --git a/src/main/java/net/floodlightcontroller/util/DataPath.java b/src/main/java/net/floodlightcontroller/util/DataPath.java
index b2dded6..0ca0d13 100644
--- a/src/main/java/net/floodlightcontroller/util/DataPath.java
+++ b/src/main/java/net/floodlightcontroller/util/DataPath.java
@@ -79,6 +79,37 @@
     }
 
     /**
+     * Get a string with the summary of the shortest-path data path
+     * computation.
+     *
+     * NOTE: This method assumes the DataPath was created by
+     * using FlowManager::getShortestPath() so the inPort and outPort
+     * of the Flow Entries are set.
+     * NOTE: This method is a temporary solution and will be removed
+     * in the future.
+     *
+     * @return a string with the summary of the shortest-path
+     * data path computation if valid, otherwise the string "X".
+     * If the shortest-path was valid, The string has the following form:
+     * inPort/dpid/outPort;inPort/dpid/outPort;...
+     */
+    public String dataPathSummary() {
+	String resultStr = new String();
+	if (this.flowEntries != null) {
+	    for (FlowEntry flowEntry : this.flowEntries) {
+		// The data path summary string
+		resultStr = resultStr +
+		    flowEntry.inPort().toString() + "/"
+		    + flowEntry.dpid().toString() + "/" +
+		    flowEntry.outPort().toString() + ";";
+	    }
+	}
+	if (resultStr.isEmpty())
+	    resultStr = "X";		// Invalid shortest-path
+	return resultStr;
+    }
+
+    /**
      * Convert the data path to a string.
      *
      * The string has the following form:
diff --git a/src/main/java/net/onrc/onos/util/GraphDBUtils.java b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
index ba48103..7283d09 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
@@ -44,6 +44,16 @@
 	}
 
 	@Override
+	public ISwitchObject searchActiveSwitch(GraphDBConnection conn, String dpid) {
+	    ISwitchObject sw = searchSwitch(conn, dpid);
+	    if ((sw != null) &&
+		sw.getState().equals(SwitchState.ACTIVE.toString())) {
+		return sw;
+	    }
+	    return null;
+	}
+
+	@Override
 	public IDeviceObject searchDevice(GraphDBConnection conn, String macAddr) {
 		// TODO Auto-generated method stub
 		FramedGraph<TitanGraph> fg = conn.getFramedGraph();	
diff --git a/src/main/java/net/onrc/onos/util/IDBUtils.java b/src/main/java/net/onrc/onos/util/IDBUtils.java
index 864e227..51948a2 100644
--- a/src/main/java/net/onrc/onos/util/IDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/IDBUtils.java
@@ -10,6 +10,7 @@
 
 public interface IDBUtils {	
 	public ISwitchObject searchSwitch(GraphDBConnection conn, String dpid);
+	public ISwitchObject searchActiveSwitch(GraphDBConnection conn, String dpid);
 	public Iterable<ISwitchObject> getActiveSwitches(GraphDBConnection conn);
 	public Iterable<ISwitchObject> getAllSwitches(GraphDBConnection conn);
 	public Iterable<ISwitchObject> getInactiveSwitches(GraphDBConnection conn);
diff --git a/web/add_flow.py b/web/add_flow.py
index 0ab6847..5a248eb 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -21,6 +21,7 @@
 ControllerIP = "127.0.0.1"
 ControllerPort = 8080
 MonitoringEnabled = False
+MonitoringByOnos = False
 ReadFromFile = ""
 
 DEBUG=0
@@ -91,6 +92,20 @@
     log_error("Controller IF has issue")
     exit(1)
 
+def add_shortest_path_flow(flow_path):
+  flow_path_json = json.dumps(flow_path)
+
+  try:
+    command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/add-shortest-path/json" % (flow_path_json, ControllerIP, ControllerPort)
+    debug("add_shortest_path_flow %s" % command)
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    # parsedResult = json.loads(result)
+    # debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
 def delete_flow_path(flow_id):
   command = "curl -s \"http://%s:%s/wm/flow/delete/%s/json\"" % (ControllerIP, ControllerPort, flow_id)
   debug("delete_flow_path %s" % command)
@@ -366,17 +381,97 @@
 
   flow_path['dataPath'] = my_data_path
   debug("Flow Path: %s" % flow_path)
+  return flow_path
 
-  add_flow_path(flow_path)
+def exec_monitoring_by_onos(parsed_args):
+  idx = 0
+  while idx < len(parsed_args):
+    data_path = {}
+    src_dpid = {}
+    src_port = {}
+    dst_dpid = {}
+    dst_port = {}
+    src_switch_port = {}
+    dst_switch_port = {}
+    flow_entry = {}
+    flow_entries = []
+
+    src_dpid['value'] = parsed_args[idx]['my_src_dpid']
+    src_port['value'] = parsed_args[idx]['my_src_port']
+    dst_dpid['value'] = parsed_args[idx]['my_dst_dpid']
+    dst_port['value'] = parsed_args[idx]['my_dst_port']
+    src_switch_port['dpid'] = src_dpid
+    src_switch_port['port'] = src_port
+    dst_switch_port['dpid'] = dst_dpid
+    dst_switch_port['port'] = dst_port
+    match = parsed_args[idx]['match']
+    flow_entry['flowEntryMatch'] = match
+    flow_entries.append(flow_entry)
+
+    data_path['srcPort'] = copy.deepcopy(src_switch_port)
+    data_path['dstPort'] = copy.deepcopy(dst_switch_port)
+    data_path['flowEntries'] = copy.deepcopy(flow_entries)
+
+    #
+    # XXX: Explicitly disable the InPort matching, and
+    # the Output action, because they get in the way
+    # during the compute_flow_path() processing.
+    #
+    parsed_args[idx]['matchInPortEnabled'] = False
+    parsed_args[idx]['actionOutputEnabled'] = False
+
+    flow_path = compute_flow_path(parsed_args[idx], data_path)
+    add_shortest_path_flow(flow_path)
+
+    idx = idx + 1
+
+
+def exec_processing_by_script(parsed_args):
+  #
+  # Initialization
+  #
+  last_data_paths = []
+  idx = 0
+  while idx < len(parsed_args):
+    last_data_path = []
+    last_data_paths.append(copy.deepcopy(last_data_path))
+    idx = idx + 1
+
+  #
+  # Do the work: install and/or periodically monitor each flow
+  #
+  while True:
+    idx = 0
+    while idx < len(parsed_args):
+      last_data_path = last_data_paths[idx]
+      my_flow_id = parsed_args[idx]['my_flow_id']
+      data_path = compute_data_path(parsed_args[idx])
+      if data_path != last_data_path:
+	print_data_path(data_path)
+	if len(last_data_path) > 0:
+	  delete_flow_path(my_flow_id)
+	if len(data_path) > 0:
+	  flow_path = compute_flow_path(parsed_args[idx], data_path)
+	  add_flow_path(flow_path)
+	last_data_paths[idx] = copy.deepcopy(data_path)
+      idx = idx + 1
+
+    if MonitoringEnabled != True:
+      break
+    time.sleep(1)
 
 
 if __name__ == "__main__":
   usage_msg = "Usage: %s [Flags] <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port> [Match Conditions] [Actions]\n" % (sys.argv[0])
   usage_msg = usage_msg + "\n"
   usage_msg = usage_msg + "    Flags:\n"
-  usage_msg = usage_msg + "        -m              Monitor and maintain the installed shortest path(s)\n"
-  usage_msg = usage_msg + "        -f <filename>   Read the flow(s) to install from a file\n"
-  usage_msg = usage_msg + "                        File format: one line per flow starting with <flow-id>\n"
+  usage_msg = usage_msg + "        -m [monitorname]  Monitor and maintain the installed shortest path(s)\n"
+  usage_msg = usage_msg + "                          If 'monitorname' is specified and is set to 'ONOS'\n"
+  usage_msg = usage_msg + "                          ((case insensitive), then the flow generation and\n"
+  usage_msg = usage_msg + "                          maintanenance is done by ONOS itself.\n"
+  usage_msg = usage_msg + "                          Otherwise, it is done by this script.\n"
+  usage_msg = usage_msg + "        -f <filename>     Read the flow(s) to install from a file\n"
+  usage_msg = usage_msg + "                          File format: one line per flow starting with <flow-id>\n"
   usage_msg = usage_msg + "\n"
   usage_msg = usage_msg + "    Match Conditions:\n"
   usage_msg = usage_msg + "        matchInPort <True|False> (default to True)\n"
@@ -427,6 +522,11 @@
     idx = idx + 1
     if arg1 == "-m":
       MonitoringEnabled = True
+      if idx < len(sys.argv):
+	arg2 = sys.argv[idx]
+	if arg2.lower() == "onos":
+	  MonitoringByOnos = True
+	  idx = idx + 1
       start_argv_index = idx
     elif arg1 == "-f":
       if idx >= len(sys.argv):
@@ -476,24 +576,8 @@
     idx = idx + 1
 
   #
-  # Do the work: install and/or periodically monitor each flow
-  #
-  while True:
-    idx = 0
-    while idx < len(parsed_args):
-      last_data_path = last_data_paths[idx]
-      my_flow_id = parsed_args[idx]['my_flow_id']
-      data_path = compute_data_path(parsed_args[idx])
-      if data_path != last_data_path:
-	print_data_path(data_path)
-	if len(last_data_path) > 0:
-	  delete_flow_path(my_flow_id)
-	if len(data_path) > 0:
-	  flow_path = compute_flow_path(parsed_args[idx], data_path)
-	  add_flow_path(flow_path)
-	last_data_paths[idx] = copy.deepcopy(data_path)
-      idx = idx + 1
+  if MonitoringByOnos == True:
+    exec_monitoring_by_onos(parsed_args)
+  else:
+    exec_processing_by_script(parsed_args)
 
-    if MonitoringEnabled != True:
-      break
-    time.sleep(1)
diff --git a/web/ons-demo/RELEASE_NOTES.txt b/web/ons-demo/RELEASE_NOTES.txt
index 48834fb..8649e9b 100644
--- a/web/ons-demo/RELEASE_NOTES.txt
+++ b/web/ons-demo/RELEASE_NOTES.txt
@@ -1,4 +1,16 @@
 ** March 28, 2013 **
+- add and delete flow implemented
+- to add flow
+	- mouse down in src edge switch
+	- drag to dst edge switch
+	- release/confirm to create flow
+- to delete flow
+	- select flow in flow chooser
+	- click "close" button in flow table
+	- confirm
+- same "pending" style as other actions
+
+** March 28, 2013 **
 - basic flow chooser
 	- click in "eye" to show full list
 	- click on "eye" in full list to monitor that flow in the top slot (and show the flow in topology)
diff --git a/web/ons-demo/assets/delete.svg b/web/ons-demo/assets/delete.svg
new file mode 100644
index 0000000..a32a278
--- /dev/null
+++ b/web/ons-demo/assets/delete.svg
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_1"
+   x="0px"
+   y="0px"
+   width="283.465px"
+   height="283.465px"
+   viewBox="0 0 283.465 283.465"
+   enable-background="new 0 0 283.465 283.465"
+   xml:space="preserve"
+   inkscape:version="0.48.0 r9654"
+   sodipodi:docname="delete.svg"><metadata
+   id="metadata9"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+   id="defs7" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="721"
+   inkscape:window-height="480"
+   id="namedview5"
+   showgrid="false"
+   inkscape:zoom="0.92427638"
+   inkscape:cx="141.7325"
+   inkscape:cy="141.7325"
+   inkscape:window-x="0"
+   inkscape:window-y="0"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="Layer_1" />
+<path
+   fill="#772953"
+   d="M141.733,198.425c-15.143,0-29.38-5.897-40.088-16.605c-10.708-10.708-16.605-24.945-16.605-40.088  c0-15.142,5.897-29.378,16.605-40.086c10.708-10.708,24.945-16.605,40.088-16.605c15.142,0,29.379,5.896,40.087,16.604  c10.708,10.708,16.605,24.946,16.605,40.089c0,15.143-5.897,29.381-16.605,40.088C171.112,192.528,156.875,198.425,141.733,198.425  L141.733,198.425z M141.733,99.213c-11.357,0-22.035,4.423-30.067,12.454c-8.031,8.031-12.454,18.708-12.454,30.064  c0,11.357,4.423,22.035,12.454,30.065c8.032,8.031,18.709,12.455,30.067,12.455c11.356,0,22.034-4.423,30.064-12.453  c8.031-8.03,12.455-18.709,12.455-30.066s-4.424-22.036-12.455-30.067C163.768,103.636,153.09,99.213,141.733,99.213L141.733,99.213  z M151.703,141.79l10.021-10.022c2.768-2.768,2.768-7.254,0-10.021c-2.767-2.767-7.255-2.767-10.021,0l-10.022,10.021  l-10.021-10.021c-2.767-2.767-7.255-2.767-10.022,0c-2.768,2.768-2.768,7.254,0,10.021l10.022,10.022l-10.023,10.023  c-2.768,2.768-2.768,7.254,0,10.021c1.383,1.384,3.197,2.076,5.011,2.076c1.813,0,3.627-0.692,5.011-2.076l10.023-10.023  l10.022,10.021c1.383,1.384,3.197,2.076,5.011,2.076s3.627-0.692,5.011-2.076c2.768-2.768,2.768-7.254,0-10.021L151.703,141.79z"
+   id="path3"
+   style="fill:#ffffff;fill-opacity:1" />
+</svg>
\ No newline at end of file
diff --git a/web/ons-demo/css/skin.default.css b/web/ons-demo/css/skin.default.css
index aebf68b..d88814e 100644
--- a/web/ons-demo/css/skin.default.css
+++ b/web/ons-demo/css/skin.default.css
@@ -61,6 +61,10 @@
 	border-bottom: 1px solid #AAA;
 }
 
+.selectedFlow:hover {
+	background-color: #333;
+}
+
 .selectedFlow:last-child {
 	border-bottom: none;
 }
@@ -132,6 +136,11 @@
 	stroke: rgba(255, 255, 255, .35);
 }
 
+path.flow.highlight {
+	stroke-width: 6px;
+	stroke: rgba(255, 255, 255, .75);
+}
+
 #selectedFlowsHeader {
 	border-top: 1px solid #AAA;
 }
@@ -142,7 +151,7 @@
 }
 
 
-.flowId, .srcDPID, .dstDPID, .iperf {
+.flowId, .srcDPID, .dstDPID, .iperf, #deleteFlow, .deleteFlow {
 	display: -webkit-box;
 	-webkit-box-pack: center;
 	-webkit-box-align: center;
@@ -155,10 +164,14 @@
 }
 
 
-.flowId, .srcDPID, .dstDPID {
+.srcDPID, .dstDPID, .deleteFlow, #deleteFlow {
 	border-right: 1px solid #AAA;
 }
 
+.srcDPID {
+	border-left: 1px solid #AAA;
+}
+
 
 #controllers {
 	border-right: 1px solid #AAA;
@@ -191,6 +204,14 @@
 	background-position: .25em center;
 }
 
+.deleteFlow {
+	height: 100%;
+	background-image: url('../assets/delete.svg');
+	background-size: auto 150%;
+	background-repeat: no-repeat;
+	background-position: center;
+}
+
 #logo {
 	height: 50px;
 }
diff --git a/web/ons-demo/index.html b/web/ons-demo/index.html
index d157ecc..55237ac 100644
--- a/web/ons-demo/index.html
+++ b/web/ons-demo/index.html
@@ -38,6 +38,7 @@
 	</div>
 </div>
 <div id='selectedFlowsHeader'>
+	<div id='deleteFlow'></div>
 	<div id='showFlowChooser' class='flowId'><div class='white-eye'></div></div>
 	<div class='srcDPID'>src</div>
 	<div class='dstDPID'>dst</div>
diff --git a/web/ons-demo/js/app.js b/web/ons-demo/js/app.js
index 0fa5fa3..e342213 100644
--- a/web/ons-demo/js/app.js
+++ b/web/ons-demo/js/app.js
@@ -18,6 +18,9 @@
 var svg;
 var updateTopology;
 var pendingLinks = {};
+var selectedFlows = [];
+
+var pendingTimeout = 10000;
 
 var colors = [
 	'color1',
@@ -65,13 +68,9 @@
 			attr('id', 'viewbox').append('svg:g').attr('transform', 'translate(500 500)');
 }
 
-var selectedFlows = [null, null, null];
-
-function drawFlows() {
+function updateSelectedFlowsTopology() {
 	// DRAW THE FLOWS
-	var flows = d3.select('svg').selectAll('.flow').data(selectedFlows, function (d) {
-		return d ? d.flowId.value : null;
-	});
+	var flows = d3.select('svg').selectAll('.flow').data(selectedFlows);
 
 	flows.enter().append("svg:path").attr('class', 'flow')
 		.attr('stroke-dasharray', '4, 10')
@@ -85,27 +84,212 @@
 
 
 	flows.attr('d', function (d) {
-		if (!d) {
-			return;
-		}
-		var pts = [];
-		d.dataPath.flowEntries.forEach(function (flowEntry) {
-			var s = d3.select(document.getElementById(flowEntry.dpid.value));
-			var pt = document.querySelector('svg').createSVGPoint();
-			pt.x = s.attr('x');
-			pt.y = s.attr('y');
-			pt = pt.matrixTransform(s[0][0].getCTM());
-			pts.push(pt);
+			if (!d) {
+				return;
+			}
+			var pts = [];
+			if (!d.dataPath.flowEntries) {
+				// create a temporary vector to indicate the pending flow
+				var s1 = d3.select(document.getElementById(d.dataPath.srcPort.dpid.value));
+				var s2 = d3.select(document.getElementById(d.dataPath.dstPort.dpid.value));
+
+				var pt1 = document.querySelector('svg').createSVGPoint();
+				pt1.x = s1.attr('x');
+				pt1.y = s1.attr('y');
+				pt1 = pt1.matrixTransform(s1[0][0].getCTM());
+				pts.push(pt1);
+
+				var pt2 = document.querySelector('svg').createSVGPoint();
+				pt2.x = s2.attr('x');
+				pt2.y = s2.attr('y');
+				pt2 = pt2.matrixTransform(s2[0][0].getCTM());
+				pts.push(pt2);
+
+			} else {
+				d.dataPath.flowEntries.forEach(function (flowEntry) {
+					var s = d3.select(document.getElementById(flowEntry.dpid.value));
+					// s[0] is null if the flow entry refers to a non-existent switch
+					if (s[0][0]) {
+						var pt = document.querySelector('svg').createSVGPoint();
+						pt.x = s.attr('x');
+						pt.y = s.attr('y');
+						pt = pt.matrixTransform(s[0][0].getCTM());
+						pts.push(pt);
+					} else {
+						console.log('flow refers to non-existent switch: ' + flowEntry.dpid.value);
+					}
+				});
+			}
+			return line(pts);
+		})
+		.attr('id', function (d) {
+			if (d) {
+				return makeFlowKey(d);
+			}
+		})
+		.classed('pending', function (d) {
+			return d && (d.createPending || d.deletePending);
 		});
-		return line(pts);
-	})
 
 	// "marching ants"
 	flows.select('animate').attr('from', 500);
 
+}
+
+function updateSelectedFlowsTable() {
+	function rowEnter(d) {
+		var row = d3.select(this);
+		row.append('div').classed('deleteFlow', true);
+		row.append('div').classed('flowId', true);
+		row.append('div').classed('srcDPID', true);
+		row.append('div').classed('dstDPID', true);
+		row.append('div').classed('iperf', true);
+
+		row.on('mouseover', function (d) {
+			if (d) {
+				var path = document.getElementById(makeFlowKey(d));
+				d3.select(path).classed('highlight', true);
+			}
+		});
+		row.on('mouseout', function (d) {
+			if (d) {
+				var path = document.getElementById(makeFlowKey(d));
+				d3.select(path).classed('highlight', false);
+			}
+		})
+	}
+
+	function rowUpdate(d) {
+		var row = d3.select(this);
+		row.select('.deleteFlow').on('click', function () {
+			if (d) {
+				var prompt = 'Delete flow ' + d.flowId.value + '?';
+				if (confirm(prompt)) {
+					deleteFlow(d);
+					d.deletePending = true;
+					updateSelectedFlows();
+
+					setTimeout(function () {
+						d.deletePending = false;
+						updateSelectedFlows();
+					}, pendingTimeout)
+				};
+			}
+		});
+
+		row.select('.flowId')
+			.text(function (d) {
+				if (d) {
+					if (d.flowId) {
+						return d.flowId.value;
+					} else {
+						return '0x--';
+					}
+				}
+			})
+			.classed('pending', d && (d.deletePending || d.createPending));
+
+		row.select('.srcDPID')
+			.text(function (d) {
+				if (d) {
+					return d.dataPath.srcPort.dpid.value;
+				}
+			});
+
+		row.select('.dstDPID')
+			.text(function (d) {
+				if (d) {
+					return d.dataPath.dstPort.dpid.value;
+				}
+			});
+	}
+
+	var flows = d3.select('#selectedFlows')
+		.selectAll('.selectedFlow')
+		.data(selectedFlows);
+
+	flows.enter()
+		.append('div')
+		.classed('selectedFlow', true)
+		.each(rowEnter);
+
+	flows.each(rowUpdate);
+
 	flows.exit().remove();
 }
 
+function updateSelectedFlows() {
+	// make sure that all of the selected flows are either
+	// 1) valid (meaning they are in the latest list of flows)
+	// 2) pending
+	if (model) {
+		var flowMap = {};
+		model.flows.forEach(function (flow) {
+			flowMap[makeFlowKey(flow)] = flow;
+		});
+
+		var newSelectedFlows = [];
+		selectedFlows.forEach(function (flow) {
+			if (flow) {
+				var liveFlow = flowMap[makeFlowKey(flow)];
+				if (liveFlow) {
+					newSelectedFlows.push(liveFlow);
+					liveFlow.deletePending = flow.deletePending;
+				} else if (flow.createPending) {
+					newSelectedFlows.push(flow);
+				}
+			} else {
+				newSelectedFlows.push(null);
+			}
+		});
+		selectedFlows = newSelectedFlows;
+	}
+	while (selectedFlows.length < 3) {
+		selectedFlows.push(null);
+	}
+
+	updateSelectedFlowsTable();
+	updateSelectedFlowsTopology();
+}
+
+function selectFlow(flow) {
+	var flowKey = makeFlowKey(flow);
+	var alreadySelected = false;
+	selectedFlows.forEach(function (f) {
+		if (f && makeFlowKey(f) === flowKey) {
+			alreadySelected = true;
+		}
+	});
+
+	if (!alreadySelected) {
+		selectedFlows.unshift(flow);
+		selectedFlows = selectedFlows.slice(0, 3);
+		updateSelectedFlows();
+	}
+}
+
+function deselectFlow(flow, ifCreatePending) {
+	var flowKey = makeFlowKey(flow);
+	var newSelectedFlows = [];
+	selectedFlows.forEach(function (flow) {
+		if (!flow ||
+				flowKey !== makeFlowKey(flow) ||
+				flowKey === makeFlowKey(flow) && ifCreatePending && !flow.createPending ) {
+			newSelectedFlows.push(flow);
+		}
+	});
+	selectedFlows = newSelectedFlows;
+	while (selectedFlows.length < 3) {
+		selectedFlows.push(null);
+	}
+
+	updateSelectedFlows();
+}
+
+function deselectFlowIfCreatePending(flow) {
+	deselectFlow(flow, true);
+}
+
 function showFlowChooser() {
 	function rowEnter(d) {
 		var row = d3.select(this);
@@ -113,11 +297,7 @@
 		row.append('div')
 			.classed('black-eye', true).
 			on('click', function () {
-				selectedFlows.unshift(d);
-				selectedFlows = selectedFlows.slice(0, 3);
-
-				updateSelectedFlows();
-				updateTopology();
+				selectFlow(d);
 			});
 
 		row.append('div')
@@ -159,54 +339,7 @@
 	}, 0);
 }
 
-function updateSelectedFlows() {
-	function rowEnter(d) {
-		var row = d3.select(this);
-		row.append('div').classed('flowId', true);
-		row.append('div').classed('srcDPID', true);
-		row.append('div').classed('dstDPID', true);
-		row.append('div').classed('iperf', true);
-	}
 
-	function rowUpdate(d) {
-		var row = d3.select(this);
-		row.select('.flowId')
-			.text(function (d) {
-				if (d) {
-					return d.flowId.value;
-				}
-			});
-
-		row.select('.srcDPID')
-			.text(function (d) {
-				if (d) {
-					return d.dataPath.srcPort.dpid.value;
-				}
-			});
-
-		row.select('.dstDPID')
-			.text(function (d) {
-				if (d) {
-					return d.dataPath.dstPort.dpid.value;
-				}
-			});
-	}
-
-	var flows = d3.select('#selectedFlows')
-		.selectAll('.selectedFlow')
-		.data(selectedFlows);
-
-	flows.enter()
-		.append('div')
-		.classed('selectedFlow', true)
-		.each(rowEnter);
-
-	flows.each(rowUpdate);
-
-	flows.exit().remove();
-
-	return flows;
-}
 
 function updateHeader(model) {
 	d3.select('#lastUpdate').text(new Date());
@@ -325,6 +458,10 @@
 	return link['src-switch'] + '=>' + link['dst-switch'];
 }
 
+function makeFlowKey(flow) {
+	return flow.dataPath.srcPort.dpid.value + '=>' + flow.dataPath.dstPort.dpid.value;
+}
+
 function createLinkMap(links) {
 	var linkMap = {};
 	links.forEach(function (link) {
@@ -361,7 +498,6 @@
 
 	var links = reconcilePendingLinks(model);
 	var linkMap = createLinkMap(links);
-//	var flowMap = createFlowMap(model);
 
 	function mouseOverSwitch(data) {
 
@@ -650,9 +786,29 @@
 			if (s1Data.className == 'edge' && s2Data.className == 'edge') {
 				var prompt = 'Create flow from ' + srcData.dpid + ' to ' + dstData.dpid + '?';
 				if (confirm(prompt)) {
-					alert('do create flow');
-				} else {
-					alert('do not create flow');
+					addFlow(srcData, dstData);
+
+					var flow = {
+						dataPath: {
+							srcPort: {
+								dpid: {
+									value: srcData.dpid
+								}
+							},
+							dstPort: {
+								dpid: {
+									value: dstData.dpid
+								}
+							}
+						},
+						createPending: true
+					};
+
+					selectFlow(flow);
+
+					setTimeout(function () {
+						deselectFlowIfCreatePending(flow);
+					}, pendingTimeout);
 				}
 			} else {
 				var map = linkMap[srcData.dpid];
@@ -697,7 +853,7 @@
 								delete pendingLinks[makeLinkKey(link2)];
 
 								updateTopology();
-							}, 10000);
+							}, pendingTimeout);
 						}
 					}
 				}
@@ -830,9 +986,6 @@
 
 	// remove old links
 	links.exit().remove();
-
-
-	drawFlows();
 }
 
 function updateControllers() {
diff --git a/web/ons-demo/js/controller.js b/web/ons-demo/js/controller.js
index a36ae1c..fd3f6ae 100644
--- a/web/ons-demo/js/controller.js
+++ b/web/ons-demo/js/controller.js
@@ -10,57 +10,64 @@
 	});
 }
 
+function MAC(dpid) {
+	var cmps = dpid.split(':');
+	var MAC = '00:00:c0:a8:' + [cmps[6], cmps[7]].join(':');
+	return MAC;
+}
 
 var controllerFunctions = {
-	l: function (cmd, link) {
+	linkCmd: function (cmd, link) {
 		var url = '/proxy/gui/link/' + [cmd, link['src-switch'], link['src-port'], link['dst-switch'], link['dst-port']].join('/');
 		callURL(url);
 
 	},
-	s: function (cmd, s) {
+	switchCmd: function (cmd, s) {
 		var url = '/proxy/gui/switch/' + [cmd, s.dpid].join('/');
 		callURL(url);
 	},
-	c: function (cmd, c) {
+	ctrlCmd: function (cmd, c) {
 		var url = '/proxy/gui/controller/' + [cmd, c].join('/');
 		callURL(url);
+	},
+	addFlowCmd: function (src, dst) {
+		var url = '/proxy/gui/addflow/' + [src.dpid, 1, dst.dpid, 1, MAC(src.dpid), MAC(dst.dpid)].join('/');
+		callURL(url);
+	},
+	delFlowCmd: function (flow) {
+		var url = '/proxy/gui/delflow/' + flow.flowId.value;
+		callURL(url);
 	}
 };
 
-
-// if (parseURLParameters().mock) {
-// 	urls = mockURLs;
-// }
-
-
 function linkUp(link) {
-	controllerFunctions.l('up', link);
+	controllerFunctions.linkCmd('up', link);
 }
 
 function linkDown(link) {
-	controllerFunctions.l('down', link);
+	controllerFunctions.linkCmd('down', link);
 }
 
 function switchUp(s) {
-	controllerFunctions.s('up', s);
+	controllerFunctions.switchCmd('up', s);
 }
 
 function switchDown(s) {
-	controllerFunctions.s('down', s);
+	controllerFunctions.switchCmd('down', s);
 }
 
 function controllerUp(c) {
-	controllerFunctions.c('up', c);
+	controllerFunctions.ctrlCmd('up', c);
 }
 
 function controllerDown(c) {
-	controllerFunctions.c('down', c);
+	controllerFunctions.ctrlCmd('down', c);
 }
 
-function createFlow(src, dst) {
-
+function addFlow(src, dst) {
+	controllerFunctions.addFlowCmd(src, dst);
 }
 
-function deleteFlow(src, dst) {
-
+function deleteFlow(flow) {
+	controllerFunctions.delFlowCmd(flow);
 }
\ No newline at end of file
diff --git a/web/topology_rest.py b/web/topology_rest.py
index f3a4a3b..0306de9 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -127,6 +127,31 @@
   resp = Response(result, status=200, mimetype='application/json')
   return resp
 
+@app.route("/proxy/gui/addflow/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>/<srcMAC>/<dstMAC>")
+def proxy_add_flow(src_dpid, src_port, dst_dpid, dst_port, srcMAC, dstMAC):
+  try:
+    command = "curl -s %s/gui/addflow/%s/%s/%s/%s/%s/%s" % (ONOS_GUI3_CONTROL_HOST, src_dpid, src_port, dst_dpid, dst_port, srcMAC, dstMAC)
+    print command
+    result = os.popen(command).read()
+  except:
+    print "REST IF has issue"
+    exit
+
+  resp = Response(result, status=200, mimetype='application/json')
+  return resp
+
+@app.route("/proxy/gui/delflow/<flow_id>")
+def proxy_del_flow(flow_id):
+  try:
+    command = "curl -s %s/gui/delflow/%s" % (ONOS_GUI3_CONTROL_HOST, flow_id)
+    print command
+    result = os.popen(command).read()
+  except:
+    print "REST IF has issue"
+    exit
+
+  resp = Response(result, status=200, mimetype='application/json')
+  return resp
 @app.route("/wm/core/topology/switches/all/json")
 def switches():
   if request.args.get('proxy') == None:
@@ -729,7 +754,7 @@
   cmd = 'up'
   result=""
 
-  for dpid in (src_dpid, dst_dpid): 
+  for dpid in (src_dpid, dst_dpid):
     if dpid in core_switches:
       host = controllers[0]
       src_ports = [1, 2, 3, 4, 5]