Merge "Unmask l2gw agent service explicitly"
diff --git a/README.rst b/README.rst
index 3d851a2..a5f8e8a 100644
--- a/README.rst
+++ b/README.rst
@@ -678,8 +678,8 @@
             ovs:
               driver: openvswitch
 
-Neutron with additional physical networks
------------------------------------------
+Neutron with explicit physical networks
+---------------------------------------
 
 Neutron Server only
 
@@ -693,9 +693,11 @@
           engine: ml2
           tenant_network_types: "flat,vlan" # Can be mixed flat,vlan,vxlan
           ...
-          # also need to configure corresponding additional bridge_mappings on
+          # also need to configure corresponding bridge_mappings on
           # compute and gateway nodes
-          physnets:
+          physnets: # only listed physnets will be configured (overrides physnet1/2/3)
+            external:
+              mtu: 1500
             sriov_net:
               mtu: 9000 # Optional, defaults to 1500
               vlan_range: '100:200' # Optional
@@ -749,6 +751,10 @@
               driver: openvswitch
             sriov:
               driver: sriovnicswitch
+              # Driver w/ highest number will be placed ahead in the list (default is 0).
+              # It's recommended for SR-IOV driver to set an order >0 to get it
+              # before (for example) the opendaylight one.
+              order: 9
 
     neutron:
       compute:
@@ -790,13 +796,13 @@
         version: pike
         bgp_vpn:
           enabled: true
-          driver: bagpipe # Options: bagpipe/opencontrail/opendaylight
+          driver: bagpipe # Options: bagpipe/opencontrail/opendaylight[_v2]
       ....
       compute:
         version: pike
         bgp_vpn:
           enabled: true
-          driver: bagpipe # Options: bagpipe/opencontrail/opendaylight
+          driver: bagpipe # Options: bagpipe/opencontrail/opendaylight[_v2]
           bagpipe:
             local_address: 192.168.20.20 # IP address for mpls/gre tunnels
             peers: 192.168.20.30 # IP addresses of BGP peers
@@ -913,6 +919,7 @@
         mechanism:
           ovs:
             driver: opendaylight_v2
+            order: 1
 
 Network/Gateway node:
 
diff --git a/neutron/files/ocata/ml2_conf.ini b/neutron/files/ocata/ml2_conf.ini
index 300afce..6f2df28 100644
--- a/neutron/files/ocata/ml2_conf.ini
+++ b/neutron/files/ocata/ml2_conf.ini
@@ -166,6 +166,13 @@
 # (list value)
 #physical_network_mtus =
 {%- set physical_network_mtus = [] %}
+
+{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
+{%- do physical_network_mtus.append([physnet, params.get('mtu')]|join(":")) if params.get('mtu', False) %}
+{%- endfor %}
+
+{%- if not physical_network_mtus %}
+
 {%- if server.get('external_access', True) %}
 {%- do physical_network_mtus.append(['physnet1',server.backend.get('external_mtu', '1500')]|join(":")) %}
 {%- endif %}
@@ -176,9 +183,8 @@
 {%- do physical_network_mtus.append(['physnet3',server.backend.get('ironic_net_mtu', server.backend.get('external_mtu', '1500'))]|join(":")) %}
 {%- endif %}
 
-{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
-{%- do physical_network_mtus.append([physnet, params.get('mtu')]|join(":")) if params.get('mtu', False) %}
-{%- endfor %}
+{%- endif %}
+
 physical_network_mtus = {{ ','.join(physical_network_mtus) }}
 
 # Default network type for external networks when no provider attributes are
@@ -251,6 +257,13 @@
 # tenant networks. (list value)
 #network_vlan_ranges =
 {%- set network_vlan_ranges = [] %}
+
+{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
+{%- do network_vlan_ranges.append([physnet, params.get('vlan_range')]|join(":") if params.get('vlan_range', False) else physnet) %}
+{%- endfor %}
+
+{%- if not network_vlan_ranges %}
+
 {%- if server.backend.external_vlan_range is defined %}
 {%- do network_vlan_ranges.append(['physnet1',server.backend.get('external_vlan_range')]|join(":")) %}
 {%- endif %}
@@ -261,9 +274,8 @@
 {%- do network_vlan_ranges.append(['physnet3',server.backend.get('ironic_vlan_range')]|join(":")) %}
 {%- endif %}
 
-{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
-{%- do network_vlan_ranges.append([physnet, params.get('vlan_range')]|join(":") if params.get('vlan_range', False) else physnet) %}
-{%- endfor %}
+{%- endif %}
+
 network_vlan_ranges = {{ ','.join(network_vlan_ranges) }}
 
 [ml2_type_vxlan]
diff --git a/neutron/files/pike/ml2_conf.ini b/neutron/files/pike/ml2_conf.ini
index d5b8a2f..aaee0b0 100644
--- a/neutron/files/pike/ml2_conf.ini
+++ b/neutron/files/pike/ml2_conf.ini
@@ -131,14 +131,17 @@
 # the neutron.ml2.mechanism_drivers namespace. (list value)
 #mechanism_drivers =
 {%- set mechanism_drivers = [] %}
+{%- set default_order = 0 %}
 {%- for mechanism_name, mechanism in server.get('backend', {}).get('mechanism', []).items() %}
-{%- do mechanism_drivers.append(mechanism.get('driver')) if 'driver' in mechanism %}
+{%-   if 'driver' in mechanism %}
+{%-     do mechanism.update({'order': default_order}) if 'order' not in mechanism %}
+{%-     do mechanism_drivers.append(mechanism) %}
+{%-   endif %}
 {%- endfor %}
-{%- set opendaylight_enabled = true if 'opendaylight' in mechanism_drivers|join else false %}
-{%- if "vxlan" in server.backend.tenant_network_types and not opendaylight_enabled %}
-{%- do mechanism_drivers.append('l2population') %}
+{%- if "vxlan" in server.backend.tenant_network_types and not server.backend.opendaylight|default(False) %}
+{%-   do mechanism_drivers.append({'driver': 'l2population', 'order': default_order}) %}
 {%- endif %}
-mechanism_drivers = {{ ','.join(mechanism_drivers) }}
+mechanism_drivers = {{ mechanism_drivers|sort(True, attribute='order')|join(',', attribute='driver') }}
 
 # An ordered list of extension driver entrypoints to be loaded from the
 # neutron.ml2.extension_drivers namespace. For example: extension_drivers =
@@ -167,6 +170,13 @@
 # (list value)
 #physical_network_mtus =
 {%- set physical_network_mtus = [] %}
+
+{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
+{%- do physical_network_mtus.append([physnet, params.get('mtu')]|join(":")) if params.get('mtu', False) %}
+{%- endfor %}
+
+{%- if not physical_network_mtus %}
+
 {%- if server.get('external_access', True) %}
 {%- do physical_network_mtus.append(['physnet1',server.backend.get('external_mtu', '1500')]|join(":")) %}
 {%- endif %}
@@ -177,9 +187,8 @@
 {%- do physical_network_mtus.append(['physnet3',server.backend.get('ironic_net_mtu', server.backend.get('external_mtu', '1500'))]|join(":")) %}
 {%- endif %}
 
-{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
-{%- do physical_network_mtus.append([physnet, params.get('mtu')]|join(":")) if params.get('mtu', False) %}
-{%- endfor %}
+{%- endif %}
+
 physical_network_mtus = {{ ','.join(physical_network_mtus) }}
 
 # Default network type for external networks when no provider attributes are
@@ -250,8 +259,14 @@
 # specifying physical_network names usable for VLAN provider and tenant
 # networks, as well as ranges of VLAN tags on each available for allocation to
 # tenant networks. (list value)
-#network_vlan_ranges =
 {%- set network_vlan_ranges = [] %}
+
+{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
+{%- do network_vlan_ranges.append([physnet, params.get('vlan_range')]|join(":") if params.get('vlan_range', False) else physnet) %}
+{%- endfor %}
+
+{%- if not network_vlan_ranges %}
+
 {%- if server.backend.external_vlan_range is defined %}
 {%- do network_vlan_ranges.append(['physnet1',server.backend.get('external_vlan_range')]|join(":")) %}
 {%- endif %}
@@ -262,9 +277,8 @@
 {%- do network_vlan_ranges.append(['physnet3',server.backend.get('ironic_vlan_range')]|join(":")) %}
 {%- endif %}
 
-{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
-{%- do network_vlan_ranges.append([physnet, params.get('vlan_range')]|join(":") if params.get('vlan_range', False)) %}
-{%- endfor %}
+{%- endif %}
+
 network_vlan_ranges = {{ ','.join(network_vlan_ranges) }}
 
 [ml2_type_vxlan]
@@ -319,7 +333,7 @@
 ovn_l3_scheduler = leastloaded
 {%- endif %}
 
-{%- if opendaylight_enabled %}
+{%- if server.backend.opendaylight|default(False) %}
 [ml2_odl]
 # HTTP URL of OpenDaylight REST interface. (string value)
 url = {{ server.backend.protocol|default('http') }}://{{ server.backend.host }}:{{ server.backend.rest_api_port }}/controller/nb/v2/neutron
diff --git a/neutron/files/pike/neutron-server.conf.Debian b/neutron/files/pike/neutron-server.conf.Debian
index 963aa65..63e377b 100644
--- a/neutron/files/pike/neutron-server.conf.Debian
+++ b/neutron/files/pike/neutron-server.conf.Debian
@@ -2206,23 +2206,17 @@
 # cipher list format. (string value)
 #ciphers = <None>
 [service_providers]
-
-{% if server.get('bgp_vpn', {}).get('enabled', False) -%}
-
-{%- if server.bgp_vpn.driver == "bagpipe" -%}
-
-service_provider = BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver:default
-
-{%- elif server.bgp_vpn.driver == "opencontrail" -%}
-
-service_provider = BGPVPN:OpenContrail:networking_bgpvpn.neutron.services.service_drivers.opencontrail.opencontrail.OpenContrailBGPVPNDriver:default
-
-{%- elif server.bgp_vpn.driver == "opendaylight" -%}
-
-service_provider = BGPVPN:OpenDaylight:networking_bgpvpn.neutron.services.service_drivers.opendaylight.odl.OpenDaylightBgpvpnDriver:default
-
-{%- endif -%}
-{%- endif -%}
+{%- if server.get('bgp_vpn', {}).get('enabled', False) %}
+{%-
+  set _service_providers = {
+    'bagpipe': 'BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver:default',
+    'opencontrail': 'BGPVPN:OpenContrail:networking_bgpvpn.neutron.services.service_drivers.opencontrail.opencontrail.OpenContrailBGPVPNDriver:default',
+    'opendaylight': 'BGPVPN:OpenDaylight:networking_bgpvpn.neutron.services.service_drivers.opendaylight.odl.OpenDaylightBgpvpnDriver:default',
+    'opendaylight_v2': 'BGPVPN:OpenDaylight:networking_odl.bgpvpn.odl_v2.OpenDaylightBgpvpnDriver:default'
+  }
+%}
+service_provider = {{ _service_providers[server.bgp_vpn.driver] }}
+{%- endif %}
 
 {% if server.lbaas is defined -%}
 
diff --git a/neutron/files/queens/bgpvpn.conf b/neutron/files/queens/bgpvpn.conf
index 976d0e1..974784e 100644
--- a/neutron/files/queens/bgpvpn.conf
+++ b/neutron/files/queens/bgpvpn.conf
@@ -1,20 +1,14 @@
 # BGPVPN config
 
 [service_providers]
-
-{% if server.get('bgp_vpn', {}).get('enabled', False) -%}
-
-{%- if server.bgp_vpn.driver == "bagpipe" %}
-
-service_provider = BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver:default
-
-{%- elif server.bgp_vpn.driver == "opencontrail" -%}
-
-service_provider = BGPVPN:OpenContrail:networking_bgpvpn.neutron.services.service_drivers.opencontrail.opencontrail.OpenContrailBGPVPNDriver:default
-
-{%- elif server.bgp_vpn.driver == "opendaylight" -%}
-
-service_provider = BGPVPN:OpenDaylight:networking_bgpvpn.neutron.services.service_drivers.opendaylight.odl.OpenDaylightBgpvpnDriver:default
-
-{%- endif -%}
-{%- endif -%}
+{%- if server.get('bgp_vpn', {}).get('enabled', False) %}
+{%-
+  set _service_providers = {
+    'bagpipe': 'BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver:default',
+    'opencontrail': 'BGPVPN:OpenContrail:networking_bgpvpn.neutron.services.service_drivers.opencontrail.opencontrail.OpenContrailBGPVPNDriver:default',
+    'opendaylight': 'BGPVPN:OpenDaylight:networking_bgpvpn.neutron.services.service_drivers.opendaylight.odl.OpenDaylightBgpvpnDriver:default',
+    'opendaylight_v2': 'BGPVPN:OpenDaylight:networking_odl.bgpvpn.odl_v2.OpenDaylightBgpvpnDriver:default'
+  }
+%}
+service_provider = {{ _service_providers[server.bgp_vpn.driver] }}
+{%- endif %}
diff --git a/neutron/files/queens/ml2_conf.ini b/neutron/files/queens/ml2_conf.ini
index 9dfcba1..d6cefbe 100644
--- a/neutron/files/queens/ml2_conf.ini
+++ b/neutron/files/queens/ml2_conf.ini
@@ -37,14 +37,17 @@
 # the neutron.ml2.mechanism_drivers namespace. (list value)
 #mechanism_drivers =
 {%- set mechanism_drivers = [] %}
+{%- set default_order = 0 %}
 {%- for mechanism_name, mechanism in server.get('backend', {}).get('mechanism', []).items() %}
-{%- do mechanism_drivers.append(mechanism.get('driver')) if 'driver' in mechanism %}
+{%-   if 'driver' in mechanism %}
+{%-     do mechanism.update({'order': default_order}) if 'order' not in mechanism %}
+{%-     do mechanism_drivers.append(mechanism) %}
+{%-   endif %}
 {%- endfor %}
-{%- set opendaylight_enabled = true if 'opendaylight' in mechanism_drivers|join else false %}
-{%- if "vxlan" in server.backend.tenant_network_types and not opendaylight_enabled %}
-{%- do mechanism_drivers.append('l2population') %}
+{%- if "vxlan" in server.backend.tenant_network_types and not server.backend.opendaylight|default(False) %}
+{%-   do mechanism_drivers.append({'driver': 'l2population', 'order': default_order}) %}
 {%- endif %}
-mechanism_drivers = {{ ','.join(mechanism_drivers) }}
+mechanism_drivers = {{ mechanism_drivers|sort(True, attribute='order')|join(',', attribute='driver') }}
 
 # An ordered list of extension driver entrypoints to be loaded from the
 # neutron.ml2.extension_drivers namespace. For example: extension_drivers =
@@ -73,6 +76,13 @@
 # (list value)
 #physical_network_mtus =
 {%- set physical_network_mtus = [] %}
+
+{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
+{%- do physical_network_mtus.append([physnet, params.get('mtu')]|join(":")) if params.get('mtu', False) %}
+{%- endfor %}
+
+{%- if not physical_network_mtus %}
+
 {%- if server.get('external_access', True) %}
 {%- do physical_network_mtus.append(['physnet1',server.backend.get('external_mtu', '1500')]|join(":")) %}
 {%- endif %}
@@ -83,9 +93,8 @@
 {%- do physical_network_mtus.append(['physnet3',server.backend.get('ironic_net_mtu', server.backend.get('external_mtu', '1500'))]|join(":")) %}
 {%- endif %}
 
-{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
-{%- do physical_network_mtus.append([physnet, params.get('mtu')]|join(":")) if params.get('mtu', False) %}
-{%- endfor %}
+{%- endif %}
+
 physical_network_mtus = {{ ','.join(physical_network_mtus) }}
 
 # Default network type for external networks when no provider attributes are
@@ -158,6 +167,13 @@
 # tenant networks. (list value)
 #network_vlan_ranges =
 {%- set network_vlan_ranges = [] %}
+
+{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
+{%- do network_vlan_ranges.append([physnet, params.get('vlan_range')]|join(":") if params.get('vlan_range', False) else physnet) %}
+{%- endfor %}
+
+{%- if not network_vlan_ranges %}
+
 {%- if server.backend.external_vlan_range is defined %}
 {%- do network_vlan_ranges.append(['physnet1',server.backend.get('external_vlan_range')]|join(":")) %}
 {%- endif %}
@@ -168,9 +184,8 @@
 {%- do network_vlan_ranges.append(['physnet3',server.backend.get('ironic_vlan_range')]|join(":")) %}
 {%- endif %}
 
-{%- for physnet, params in server.backend.get('physnets', {}).iteritems() %}
-{%- do network_vlan_ranges.append([physnet, params.get('vlan_range')]|join(":") if params.get('vlan_range', False) else physnet) %}
-{%- endfor %}
+{%- endif %}
+
 network_vlan_ranges = {{ ','.join(network_vlan_ranges) }}
 
 [ml2_type_vxlan]
@@ -228,7 +243,7 @@
 enable_distributed_floating_ip = {{ server.dvr|default('false') }}
 {%- endif %}
 
-{%- if opendaylight_enabled %}
+{%- if server.backend.opendaylight|default(False) %}
 [ml2_odl]
 # HTTP URL of OpenDaylight REST interface. (string value)
 url = {{ server.backend.protocol|default('http') }}://{{ server.backend.host }}:{{ server.backend.rest_api_port }}/controller/nb/v2/neutron
diff --git a/tests/pillar/control_opendaylight.sls b/tests/pillar/control_opendaylight.sls
index 174790e..9064406 100644
--- a/tests/pillar/control_opendaylight.sls
+++ b/tests/pillar/control_opendaylight.sls
@@ -59,6 +59,7 @@
       mechanism:
         ovs:
           driver: opendaylight
+          order: 77
 linux:
   system:
     enabled: true
diff --git a/tests/pillar/control_single.sls b/tests/pillar/control_single.sls
index 445806c..79894f7 100644
--- a/tests/pillar/control_single.sls
+++ b/tests/pillar/control_single.sls
@@ -18,6 +18,8 @@
       token: token
       tenant: admin
       physnets:
+        external:
+          mtu: 1500
         sriov_net:
           mtu: 9000 # Optional, defaults to 1500
           vlan_range: '100:200' # Optional