Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS

At some point my fix for #296 got clobbered in a merge.
This commit adds it back in.
diff --git a/scripts/check-db-clean b/scripts/check-db-clean
new file mode 100644
index 0000000..e49d267
--- /dev/null
+++ b/scripts/check-db-clean
@@ -0,0 +1,6 @@
+g = TitanFactory.open('cassandra.local')
+g.stopTransaction(SUCCESS);
+g.V('type', 'port').each{println it.type};
+g.V('type', 'switch').each{println it.type};
+g.V('type', 'flow').each{println it.type};
+g.V('type', 'flow_entry').each{println it.type};
diff --git a/scripts/check-db-status.sh b/scripts/check-db-status.sh
new file mode 100755
index 0000000..b81e02d
--- /dev/null
+++ b/scripts/check-db-status.sh
@@ -0,0 +1,8 @@
+#! /bin/bash
+DIR=~/ONOS
+status=`~/titan-0.2.0/bin/gremlin.sh -e $DIR/scripts/check-db-clean | grep null | wc -l`
+if [ $status == 0 ]; then
+  echo "OK"
+else
+  echo "BAD"
+fi
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 098e02f..40adf6b 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -265,6 +265,12 @@
 
     final Runnable mapReader = new Runnable() {
 	    public void run() {
+		long startTime = System.nanoTime();
+		int counterAllFlowEntries = 0;
+		int counterMyNotUpdatedFlowEntries = 0;
+		int counterAllFlowPaths = 0;
+		int counterMyFlowPaths = 0;
+
 		if (floodlightProvider == null) {
 		    log.debug("FloodlightProvider service not found!");
 		    return;
@@ -284,6 +290,7 @@
 		Iterable<IFlowEntry> allFlowEntries =
 		    conn.utils().getAllFlowEntries(conn);
 		for (IFlowEntry flowEntryObj : allFlowEntries) {
+		    counterAllFlowEntries++;
 		    String flowEntryIdStr = flowEntryObj.getFlowEntryId();
 		    String userState = flowEntryObj.getUserState();
 		    String switchState = flowEntryObj.getSwitchState();
@@ -316,11 +323,15 @@
 		    myFlowEntries.put(flowEntryId.value(), flowEntryObj);
 		}
 
+		log.debug("MEASUREMENT: Found {} My Flow Entries NOT_UPDATED",
+			  myFlowEntries.size());
+
 		//
 		// Process my Flow Entries
 		//
 		boolean processed_measurement_flow = false;
 		for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
+		    counterMyNotUpdatedFlowEntries++;
 		    IFlowEntry flowEntryObj = entry.getValue();
 		    IFlowPath flowObj =
 			conn.utils().getFlowPathByFlowEntry(conn,
@@ -477,6 +488,9 @@
 		    }
 		}
 
+		log.debug("MEASUREMENT: Found {} Flow Entries to delete",
+			  deleteFlowEntries.size());
+
 		//
 		// Delete all entries marked for deletion
 		//
@@ -514,15 +528,11 @@
 		// 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) {
+		    counterAllFlowPaths++;
 		    if (flowPathObj == null)
 			continue;
 		    String dataPathSummaryStr = flowPathObj.getDataPathSummary();
@@ -553,6 +563,21 @@
 		    Port dstPort = new Port(dstPortShort);
 		    SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
 		    SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
+
+		    //
+		    // 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
+
+		    counterMyFlowPaths++;
+
 		    //
 		    // NOTE: Using here the regular getShortestPath() method
 		    // won't work here, because that method calls internally
@@ -576,22 +601,12 @@
 		    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);
 		}
+		log.debug("MEASUREMENT: Found {} Flows to reconcile",
+			  flowObjSet.size());
 		reconcileFlows(flowObjSet);
 		topoRouteService.dropShortestPathTopo();
 
@@ -604,6 +619,11 @@
 			(double)estimatedTime / 1000000000 + " sec";
 		    log.debug(logMsg);
 		}
+
+		long estimatedTime = System.nanoTime() - startTime;
+		double rate = (estimatedTime > 0)? ((double)counterAllFlowPaths * 1000000000) / estimatedTime: 0.0;
+		String logMsg = "MEASUREMENT: Processed AllFlowEntries: " + counterAllFlowEntries + " MyNotUpdatedFlowEntries: " + counterMyNotUpdatedFlowEntries + " AllFlowPaths: " + counterAllFlowPaths + " MyFlowPaths: " + counterMyFlowPaths + " in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " paths/s";
+		log.debug(logMsg);
 	    }
 	};
 
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index a9a2212..634f7eb 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -186,13 +186,13 @@
     // Link discovery task details.
     protected SingletonTask discoveryTask;
     protected final int DISCOVERY_TASK_INTERVAL = 1; 
-    protected final int LINK_TIMEOUT = 5; // decreased timeout as part of LLDP process from 35 secs
-    protected final int LLDP_TO_ALL_INTERVAL = 2 ; //decreased from 15 seconds.
+    protected final int LINK_TIMEOUT = 35; // original 35 secs, aggressive 5 secs
+    protected final int LLDP_TO_ALL_INTERVAL = 15 ; //original 15 seconds, aggressive 2 secs.
     protected long lldpClock = 0;
     // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
     // If we want to identify link failures faster, we could decrease this
     // value to a small number, say 1 or 2 sec.
-    protected final int LLDP_TO_KNOWN_INTERVAL= 2; // LLDP frequency for known links from 20 secs
+    protected final int LLDP_TO_KNOWN_INTERVAL= 20; // LLDP frequency for known links
 
     protected LLDPTLV controllerTLV;
     protected ReentrantReadWriteLock lock;
diff --git a/web/ons-demo/RELEASE_NOTES.txt b/web/ons-demo/RELEASE_NOTES.txt
index 8649e9b..916a965 100644
--- a/web/ons-demo/RELEASE_NOTES.txt
+++ b/web/ons-demo/RELEASE_NOTES.txt
@@ -1,3 +1,11 @@
+** April 4, 2013 **
+Fix issues:
+	305 - "close x" now unselects flow. double click to delete a flow
+	323 - gui now recovers on timeout errors and polls again
+	324 - fixed problem with added flows not displaying
+	325 - fixed logic displaying flows in topology view
+
+
 ** March 28, 2013 **
 - add and delete flow implemented
 - to add flow
diff --git a/web/ons-demo/css/skin.default.css b/web/ons-demo/css/skin.default.css
index d88814e..1ce4897 100644
--- a/web/ons-demo/css/skin.default.css
+++ b/web/ons-demo/css/skin.default.css
@@ -366,6 +366,6 @@
 	-webkit-animation-duration: .5s;
 	-webkit-animation-direction: alternate;
 	-webkit-animation-timing-function: ease-in-out;
-	-webkit-animation-iteration-count: 24;
+	-webkit-animation-iteration-count: 70;
 }
 
diff --git a/web/ons-demo/js/app.js b/web/ons-demo/js/app.js
index e342213..695e4bb 100644
--- a/web/ons-demo/js/app.js
+++ b/web/ons-demo/js/app.js
@@ -20,7 +20,7 @@
 var pendingLinks = {};
 var selectedFlows = [];
 
-var pendingTimeout = 10000;
+var pendingTimeout = 30000;
 
 var colors = [
 	'color1',
@@ -70,7 +70,14 @@
 
 function updateSelectedFlowsTopology() {
 	// DRAW THE FLOWS
-	var flows = d3.select('svg').selectAll('.flow').data(selectedFlows);
+	var topologyFlows = [];
+	selectedFlows.forEach(function (flow) {
+		if (flow) {
+			topologyFlows.push(flow);
+		}
+	});
+
+	var flows = d3.select('svg').selectAll('.flow').data(topologyFlows);
 
 	flows.enter().append("svg:path").attr('class', 'flow')
 		.attr('stroke-dasharray', '4, 10')
@@ -82,13 +89,14 @@
 		.attr('dur', '20s')
 		.attr('repeatCount', 'indefinite');
 
+	flows.exit().remove();
 
 	flows.attr('d', function (d) {
 			if (!d) {
 				return;
 			}
 			var pts = [];
-			if (!d.dataPath.flowEntries) {
+			if (!d.dataPath.flowEntries || !d.dataPath.flowEntries.length) {
 				// 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));
@@ -162,6 +170,10 @@
 	function rowUpdate(d) {
 		var row = d3.select(this);
 		row.select('.deleteFlow').on('click', function () {
+			selectedFlows[selectedFlows.indexOf(d)] = null;
+			updateSelectedFlows();
+		});
+		row.on('dblclick', function () {
 			if (d) {
 				var prompt = 'Delete flow ' + d.flowId.value + '?';
 				if (confirm(prompt)) {
@@ -187,7 +199,9 @@
 					}
 				}
 			})
-			.classed('pending', d && (d.deletePending || d.createPending));
+			.classed('pending', function (d) {
+				return d && (d.createPending || d.deletePending);
+			});
 
 		row.select('.srcDPID')
 			.text(function (d) {
@@ -238,8 +252,6 @@
 				} else if (flow.createPending) {
 					newSelectedFlows.push(flow);
 				}
-			} else {
-				newSelectedFlows.push(null);
 			}
 		});
 		selectedFlows = newSelectedFlows;
@@ -1059,21 +1071,23 @@
 	updateModel(function (newModel) {
 //		console.log('Update time: ' + (Date.now() - d)/1000 + 's');
 
-		var modelChanged = false;
-		if (!model || JSON.stringify(model) != JSON.stringify(newModel)) {
-			modelChanged = true;
-			model = newModel;
-		} else {
-//			console.log('no change');
-		}
+		if (newModel) {
+			var modelChanged = false;
+			if (!model || JSON.stringify(model) != JSON.stringify(newModel)) {
+				modelChanged = true;
+				model = newModel;
+			} else {
+	//			console.log('no change');
+			}
 
-		if (modelChanged) {
-			updateControllers();
-			updateSelectedFlows();
-			updateTopology();
-		}
+			if (modelChanged) {
+				updateControllers();
+				updateSelectedFlows();
+				updateTopology();
+			}
 
-		updateHeader(newModel);
+			updateHeader(newModel);
+		}
 
 		// do it again in 1s
 		setTimeout(function () {
diff --git a/web/ons-demo/js/model.js b/web/ons-demo/js/model.js
index 29859a6..5026bd4 100644
--- a/web/ons-demo/js/model.js
+++ b/web/ons-demo/js/model.js
@@ -126,7 +126,8 @@
 			var model = toD3(results);
 			cb(model);
 		} else {
-			alert(JSON.stringify(err));
+			console.log(JSON.stringify(err));
+			cb(null);
 		}
 	});
 }