Merge "Automate OVS bridge creation for multiple mappings"
diff --git a/clean.sh b/clean.sh
index c8b8223..3db4858 100755
--- a/clean.sh
+++ b/clean.sh
@@ -51,6 +51,7 @@
 source $TOP_DIR/lib/heat
 source $TOP_DIR/lib/neutron-legacy
 
+set -o xtrace
 
 # Extras Source
 # --------------
diff --git a/doc/source/guides/neutron.rst b/doc/source/guides/neutron.rst
index be85b63..c8b5c44 100644
--- a/doc/source/guides/neutron.rst
+++ b/doc/source/guides/neutron.rst
@@ -448,6 +448,7 @@
 the `localrc` part of `local.conf` on each machine.
 
 ::
+
     Q_ML2_PLUGIN_PATH_MTU=1500
 
 
diff --git a/extras.d/80-tempest.sh b/extras.d/80-tempest.sh
index 5e8da99..fcf79bd 100644
--- a/extras.d/80-tempest.sh
+++ b/extras.d/80-tempest.sh
@@ -9,7 +9,7 @@
         install_tempest
     elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
         # Tempest config must come after layer 2 services are running
-        create_tempest_accounts
+        :
     elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
         echo_summary "Initializing Tempest"
         configure_tempest
@@ -28,4 +28,3 @@
         :
     fi
 fi
-
diff --git a/files/apache-keystone.template b/files/apache-keystone.template
index f9fa265..428544f 100644
--- a/files/apache-keystone.template
+++ b/files/apache-keystone.template
@@ -3,13 +3,7 @@
 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D(us)" keystone_combined
 
 <Directory %KEYSTONE_BIN%>
-    <IfVersion >= 2.4>
-        Require all granted
-    </IfVersion>
-    <IfVersion < 2.4>
-        Order allow,deny
-        Allow from all
-    </IfVersion>
+    Require all granted
 </Directory>
 
 <VirtualHost *:%PUBLICPORT%>
@@ -18,9 +12,7 @@
     WSGIScriptAlias / %KEYSTONE_BIN%/keystone-wsgi-public
     WSGIApplicationGroup %{GLOBAL}
     WSGIPassAuthorization On
-    <IfVersion >= 2.4>
-      ErrorLogFormat "%{cu}t %M"
-    </IfVersion>
+    ErrorLogFormat "%M"
     ErrorLog /var/log/%APACHE_NAME%/keystone.log
     CustomLog /var/log/%APACHE_NAME%/keystone_access.log keystone_combined
     %SSLENGINE%
@@ -34,9 +26,7 @@
     WSGIScriptAlias / %KEYSTONE_BIN%/keystone-wsgi-admin
     WSGIApplicationGroup %{GLOBAL}
     WSGIPassAuthorization On
-    <IfVersion >= 2.4>
-      ErrorLogFormat "%{cu}t %M"
-    </IfVersion>
+    ErrorLogFormat "%M"
     ErrorLog /var/log/%APACHE_NAME%/keystone.log
     CustomLog /var/log/%APACHE_NAME%/keystone_access.log keystone_combined
     %SSLENGINE%
diff --git a/files/rpms/general b/files/rpms/general
index 5bc87b6..e0ef54c 100644
--- a/files/rpms/general
+++ b/files/rpms/general
@@ -26,7 +26,6 @@
 psmisc
 pyOpenSSL # version in pip uses too much memory
 python-devel
-redhat-rpm-config # MySQL-python rhbz-1195207
 screen
 tar
 tcpdump
diff --git a/functions-common b/functions-common
index 12c925b..6019a9c 100644
--- a/functions-common
+++ b/functions-common
@@ -86,6 +86,7 @@
     if [ -f "$SSL_BUNDLE_FILE" ]; then
         CA_CERT_ARG="--os-cacert $SSL_BUNDLE_FILE"
     fi
+    # demo -> devstack
     $TOP_DIR/tools/update_clouds_yaml.py \
         --file $CLOUDS_YAML \
         --os-cloud devstack \
@@ -96,6 +97,20 @@
         --os-username demo \
         --os-password $ADMIN_PASSWORD \
         --os-project-name demo
+
+    # alt_demo -> devstack-alt
+    $TOP_DIR/tools/update_clouds_yaml.py \
+        --file $CLOUDS_YAML \
+        --os-cloud devstack-alt \
+        --os-region-name $REGION_NAME \
+        --os-identity-api-version 3 \
+        $CA_CERT_ARG \
+        --os-auth-url $KEYSTONE_AUTH_URI \
+        --os-username alt_demo \
+        --os-password $ADMIN_PASSWORD \
+        --os-project-name alt_demo
+
+    # admin -> devstack-admin
     $TOP_DIR/tools/update_clouds_yaml.py \
         --file $CLOUDS_YAML \
         --os-cloud devstack-admin \
@@ -1323,27 +1338,35 @@
 # Uses globals ``OFFLINE``, ``*_proxy``, ``YUM``
 # yum_install package [package ...]
 function yum_install {
+    local result parse_yum_result
+
     [[ "$OFFLINE" = "True" ]] && return
-    local sudo="sudo"
-    [[ "$(id -u)" = "0" ]] && sudo="env"
+
+    time_start "yum_install"
+
+    # Warning: this would not work if yum output message
+    # have been translated to another language
+    parse_yum_result='\
+        BEGIN { result=0 }\
+        /^YUM_FAILED/ { exit $2 }\
+        /^No package/ { result=1 }\
+        //{ print }\
+        END { exit result }'
 
     # The manual check for missing packages is because yum -y assumes
-    # missing packages are OK.  See
-    # https://bugzilla.redhat.com/show_bug.cgi?id=965567
-    $sudo http_proxy="${http_proxy:-}" https_proxy="${https_proxy:-}" \
-        no_proxy="${no_proxy:-}" \
-        ${YUM:-yum} install -y "$@" 2>&1 | \
-        awk '
-            BEGIN { fail=0 }
-            /No package/ { fail=1 }
-            { print }
-            END { exit fail }' || \
-                die $LINENO "Missing packages detected"
+    # missing packages are OK.
+    # See https://bugzilla.redhat.com/show_bug.cgi?id=965567
+    (sudo_with_proxies "${YUM:-yum}" install -y "$@" 2>&1 || echo YUM_FAILED $?) \
+        | awk "$parse_yum_result"
+    result=$?
 
-    # also ensure we catch a yum failure
-    if [[ ${PIPESTATUS[0]} != 0 ]]; then
-        die $LINENO "${YUM:-yum} install failure"
+    if [ "$result" != 0 ]; then
+        echo $LINENO "${YUM:-yum}" install failure: $result
     fi
+
+    time_stop "yum_install"
+
+    return "$result"
 }
 
 # zypper wrapper to set arguments correctly
@@ -2282,6 +2305,18 @@
     time_stop "test_with_retry"
 }
 
+# Like sudo but forwarding http_proxy https_proxy no_proxy environment vars.
+# If it is run as superuser then sudo is replaced by env.
+#
+function sudo_with_proxies {
+    local sudo
+
+    [[ "$(id -u)" = "0" ]] && sudo="env" || sudo="sudo"
+
+    $sudo http_proxy="${http_proxy:-}" https_proxy="${https_proxy:-}"\
+        no_proxy="${no_proxy:-}" "$@"
+}
+
 # Timing infrastructure - figure out where large blocks of time are
 # used in DevStack
 #
diff --git a/lib/glance b/lib/glance
index 0431bba..fa93e6e 100644
--- a/lib/glance
+++ b/lib/glance
@@ -49,10 +49,8 @@
 GLANCE_METADEF_DIR=$GLANCE_CONF_DIR/metadefs
 GLANCE_REGISTRY_CONF=$GLANCE_CONF_DIR/glance-registry.conf
 GLANCE_API_CONF=$GLANCE_CONF_DIR/glance-api.conf
-GLANCE_SEARCH_CONF=$GLANCE_CONF_DIR/glance-search.conf
 GLANCE_REGISTRY_PASTE_INI=$GLANCE_CONF_DIR/glance-registry-paste.ini
 GLANCE_API_PASTE_INI=$GLANCE_CONF_DIR/glance-api-paste.ini
-GLANCE_SEARCH_PASTE_INI=$GLANCE_CONF_DIR/glance-search-paste.ini
 GLANCE_CACHE_CONF=$GLANCE_CONF_DIR/glance-cache.conf
 GLANCE_POLICY_JSON=$GLANCE_CONF_DIR/policy.json
 GLANCE_SCHEMA_JSON=$GLANCE_CONF_DIR/schema-image.json
@@ -71,9 +69,6 @@
 GLANCE_SERVICE_PROTOCOL=${GLANCE_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
 GLANCE_REGISTRY_PORT=${GLANCE_REGISTRY_PORT:-9191}
 GLANCE_REGISTRY_PORT_INT=${GLANCE_REGISTRY_PORT_INT:-19191}
-GLANCE_SEARCH_PORT=${GLANCE_SEARCH_PORT:-9393}
-GLANCE_SEARCH_PORT_INT=${GLANCE_SEARCH_PORT_INT:-19393}
-GLANCE_SEARCH_HOSTPORT=${GLANCE_SEARCH_HOSTPORT:-$GLANCE_SERVICE_HOST:$GLANCE_SEARCH_PORT}
 
 # Functions
 # ---------
@@ -91,10 +86,6 @@
     # kill instances (nova)
     # delete image files (glance)
     sudo rm -rf $GLANCE_CACHE_DIR $GLANCE_IMAGE_DIR $GLANCE_AUTH_CACHE_DIR
-
-    if is_service_enabled g-search; then
-        ${TOP_DIR}/pkg/elasticsearch.sh stop
-    fi
 }
 
 # configure_glance() - Set config files, create data dirs, etc
@@ -229,30 +220,6 @@
         iniset $GLANCE_API_CONF DEFAULT cinder_endpoint_template "https://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/%(project_id)s"
         iniset $GLANCE_CACHE_CONF DEFAULT cinder_endpoint_template "https://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/%(project_id)s"
     fi
-
-    # Configure search
-    if is_service_enabled g-search; then
-        cp $GLANCE_DIR/etc/glance-search.conf $GLANCE_SEARCH_CONF
-        iniset $GLANCE_SEARCH_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
-        iniset $GLANCE_SEARCH_CONF DEFAULT bind_host $GLANCE_SERVICE_LISTEN_ADDRESS
-        inicomment $GLANCE_SEARCH_CONF DEFAULT log_file
-        iniset $GLANCE_SEARCH_CONF DEFAULT use_syslog $SYSLOG
-        iniset $GLANCE_SEARCH_CONF database connection $dburl
-        iniset $GLANCE_SEARCH_CONF paste_deploy flavor keystone
-        configure_auth_token_middleware $GLANCE_SEARCH_CONF glance $GLANCE_AUTH_CACHE_DIR/search
-
-        if is_service_enabled tls-proxy; then
-            iniset $GLANCE_SEARCH_CONF DEFAULT bind_port $GLANCE_SEARCH_PORT_INT
-        fi
-        # Register SSL certificates if provided
-        if is_ssl_enabled_service glance; then
-            ensure_certificates GLANCE
-            iniset $GLANCE_SEARCH_CONF DEFAULT cert_file "$GLANCE_SSL_CERT"
-            iniset $GLANCE_SEARCH_CONF DEFAULT key_file "$GLANCE_SSL_KEY"
-        fi
-
-        cp $GLANCE_DIR/etc/glance-search-paste.ini $GLANCE_SEARCH_PASTE_INI
-    fi
 }
 
 # create_glance_accounts() - Set up common required glance accounts
@@ -287,19 +254,6 @@
                 "$GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT"
         fi
     fi
-
-    # Add glance-search service and endpoints
-    if is_service_enabled g-search; then
-        if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
-            get_or_create_service "glance-search" "search" "EXPERIMENTAL - Glance Graffiti Search Service"
-
-            get_or_create_endpoint "search" \
-                "$REGION_NAME" \
-                "$GLANCE_SERVICE_PROTOCOL://$GLANCE_SEARCH_HOSTPORT" \
-                "$GLANCE_SERVICE_PROTOCOL://$GLANCE_SEARCH_HOSTPORT" \
-                "$GLANCE_SERVICE_PROTOCOL://$GLANCE_SEARCH_HOSTPORT"
-        fi
-    fi
 }
 
 # create_glance_cache_dir() - Part of the init_glance() process
@@ -329,12 +283,6 @@
     $GLANCE_BIN_DIR/glance-manage db_load_metadefs
 
     create_glance_cache_dir
-
-    # Init glance search by exporting found metadefs/images to elasticsearch
-    if is_service_enabled g-search; then
-        ${TOP_DIR}/pkg/elasticsearch.sh start
-        $GLANCE_BIN_DIR/glance-index
-    fi
 }
 
 # install_glanceclient() - Collect source and prepare
@@ -371,11 +319,6 @@
     if is_service_enabled tls-proxy; then
         start_tls_proxy '*' $GLANCE_SERVICE_PORT $GLANCE_SERVICE_HOST $GLANCE_SERVICE_PORT_INT &
         start_tls_proxy '*' $GLANCE_REGISTRY_PORT $GLANCE_SERVICE_HOST $GLANCE_REGISTRY_PORT_INT &
-
-        # Handle g-search
-        if is_service_enabled g-search; then
-            start_tls_proxy '*' $GLANCE_SEARCH_PORT $GLANCE_SERVICE_HOST $GLANCE_SEARCH_PORT_INT &
-        fi
     fi
 
     run_process g-reg "$GLANCE_BIN_DIR/glance-registry --config-file=$GLANCE_CONF_DIR/glance-registry.conf"
@@ -385,15 +328,6 @@
     if ! wait_for_service $SERVICE_TIMEOUT $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT; then
         die $LINENO "g-api did not start"
     fi
-
-    # Start g-search after g-reg/g-api
-    if is_service_enabled g-search; then
-        run_process g-search "$GLANCE_BIN_DIR/glance-search --config-file=$GLANCE_CONF_DIR/glance-search.conf"
-        echo "Waiting for g-search ($GLANCE_SEARCH_HOSTPORT) to start..."
-        if ! wait_for_service $SERVICE_TIMEOUT $GLANCE_SERVICE_PROTOCOL://$GLANCE_SEARCH_HOSTPORT; then
-            die $LINENO "g-search did not start"
-        fi
-    fi
 }
 
 # stop_glance() - Stop running processes
@@ -401,10 +335,6 @@
     # Kill the Glance screen windows
     stop_process g-api
     stop_process g-reg
-
-    if is_service_enabled g-search; then
-        stop_process g-search
-    fi
 }
 
 # Restore xtrace
diff --git a/lib/keystone b/lib/keystone
index d60a4ba..38a0acf 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -259,6 +259,7 @@
         # Configure ``keystone.conf`` to use sql
         inicomment $KEYSTONE_CONF catalog template_file
     else
+        deprecated "Using templated service catalog in devstack is deprecated"
         cp -p $FILES/default_catalog.templates $KEYSTONE_CATALOG
 
         # Add swift endpoints to service catalog if swift is enabled
@@ -299,11 +300,10 @@
     iniset $KEYSTONE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
 
     if [ "$KEYSTONE_USE_MOD_WSGI" == "True" ]; then
-        # Eliminate the %(asctime)s.%(msecs)03d from the log format strings
-        iniset $KEYSTONE_CONF DEFAULT logging_context_format_string "%(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s"
-        iniset $KEYSTONE_CONF DEFAULT logging_default_format_string "%(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s"
-        iniset $KEYSTONE_CONF DEFAULT logging_debug_format_suffix "%(funcName)s %(pathname)s:%(lineno)d"
-        iniset $KEYSTONE_CONF DEFAULT logging_exception_prefix "%(process)d TRACE %(name)s %(instance)s"
+        iniset $KEYSTONE_CONF DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s"
+        iniset $KEYSTONE_CONF DEFAULT logging_default_format_string "%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s"
+        iniset $KEYSTONE_CONF DEFAULT logging_debug_format_suffix "%(asctime)s.%(msecs)03d %(funcName)s %(pathname)s:%(lineno)d"
+        iniset $KEYSTONE_CONF DEFAULT logging_exception_prefix "%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s"
         _config_keystone_apache_wsgi
     else
         iniset $KEYSTONE_CONF eventlet_server admin_bind_host "$KEYSTONE_ADMIN_BIND_HOST"
@@ -327,6 +327,8 @@
 # --                   --         Member
 # demo                 admin      admin
 # demo                 demo       Member, anotherrole
+# alt_demo             admin      admin
+# alt_demo             alt_demo   Member, anotherrole
 # invisible_to_admin   demo       Member
 
 # Group                Users      Roles                 Tenant
@@ -387,6 +389,18 @@
     get_or_add_user_project_role $another_role $demo_user $demo_tenant
     get_or_add_user_project_role $member_role $demo_user $invis_tenant
 
+    # alt_demo
+    local alt_demo_tenant
+    alt_demo_tenant=$(get_or_create_project "alt_demo" default)
+    local alt_demo_user
+    alt_demo_user=$(get_or_create_user "alt_demo" \
+        "$ADMIN_PASSWORD" "default" "alt_demo@example.com")
+
+    get_or_add_user_project_role $member_role $alt_demo_user $alt_demo_tenant
+    get_or_add_user_project_role $admin_role $admin_user $alt_demo_tenant
+    get_or_add_user_project_role $another_role $alt_demo_user $alt_demo_tenant
+
+    # groups
     local admin_group
     admin_group=$(get_or_create_group "admins" \
         "default" "openstack admin group")
@@ -396,6 +410,8 @@
 
     get_or_add_group_project_role $member_role $non_admin_group $demo_tenant
     get_or_add_group_project_role $another_role $non_admin_group $demo_tenant
+    get_or_add_group_project_role $member_role $non_admin_group $alt_demo_tenant
+    get_or_add_group_project_role $another_role $non_admin_group $alt_demo_tenant
     get_or_add_group_project_role $admin_role $admin_group $admin_tenant
 }
 
diff --git a/lib/neutron-legacy b/lib/neutron-legacy
index 78eb55d..73a1bcd 100644
--- a/lib/neutron-legacy
+++ b/lib/neutron-legacy
@@ -433,15 +433,18 @@
 
     # goes before q-svc to init Q_SERVICE_PLUGIN_CLASSES
     if is_service_enabled q-lbaas; then
+        deprecated "Configuring q-lbaas through devstack is deprecated"
         _configure_neutron_lbaas
     fi
     if is_service_enabled q-metering; then
         _configure_neutron_metering
     fi
     if is_service_enabled q-vpn; then
+        deprecated "Configuring q-vpn through devstack is deprecated"
         _configure_neutron_vpn
     fi
     if is_service_enabled q-fwaas; then
+        deprecated "Configuring q-fwaas through devstack is deprecated"
         _configure_neutron_fwaas
     fi
     if is_service_enabled q-agt q-svc; then
diff --git a/lib/neutron_plugins/services/firewall b/lib/neutron_plugins/services/firewall
index 2b7f32d..40968fa 100644
--- a/lib/neutron_plugins/services/firewall
+++ b/lib/neutron_plugins/services/firewall
@@ -7,7 +7,8 @@
 _XTRACE_NEUTRON_FIREWALL=$(set +o | grep xtrace)
 set +o xtrace
 
-FWAAS_PLUGIN=neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin
+FWAAS_PLUGIN=${FWAAS_PLUGIN:-neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin}
+FWAAS_DRIVER=${FWAAS_DRIVER:-neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver}
 
 function neutron_fwaas_configure_common {
     _neutron_service_plugin_class_add $FWAAS_PLUGIN
@@ -21,7 +22,7 @@
     cp $NEUTRON_FWAAS_DIR/etc/fwaas_driver.ini.sample $FWAAS_DRIVER_CONF_FILENAME
 
     iniset_multiline $FWAAS_DRIVER_CONF_FILENAME fwaas enabled True
-    iniset_multiline $FWAAS_DRIVER_CONF_FILENAME fwaas driver "neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver"
+    iniset_multiline $FWAAS_DRIVER_CONF_FILENAME fwaas driver "$FWAAS_DRIVER"
 }
 
 function neutron_fwaas_stop {
diff --git a/lib/nova b/lib/nova
index 79bef9b..dc425a1 100644
--- a/lib/nova
+++ b/lib/nova
@@ -579,7 +579,7 @@
         iniset $NOVA_CONF vnc vncserver_listen "$VNCSERVER_LISTEN"
         iniset $NOVA_CONF vnc vncserver_proxyclient_address "$VNCSERVER_PROXYCLIENT_ADDRESS"
         iniset $NOVA_CONF DEFAULT novncproxy_host "$NOVA_SERVICE_LISTEN_ADDRESS"
-        iniset $NOVA_CONF DEFAULT xvpvncproxy_host "$NOVA_SERVICE_LISTEN_ADDRESS"
+        iniset $NOVA_CONF vnc xvpvncproxy_host "$NOVA_SERVICE_LISTEN_ADDRESS"
     else
         iniset $NOVA_CONF vnc enabled false
     fi
diff --git a/lib/tempest b/lib/tempest
index 5c771f9..e90ff93 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -321,7 +321,7 @@
     local tmp_cfg_file
     tmp_cfg_file=$(mktemp)
     cd $TEMPEST_DIR
-    tox -revenv -- tempest verify-config -uro $tmp_cfg_file
+    tox -revenv -- tempest verify-config -u -r -o $tmp_cfg_file
 
     local compute_api_extensions=${COMPUTE_API_EXTENSIONS:-"all"}
     if [[ ! -z "$DISABLE_COMPUTE_API_EXTENSIONS" ]]; then
@@ -568,21 +568,6 @@
     IFS=$ifs
 }
 
-# create_tempest_accounts() - Set up common required tempest accounts
-
-# Project              User         Roles
-# ------------------------------------------------------------------
-# alt_demo             alt_demo     Member
-
-function create_tempest_accounts {
-    if is_service_enabled tempest; then
-        # Tempest has some tests that validate various authorization checks
-        # between two regular users in separate tenants
-        get_or_create_project alt_demo default
-        get_or_create_user alt_demo "$ADMIN_PASSWORD" "default" "alt_demo@example.com"
-        get_or_add_user_project_role Member alt_demo alt_demo
-    fi
-}
 
 # install_tempest_lib() - Collect source, prepare, and install ``tempest-lib``
 function install_tempest_lib {
diff --git a/stack.sh b/stack.sh
index c56024f..c21ff77 100755
--- a/stack.sh
+++ b/stack.sh
@@ -240,6 +240,7 @@
 # see them by forcing ``PATH``
 echo "Defaults:$STACK_USER secure_path=/sbin:/usr/sbin:/usr/bin:/bin:/usr/local/sbin:/usr/local/bin" >> $TEMPFILE
 echo "Defaults:$STACK_USER !requiretty" >> $TEMPFILE
+echo "Defaults env_keep += PS4" >> $TEMPFILE
 chmod 0440 $TEMPFILE
 sudo chown root:root $TEMPFILE
 sudo mv $TEMPFILE /etc/sudoers.d/50_stack_sh
diff --git a/stackrc b/stackrc
index 58146a4..887d4b3 100644
--- a/stackrc
+++ b/stackrc
@@ -14,6 +14,9 @@
 LC_ALL=C
 export LC_ALL
 
+# Make tracing more educational
+export PS4='+ ${BASH_SOURCE}:${FUNCNAME[0]}:L${LINENO}:   '
+
 # Find the other rc files
 RC_DIR=$(cd $(dirname "${BASH_SOURCE:-$0}") && pwd)
 
diff --git a/unstack.sh b/unstack.sh
index 8eded83..47beb04 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -87,6 +87,8 @@
 # ``os_RELEASE``, ``os_UPDATE``, ``os_PACKAGE``, ``os_CODENAME``
 GetOSVersion
 
+set -o xtrace
+
 # Run extras
 # ==========