* Add new command-line option:
  -f <filename>   Read the flow(s) to install from a file
  File format: one line per flow starting with <flow-id>
  Thus, a single script can install and maintain multiple flows.

* NOTE: Change the behavior: now the computed shortest path is printed
  ONLY when there is a change, instead of periodic printing once
  every second.

* If there is a failure in computing the shortest path, print
  the path endpoints.
diff --git a/web/add_flow.py b/web/add_flow.py
index 56b305a..0440c39 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -21,6 +21,7 @@
 ControllerIP = "127.0.0.1"
 ControllerPort = 8080
 MonitoringEnabled = False
+ReadFromFile = ""
 
 DEBUG=0
 pp = pprint.PrettyPrinter(indent=4)
@@ -49,31 +50,33 @@
     result = os.popen(command).read()
     debug("result %s" % result)
     if len(result) == 0:
-	log_error("No Path found")
-	return parsedResult
-
-    parsedResult = json.loads(result)
-    debug("parsed %s" % parsedResult)
+      log_error("No Path found from %s/%s to %s/%s" % (v1, p1, v2, p2))
+    else:
+      parsedResult = json.loads(result)
+      debug("parsed %s" % parsedResult)
 
   except:
-    log_error("Controller IF has issue")
-    exit(1)
+    log_error("Controller IF has issue: No Path found from %s/%s to %s/%s" % (v1, p1, v2, p2))
 
-  srcSwitch = parsedResult['srcPort']['dpid']['value'];
-  srcPort = parsedResult['srcPort']['port']['value'];
-  dstSwitch = parsedResult['dstPort']['dpid']['value'];
-  dstPort = parsedResult['dstPort']['port']['value'];
+  return parsedResult
+
+def print_data_path(data_path):
+  if len(data_path) == 0:
+    return
+
+  srcSwitch = data_path['srcPort']['dpid']['value'];
+  srcPort = data_path['srcPort']['port']['value'];
+  dstSwitch = data_path['dstPort']['dpid']['value'];
+  dstPort = data_path['dstPort']['port']['value'];
 
   print "DataPath: (src = %s/%s dst = %s/%s)" % (srcSwitch, srcPort, dstSwitch, dstPort);
 
-  for f in parsedResult['flowEntries']:
+  for f in data_path['flowEntries']:
     inPort = f['inPort']['value'];
     outPort = f['outPort']['value'];
     dpid = f['dpid']['value']
     print "  FlowEntry: (%s, %s, %s)" % (inPort, dpid, outPort)
 
-  return parsedResult
-
 def add_flow_path(flow_path):
   flow_path_json = json.dumps(flow_path)
 
@@ -370,7 +373,10 @@
 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 + "        -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 + "\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"
@@ -378,7 +384,7 @@
   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 + "\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"
@@ -386,12 +392,14 @@
   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 + "\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 + "\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"
@@ -412,30 +420,78 @@
   # 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
+  idx = 1
+  while idx < len(sys.argv):
+    arg1 = sys.argv[idx]
+    idx = idx + 1
+    if arg1 == "-m":
+      MonitoringEnabled = True
+      start_argv_index = idx
+    elif arg1 == "-f":
+      if idx >= len(sys.argv):
+	error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
+	log_error(error_arg)
+	log_error(usage_msg)
+	exit(1)
+      ReadFromFile = sys.argv[idx]
+      idx = idx + 1
+      start_argv_index = idx
+    else:
+      break;
 
   #
-  # Parse the remaining arguments
+  # Read the arguments from a file or from the remaining command line options
   #
-  my_args = sys.argv[start_argv_index:]
-  parsed_args = copy.deepcopy(extract_flow_args(my_args))
+  my_lines = []
+  if len(ReadFromFile) > 0:
+    f = open(ReadFromFile, "rt")
+    my_line = f.readline()
+    while my_line:
+      if len(my_line.rstrip()) > 0 and my_line[0] != "#":
+	my_token_line = my_line.rstrip().split()
+	my_lines.append(my_token_line)
+      my_line = f.readline()
+  else:
+    my_lines.append(copy.deepcopy(sys.argv[start_argv_index:]))
 
-  last_data_path = []
-  my_flow_id = parsed_args['my_flow_id']
-  # Cleanup leftover state
-  delete_flow_path(my_flow_id)
+  #
+  # Initialization
+  #
+  last_data_paths = []
+  parsed_args = []
+  idx = 0
+  while idx < len(my_lines):
+    last_data_path = []
+    last_data_paths.append(copy.deepcopy(last_data_path))
+    #
+    # Parse the flow arguments
+    #
+    my_args = my_lines[idx]
+    parsed_args.append(copy.deepcopy(extract_flow_args(my_args)))
+    # Cleanup leftover state
+    my_flow_id = parsed_args[idx]['my_flow_id']
+    delete_flow_path(my_flow_id)
 
+    idx = idx + 1
+
+  #
+  # Do the work: install and/or periodically monitor each flow
+  #
   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)
-      if len(data_path) > 0:
-	flow_path = compute_flow_path(parsed_args, data_path)
-	add_flow_path(flow_path)
-      last_data_path = data_path
+    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