Implement non-stop monitoring of the installed flow.
If the shortest path changes, the old flow is deleted,
and a new one is installed.

Currently, this is implemented by computing the shortest path
periodically (once a second).

The (new) monitoring feature can be enabled by using the "-m" argument.
Example:

./add_flow.py -m 1 FOOBAR 00:00:00:00:00:00:01:01 1 00:00:00:00:00:00:01:31 1 matchDstMac 01:02:03:04:05:06
diff --git a/web/add_flow.py b/web/add_flow.py
index 7adaf98..28819c9 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)