Merged from Ed Swierk test-day pkt-in/out
diff --git a/README b/README
index 5d1b5c5..ea5b7a4 100644
--- a/README
+++ b/README
@@ -1,5 +1,6 @@
 OpenFlow Testing Framework
 July, 2010
+Last updated March 2012
 
 Copyright (c) 2010 The Board of Trustees of The Leland Stanford 
 Junior University
@@ -30,7 +31,7 @@
 
     You can check out OFTest with git with the following command:
 
-    git clone git://openflow.org/oftest
+    git clone git://github.com/floodlight/oftest
 
 Introduction
 ++++++++++++
@@ -62,15 +63,15 @@
 
     Currently, switches must be running version 1.0 of OpenFlow. 
 
-      # git clone yuba:/usr/local/git/openflow-projects/oftest
+      # git clone git://github.com/floodlight/oftest
       # cd oftest/tools/munger
       # make install
       # cd ../../tests
          Make sure the switch you want to test is running --
          see (4) below for the reference switch example.
       # ./oft --list
-      # sudo ./oft
-      # sudo ./oft --verbose --log-file=""    
+      # sudo ./oft --test-spec=Echo
+      # sudo ./oft --verbose --log-file=""
       # sudo ./oft --test-spec=<mod> --platform=remote --host=...
 
 Longer Start
@@ -81,8 +82,8 @@
         * Root privilege on host running oft
         * Switch running OpenFlow 1.0 and attempting to connect 
           to a controller on the machine running oft.
-        * Python 2.5.  You can run platforms using eth interfaces
-          with Python 2.4.
+        * Python 2.5 or 2.6.  You can run platforms using eth interfaces
+          with Python 2.4.  Python 2.7 may work.
         * Python setup tools (e.g.: sudo apt-get install python-setuptools)
         * oftest checked out (called <oftest> here)
         * scapy installed:  http://www.secdev.org/projects/scapy/
@@ -139,6 +140,10 @@
         4F. To clean up the virtual ethernet interfaces, use
             sudo rmmod veth
 
+        New tools allow you to run an OVS instance as well.  See
+        oftest/tools/ovs-ctl.  You will need to install a version of
+        openvswitch.  See http://openvswitch.org/.
+
     5.  Run oft
         See Warning above; requires sudo to control the dataplane
         cd <oftest>/tests
diff --git a/tests/oft b/tests/oft
index bbbb0fa..c397372 100755
--- a/tests/oft
+++ b/tests/oft
@@ -246,7 +246,9 @@
     parser.add_option("--param", type="int",
                       help="Parameter sent to test (for debugging)")
     parser.add_option("-t", "--test-params",
-                      help="Set test parameters: key=val;... See --list")
+                      help="""Set test parameters: key=val;...
+        NOTE:  key MUST be a valid Python identifier, egr_count not egr-count
+        See --list""")
     # Might need this if other parsers want command line
     # parser.allow_interspersed_args = False
     (options, args) = parser.parse_args()
diff --git a/tests/pktact.py b/tests/pktact.py
index 5357b7c..9230afd 100644
--- a/tests/pktact.py
+++ b/tests/pktact.py
@@ -667,7 +667,12 @@
     """
     def runTest(self):
         for wc in WILDCARD_VALUES:
-            flow_match_test(self, pa_port_map, wildcards=wc, max_test=10)
+            if wc & ofp.OFPFW_DL_VLAN:
+                dl_vlan = 0
+            else:
+                dl_vlan = -1
+            flow_match_test(self, pa_port_map, wildcards=wc, 
+                            dl_vlan=dl_vlan, max_test=10)
 
 class SingleWildcardMatchTagged(BaseMatchCase):
     """
@@ -693,7 +698,12 @@
     def runTest(self):
         for wc in WILDCARD_VALUES:
             all_exp_one_wildcard = ofp.OFPFW_ALL ^ wc
-            flow_match_test(self, pa_port_map, wildcards=all_exp_one_wildcard)
+            if all_exp_one_wildcard & ofp.OFPFW_DL_VLAN:
+                dl_vlan = 0
+            else:
+                dl_vlan = -1
+            flow_match_test(self, pa_port_map, wildcards=all_exp_one_wildcard,
+                            dl_vlan=dl_vlan)
 
 class AllExceptOneWildcardMatchTagged(BaseMatchCase):
     """
@@ -943,7 +953,105 @@
         (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['ip_tos'],
                                                 check_test_params=True)
         flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
-                        action_list=acts, max_test=2)
+                        action_list=acts, max_test=2, egr_count=-1)
+
+class ModifyL2DstMC(BaseMatchCase):
+    """
+    Modify the L2 dest and send to 2 ports
+    """
+    def runTest(self):
+        sup_acts = supported_actions_get(self)
+        if not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST):
+            skip_message_emit(self, "ModifyL2dstMC test")
+            return
+
+        (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_dst'],
+                                                check_test_params=True)
+        flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
+                        action_list=acts, max_test=2, egr_count=-1)
+
+class ModifyL2DstIngress(BaseMatchCase):
+    """
+    Modify the L2 dest and send to the ingress port
+    """
+    def runTest(self):
+        sup_acts = supported_actions_get(self)
+        if not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST):
+            skip_message_emit(self, "ModifyL2dstMC test")
+            return
+
+        (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_dst'],
+                                                check_test_params=True)
+        flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
+                        action_list=acts, max_test=2, egr_count=0,
+                        ing_port=True)
+
+class ModifyL2DstIngressMC(BaseMatchCase):
+    """
+    Modify the L2 dest and send to the ingress port
+    """
+    def runTest(self):
+        sup_acts = supported_actions_get(self)
+        if not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST):
+            skip_message_emit(self, "ModifyL2dstMC test")
+            return
+
+        (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_dst'],
+                                                check_test_params=True)
+        flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
+                        action_list=acts, max_test=2, egr_count=-1,
+                        ing_port=True)
+
+class ModifyL2SrcMC(BaseMatchCase):
+    """
+    Modify the source MAC address (TP1) and send to multiple
+    """
+    def runTest(self):
+        sup_acts = supported_actions_get(self)
+        if not (sup_acts & 1 << ofp.OFPAT_SET_DL_SRC):
+            skip_message_emit(self, "ModifyL2SrcMC test")
+            return
+
+        (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_src'],
+                                                check_test_params=True)
+        flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
+                        action_list=acts, max_test=2, egr_count=-1)
+
+class ModifyL2SrcDstMC(BaseMatchCase):
+    """
+    Modify the L2 source and dest and send to 2 ports
+    """
+    def runTest(self):
+        sup_acts = supported_actions_get(self)
+        if (not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST) or
+                not (sup_acts & 1 << ofp.OFPAT_SET_DL_SRC)):
+            skip_message_emit(self, "ModifyL2SrcDstMC test")
+            return
+
+        mod_fields = ['dl_dst', 'dl_src']
+        (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=mod_fields,
+                                                check_test_params=True)
+        flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
+                        action_list=acts, max_test=2, egr_count=-1)
+
+class ModifyL2DstVIDMC(BaseMatchCase):
+    """
+    Modify the L2 dest and send to 2 ports
+    """
+    def runTest(self):
+        sup_acts = supported_actions_get(self)
+        if (not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST) or
+                not (sup_acts & 1 << ofp.OFPAT_SET_VLAN_VID)):
+            skip_message_emit(self, "ModifyL2DstVIDMC test")
+            return
+
+        mod_fields = ['dl_dst', 'dl_vlan']
+        (pkt, exp_pkt, acts) = pkt_action_setup(self, 
+             start_field_vals={'dl_vlan_enable':True}, mod_fields=mod_fields,
+                                                check_test_params=True)
+        flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
+                        action_list=acts, max_test=2, egr_count=-1)
+
 
 #@todo Need to implement tagged versions of the above tests
 #
diff --git a/tests/testutils.py b/tests/testutils.py
index c6252bc..5a99a00 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -256,9 +256,10 @@
                              "Unexpected pkt on port " + str(ofport))
 
 
-def receive_pkt_verify(parent, egr_port, exp_pkt):
+def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
     """
     Receive a packet and verify it matches an expected value
+    @param egr_port A single port or list of ports
 
     parent must implement dataplane, assertTrue and assertEqual
     """
@@ -266,27 +267,37 @@
     if parent.config["relax"]:
         exp_pkt_arg = exp_pkt
 
-    (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(port_number=egr_port,
-                                                          timeout=1, 
-                                                          exp_pkt=exp_pkt_arg)
+    if type(egr_ports) == type([]):
+        egr_port_list = egr_ports
+    else:
+        egr_port_list = [egr_ports]
 
-    if rcv_pkt is None:
-        parent.logger.error("ERROR: No packet received from " + str(egr_port))
+    # Expect a packet from each port on egr port list
+    for egr_port in egr_port_list:
+        check_port = egr_port
+        if egr_port == ofp.OFPP_IN_PORT:
+            check_port = ing_port
+        (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
+            port_number=check_port, timeout=1, exp_pkt=exp_pkt_arg)
 
-    parent.assertTrue(rcv_pkt is not None,
-                      "Did not receive packet port " + str(egr_port))
-    parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " + 
-                    str(rcv_port))
+        if rcv_pkt is None:
+            parent.logger.error("ERROR: No packet received from " + 
+                                str(check_port))
 
-    if str(exp_pkt) != str(rcv_pkt):
-        parent.logger.error("ERROR: Packet match failed.")
-        parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
-                        + str(exp_pkt).encode('hex'))
-        parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
-                        + str(rcv_pkt).encode('hex'))
-    parent.assertEqual(str(exp_pkt), str(rcv_pkt),
-                       "Packet match error on port " + str(egr_port))
-    
+        parent.assertTrue(rcv_pkt is not None,
+                          "Did not receive packet port " + str(check_port))
+        parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " + 
+                            str(rcv_port))
+
+        if str(exp_pkt) != str(rcv_pkt):
+            parent.logger.error("ERROR: Packet match failed.")
+            parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
+                                + str(exp_pkt).encode('hex'))
+            parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
+                                + str(rcv_pkt).encode('hex'))
+        parent.assertEqual(str(exp_pkt), str(rcv_pkt),
+                           "Packet match error on port " + str(check_port))
+
 def match_verify(parent, req_match, res_match):
     """
     Verify flow matches agree; if they disagree, report where
@@ -387,7 +398,7 @@
                                str(byte_count))
 
 def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=0,
-               egr_port=None, egr_queue=None, check_expire=False, in_band=True):
+               egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
     """
     Create a flow message
 
@@ -395,6 +406,7 @@
     See flow_match_test for other parameter descriptoins
     @param egr_queue if not None, make the output an enqueue action
     @param in_band if True, do not wildcard ingress port
+    @param egr_ports None (drop), single port or list of ports
     """
     match = parse.packet_to_flow_match(pkt)
     parent.assertTrue(match is not None, "Flow match from pkt failed")
@@ -403,6 +415,11 @@
     match.wildcards = wildcards
     match.in_port = ing_port
 
+    if type(egr_ports) == type([]):
+        egr_port_list = egr_ports
+    else:
+        egr_port_list = [egr_ports]
+
     request = message.flow_mod()
     request.match = match
     request.buffer_id = 0xffffffff
@@ -418,18 +435,21 @@
 
     # Set up output/enqueue action if directed
     if egr_queue is not None:
-        parent.assertTrue(egr_port is not None, "Egress port not set")
+        parent.assertTrue(egr_ports is not None, "Egress port not set")
         act = action.action_enqueue()
-        act.port = egr_port
-        act.queue_id = egr_queue
-        rv = request.actions.add(act)
-        parent.assertTrue(rv, "Could not add enqueue action " + 
-                          str(egr_port) + " Q: " + str(egr_queue))
-    elif egr_port is not None:
-        act = action.action_output()
-        act.port = egr_port
-        rv = request.actions.add(act)
-        parent.assertTrue(rv, "Could not add output action " + str(egr_port))
+        for egr_port in egr_port_list:
+            act.port = egr_port
+            act.queue_id = egr_queue
+            rv = request.actions.add(act)
+            parent.assertTrue(rv, "Could not add enqueue action " + 
+                              str(egr_port) + " Q: " + str(egr_queue))
+    elif egr_ports is not None:
+        for egr_port in egr_port_list:
+            act = action.action_output()
+            act.port = egr_port
+            rv = request.actions.add(act)
+            parent.assertTrue(rv, "Could not add output action " + 
+                              str(egr_port))
 
     parent.logger.debug(request.show())
 
@@ -454,42 +474,69 @@
     parent.assertTrue(rv != -1, "Error installing flow mod")
     do_barrier(parent.controller)
 
-def flow_match_test_port_pair(parent, ing_port, egr_port, wildcards=0, 
+def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=0, 
                               dl_vlan=-1, pkt=None, exp_pkt=None,
                               action_list=None, check_expire=False):
     """
     Flow match test on single TCP packet
+    @param egr_ports A single port or list of ports
 
     Run test with packet through switch from ing_port to egr_port
     See flow_match_test for parameter descriptions
     """
 
-    parent.logger.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
+    parent.logger.info("Pkt match test: " + str(ing_port) + " to " + 
+                       str(egr_ports))
     parent.logger.debug("  WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
                     " expire: " + str(check_expire))
     if pkt is None:
         pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
 
     request = flow_msg_create(parent, pkt, ing_port=ing_port, 
-                              wildcards=wildcards, egr_port=egr_port,
+                              wildcards=wildcards, egr_ports=egr_ports,
                               action_list=action_list)
 
     flow_msg_install(parent, request)
 
-    parent.logger.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
+    parent.logger.debug("Send packet: " + str(ing_port) + " to " + 
+                        str(egr_ports))
     parent.dataplane.send(ing_port, str(pkt))
 
     if exp_pkt is None:
         exp_pkt = pkt
-    receive_pkt_verify(parent, egr_port, exp_pkt)
+    receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
 
     if check_expire:
         #@todo Not all HW supports both pkt and byte counters
         flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
 
+def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
+    """
+    Generate a list of ports avoiding those in the exclude list
+    @param parent Supplies logger
+    @param of_ports List of OF port numbers
+    @param how_many Number of ports to be added to the list
+    @param exclude_list List of ports not to be used
+    @returns An empty list if unable to find enough ports
+    """
+
+    if how_many == 0:
+        return []
+
+    count = 0
+    egr_ports = []
+    for egr_idx in range(len(of_ports)): 
+        if of_ports[egr_idx] not in exclude_list:
+            egr_ports.append(of_ports[egr_idx])
+            count += 1
+            if count >= how_many:
+                return egr_ports
+    parent.logger.debug("Could not generate enough egress ports for test")
+    return []
+    
 def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None, 
                     exp_pkt=None, action_list=None, check_expire=False, 
-                    max_test=0):
+                    max_test=0, egr_count=1, ing_port=False):
     """
     Run flow_match_test_port_pair on all port pairs
 
@@ -502,27 +549,34 @@
     @param exp_pkt If not None, use this as the expected output pkt; els use pkt
     @param action_list Additional actions to add to flow mod
     @param check_expire Check for flow expiration message
+    @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
     """
     of_ports = port_map.keys()
     of_ports.sort()
     parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
     test_count = 0
 
+    if egr_count == -1:
+        egr_count = test_param_get(parent.config, 'egr_count', default=2)
+    
     for ing_idx in range(len(of_ports)):
         ingress_port = of_ports[ing_idx]
-        for egr_idx in range(len(of_ports)):
-            if egr_idx == ing_idx:
-                continue
-            egress_port = of_ports[egr_idx]
-            flow_match_test_port_pair(parent, ingress_port, egress_port, 
-                                      wildcards=wildcards, dl_vlan=dl_vlan, 
-                                      pkt=pkt, exp_pkt=exp_pkt,
-                                      action_list=action_list,
-                                      check_expire=check_expire)
-            test_count += 1
-            if (max_test > 0) and (test_count > max_test):
-                parent.logger.info("Ran " + str(test_count) + " tests; exiting")
-                return
+        egr_ports = get_egr_list(parent, of_ports, egr_count, 
+                                 exclude_list=[ingress_port])
+        if ing_port:
+            egr_ports.append(ofp.OFPP_IN_PORT)
+        if len(egr_ports) == 0:
+            parent.assertTrue(0, "Failed to generate egress port list")
+
+        flow_match_test_port_pair(parent, ingress_port, egr_ports, 
+                                  wildcards=wildcards, dl_vlan=dl_vlan, 
+                                  pkt=pkt, exp_pkt=exp_pkt,
+                                  action_list=action_list,
+                                  check_expire=check_expire)
+        test_count += 1
+        if (max_test > 0) and (test_count > max_test):
+            parent.logger.info("Ran " + str(test_count) + " tests; exiting")
+            return
 
 def test_param_get(config, key, default=None):
     """
@@ -535,6 +589,9 @@
     If the pair 'key=val' appeared in the string passed to --test-params
     on the command line, return val (as interpreted by exec).  Otherwise
     return default value.
+
+    WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS; 
+    eg egr_count, not egr-count.
     """
     try:
         exec config["test_params"]
diff --git a/tools/ovs-ctl/ovs-ctl-default.example.conf b/tools/ovs-ctl/ovs-ctl-default.example.conf
new file mode 100644
index 0000000..2996dce
--- /dev/null
+++ b/tools/ovs-ctl/ovs-ctl-default.example.conf
@@ -0,0 +1,96 @@
+###############################################################################
+#
+# This is the example/default ovs-ctl config file. 
+#
+# ovs-ctl config files will be searched and read, by default, int he following
+# order:
+#
+# /opt/ovs/ovs-ctl-default.conf
+# ~/.ovs-ctl
+#
+# This behavior can be modified and change specifically on the command line:
+#
+# Change the location of the default config file:
+# > ovs-ctl --default-config-file=/path/to/file
+#
+# Disable the default config file:
+# > ovs-ctl --no-default 
+#
+# Read these specific config files:
+# > ovs-ctl --config-file file1 file2 file3
+#
+# Note: The default config file reading behavior is equivalent to:
+#
+# > ovs-ctl --default /opt/ovs/ovs-ctl-default.conf --config-file ~/.ovs-ctl
+# 
+# This is also equivalent to:
+# > ovs-ctl --no-default --config-file /opt/ovs/ovs-ctl-default.conf ~/.ovs-ctl
+#
+#
+###############################################################################
+
+#
+# Default options can be specified here
+#
+[Defaults]
+# Default all configurations to 1.4.0
+config:1.4.0
+
+#
+# Specific OVS configurations go here
+#
+[1.4.0]
+# ovs-1.4.0, configured and built into /opt/ovs/1.4.0
+# Can be selected with 'ovs-ctl.py --config 1.4.0'
+ovs_src_dir:/opt/ovs/src/openvswitch-1.4.0
+ovs_base_dir:/opt/ovs/1.4.0
+ovs_runtime_dir:/var/run/ovs/1.4.0
+
+
+[1.3.0]
+# ovs-1.3.0, configured and built into /opt/ovs/1.3.0
+# Can be selected with 'ovsctl.py --config 1.3.0'
+ovs_src_dir:/opt/ovs/src/openvswitch-1.3.0
+ovs_base_dir:/opt/ovs/1.3.0
+ovs_runtime_dir:/var/run/ovs/1.3.0
+
+[1.2.2]
+# ovs-1.2.2, configured and built into /opt/ovs/1.2.2
+# Can be selected with 'ovsctl.py --config 1.2.2'
+ovs_src_dir:/opt/ovs/src/openvswitch-1.2.2
+ovs_base_dir:/opt/ovs/1.2.2
+ovs_runtime_dir:/var/run/ovs/1.2.2
+
+#
+# In the above configurations, the locations of the tools
+# are derived by ovs-ctl relative to 'ovs_src_dir' and 'ovs_base_dir'
+#
+# Individual tools and locations can be specified directly in 
+# the configuration (as well as the command line):
+#
+# Try:
+# 'ovs-ctl.py --no-default --config-file ovs-ctl-default.example.conf --config MyExample'
+#
+# In general, every command line option can be specified in a config file section. 
+#
+# The precedence of options is:
+#     1. Command Line Options
+#     2. [The Config You Specified]
+#     3. [The Defaults sections]
+#
+
+[MyExampleConfig]
+ovs_vswitchd_schema:/path/to/vswitch/schema/file
+ovs_vswitchdL:/path/to/vswitchd
+ovs_vsctl:/path/to/vsctl
+ovs_ofctl:/path/to/ofctl
+ovsdb_tool:/path/to/ovsdb_tool
+ovsd_db_file:/path/to/ovs_db_file
+ovs_db_sock:/path/to/ovs_db_sock
+ovs_kmod:/path/to/ovs_kmod
+
+
+
+
+
+
diff --git a/tools/ovs-ctl/ovs-ctl.py b/tools/ovs-ctl/ovs-ctl.py
new file mode 100755
index 0000000..5df57d9
--- /dev/null
+++ b/tools/ovs-ctl/ovs-ctl.py
@@ -0,0 +1,465 @@
+#!/usr/bin/python
+
+import os
+import subprocess
+import argparse
+import sys
+import signal
+import time
+import ConfigParser
+import pprint
+
+###############################################################################
+#
+# Arguments
+#
+# Arguments can be specified directly, or via config file. 
+# TODO -- Make this a separate reusable class
+#
+################################################################################
+gBasename = "ovs-ctl"
+
+gConfigParser = argparse.ArgumentParser(description=gBasename, add_help=False)
+
+gConfigParser.add_argument('-cf', '--config-file', 
+                     help="Load settings from the given config file", 
+                     nargs='+', metavar="FILE", 
+                     default=os.path.expanduser("~/."+gBasename))
+
+gConfigParser.add_argument('-c', '--config', 
+                     help="Use the specified configuration section", 
+                     default=None)
+
+gConfigParser.add_argument('-d', '--default-config-file', 
+                     help="Location of the local default config file", 
+                     metavar="FILE", 
+                     default="/opt/ovs/%s-default.conf" % (gBasename))
+
+gConfigParser.add_argument('-nd', '--no-default', 
+                     help="Do not load the default config file", 
+                     action='store_true', default=False)
+gConfigParser.add_argument('--dump-config',
+                     help="Dump the loaded configuration settings", 
+                     action='store_true', default=False)
+
+#
+# Parse the config files first, then parse remaining command line arguments
+#
+gConfig = ConfigParser.SafeConfigParser()
+configArgs, remainingArgs = gConfigParser.parse_known_args()
+
+if not configArgs.no_default:
+    gConfig.read([configArgs.default_config_file])
+
+if isinstance(configArgs.config_file, str):
+    configFiles = [ configArgs.config_file]
+else:
+    configFiles = configArgs.config_file
+
+configFiles = [ os.path.expanduser(x) if x.startswith('~') else x 
+                for x in configFiles ]
+
+gConfig.read(configFiles)
+
+# Dump loaded config if requested
+if configArgs.dump_config:
+    for section in gConfig.sections():
+        print section
+        for option in gConfig.options(section):
+            print " ", option, "=", gConfig.get(section, option)
+    sys.exit()
+
+
+
+#
+# Functional arguments go here
+#
+
+#
+# OVS target binaries -- these can all be specified individually. 
+# If not specified, they are determined by the base directory settings
+#
+gParser = argparse.ArgumentParser(parents=[gConfigParser])
+
+gParser.add_argument('--ovs-vswitchd-schema', 
+                     help="""Path to the vswitchd.ovsschema file""")
+gParser.add_argument('--ovs-vswitchd-log', 
+                     help="""Path to the vswitchd log file""")
+gParser.add_argument('--ovs-vswitchd', 
+                     help="""Path to the target ovs-vswitchd binary""")
+gParser.add_argument('--ovs-vsctl', 
+                     help="""Path to the target ovs-vsctl binary""")
+gParser.add_argument('--ovs-ofctl', 
+                     help="""Path to the target ovs-ofctl binary""")
+gParser.add_argument('--ovsdb-tool', 
+                     help="""Path to the target ovsdb-tool binary""")
+gParser.add_argument('--ovsdb-server', 
+                     help="""Path to the target ovsdb-server binary""")
+gParser.add_argument('--ovs-kmod', 
+                     help="""Path to the OVS kernel module""")
+gParser.add_argument('--ovs-src-dir',
+                     help="""Directory for the OVS Source Files""")
+
+gParser.add_argument('--ovs-log-dir',
+                     help="""Directory for the OVS Runtime Log Files""")
+
+gParser.add_argument('--ovs-version')
+
+gParser.add_argument("--ovs-base-dir", help="OVS Base Installation Directory")
+
+gParser.add_argument("--ovs-runtime-dir", 
+                     help="OVS Runtime Directory", 
+                     default="/var/run/ovs")
+
+gParser.add_argument('--ovs-db-sock', 
+                     help="Domain Socket Location")
+                     
+gParser.add_argument('--ovs-db-file', 
+                     help="Location for the OVS database file")
+
+
+#
+# Logging/Debugging/Testing options
+#
+gParser.add_argument('--dry', 
+                     help="""Dry run only. Don't actually do anything""", 
+                     action='store_true',  default=False)
+
+gParser.add_argument('--log', 
+                     help='Enable logging', 
+                     action='store_true', default=False)
+
+gParser.add_argument('--verbose', 
+                     help='Enable verbose output information', 
+                     action='store_true', default=False)
+
+gParser.add_argument("--kill", help="Kill running OVS", 
+                     default=False, action='store_true')
+
+gParser.add_argument("--keep-veths", 
+                     help="""By default, all existing veths will be destroyed and
+the veth module removed before initializing. If you don't want the veth module
+removed, specify this argument. Your mileage may vary if you use this.""", 
+                     default=False, action='store_true')
+
+gParser.add_argument("--no-kmod", 
+                     help="""Do not attempt to insert or remove the OVS kernel module. 
+Your mileage may vary.""", 
+                     default=False, action='store_true')
+
+gParser.add_argument("--vlog", 
+                     help="""Tail the running vswitch.log file in a new xterm""", 
+                     default=False, action='store_true')
+
+#
+# Runtime and setup arguments
+#
+gParser.add_argument('-p', '--port-count', type=int, 
+                     help="Number of veth ports to connect.", 
+                     default='4')
+
+
+gParser.add_argument("--bridge", help="Name of OF OVS Bridge", 
+                     default="ofbr0")
+
+gParser.add_argument("--cip", help="Controller Connection", 
+                     default="127.0.0.1")
+
+gParser.add_argument("--cport", type=int, help="Controller Port", 
+                     default=6633)
+
+gParser.add_argument("--max_backoff", help="VSwitchD max backoff value", 
+                     default=1000, type=int)
+
+
+gParser.add_argument("--keep-db", 
+                     help="""By default, a new database is initialized at each
+execution. If you want to keep and use the old database (if it exists), 
+use this option""", 
+                     action='store_true', default=False)
+
+
+
+#
+# Reset defaults based on config files and override
+#
+# Anything in the "Defaults" section gets slurped first:
+defaults = {}
+if gConfig.has_section('Defaults'):
+    defaults = dict(gConfig.items('Defaults'))
+    gParser.set_defaults(**defaults)
+
+#
+# The configuration to execute might be specified in on the command line, or in the Defaults section(s)
+# of the config files. 
+#
+# If its specified on the command line, it will be present in configArgs.config
+# If its specified in the section, it will only be present in the defaults dict. 
+# Need to check both. Command line takes precedence. 
+#
+gConfigSection = None
+if configArgs.config != None:
+    gConfigSection = configArgs.config
+elif defaults.has_key('config'):
+    gConfigSection = defaults['config']
+
+
+if gConfigSection != None:
+    if gConfig.has_section(gConfigSection):
+        section = dict(gConfig.items(gConfigSection))
+        gParser.set_defaults(**section)
+    else:
+        print >>sys.stderr, "Requestion configuration '%s' does not exist" % (configArgs.config)
+        sys.exit()
+
+
+###############################################################################
+# 
+# Done with argument setup. Parser remaining arguments
+#
+###############################################################################
+gArgs = gParser.parse_args(remainingArgs)
+
+
+#
+# At the end of all of this, we need the following things to be defined
+# or derived:
+#
+gRequiredOptions = [
+    [ 'ovs_vswitchd_schema', 'ovs_src_dir',     '/vswitchd/vswitch.ovsschema', True,  True  ], 
+    [ 'ovs_vswitchd',        'ovs_base_dir',    '/sbin/ovs-vswitchd',          True,  True  ], 
+    [ 'ovs_vsctl',           'ovs_base_dir',    '/bin/ovs-vsctl',              True,  True  ], 
+    [ 'ovs_ofctl',           'ovs_base_dir',    '/bin/ovs-ofctl',              True,  True  ], 
+    [ 'ovsdb_tool',          'ovs_base_dir',    '/bin/ovsdb-tool',             True,  True, ], 
+    [ 'ovsdb_server',        'ovs_base_dir',    '/sbin/ovsdb-server',          True,  True, ], 
+    [ 'ovs_db_file',          'ovs_base_dir',    '/ovs.db',                    False, True, ], 
+    [ 'ovs_db_sock',         'ovs_runtime_dir', '/db.sock',                    False, True, ], 
+    [ 'ovs_kmod',            'ovs_base_dir',    '/sbin/openvswitch_mod.ko',    True,  not gArgs.no_kmod ], 
+]
+
+def __require_option(key, basedir, path, exists=True):
+    value = getattr(gArgs, key)
+    if value is None:
+        # Unspecified -- try to default based on given path
+        value = getattr(gArgs, basedir)
+
+        if value is None:
+            return False
+
+        value += path
+
+    if exists and not os.path.exists(value):
+        return False
+
+    if gArgs.verbose:
+        print '--v-- option: %s @ %s' % (key, value)
+
+    setattr(gArgs, key, value)
+
+
+Validated = True
+# Try to validate as many things as we can
+for option in gRequiredOptions:
+    if option[4]:
+        if __require_option(option[0], option[1], option[2], option[3]) == False:
+            Validated = False
+
+# If any of them failed, try to be as helpful as possible
+if Validated == False:
+    print >>sys.stdout, "\nConfiguration problem. One or more required settings are missing or could not be derived:\n"
+    for option in gRequiredOptions:
+        if option[4] is False:
+            continue
+
+        value = getattr(gArgs, option[0])
+        if value:
+            print >>sys.stdout, " %s: %s" % (option[0], value), 
+            if option[3] and not os.path.exists(value):
+                print >>sys.stdout, "-- does not exist"
+            else:
+                print "(OK)"
+        else:
+            print >>sys.stdout, " %s: Unknown. " % (option[0]), 
+            base = getattr(gArgs, option[1])
+            if base:
+                print >>sys.stdout, "Search path was '%s'." % (base + option[2])
+            else:
+                print >>sys.stdout, "Could not derive path because '%s' was also unspecified." % (option[1])
+    print >>sys.stdout
+    sys.exit()
+
+
+
+
+#
+# If we aren't in a dry run, you must execute as root to accompish anything
+#
+if not os.geteuid() == 0 and gArgs.dry == False:
+    print >>sys.stderr, "Must run as root to accomplish any of this."
+    sys.exit()
+
+
+###############################################################################
+#
+# Helpers 
+#
+###############################################################################
+
+def createVeths(count):
+    for idx in range(0, count):
+        lcall(["/sbin/ip", "link", "add", "type", "veth"])
+
+def vethsUp(count):
+    for idx in range(0, count*2):
+        lcall(["/sbin/ifconfig", 'veth%s' % (idx), "up"])
+
+def lcall(cmd, log=gArgs.log, dry=gArgs.dry, popen=False, 
+          pidFile=None):
+    
+    if log or gArgs.verbose:
+        print "%s: %s" % ('popen' if popen else 'call', cmd)
+    
+    if not dry:
+        if not popen:
+            subprocess.call(cmd)
+        else:
+            p = subprocess.Popen(cmd)
+            if pidFile != None:
+                pidf = open(pidFile, "w"); 
+                print >>pidf, p.pid
+                pidf.close()
+
+    
+
+def vsctl(argsList):
+    argsList.insert(0, "--db=unix:%s" % (gArgs.ovs_db_sock))
+    argsList.insert(0, gArgs.ovs_vsctl)
+    lcall(argsList)
+
+def ofctl(argsList):
+    argsList.insert(0, gArgs.ovs_ofctl)
+    lcall(argsList)
+
+def killpid(pid):
+    try:
+        os.kill(pid, signal.SIGTERM)
+        return False
+    except OSError, e:
+        return True
+
+
+def killp(pidfile):
+    if os.path.exists(pidfile):
+        pid=int(open(pidfile).read())
+        print "Killing %s, pid=%s..." % (pidfile, pid),
+        if not gArgs.dry:
+            while not killpid(pid):
+                time.sleep(1)
+                pass
+        print "dead"
+
+
+###############################################################################
+#
+# main
+#
+###############################################################################
+
+gServerPid = gArgs.ovs_runtime_dir + "/ovsdb-server.pid"
+gSwitchPid = gArgs.ovs_runtime_dir + "/ovs-vswitchd.pid"
+gLogPid=     gArgs.ovs_runtime_dir + "/ovs-vswitchd-tail.pid"
+
+# Kill previous execution based on contents of the runtime dir
+if os.path.exists(gServerPid):
+    print gServerPid
+    vsctl(["del-br", gArgs.bridge])
+
+# Kill existing DB/vswitchd
+killp(gSwitchPid)
+killp(gServerPid)
+killp(gLogPid)
+
+# Remove old logpid file, since this does not happen automagically
+if os.path.exists(gLogPid):
+    os.remove(gLogPid)
+
+if gArgs.keep_veths == False:
+    lcall(['/sbin/rmmod', 'veth'])
+    lcall(['/sbin/modprobe', 'veth'])
+
+# Remove kmod
+lcall(['/sbin/rmmod', gArgs.ovs_kmod])
+
+if gArgs.kill == True:
+    # Don't do anything else
+    sys.exit()
+
+
+# Remove bridge module
+lcall(['/sbin/rmmod', 'bridge'])
+# Insert openvswitch module
+lcall(['/sbin/insmod', gArgs.ovs_kmod])
+
+createVeths(gArgs.port_count)
+vethsUp(gArgs.port_count)
+
+if not os.path.exists(gArgs.ovs_db_file) or gArgs.keep_db == False:
+    print "Initializing DB @ %s" % (gArgs.ovs_db_file)
+    if os.path.exists(gArgs.ovs_db_file) and not gArgs.dry:
+        os.remove(gArgs.ovs_db_file)
+
+    lcall([gArgs.ovsdb_tool, "create", gArgs.ovs_db_file, 
+           gArgs.ovs_vswitchd_schema])
+else:
+    print "Keeping existing DB @ %s" % (gArgs.ovs_db_file)
+
+
+if not os.path.exists(gArgs.ovs_runtime_dir):
+    os.makedirs(gArgs.ovs_runtime_dir)
+
+# Start dbserver
+lcall([gArgs.ovsdb_server, gArgs.ovs_db_file, 
+       "--remote=punix:%s" % (gArgs.ovs_db_sock), 
+       "--detach", "--pidfile=%s" % (gServerPid)])
+
+# Init db
+vsctl(["--no-wait", "init"])
+
+# Start vswitchd
+startV = [ gArgs.ovs_vswitchd, 
+          "unix:%s" % (gArgs.ovs_db_sock), 
+          "--verbose", "--detach",
+          "--pidfile=%s" % (gSwitchPid) ]
+
+if gArgs.ovs_vswitchd_log:
+    startV.append("--log-file=%s" % (gArgs.ovs_vswitchd_log))
+
+lcall(startV)
+
+if gArgs.vlog:
+    lcall(["xterm", "-T", "vswitchd-log", "-e", "tail", "-f", 
+           gArgs.ovs_vswitchd_log], 
+          popen=True, pidFile=gLogPid)
+
+
+# Add a bridge
+vsctl(["add-br", gArgs.bridge])
+ofctl(["show", gArgs.bridge])
+
+# Add Veths to bridge
+for idx in range(0, gArgs.port_count):
+    vsctl(["add-port", gArgs.bridge, "veth%s" % (idx*2)])
+
+
+# Set controller
+vsctl(["set-controller", gArgs.bridge, "tcp:%s:%s" % (
+        gArgs.cip, gArgs.cport)])
+
+# Minimize default backoff for controller connections
+vsctl(["set", "Controller", gArgs.bridge, 
+       "max_backoff=%s" % (gArgs.max_backoff)])
+
+
+ofctl(["show", gArgs.bridge])
+
+