Merge branch 'experimental' - Improved stability and thread safety of ZookeeperRegistry
diff --git a/scripts/localssh.sh b/scripts/localssh.sh
new file mode 100755
index 0000000..666330f
--- /dev/null
+++ b/scripts/localssh.sh
@@ -0,0 +1,2 @@
+#! /bin/sh
+ssh -i ~/.ssh/onlabkey.pem 1.1.$1.1
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index c70ab16..2c19f68 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -1,9 +1,5 @@
 package net.floodlightcontroller.core;
 
-import java.util.List;
-
-import net.floodlightcontroller.devicemanager.SwitchPort;
-
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
 
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index d4a44fc..4057bc9 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -1560,9 +1560,14 @@
         // this method is only called after netty has processed all
         // pending messages
         log.debug("removeSwitch: {}", sw);
-        if (registryService.hasControl(sw.getId())) {
-        	swStore.update(sw.getStringId(), SwitchState.INACTIVE, DM_OPERATION.UPDATE);
-        }
+ //
+ //     Cannot set sw to inactive in network map due to race condition
+ //     Need a cleanup thread to periodically check switches not active in registry
+ //     and acquire control to set to inactive state in network map and release it       
+ //
+ //       if (registryService.hasControl(sw.getId())) {
+ //      	swStore.update(sw.getStringId(), SwitchState.INACTIVE, DM_OPERATION.UPDATE);
+ //       }
         if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
             log.debug("Not removing switch {}; already removed", sw);
             return;
diff --git a/start-onos.sh b/start-onos.sh
index 012a994..166ce74 100755
--- a/start-onos.sh
+++ b/start-onos.sh
@@ -80,7 +80,7 @@
   echo 
   java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -jar ${FL_JAR} -cf ${FL_HOME}/onos.properties > /dev/null 2>&1 &
 #  echo "java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -jar ${FL_JAR} -cf ./onos.properties > /dev/null 2>&1 &"
-  sudo -b /usr/sbin/tcpdump -n -i eth0 -s0 -w ${PCAP_LOG} 'tcp port 6633' > /dev/null  2>&1
+#  sudo -b /usr/sbin/tcpdump -n -i eth0 -s0 -w ${PCAP_LOG} 'tcp port 6633' > /dev/null  2>&1
 }
 
 function stop {
diff --git a/web/add_flow.py b/web/add_flow.py
index 7adaf98..9b5ab42 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -18,8 +18,9 @@
 #
 
 ## Global Var ##
-ControllerIP="127.0.0.1"
-ControllerPort=8080
+ControllerIP = "127.0.0.1"
+ControllerPort = 8080
+MonitoringEnabled = False
 
 DEBUG=0
 pp = pprint.PrettyPrinter(indent=4)
@@ -68,13 +69,15 @@
     inPort = f['inPort']['value'];
     outPort = f['outPort']['value'];
     dpid = f['dpid']['value']
-    print "FlowEntry: (%s, %s, %s)" % (inPort, dpid, outPort)
+    print "  FlowEntry: (%s, %s, %s)" % (inPort, dpid, outPort)
 
   return parsedResult
 
 def add_flow_path(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/json" % (flow_path, ControllerIP, ControllerPort)
+    command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/add/json" % (flow_path_json, ControllerIP, ControllerPort)
     debug("add_flow_path %s" % command)
     result = os.popen(command).read()
     debug("result %s" % result)
@@ -84,91 +87,47 @@
     log_error("Controller IF has issue")
     exit(1)
 
-if __name__ == "__main__":
-  usage_msg = "Usage: %s <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port> [Match Conditions] [Actions]\n" % (sys.argv[0])
-  usage_msg = usage_msg + "    Match Conditions:\n"
-  usage_msg = usage_msg + "        matchInPort <True|False> (default to True)\n"
-  usage_msg = usage_msg + "        matchSrcMac <source MAC address>\n"
-  usage_msg = usage_msg + "        matchDstMac <destination MAC address>\n"
-  usage_msg = usage_msg + "        matchSrcIPv4Net <source IPv4 network address>\n"
-  usage_msg = usage_msg + "        matchDstIPv4Net <destination IPv4 network address>\n"
-  usage_msg = usage_msg + "        matchEthernetFrameType <Ethernet frame type>\n"
+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)
+  result = os.popen(command).read()
+  debug("result %s" % result)
+  # parsedResult = json.loads(result)
+  # debug("parsed %s" % parsedResult)
 
-  usage_msg = usage_msg + "    Match Conditions (not implemented yet):\n"
-  usage_msg = usage_msg + "        matchVlanId <VLAN ID>\n"
-  usage_msg = usage_msg + "        matchVlanPriority <VLAN priority>\n"
-  usage_msg = usage_msg + "        matchIpToS <IP ToS (DSCP field, 6 bits)>\n"
-  usage_msg = usage_msg + "        matchIpProto <IP protocol>\n"
-  usage_msg = usage_msg + "        matchSrcTcpUdpPort <source TCP/UDP port>\n"
-  usage_msg = usage_msg + "        matchDstTcpUdpPort <destination TCP/UDP port>\n"
-  usage_msg = usage_msg + "    Actions:\n"
-  usage_msg = usage_msg + "        actionOutput <True|False> (default to True)\n"
-  usage_msg = usage_msg + "        actionSetEthernetSrcAddr <source MAC address>\n"
-  usage_msg = usage_msg + "        actionSetEthernetDstAddr <destination MAC address>\n"
-  usage_msg = usage_msg + "        actionSetIPv4SrcAddr <source IPv4 address>\n"
-  usage_msg = usage_msg + "        actionSetIPv4DstAddr <destination IPv4 address>\n"
-  usage_msg = usage_msg + "    Actions (not implemented yet):\n"
-  usage_msg = usage_msg + "        actionSetVlanId <VLAN ID>\n"
-  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
-  usage_msg = usage_msg + "        actionSetIpToS <IP ToS (DSCP field, 6 bits)>\n"
-  usage_msg = usage_msg + "        actionSetTcpUdpSrcPort <source TCP/UDP port>\n"
-  usage_msg = usage_msg + "        actionSetTcpUdpDstPort <destination TCP/UDP port>\n"
-  usage_msg = usage_msg + "        actionStripVlan <True|False>\n"
-  usage_msg = usage_msg + "        actionEnqueue <dummy argument>\n"
-
-  # app.debug = False;
-
-  # Usage info
-  if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
-    print(usage_msg)
-    exit(0)
-
-  # Check arguments
-  if len(sys.argv) < 7:
+def extract_flow_args(my_args):
+  # Check the arguments
+  if len(my_args) < 6:
     log_error(usage_msg)
     exit(1)
 
   # Extract the mandatory arguments
-  my_flow_id = sys.argv[1]
-  my_installer_id = sys.argv[2]
-  my_src_dpid = sys.argv[3]
-  my_src_port = sys.argv[4]
-  my_dst_dpid = sys.argv[5]
-  my_dst_port = sys.argv[6]
-
-  # Compute the shortest path
-  data_path = shortest_path(my_src_dpid, my_src_port, my_dst_dpid, my_dst_port)
-
-  debug("Data Path: %s" % data_path)
-
-  flow_id = {}
-  flow_id['value'] = my_flow_id
-  installer_id = {}
-  installer_id['value'] = my_installer_id
-
-  flow_path = {}
-  flow_path['flowId'] = flow_id
-  flow_path['installerId'] = installer_id
+  my_flow_id = my_args[0]
+  my_installer_id = my_args[1]
+  my_src_dpid = my_args[2]
+  my_src_port = my_args[3]
+  my_dst_dpid = my_args[4]
+  my_dst_port = my_args[5]
 
   #
   # Extract the "match" and "action" arguments
   #
-  idx = 7
   match = {}
   matchInPortEnabled = True		# NOTE: Enabled by default
   actions = []
   actionOutputEnabled = True		# NOTE: Enabled by default
-  while idx < len(sys.argv):
+  idx = 6
+  while idx < len(my_args):
     action = {}
-    arg1 = sys.argv[idx]
+    arg1 = my_args[idx]
     idx = idx + 1
     # Extract the second argument
-    if idx >= len(sys.argv):
+    if idx >= len(my_args):
       error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
       log_error(error_arg)
       log_error(usage_msg)
       exit(1)
-    arg2 = sys.argv[idx]
+    arg2 = my_args[idx]
     idx = idx + 1
 
     if arg1 == "matchInPort":
@@ -317,18 +276,62 @@
       log_error(usage_msg)
       exit(1)
 
+  return {
+    'my_flow_id' : my_flow_id,
+    'my_installer_id' : my_installer_id,
+    'my_src_dpid' : my_src_dpid,
+    'my_src_port' : my_src_port,
+    'my_dst_dpid' : my_dst_dpid,
+    'my_dst_port' : my_dst_port,
+    'match' : match,
+    'matchInPortEnabled' : matchInPortEnabled,
+    'actions' : actions,
+    'actionOutputEnabled' : actionOutputEnabled
+    }
+
+def compute_data_path(parsed_args):
+
+  my_src_dpid = parsed_args['my_src_dpid']
+  my_src_port = parsed_args['my_src_port']
+  my_dst_dpid = parsed_args['my_dst_dpid']
+  my_dst_port = parsed_args['my_dst_port']
+
+  # Compute the shortest path
+  data_path = shortest_path(my_src_dpid, my_src_port, my_dst_dpid, my_dst_port)
+
+  debug("Data Path: %s" % data_path)
+  return data_path
+
+def compute_flow_path(parsed_args, data_path):
+
+  my_flow_id = parsed_args['my_flow_id']
+  my_installer_id = parsed_args['my_installer_id']
+  match = parsed_args['match']
+  matchInPortEnabled = parsed_args['matchInPortEnabled']
+  actions = parsed_args['actions']
+  actionOutputEnabled = parsed_args['actionOutputEnabled']
+  my_data_path = copy.deepcopy(data_path)
+
+  flow_id = {}
+  flow_id['value'] = my_flow_id
+  installer_id = {}
+  installer_id['value'] = my_installer_id
+
+  flow_path = {}
+  flow_path['flowId'] = flow_id
+  flow_path['installerId'] = installer_id
 
   #
   # Add the match conditions to each flow entry
   #
   if (len(match) > 0) or matchInPortEnabled:
     idx = 0
-    while idx < len(data_path['flowEntries']):
+    while idx < len(my_data_path['flowEntries']):
       if matchInPortEnabled:
-	inPort = data_path['flowEntries'][idx]['inPort']
+	inPort = my_data_path['flowEntries'][idx]['inPort']
 	match['inPort'] = copy.deepcopy(inPort)
 	# match['matchInPort'] = True
-      data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
+      my_data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
       idx = idx + 1
 
   #
@@ -341,11 +344,11 @@
   #
   if (len(actions) > 0) or actionOutputEnabled:
     idx = 0
-    while idx < len(data_path['flowEntries']):
+    while idx < len(my_data_path['flowEntries']):
       if idx > 0:
 	actions = []	# Reset the actions for all but first entry
       action = {}
-      outPort = data_path['flowEntries'][idx]['outPort']
+      outPort = my_data_path['flowEntries'][idx]['outPort']
       actionOutput = {}
       actionOutput['port'] = copy.deepcopy(outPort)
       # actionOutput['maxLen'] = 0	# TODO: not used for now
@@ -353,13 +356,85 @@
       # action['actionType'] = 'ACTION_OUTPUT'
       actions.append(copy.deepcopy(action))
 
-      data_path['flowEntries'][idx]['flowEntryActions'] = copy.deepcopy(actions)
+      my_data_path['flowEntries'][idx]['flowEntryActions'] = copy.deepcopy(actions)
       idx = idx + 1
 
 
-  flow_path['dataPath'] = data_path
+  flow_path['dataPath'] = my_data_path
+  debug("Flow Path: %s" % flow_path)
 
-  flow_path_json = json.dumps(flow_path)
-  debug("Flow Path: %s" % flow_path_json)
+  add_flow_path(flow_path)
 
-  add_flow_path(flow_path_json)
+
+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 + "    Flags:\n"
+  usage_msg = usage_msg + "        -m        Monitor and maintain the installed shortest path\n"
+  usage_msg = usage_msg + "    Match Conditions:\n"
+  usage_msg = usage_msg + "        matchInPort <True|False> (default to True)\n"
+  usage_msg = usage_msg + "        matchSrcMac <source MAC address>\n"
+  usage_msg = usage_msg + "        matchDstMac <destination MAC address>\n"
+  usage_msg = usage_msg + "        matchSrcIPv4Net <source IPv4 network address>\n"
+  usage_msg = usage_msg + "        matchDstIPv4Net <destination IPv4 network address>\n"
+  usage_msg = usage_msg + "        matchEthernetFrameType <Ethernet frame type>\n"
+
+  usage_msg = usage_msg + "    Match Conditions (not implemented yet):\n"
+  usage_msg = usage_msg + "        matchVlanId <VLAN ID>\n"
+  usage_msg = usage_msg + "        matchVlanPriority <VLAN priority>\n"
+  usage_msg = usage_msg + "        matchIpToS <IP ToS (DSCP field, 6 bits)>\n"
+  usage_msg = usage_msg + "        matchIpProto <IP protocol>\n"
+  usage_msg = usage_msg + "        matchSrcTcpUdpPort <source TCP/UDP port>\n"
+  usage_msg = usage_msg + "        matchDstTcpUdpPort <destination TCP/UDP port>\n"
+  usage_msg = usage_msg + "    Actions:\n"
+  usage_msg = usage_msg + "        actionOutput <True|False> (default to True)\n"
+  usage_msg = usage_msg + "        actionSetEthernetSrcAddr <source MAC address>\n"
+  usage_msg = usage_msg + "        actionSetEthernetDstAddr <destination MAC address>\n"
+  usage_msg = usage_msg + "        actionSetIPv4SrcAddr <source IPv4 address>\n"
+  usage_msg = usage_msg + "        actionSetIPv4DstAddr <destination IPv4 address>\n"
+  usage_msg = usage_msg + "    Actions (not implemented yet):\n"
+  usage_msg = usage_msg + "        actionSetVlanId <VLAN ID>\n"
+  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
+  usage_msg = usage_msg + "        actionSetIpToS <IP ToS (DSCP field, 6 bits)>\n"
+  usage_msg = usage_msg + "        actionSetTcpUdpSrcPort <source TCP/UDP port>\n"
+  usage_msg = usage_msg + "        actionSetTcpUdpDstPort <destination TCP/UDP port>\n"
+  usage_msg = usage_msg + "        actionStripVlan <True|False>\n"
+  usage_msg = usage_msg + "        actionEnqueue <dummy argument>\n"
+
+  # app.debug = False;
+
+  # Usage info
+  if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+    print(usage_msg)
+    exit(0)
+
+  #
+  # Check the flags
+  #
+  start_argv_index = 1
+  if len(sys.argv) > 1 and sys.argv[1] == "-m":
+    MonitoringEnabled = True
+    start_argv_index = start_argv_index + 1
+
+  #
+  # Parse the remaining arguments
+  #
+  my_args = sys.argv[start_argv_index:]
+  parsed_args = copy.deepcopy(extract_flow_args(my_args))
+
+  last_data_path = []
+  my_flow_id = parsed_args['my_flow_id']
+  # Cleanup leftover state
+  delete_flow_path(my_flow_id)
+
+  while True:
+    data_path = compute_data_path(parsed_args)
+    if data_path != last_data_path:
+      if len(last_data_path) > 0:
+	delete_flow_path(my_flow_id)
+      flow_path = compute_flow_path(parsed_args, data_path)
+      add_flow_path(flow_path)
+      last_data_path = data_path
+
+    if MonitoringEnabled != True:
+      break
+    time.sleep(1)