Update the ML2 configuration in devstack

This commit adds support to devstack to set ML2 options including
MechanismDrivers, TypeDrivers, and TypeDriver specific configuration for ML2
when running with devstack.

This also allows for simple configuration of VLAN, GRE, and VXLAN ML2 devstack
setups with the OVS agent as follows:

    # VLAN configuration
    Q_PLUGIN=ml2
    ENABLE_TENANT_VLANS=True

    # GRE tunnel configuration
    Q_PLUGIN=ml2
    ENABLE_TENANT_TUNNELS=True

    # VXLAN tunnel configuration
    Q_PLUGIN=ml2
    Q_ML2_TENANT_NETWORK_TYPE=vxlan

Fixes bug 1200767

Change-Id: Ib16efac13440b5d50658a0e6be35bc735510a262
diff --git a/README.md b/README.md
index fbf7b4a..63b0421 100644
--- a/README.md
+++ b/README.md
@@ -148,6 +148,32 @@
     Q_AGENT_EXTRA_AGENT_OPTS=(tunnel_type=vxlan vxlan_udp_port=8472)
     Q_SRV_EXTRA_OPTS=(tenant_network_type=vxlan)
 
+devstack also supports configuring the Neutron ML2 plugin. The ML2 plugin can run with the OVS, LinuxBridge, or Hyper-V agents on compute hosts. A simple way to configure the ml2 plugin is shown below:
+
+    # VLAN configuration
+    Q_PLUGIN=ml2
+    ENABLE_TENANT_VLANS=True
+
+    # GRE tunnel configuration
+    Q_PLUGIN=ml2
+    ENABLE_TENANT_TUNNELS=True
+
+    # VXLAN tunnel configuration
+    Q_PLUGIN=ml2
+    Q_ML2_TENANT_NETWORK_TYPE=vxlan
+
+The above will default in devstack to using the OVS on each compute host. To change this, set the `Q_AGENT` variable to the agent you want to run (e.g. linuxbridge).
+
+    Variable Name                    Notes
+    -------------------------------------------------------------------------------------
+    Q_AGENT                          This specifies which agent to run with the ML2 Plugin (either `openvswitch` or `linuxbridge`).
+    Q_ML2_PLUGIN_MECHANISM_DRIVERS   The ML2 MechanismDrivers to load. The default is none. Note, ML2 will work with the OVS and LinuxBridge agents by default.
+    Q_ML2_PLUGIN_TYPE_DRIVERS        The ML2 TypeDrivers to load. Defaults to all available TypeDrivers.
+    Q_ML2_PLUGIN_GRE_TYPE_OPTIONS    GRE TypeDriver options. Defaults to none.
+    Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS  VXLAN TypeDriver options. Defaults to none.
+    Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS   VLAN TypeDriver options. Defaults to none.
+    Q_AGENT_EXTRA_AGENT_OPTS         Extra configuration options to pass to the OVS or LinuxBridge Agent.
+
 # Tempest
 
 If tempest has been successfully configured, a basic set of smoke tests can be run as follows:
diff --git a/lib/neutron_plugins/ml2 b/lib/neutron_plugins/ml2
index fcff870..ff49d8e 100644
--- a/lib/neutron_plugins/ml2
+++ b/lib/neutron_plugins/ml2
@@ -5,10 +5,42 @@
 MY_XTRACE=$(set +o | grep xtrace)
 set +o xtrace
 
+# Enable this to simply and quickly enable tunneling with ML2.
+# Select either 'gre', 'vxlan', or '(gre vxlan)'
+Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-}
+# This has to be set here since the agent will set this in the config file
+if [[ "$Q_ML2_TENANT_NETWORK_TYPE" != "" ]]; then
+    Q_AGENT_EXTRA_AGENT_OPTS=(tunnel_types=$Q_ML2_TENANT_NETWORK_TYPE)
+elif [[ "$ENABLE_TENANT_TUNNELS" = "True" ]]; then
+    Q_AGENT_EXTRA_AGENT_OPTS=(tunnel_types=gre)
+fi
+
 # Default openvswitch L2 agent
 Q_AGENT=${Q_AGENT:-openvswitch}
 source $TOP_DIR/lib/neutron_plugins/${Q_AGENT}_agent
 
+# List of MechanismDrivers to load
+Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_PLUGIN_MECHANISM_DRIVERS:-}
+# List of Type Drivers to load
+Q_ML2_PLUGIN_TYPE_DRIVERS=${Q_ML2_PLUGIN_TYPE_DRIVERS:-local,flat,vlan,gre,vxlan}
+# Default GRE TypeDriver options
+Q_ML2_PLUGIN_GRE_TYPE_OPTIONS=${Q_ML2_PLUGIN_GRE_TYPE_OPTIONS:-tunnel_id_ranges=$TENANT_TUNNEL_RANGES}
+# Default VXLAN TypeDriver options
+Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS=${Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS:-vni_ranges=1001:2000}
+# Default VLAN TypeDriver options
+Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS=${Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS:-}
+
+function populate_ml2_config() {
+    OPTS=$1
+    CONF=$2
+    SECTION=$3
+
+    for I in "${OPTS[@]}"; do
+        # Replace the first '=' with ' ' for iniset syntax
+        iniset $CONF $SECTION ${I/=/ }
+    done
+}
+
 function neutron_plugin_configure_common() {
     Q_PLUGIN_CONF_PATH=etc/neutron/plugins/ml2
     Q_PLUGIN_CONF_FILENAME=ml2_conf.ini
@@ -17,26 +49,31 @@
 }
 
 function neutron_plugin_configure_service() {
-    if [[ "$ENABLE_TENANT_TUNNELS" = "True" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE ml2 tenant_network_types gre
-        iniset /$Q_PLUGIN_CONF_FILE ml2_type_gre tunnel_id_ranges $TENANT_TUNNEL_RANGES
+    if [[ "$Q_ML2_TENANT_NETWORK_TYPE" != "" ]]; then
+        Q_SRV_EXTRA_OPTS=(tenant_network_types=$Q_ML2_TENANT_NETWORK_TYPE)
+    elif [[ "$ENABLE_TENANT_TUNNELS" = "True" ]]; then
+        # This assumes you want a simple configuration, and will overwrite
+        # Q_SRV_EXTRA_OPTS if set in addition to ENABLE_TENANT_TUNNELS.
+        Q_SRV_EXTRA_OPTS=(tenant_network_types=gre)
+        Q_ML2_PLUGIN_GRE_TYPE_OPTIONS=(tunnel_id_ranges=$TENANT_TUNNEL_RANGES)
     elif [[ "$ENABLE_TENANT_VLANS" = "True" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE ml2 tenant_network_types vlan
+        Q_SRV_EXTRA_OPTS=(tenant_network_types=vlan)
     else
         echo "WARNING - The ml2 plugin is using local tenant networks, with no connectivity between hosts."
     fi
 
-    # Override ``ML2_VLAN_RANGES`` and any needed agent configuration
-    # variables in ``localrc`` for more complex physical network
-    # configurations.
-    if [[ "$ML2_VLAN_RANGES" = "" ]] && [[ "$PHYSICAL_NETWORK" != "" ]]; then
-        ML2_VLAN_RANGES=$PHYSICAL_NETWORK
-        if [[ "$TENANT_VLAN_RANGE" != "" ]]; then
-            ML2_VLAN_RANGES=$ML2_VLAN_RANGES:$TENANT_VLAN_RANGE
+    # Allow for overrding VLAN configuration (for example, to configure provider
+    # VLANs) by first checking if Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS is set.
+    if [ "$Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS" == "" ]; then
+        if [[ "$ML2_VLAN_RANGES" = "" ]] && [[ "$PHYSICAL_NETWORK" != "" ]]; then
+            ML2_VLAN_RANGES=$PHYSICAL_NETWORK
+            if [[ "$TENANT_VLAN_RANGE" != "" ]]; then
+                ML2_VLAN_RANGES=$ML2_VLAN_RANGES:$TENANT_VLAN_RANGE
+            fi
         fi
-    fi
-    if [[ "$ML2_VLAN_RANGES" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE ml2_type_vlan network_vlan_ranges $ML2_VLAN_RANGES
+        if [[ "$ML2_VLAN_RANGES" != "" ]]; then
+            Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS=(network_vlan_ranges=$ML2_VLAN_RANGES)
+        fi
     fi
 
     # REVISIT(rkukura): Setting firewall_driver here for
@@ -52,6 +89,20 @@
         iniset /$Q_PLUGIN_CONF_FILE SECURITYGROUP firewall_driver neutron.agent.firewall.NoopFirewallDriver
     fi
 
+    # Since we enable the tunnel TypeDrivers, also enable a local_ip
+    iniset /$Q_PLUGIN_CONF_FILE ovs local_ip $HOST_IP
+
+    populate_ml2_config type_drivers=$Q_ML2_PLUGIN_TYPE_DRIVERS /$Q_PLUGIN_CONF_FILE ml2
+
+    populate_ml2_config $Q_SRV_EXTRA_OPTS /$Q_PLUGIN_CONF_FILE ml2
+
+    populate_ml2_config $Q_ML2_PLUGIN_GRE_TYPE_OPTIONS /$Q_PLUGIN_CONF_FILE ml2_type_gre
+
+    populate_ml2_config $Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS /$Q_PLUGIN_CONF_FILE ml2_type_vxlan
+
+    if [ "$Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS" != "" ]; then
+        populate_ml2_config $Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS /$Q_PLUGIN_CONF_FILE ml2_type_vlan
+    fi
 }
 
 function has_neutron_plugin_security_group() {