Merge "Remove old guides"
diff --git a/doc/source/guides/multinode-lab.rst b/doc/source/guides/multinode-lab.rst
index a63260f..0b1ebb9 100644
--- a/doc/source/guides/multinode-lab.rst
+++ b/doc/source/guides/multinode-lab.rst
@@ -167,6 +167,7 @@
 
 ::
 
+    [[local|localrc]]
     HOST_IP=192.168.42.12 # change this per compute node
     FLAT_INTERFACE=eth0
     FIXED_RANGE=10.4.128.0/20
diff --git a/files/apts/horizon b/files/apts/horizon
index 03df3cb..5d06928 100644
--- a/files/apts/horizon
+++ b/files/apts/horizon
@@ -17,3 +17,4 @@
 python-coverage
 python-cherrypy3 # why?
 python-migrate
+libpcre3-dev  # pyScss
diff --git a/files/rpms/horizon b/files/rpms/horizon
index fe3a2f4..7add23a 100644
--- a/files/rpms/horizon
+++ b/files/rpms/horizon
@@ -19,3 +19,4 @@
 python-sqlalchemy
 python-webob
 pyxattr
+pcre-devel  # pyScss
diff --git a/functions-common b/functions-common
index e48ceaf..9f8476e 100644
--- a/functions-common
+++ b/functions-common
@@ -575,7 +575,7 @@
 
     RECLONE=$(trueorfalse False $RECLONE)
 
-    if [[ "$GIT_DEPTH" ]]; then
+    if [[ -n "${GIT_DEPTH}" ]]; then
         git_clone_flags="$git_clone_flags --depth $GIT_DEPTH"
     fi
 
@@ -993,6 +993,8 @@
     local file_to_parse
     local service
 
+    INSTALL_TESTONLY_PACKAGES=$(trueorfalse False $INSTALL_TESTONLY_PACKAGES)
+
     if [[ -z "$package_dir" ]]; then
         echo "No package directory supplied"
         return 1
@@ -1599,6 +1601,7 @@
         $cmd_pip install \
         $pip_mirror_opt $@
 
+    INSTALL_TESTONLY_PACKAGES=$(trueorfalse False $INSTALL_TESTONLY_PACKAGES)
     if [[ "$INSTALL_TESTONLY_PACKAGES" == "True" ]]; then
         local test_req="$@/test-requirements.txt"
         if [[ -e "$test_req" ]]; then
@@ -1645,6 +1648,16 @@
     setup_package_with_req_sync $project_dir -e
 }
 
+# determine if a project as specified by directory is in
+# projects.txt. This will not be an exact match because we throw away
+# the namespacing when we clone, but it should be good enough in all
+# practical ways.
+function is_in_projects_txt {
+    local project_dir=$1
+    local project_name=$(basename $project_dir)
+    return grep "/$project_name\$" $REQUIREMENTS_DIR/projects.txt >/dev/null
+}
+
 # ``pip install -e`` the package, which processes the dependencies
 # using pip before running `setup.py develop`
 #
@@ -1663,8 +1676,19 @@
     local update_requirements=$(cd $project_dir && git diff --exit-code >/dev/null || echo "changed")
 
     if [[ $update_requirements != "changed" ]]; then
-        (cd $REQUIREMENTS_DIR; \
-            python update.py $project_dir)
+        if [[ "$REQUIREMENTS_MODE" == "soft" ]]; then
+            if is_in_projects_txt $project_dir; then
+                (cd $REQUIREMENTS_DIR; \
+                    python update.py $project_dir)
+            else
+                # soft update projects not found in requirements project.txt
+                (cd $REQUIREMENTS_DIR; \
+                    python update.py -s $project_dir)
+            fi
+        else
+            (cd $REQUIREMENTS_DIR; \
+                python update.py $project_dir)
+        fi
     fi
 
     setup_package $project_dir $flags
diff --git a/lib/ceilometer b/lib/ceilometer
index 9046b9d..483cd27 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -242,6 +242,18 @@
     fi
 }
 
+# install_redis() - Install the redis server.
+function install_redis {
+    if is_ubuntu; then
+        install_package redis-server
+    else
+        # This will fail (correctly) where a redis package is unavailable
+        install_package redis
+    fi
+
+    restart_service redis
+}
+
 # install_ceilometer() - Collect source and prepare
 function install_ceilometer {
     git_clone $CEILOMETER_REPO $CEILOMETER_DIR $CEILOMETER_BRANCH
@@ -249,6 +261,8 @@
 
     if echo $CEILOMETER_COORDINATION_URL | grep -q '^memcached:'; then
         install_package memcached
+    elif echo $CEILOMETER_COORDINATION_URL | grep -q '^redis:'; then
+        install_redis
     fi
 }
 
diff --git a/lib/cinder b/lib/cinder
index b30a036..29cda42 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -306,6 +306,7 @@
     iniset $CINDER_CONF DEFAULT glance_api_servers "${GLANCE_SERVICE_PROTOCOL}://${GLANCE_HOSTPORT}"
     if is_ssl_enabled_service glance || is_service_enabled tls-proxy; then
         iniset $CINDER_CONF DEFAULT glance_protocol https
+        iniset $CINDER_CONF DEFAULT glance_ca_certificates_file $SSL_BUNDLE_FILE
     fi
 
     # Register SSL certificates if provided
diff --git a/lib/config b/lib/config
index a4d59a3..a4d0328 100644
--- a/lib/config
+++ b/lib/config
@@ -82,8 +82,6 @@
     local matchgroup=$2
     local configfile=$3
 
-    # note in the awk below, \x27 is ascii for ' -- this avoids
-    # having to do nasty quoting games
     get_meta_section $file $matchgroup $configfile | \
     $CONFIG_AWK_CMD -v configfile=$configfile '
         BEGIN {
@@ -140,13 +138,13 @@
                 for (attr_no = cfg_sec_attr_count[sno] - 1; attr_no >=0; attr_no--) {
                     attr = cfg_sec_attr_name[sno, attr_no]
                     if (cfg_attr_count[section, attr] == 1)
-                        print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr, 0] "\x27"
+                        print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, 0] "\""
                     else {
                         # For multiline, invoke the ini routines in the reverse order
                         count = cfg_attr_count[section, attr]
-                        print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr, count - 1] "\x27"
+                        print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\""
                         for (l = count -2; l >= 0; l--)
-                            print "iniadd_literal " configfile " " section " " attr " \x27" cfg_attr[section, attr, l] "\x27"
+                            print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\""
                     }
                 }
             }
diff --git a/lib/ironic b/lib/ironic
index 0a84e47..cf005a7 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -279,7 +279,7 @@
         else
             die $LINENO "SWIFT_ENABLE_TEMPURLS must be True to use agent_ssh driver in Ironic."
         fi
-        iniset $IRONIC_CONF_FILE glance swift_endpoint_url http://${HOST_IP}:8080
+        iniset $IRONIC_CONF_FILE glance swift_endpoint_url http://${HOST_IP}:${SWIFT_DEFAULT_BIND_PORT:-8080}
         iniset $IRONIC_CONF_FILE glance swift_api_version v1
         local tenant_id=$(get_or_create_project $SERVICE_TENANT_NAME)
         iniset $IRONIC_CONF_FILE glance swift_account AUTH_${tenant_id}
@@ -523,7 +523,11 @@
     sudo modprobe nf_nat_tftp
     # nodes boot from TFTP and callback to the API server listening on $HOST_IP
     sudo iptables -I INPUT -d $HOST_IP -p udp --dport 69 -j ACCEPT || true
-    sudo iptables -I INPUT -d $HOST_IP -p tcp --dport 6385 -j ACCEPT || true
+    sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_HOSTPORT -j ACCEPT || true
+    if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+        # agent ramdisk gets instance image from swift
+        sudo iptables -I INPUT -d $HOST_IP -p tcp --dport ${SWIFT_DEFAULT_BIND_PORT:-8080} -j ACCEPT || true
+    fi
 }
 
 function configure_tftpd {
@@ -678,6 +682,10 @@
     restart_service xinetd
     sudo iptables -D INPUT -d $HOST_IP -p udp --dport 69 -j ACCEPT || true
     sudo iptables -D INPUT -d $HOST_IP -p tcp --dport 6385 -j ACCEPT || true
+    if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+        # agent ramdisk gets instance image from swift
+        sudo iptables -D INPUT -d $HOST_IP -p tcp --dport ${SWIFT_DEFAULT_BIND_PORT:-8080} -j ACCEPT || true
+    fi
     sudo rmmod nf_conntrack_tftp || true
     sudo rmmod nf_nat_tftp || true
 }
diff --git a/lib/keystone b/lib/keystone
index 1c67835..276e971 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -287,7 +287,7 @@
         " -i $KEYSTONE_CATALOG
 
         # Configure ``keystone.conf`` to use templates
-        iniset $KEYSTONE_CONF catalog driver "keystone.catalog.backends.templated.TemplatedCatalog"
+        iniset $KEYSTONE_CONF catalog driver "keystone.catalog.backends.templated.Catalog"
         iniset $KEYSTONE_CONF catalog template_file "$KEYSTONE_CATALOG"
     fi
 
diff --git a/lib/neutron b/lib/neutron
index ca9b16c..eb07f40 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -20,13 +20,15 @@
 # - create_neutron_cache_dir
 # - create_nova_conf_neutron
 # - start_neutron_service_and_check
+# - start_neutron_agents
 # - create_neutron_initial_network
 # - setup_neutron_debug
-# - start_neutron_agents
 #
 # ``unstack.sh`` calls the entry points in this order:
 #
 # - stop_neutron
+# - stop_neutron_third_party
+# - cleanup_neutron
 
 # Functions in lib/neutron are classified into the following categories:
 #
@@ -43,21 +45,11 @@
 # to run Neutron on this host, make sure that q-svc is also in
 # ``ENABLED_SERVICES``.
 #
-# If you're planning to use the Neutron openvswitch plugin, set
-# ``Q_PLUGIN`` to "openvswitch" and make sure the q-agt service is enabled
-# in ``ENABLED_SERVICES``.  If you're planning to use the Neutron
-# linuxbridge plugin, set ``Q_PLUGIN`` to "linuxbridge" and make sure the
-# q-agt service is enabled in ``ENABLED_SERVICES``.
-#
 # See "Neutron Network Configuration" below for additional variables
 # that must be set in localrc for connectivity across hosts with
 # Neutron.
 #
 # With Neutron networking the NETWORK_MANAGER variable is ignored.
-#
-# To enable specific configuration options for either the Open vSwitch or
-# LinuxBridge plugin, please see the top level README file under the
-# Neutron section.
 
 
 # Neutron Network Configuration
@@ -213,12 +205,11 @@
 # Provider Network Configurations
 # --------------------------------
 
-# The following variables control the Neutron openvswitch and
-# linuxbridge plugins' allocation of tenant networks and
-# availability of provider networks. If these are not configured
-# in ``localrc``, tenant networks will be local to the host (with no
-# remote connectivity), and no physical resources will be
-# available for the allocation of provider networks.
+# The following variables control the Neutron ML2 plugins' allocation
+# of tenant networks and availability of provider networks. If these
+# are not configured in ``localrc``, tenant networks will be local to
+# the host (with no remote connectivity), and no physical resources
+# will be available for the allocation of provider networks.
 
 # To disable tunnels (GRE or VXLAN) for tenant networks,
 # set to False in ``local.conf``.
@@ -231,8 +222,8 @@
 TENANT_TUNNEL_RANGES=${TENANT_TUNNEL_RANGES:-1:1000}
 
 # To use VLANs for tenant networks, set to True in localrc. VLANs
-# are supported by the openvswitch and linuxbridge plugins, each
-# requiring additional configuration described below.
+# are supported by the ML2 plugins, requiring additional configuration
+# described below.
 ENABLE_TENANT_VLANS=${ENABLE_TENANT_VLANS:-False}
 
 # If using VLANs for tenant networks, set in ``localrc`` to specify
@@ -252,7 +243,7 @@
 # Example: ``PHYSICAL_NETWORK=default``
 PHYSICAL_NETWORK=${PHYSICAL_NETWORK:-}
 
-# With the openvswitch plugin, if using VLANs for tenant networks,
+# With the openvswitch agent, if using VLANs for tenant networks,
 # or if using flat or VLAN provider networks, set in ``localrc`` to
 # the name of the OVS bridge to use for the physical network. The
 # bridge will be created if it does not already exist, but a
@@ -262,7 +253,7 @@
 # Example: ``OVS_PHYSICAL_BRIDGE=br-eth1``
 OVS_PHYSICAL_BRIDGE=${OVS_PHYSICAL_BRIDGE:-}
 
-# With the linuxbridge plugin, if using VLANs for tenant networks,
+# With the linuxbridge agent, if using VLANs for tenant networks,
 # or if using flat or VLAN provider networks, set in ``localrc`` to
 # the name of the network interface to use for the physical
 # network.
@@ -283,6 +274,14 @@
 # Example: ``OVS_ENABLE_TUNNELING=True``
 OVS_ENABLE_TUNNELING=${OVS_ENABLE_TUNNELING:-$ENABLE_TENANT_TUNNELS}
 
+# Use DHCP agent for providing metadata service in the case of
+# without L3 agent (No Route Agent), set to True in localrc.
+ENABLE_ISOLATED_METADATA=${ENABLE_ISOLATED_METADATA:-False}
+
+# Add a static route as dhcp option, so the request to 169.254.169.254
+# will be able to reach through a route(DHCP agent)
+# This option require ENABLE_ISOLATED_METADATA = True
+ENABLE_METADATA_NETWORK=${ENABLE_METADATA_NETWORK:-False}
 # Neutron plugin specific functions
 # ---------------------------------
 
@@ -595,6 +594,24 @@
 function install_neutron {
     git_clone $NEUTRON_REPO $NEUTRON_DIR $NEUTRON_BRANCH
     setup_develop $NEUTRON_DIR
+
+    if [ "$VIRT_DRIVER" == 'xenserver' ]; then
+        local dom0_ip
+        dom0_ip=$(echo "$XENAPI_CONNECTION_URL" | cut -d "/" -f 3-)
+
+        local ssh_dom0
+        ssh_dom0="sudo -u $DOMZERO_USER ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$dom0_ip"
+
+        # Find where the plugins should go in dom0
+        local xen_functions
+        xen_functions=$(cat $TOP_DIR/tools/xen/functions)
+        local plugin_dir
+        plugin_dir=$($ssh_dom0 "$xen_functions; set -eux; xapi_plugin_location")
+
+        # install neutron plugins to dom0
+        tar -czf - -C $NEUTRON_DIR/neutron/plugins/openvswitch/agent/xenapi/etc/xapi.d/plugins/ ./ |
+            $ssh_dom0 "tar -xzf - -C $plugin_dir && chmod a+x $plugin_dir/*"
+    fi
 }
 
 # install_neutronclient() - Collect source and prepare
@@ -822,6 +839,17 @@
     iniset $Q_DHCP_CONF_FILE DEFAULT use_namespaces $Q_USE_NAMESPACE
     iniset $Q_DHCP_CONF_FILE DEFAULT root_helper "$Q_RR_COMMAND"
 
+    if ! is_service_enabled q-l3; then
+        if [[ "$ENABLE_ISOLATED_METADATA" = "True" ]]; then
+            iniset $Q_DHCP_CONF_FILE DEFAULT enable_isolated_metadata $ENABLE_ISOLATED_METADATA
+            iniset $Q_DHCP_CONF_FILE DEFAULT enable_metadata_network $ENABLE_METADATA_NETWORK
+        else
+            if [[ "$ENABLE_METADATA_NETWORK" = "True" ]]; then
+                die "$LINENO" "Enable isolated metadata is a must for metadata network"
+            fi
+        fi
+    fi
+
     _neutron_setup_interface_driver $Q_DHCP_CONF_FILE
 
     neutron_plugin_configure_dhcp_agent
diff --git a/lib/neutron_plugins/linuxbridge b/lib/neutron_plugins/linuxbridge
deleted file mode 100644
index 5f989ae..0000000
--- a/lib/neutron_plugins/linuxbridge
+++ /dev/null
@@ -1,55 +0,0 @@
-# Neutron Linux Bridge plugin
-# ---------------------------
-
-# Save trace setting
-LBRIDGE_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-source $TOP_DIR/lib/neutron_plugins/linuxbridge_agent
-
-function neutron_plugin_configure_common {
-    Q_PLUGIN_CONF_PATH=etc/neutron/plugins/linuxbridge
-    Q_PLUGIN_CONF_FILENAME=linuxbridge_conf.ini
-    Q_PLUGIN_CLASS="neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2"
-}
-
-function neutron_plugin_configure_service {
-    if [[ "$ENABLE_TENANT_VLANS" == "True" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE vlans tenant_network_type vlan
-    else
-        echo "WARNING - The linuxbridge plugin is using local tenant networks, with no connectivity between hosts."
-    fi
-
-    # Override ``LB_VLAN_RANGES`` and ``LB_INTERFACE_MAPPINGS`` in ``localrc``
-    # for more complex physical network configurations.
-    if [[ "$LB_VLAN_RANGES" == "" ]] && [[ "$PHYSICAL_NETWORK" != "" ]]; then
-        LB_VLAN_RANGES=$PHYSICAL_NETWORK
-        if [[ "$TENANT_VLAN_RANGE" != "" ]]; then
-            LB_VLAN_RANGES=$LB_VLAN_RANGES:$TENANT_VLAN_RANGE
-        fi
-    fi
-    if [[ "$LB_VLAN_RANGES" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE vlans network_vlan_ranges $LB_VLAN_RANGES
-    fi
-    if [[ "$Q_USE_SECGROUP" == "True" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
-    else
-        iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
-    fi
-
-    # Define extra "LINUX_BRIDGE" configuration options when q-svc is configured by defining
-    # the array ``Q_SRV_EXTRA_OPTS``.
-    # For Example: ``Q_SRV_EXTRA_OPTS=(foo=true bar=2)``
-    for I in "${Q_SRV_EXTRA_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE linux_bridge ${I/=/ }
-    done
-}
-
-function has_neutron_plugin_security_group {
-    # 0 means True here
-    return 0
-}
-
-# Restore xtrace
-$LBRIDGE_XTRACE
diff --git a/lib/neutron_plugins/linuxbridge_agent b/lib/neutron_plugins/linuxbridge_agent
deleted file mode 100644
index 2638dd3..0000000
--- a/lib/neutron_plugins/linuxbridge_agent
+++ /dev/null
@@ -1,77 +0,0 @@
-# Neutron Linux Bridge L2 agent
-# -----------------------------
-
-# Save trace setting
-PLUGIN_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-function is_neutron_ovs_base_plugin {
-    # linuxbridge doesn't use OVS
-    return 1
-}
-
-function neutron_plugin_create_nova_conf {
-    :
-}
-
-function neutron_plugin_install_agent_packages {
-    install_package bridge-utils
-}
-
-function neutron_plugin_configure_debug_command {
-    iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT external_network_bridge
-}
-
-function neutron_plugin_configure_dhcp_agent {
-    iniset $Q_DHCP_CONF_FILE DEFAULT dhcp_agent_manager neutron.agent.dhcp_agent.DhcpAgentWithStateReport
-}
-
-function neutron_plugin_configure_l3_agent {
-    iniset $Q_L3_CONF_FILE DEFAULT external_network_bridge
-    iniset $Q_L3_CONF_FILE DEFAULT l3_agent_manager neutron.agent.l3_agent.L3NATAgentWithStateReport
-}
-
-function neutron_plugin_configure_plugin_agent {
-    # Setup physical network interface mappings.  Override
-    # ``LB_VLAN_RANGES`` and ``LB_INTERFACE_MAPPINGS`` in ``localrc`` for more
-    # complex physical network configurations.
-    if [[ "$LB_INTERFACE_MAPPINGS" == "" ]] && [[ "$PHYSICAL_NETWORK" != "" ]] && [[ "$LB_PHYSICAL_INTERFACE" != "" ]]; then
-        LB_INTERFACE_MAPPINGS=$PHYSICAL_NETWORK:$LB_PHYSICAL_INTERFACE
-    fi
-    if [[ "$LB_INTERFACE_MAPPINGS" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE linux_bridge physical_interface_mappings $LB_INTERFACE_MAPPINGS
-    fi
-    if [[ "$Q_USE_SECGROUP" == "True" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
-    else
-        iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
-    fi
-    AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-linuxbridge-agent"
-    iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
-    # Define extra "AGENT" configuration options when q-agt is configured by defining
-    # the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
-    # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
-    for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE agent ${I/=/ }
-    done
-    # Define extra "LINUX_BRIDGE" configuration options when q-agt is configured by defining
-    # the array ``Q_AGENT_EXTRA_SRV_OPTS``.
-    # For Example: ``Q_AGENT_EXTRA_SRV_OPTS=(foo=true bar=2)``
-    for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE linux_bridge ${I/=/ }
-    done
-}
-
-function neutron_plugin_setup_interface_driver {
-    local conf_file=$1
-    iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.BridgeInterfaceDriver
-}
-
-function neutron_plugin_check_adv_test_requirements {
-    is_service_enabled q-agt && is_service_enabled q-dhcp && return 0
-}
-
-# Restore xtrace
-$PLUGIN_XTRACE
diff --git a/lib/neutron_plugins/ofagent_agent b/lib/neutron_plugins/ofagent_agent
index 1c04f2f..55f3f72 100644
--- a/lib/neutron_plugins/ofagent_agent
+++ b/lib/neutron_plugins/ofagent_agent
@@ -61,7 +61,6 @@
         if [ `vercmp_numbers "$OVS_VERSION" "1.4"` -lt "0" ]; then
             die $LINENO "You are running OVS version $OVS_VERSION. OVS 1.4+ is required for tunneling between multiple hosts."
         fi
-        iniset /$Q_PLUGIN_CONF_FILE ovs enable_tunneling True
         iniset /$Q_PLUGIN_CONF_FILE ovs local_ip $TUNNEL_ENDPOINT_IP
     fi
 
diff --git a/lib/neutron_plugins/openvswitch b/lib/neutron_plugins/openvswitch
index c468132..3b6567c 100644
--- a/lib/neutron_plugins/openvswitch
+++ b/lib/neutron_plugins/openvswitch
@@ -1,5 +1,8 @@
-# Neutron Open vSwitch plugin
-# ---------------------------
+# Common code used by cisco and embrane plugins
+# ---------------------------------------------
+
+# This module used to be for Open vSwitch monolithic plugin,
+# which has been removed in Juno.
 
 # Save trace setting
 OVS_XTRACE=$(set +o | grep xtrace)
@@ -35,11 +38,6 @@
         iniset /$Q_PLUGIN_CONF_FILE ovs network_vlan_ranges $OVS_VLAN_RANGES
     fi
 
-    # Enable tunnel networks if selected
-    if [[ $OVS_ENABLE_TUNNELING == "True" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE ovs enable_tunneling True
-    fi
-
     _neutron_ovs_base_configure_firewall_driver
 
     # Define extra "OVS" configuration options when q-svc is configured by defining
diff --git a/lib/neutron_plugins/openvswitch_agent b/lib/neutron_plugins/openvswitch_agent
index 835f645..e1a6f4a 100644
--- a/lib/neutron_plugins/openvswitch_agent
+++ b/lib/neutron_plugins/openvswitch_agent
@@ -47,7 +47,6 @@
         if [ `vercmp_numbers "$OVS_VERSION" "1.4"` -lt "0" ] && ! is_service_enabled q-svc ; then
             die $LINENO "You are running OVS version $OVS_VERSION. OVS 1.4+ is required for tunneling between multiple hosts."
         fi
-        iniset /$Q_PLUGIN_CONF_FILE ovs enable_tunneling True
         iniset /$Q_PLUGIN_CONF_FILE ovs local_ip $TUNNEL_ENDPOINT_IP
     fi
 
diff --git a/lib/nova b/lib/nova
index f47dc97..0f83807 100644
--- a/lib/nova
+++ b/lib/nova
@@ -137,6 +137,10 @@
 # running the VM - removing a SPOF and bandwidth bottleneck.
 MULTI_HOST=`trueorfalse False $MULTI_HOST`
 
+# ``NOVA_ALLOW_MOVE_TO_SAME_HOST` can be set to False in multi node devstack,
+# where there are at least two nova-computes.
+NOVA_ALLOW_MOVE_TO_SAME_HOST=`trueorfalse True $NOVA_ALLOW_MOVE_TO_SAME_HOST`
+
 # Test floating pool and range are used for testing.  They are defined
 # here until the admin APIs can replace nova-manage
 TEST_FLOATING_POOL=${TEST_FLOATING_POOL:-test}
@@ -411,8 +415,10 @@
     rm -f $NOVA_CONF
     iniset $NOVA_CONF DEFAULT verbose "True"
     iniset $NOVA_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
-    iniset $NOVA_CONF DEFAULT allow_resize_to_same_host "True"
-    iniset $NOVA_CONF DEFAULT allow_migrate_to_same_host "True"
+    if [ "$NOVA_ALLOW_MOVE_TO_SAME_HOST" == "True" ]; then
+        iniset $NOVA_CONF DEFAULT allow_resize_to_same_host "True"
+        iniset $NOVA_CONF DEFAULT allow_migrate_to_same_host "True"
+    fi
     iniset $NOVA_CONF DEFAULT api_paste_config "$NOVA_API_PASTE_INI"
     iniset $NOVA_CONF DEFAULT rootwrap_config "$NOVA_CONF_DIR/rootwrap.conf"
     iniset $NOVA_CONF DEFAULT scheduler_driver "$SCHEDULER"
diff --git a/lib/sahara b/lib/sahara
index 5c7c253..6d1bef5 100644
--- a/lib/sahara
+++ b/lib/sahara
@@ -98,12 +98,17 @@
     fi
     sudo chown $STACK_USER $SAHARA_CONF_DIR
 
+    if [[ -f $SAHARA_DIR/etc/sahara/policy.json ]]; then
+        cp -p $SAHARA_DIR/etc/sahara/policy.json $SAHARA_CONF_DIR
+    fi
+
     # Copy over sahara configuration file and configure common parameters.
     cp $SAHARA_DIR/etc/sahara/sahara.conf.sample $SAHARA_CONF_FILE
 
     # Create auth cache dir
     sudo mkdir -p $SAHARA_AUTH_CACHE_DIR
     sudo chown $STACK_USER $SAHARA_AUTH_CACHE_DIR
+    sudo chmod 700 $SAHARA_AUTH_CACHE_DIR
     rm -rf $SAHARA_AUTH_CACHE_DIR/*
 
     configure_auth_token_middleware $SAHARA_CONF_FILE sahara $SAHARA_AUTH_CACHE_DIR
diff --git a/lib/trove b/lib/trove
index 1d1b5f4..4ac7293 100644
--- a/lib/trove
+++ b/lib/trove
@@ -207,7 +207,7 @@
     # The image is uploaded by stack.sh -- see $IMAGE_URLS handling
     GUEST_IMAGE_NAME=$(basename "$TROVE_GUEST_IMAGE_URL")
     GUEST_IMAGE_NAME=${GUEST_IMAGE_NAME%.*}
-    TROVE_GUEST_IMAGE_ID=$(openstack --os-token $TOKEN --os-url http://$GLANCE_HOSTPORT image list | grep "${GUEST_IMAGE_NAME}" | get_field 1)
+    TROVE_GUEST_IMAGE_ID=$(openstack --os-token $TOKEN --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image list | grep "${GUEST_IMAGE_NAME}" | get_field 1)
     if [ -z "$TROVE_GUEST_IMAGE_ID" ]; then
         # If no glance id is found, skip remaining setup
         echo "Datastore ${TROVE_DATASTORE_TYPE} will not be created: guest image ${GUEST_IMAGE_NAME} not found."
diff --git a/stackrc b/stackrc
index 6cec8e8..15b0951 100644
--- a/stackrc
+++ b/stackrc
@@ -116,6 +116,17 @@
 # Zero disables timeouts
 GIT_TIMEOUT=${GIT_TIMEOUT:-0}
 
+# Requirements enforcing mode
+#
+# - strict (default) : ensure all project requirements files match
+#   what's in global requirements.
+#
+# - soft : enforce requirements on everything in
+#   requirements/projects.txt, but do soft updates on all other
+#   repositories (i.e. sync versions for requirements that are in g-r,
+#   but pass through any extras)
+REQUIREMENTS_MODE=${REQUIREMENTS_MODE:-strict}
+
 # Repositories
 # ------------
 
@@ -522,11 +533,11 @@
     esac
 fi
 
-# Trove needs a custom image for it's work
+# Trove needs a custom image for its work
 if [[ "$ENABLED_SERVICES" =~ 'tr-api' ]]; then
     case "$VIRT_DRIVER" in
         libvirt|baremetal|ironic|xenapi)
-            TROVE_GUEST_IMAGE_URL=${TROVE_GUEST_IMAGE_URL:-"http://tarballs.openstack.org/trove/images/ubuntu_mysql.qcow2/ubuntu_mysql.qcow2"}
+            TROVE_GUEST_IMAGE_URL=${TROVE_GUEST_IMAGE_URL:-"http://tarballs.openstack.org/trove/images/ubuntu/mysql.qcow2"}
             IMAGE_URLS+=",${TROVE_GUEST_IMAGE_URL}"
             ;;
         *)
diff --git a/tests/test_config.sh b/tests/test_config.sh
index cd74cee..3252104 100755
--- a/tests/test_config.sh
+++ b/tests/test_config.sh
@@ -92,9 +92,9 @@
 [fff]
 type=new
 
-[[test-quote|test-quote.conf]]
+[[test-env|test-env.conf]]
 [foo]
-foo="foo bar" "baz"
+foo=\${FOO_BAR_BAZ}
 
 [[test5|test-equals.conf]]
 [DEFAULT]
@@ -126,9 +126,11 @@
 
 [[test-multiline|test-multiline.conf]]
 [multi]
-cfg_item1 = "ab":"cd", "ef":   "gh"
+cfg_item1 = ab:cd:ef:gh
 cfg_item1 = abcd
 cfg_item2 = efgh
+cfg_item2 = \${FOO_BAR_BAZ}
+
 EOF
 
 echo -n "get_meta_section_files: test0 doesn't exist: "
@@ -236,14 +238,17 @@
 
 echo -n "merge_config_file test-multiline: "
 rm -f test-multiline.conf
+FOO_BAR_BAZ="foo bar baz"
 merge_config_file test.conf test-multiline test-multiline.conf
 VAL=$(cat test-multiline.conf)
 EXPECT_VAL='
 [multi]
-cfg_item1 = "ab":"cd", "ef":   "gh"
+cfg_item1 = ab:cd:ef:gh
 cfg_item1 = abcd
-cfg_item2 = efgh'
+cfg_item2 = efgh
+cfg_item2 = foo bar baz'
 check_result "$VAL" "$EXPECT_VAL"
+unset FOO_BAR_BAZ
 
 echo -n "merge_config_group test2: "
 rm test2a.conf
@@ -275,14 +280,16 @@
 attribute = value"
 check_result "$VAL" "$EXPECT_VAL"
 
-echo -n "merge_config_file test-quote: "
-rm -f test-quote.conf
-merge_config_file test.conf test-quote test-quote.conf
-VAL=$(cat test-quote.conf)
+echo -n "merge_config_file test-env: "
+rm -f test-env.conf
+FOO_BAR_BAZ="foo bar baz"
+merge_config_file test.conf test-env test-env.conf
+VAL=$(cat test-env.conf)
 EXPECT_VAL='
 [foo]
-foo = "foo bar" "baz"'
+foo = foo bar baz'
 check_result "$VAL" "$EXPECT_VAL"
+unset FOO_BAR_BAZ
 
 echo -n "merge_config_group test4 variable filename: "
 setup_test4
@@ -332,6 +339,8 @@
 servers = 10.11.12.13:80"
 check_result "$VAL" "$EXPECT_VAL"
 
-rm -f test.conf test1c.conf test2a.conf test-quote.conf test-space.conf test-equals.conf test-strip.conf test-colon.conf
-rm -f test-multiline.conf test-multi-sections.conf
+rm -f test.conf test1c.conf test2a.conf \
+    test-space.conf test-equals.conf test-strip.conf \
+    test-colon.conf test-env.conf test-multiline.conf \
+    test-multi-sections.conf
 rm -rf test-etc
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 1732ecc..b8beb01 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -50,17 +50,24 @@
 # exception into the Kernel for the Keystone AUTH ports.
 keystone_ports=${KEYSTONE_AUTH_PORT:-35357},${KEYSTONE_AUTH_PORT_INT:-35358}
 
-# Get any currently reserved ports, strip off leading whitespace
-reserved_ports=$(sysctl net.ipv4.ip_local_reserved_ports | awk -F'=' '{print $2;}' | sed 's/^ //')
+# only do the reserved ports when available, on some system (like containers)
+# where it's not exposed we are almost pretty sure these ports would be
+# exclusive for our devstack.
+if sysctl net.ipv4.ip_local_reserved_ports >/dev/null 2>&1; then
+    # Get any currently reserved ports, strip off leading whitespace
+    reserved_ports=$(sysctl net.ipv4.ip_local_reserved_ports | awk -F'=' '{print $2;}' | sed 's/^ //')
 
-if [[ -z "${reserved_ports}" ]]; then
-    # If there are no currently reserved ports, reserve the keystone ports
-    sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports}
+    if [[ -z "${reserved_ports}" ]]; then
+        # If there are no currently reserved ports, reserve the keystone ports
+        sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports}
+    else
+        # If there are currently reserved ports, keep those and also reserve the
+        # keystone specific ports. Duplicate reservations are merged into a single
+        # reservation (or range) automatically by the kernel.
+        sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports},${reserved_ports}
+    fi
 else
-    # If there are currently reserved ports, keep those and also reserve the
-    # keystone specific ports. Duplicate reservations are merged into a single
-    # reservation (or range) automatically by the kernel.
-    sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports},${reserved_ports}
+    echo_summary "WARNING: unable to reserve keystone ports"
 fi
 
 
diff --git a/tools/xen/build_xva.sh b/tools/xen/build_xva.sh
index cc3cbe1..7c8e620 100755
--- a/tools/xen/build_xva.sh
+++ b/tools/xen/build_xva.sh
@@ -21,19 +21,12 @@
 # This directory
 TOP_DIR=$(cd $(dirname "$0") && pwd)
 
-# Source lower level functions
-. $TOP_DIR/../../functions
-
 # Include onexit commands
 . $TOP_DIR/scripts/on_exit.sh
 
 # xapi functions
 . $TOP_DIR/functions
 
-# Determine what system we are running on.
-# Might not be XenServer if we're using xenserver-core
-GetDistro
-
 # Source params - override xenrc params in your localrc to suite your taste
 source xenrc
 
diff --git a/tools/xen/functions b/tools/xen/functions
index 4317796..c8efd57 100644
--- a/tools/xen/functions
+++ b/tools/xen/functions
@@ -19,10 +19,6 @@
     return 1
 }
 
-function zip_snapshot_location {
-    echo $1 | sed "s,^git://,http://,g;s:\.git$::;s:$:/zipball/$2:g"
-}
-
 function create_directory_for_kernels {
     if [ -d "/boot/guest" ]; then
         echo "INFO: /boot/guest directory already exists, using that" >&2
@@ -43,42 +39,6 @@
     fi
 }
 
-function extract_remote_zipball {
-    local ZIPBALL_URL=$1
-
-    local LOCAL_ZIPBALL=$(mktemp)
-    local EXTRACTED_FILES=$(mktemp -d)
-
-    {
-        if ! wget -nv $ZIPBALL_URL -O $LOCAL_ZIPBALL --no-check-certificate; then
-            die_with_error "Failed to download [$ZIPBALL_URL]"
-        fi
-        unzip -q -o $LOCAL_ZIPBALL -d $EXTRACTED_FILES
-        rm -f $LOCAL_ZIPBALL
-    } >&2
-
-    echo "$EXTRACTED_FILES"
-}
-
-function find_xapi_plugins_dir {
-    find $1 -path '*/xapi.d/plugins' -type d -print
-}
-
-function install_xapi_plugins_from {
-    local XAPI_PLUGIN_DIR
-    local EXTRACTED_FILES
-    local EXTRACTED_PLUGINS_DIR
-
-    EXTRACTED_FILES="$1"
-
-    XAPI_PLUGIN_DIR=$(xapi_plugin_location)
-
-    EXTRACTED_PLUGINS_DIR=$(find_xapi_plugins_dir $EXTRACTED_FILES)
-
-    cp -pr $EXTRACTED_PLUGINS_DIR/* $XAPI_PLUGIN_DIR
-    chmod a+x ${XAPI_PLUGIN_DIR}*
-}
-
 function get_local_sr {
     xe pool-list params=default-SR minimal=true
 }
diff --git a/tools/xen/install_os_domU.sh b/tools/xen/install_os_domU.sh
index 439db68..3a63473 100755
--- a/tools/xen/install_os_domU.sh
+++ b/tools/xen/install_os_domU.sh
@@ -22,19 +22,12 @@
 # This directory
 THIS_DIR=$(cd $(dirname "$0") && pwd)
 
-# Source lower level functions
-. $THIS_DIR/../../functions
-
 # Include onexit commands
 . $THIS_DIR/scripts/on_exit.sh
 
 # xapi functions
 . $THIS_DIR/functions
 
-# Determine what system we are running on.
-# Might not be XenServer if we're using xenserver-core
-GetDistro
-
 #
 # Get Settings
 #
@@ -65,16 +58,6 @@
     exit 1
 fi
 
-# Install plugins
-
-## Install the netwrap xapi plugin to support agent control of dom0 networking
-if [[ "$ENABLED_SERVICES" =~ "q-agt" && "$Q_PLUGIN" = "openvswitch" ]]; then
-    NEUTRON_ZIPBALL_URL=${NEUTRON_ZIPBALL_URL:-$(zip_snapshot_location $NEUTRON_REPO $NEUTRON_BRANCH)}
-    EXTRACTED_NEUTRON=$(extract_remote_zipball "$NEUTRON_ZIPBALL_URL")
-    install_xapi_plugins_from "$EXTRACTED_NEUTRON"
-    rm -rf "$EXTRACTED_NEUTRON"
-fi
-
 #
 # Configure Networking
 #
@@ -88,9 +71,7 @@
 
 # With neutron, one more network is required, which is internal to the
 # hypervisor, and used by the VMs
-if is_service_enabled neutron; then
-    setup_network "$XEN_INT_BRIDGE_OR_NET_NAME"
-fi
+setup_network "$XEN_INT_BRIDGE_OR_NET_NAME"
 
 if parameter_is_specified "FLAT_NETWORK_BRIDGE"; then
     if [ "$(bridge_for "$VM_BRIDGE_OR_NET_NAME")" != "$(bridge_for "$FLAT_NETWORK_BRIDGE")" ]; then
@@ -292,14 +273,12 @@
 # Attach a network interface for the integration network (so that the bridge
 # is created by XenServer). This is required for Neutron. Also pass that as a
 # kernel parameter for DomU
-if is_service_enabled neutron; then
-    attach_network "$XEN_INT_BRIDGE_OR_NET_NAME"
+attach_network "$XEN_INT_BRIDGE_OR_NET_NAME"
 
-    XEN_INTEGRATION_BRIDGE=$(bridge_for "$XEN_INT_BRIDGE_OR_NET_NAME")
-    append_kernel_cmdline \
-        "$GUEST_NAME" \
-        "xen_integration_bridge=${XEN_INTEGRATION_BRIDGE}"
-fi
+XEN_INTEGRATION_BRIDGE=$(bridge_for "$XEN_INT_BRIDGE_OR_NET_NAME")
+append_kernel_cmdline \
+    "$GUEST_NAME" \
+    "xen_integration_bridge=${XEN_INTEGRATION_BRIDGE}"
 
 FLAT_NETWORK_BRIDGE="${FLAT_NETWORK_BRIDGE:-$(bridge_for "$VM_BRIDGE_OR_NET_NAME")}"
 append_kernel_cmdline "$GUEST_NAME" "flat_network_bridge=${FLAT_NETWORK_BRIDGE}"
diff --git a/tools/xen/prepare_guest_template.sh b/tools/xen/prepare_guest_template.sh
index 2d3b898..6cb2ca7 100755
--- a/tools/xen/prepare_guest_template.sh
+++ b/tools/xen/prepare_guest_template.sh
@@ -22,19 +22,12 @@
 # This directory
 TOP_DIR=$(cd $(dirname "$0") && pwd)
 
-# Source lower level functions
-. $TOP_DIR/../../functions
-
 # Include onexit commands
 . $TOP_DIR/scripts/on_exit.sh
 
 # xapi functions
 . $TOP_DIR/functions
 
-# Determine what system we are running on.
-# Might not be XenServer if we're using xenserver-core
-GetDistro
-
 # Source params - override xenrc params in your localrc to suite your taste
 source xenrc
 
@@ -118,3 +111,10 @@
 deb http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security multiverse
 deb-src http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security multiverse
 EOF
+
+rm -f $STAGING_DIR/etc/apt/apt.conf
+if [ -n "$UBUNTU_INST_HTTP_PROXY" ]; then
+    cat > $STAGING_DIR/etc/apt/apt.conf << EOF
+Acquire::http::Proxy "$UBUNTU_INST_HTTP_PROXY";
+EOF
+fi
diff --git a/tools/xen/scripts/install_ubuntu_template.sh b/tools/xen/scripts/install_ubuntu_template.sh
index d4d6567..d80ed09 100755
--- a/tools/xen/scripts/install_ubuntu_template.sh
+++ b/tools/xen/scripts/install_ubuntu_template.sh
@@ -14,9 +14,6 @@
 # This directory
 BASE_DIR=$(cd $(dirname "$0") && pwd)
 
-# Source the top level functions
-source $BASE_DIR/../../../functions
-
 # For default setings see xenrc
 source $BASE_DIR/../xenrc
 
diff --git a/tools/xen/test_functions.sh b/tools/xen/test_functions.sh
index 838f86a..924e773 100755
--- a/tools/xen/test_functions.sh
+++ b/tools/xen/test_functions.sh
@@ -116,18 +116,6 @@
     grep "[ -d /usr/lib/xcp/plugins/ ]" $LIST_OF_ACTIONS
 }
 
-function test_zip_snapshot_location_http {
-    diff \
-    <(zip_snapshot_location "http://github.com/openstack/nova.git" "master") \
-    <(echo "http://github.com/openstack/nova/zipball/master")
-}
-
-function test_zip_snapsot_location_git {
-    diff \
-    <(zip_snapshot_location "git://github.com/openstack/nova.git" "master") \
-    <(echo "http://github.com/openstack/nova/zipball/master")
-}
-
 function test_create_directory_for_kernels {
     (
         . mocks
@@ -174,37 +162,6 @@
 EOF
 }
 
-function test_extract_remote_zipball {
-    local RESULT=$(. mocks && extract_remote_zipball "someurl")
-
-    diff <(cat $LIST_OF_ACTIONS) - << EOF
-wget -nv someurl -O tempfile --no-check-certificate
-unzip -q -o tempfile -d tempdir
-rm -f tempfile
-EOF
-
-    [ "$RESULT" = "tempdir" ]
-}
-
-function test_extract_remote_zipball_wget_fail {
-    set +e
-
-    local IGNORE
-    IGNORE=$(. mocks && extract_remote_zipball "failurl")
-
-    assert_died_with "Failed to download [failurl]"
-}
-
-function test_find_nova_plugins {
-    local tmpdir=$(mktemp -d)
-
-    mkdir -p "$tmpdir/blah/blah/u/xapi.d/plugins"
-
-    [ "$tmpdir/blah/blah/u/xapi.d/plugins" = $(find_xapi_plugins_dir $tmpdir) ]
-
-    rm -rf $tmpdir
-}
-
 function test_get_local_sr {
     setup_xe_response "uuid123"
 
diff --git a/tools/xen/xenrc b/tools/xen/xenrc
index 510c5f9..0cbf861 100644
--- a/tools/xen/xenrc
+++ b/tools/xen/xenrc
@@ -91,7 +91,24 @@
 # Set the size to 0 to avoid creation of additional disk.
 XEN_XVDB_SIZE_GB=0
 
-restore_nounset=`set +o | grep nounset`
+STACK_USER=stack
+DOMZERO_USER=domzero
+
+RC_DIR="../.."
+
+restore_nounset=$(set +o | grep nounset)
 set +u
-source ../../stackrc
+
+## Note that the lines below are coming from stackrc to support
+## new-style config files
+
+# allow local overrides of env variables, including repo config
+if [[ -f $RC_DIR/localrc ]]; then
+    # Old-style user-supplied config
+    source $RC_DIR/localrc
+elif [[ -f $RC_DIR/.localrc.auto ]]; then
+    # New-style user-supplied config extracted from local.conf
+    source $RC_DIR/.localrc.auto
+fi
+
 $restore_nounset
diff --git a/tox.ini b/tox.ini
index b6f2d96..c8d3909 100644
--- a/tox.ini
+++ b/tox.ini
@@ -10,7 +10,19 @@
 [testenv:bashate]
 deps = bashate
 whitelist_externals = bash
-commands = bash -c "find {toxinidir} -not -wholename \*.tox/\* -and \( -name \*.sh -or -name \*rc -or -name functions\* -or \( -wholename lib/\* -and -not -name \*.md \) \) -print0 | xargs -0 bashate -v"
+commands = bash -c "find {toxinidir}          \
+         -not \( -type d -name .?\* -prune \) \ # prune all 'dot' dirs
+         -not \( -type d -name doc -prune \)  \ # skip documentation
+         -type f                              \ # only files
+         -not -name \*~                       \ # skip editors, readme, etc
+         -not -name \*.md                     \
+         \(                                   \
+          -name \*.sh -or                     \
+          -name \*rc -or                      \
+          -name functions\* -or               \
+          -wholename \*/lib/\*                \ # /lib files are shell, but
+         \)                                   \ #   have no extension
+         -print0 | xargs -0 bashate -v"
 
 [testenv:docs]
 deps =