Merge "clean mysql better"
diff --git a/clean.sh b/clean.sh
index f2f1338..edbd04a 100755
--- a/clean.sh
+++ b/clean.sh
@@ -18,7 +18,7 @@
 FILES=$TOP_DIR/files
 
 # Load local configuration
-source $TOP_DIR/stackrc
+source $TOP_DIR/openrc
 
 # Get the variables that are set in stack.sh
 if [[ -r $TOP_DIR/.stackenv ]]; then
diff --git a/extras.d/70-tuskar.sh b/extras.d/70-tuskar.sh
index e77c504..38aba34 100644
--- a/extras.d/70-tuskar.sh
+++ b/extras.d/70-tuskar.sh
@@ -65,7 +65,7 @@
 TUSKAR_DIR=$DEST/tuskar
 TUSKARCLIENT_DIR=$DEST/python-tuskarclient
 TUSKAR_AUTH_CACHE_DIR=${TUSKAR_AUTH_CACHE_DIR:-/var/cache/tuskar}
-TUSKAR_STANDALONE=`trueorfalse False $TUSKAR_STANDALONE`
+TUSKAR_STANDALONE=$(trueorfalse False TUSKAR_STANDALONE)
 TUSKAR_CONF_DIR=/etc/tuskar
 TUSKAR_CONF=$TUSKAR_CONF_DIR/tuskar.conf
 TUSKAR_API_HOST=${TUSKAR_API_HOST:-$HOST_IP}
diff --git a/files/debs/neutron b/files/debs/neutron
index fd99677..5a59b22 100644
--- a/files/debs/neutron
+++ b/files/debs/neutron
@@ -1,3 +1,4 @@
+acl     # testonly
 ebtables
 iptables
 iputils-ping
diff --git a/files/rpms-suse/neutron b/files/rpms-suse/neutron
index 8431bd1..50ee145 100644
--- a/files/rpms-suse/neutron
+++ b/files/rpms-suse/neutron
@@ -1,3 +1,4 @@
+acl     # testonly
 dnsmasq
 dnsmasq-utils # dist:opensuse-12.3,opensuse-13.1
 ebtables
diff --git a/files/rpms/neutron b/files/rpms/neutron
index f2473fb..5450408 100644
--- a/files/rpms/neutron
+++ b/files/rpms/neutron
@@ -1,4 +1,5 @@
 MySQL-python
+acl     # testonly
 dnsmasq # for q-dhcp
 dnsmasq-utils # for dhcp_release
 ebtables
diff --git a/functions b/functions
index 12be160..5b3a8ea 100644
--- a/functions
+++ b/functions
@@ -353,7 +353,7 @@
     local boot_timeout=$3
     local expected=${4:-"True"}
     local check_command=""
-    MULTI_HOST=`trueorfalse False $MULTI_HOST`
+    MULTI_HOST=$(trueorfalse False MULTI_HOST)
     if [[ "$MULTI_HOST" = "True" && "$from_net" = "$PRIVATE_NETWORK_NAME" ]]; then
         return
     fi
diff --git a/functions-common b/functions-common
index b0b2622..5bca836 100644
--- a/functions-common
+++ b/functions-common
@@ -21,7 +21,6 @@
 #
 # The following variables are assumed to be defined by certain functions:
 #
-# - ``GIT_DEPTH``
 # - ``ENABLED_SERVICES``
 # - ``ERROR_ON_CLONE``
 # - ``FILES``
@@ -43,6 +42,8 @@
 declare -A GITBRANCH
 declare -A GITDIR
 
+TRACK_DEPENDS=${TRACK_DEPENDS:-False}
+
 # Config Functions
 # ================
 
@@ -243,7 +244,8 @@
     local xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local default=$1
-    local testval=$2
+    local literal=$2
+    local testval=${!literal}
 
     [[ -z "$testval" ]] && { echo "$default"; return; }
     [[ "0 no No NO false False FALSE" =~ "$testval" ]] && { echo "False"; return; }
@@ -252,6 +254,14 @@
     $xtrace
 }
 
+function isset {
+    nounset=$(set +o | grep nounset)
+    set +o nounset
+    [[ -n "${!1+x}" ]]
+    result=$?
+    $nounset
+    return $result
+}
 
 # Control Functions
 # =================
@@ -381,7 +391,11 @@
 # ``os_UPDATE`` - update: ex. the ``5`` in ``RHEL6.5``
 # ``os_PACKAGE`` - package type: ``deb`` or ``rpm``
 # ``os_CODENAME`` - vendor's codename for release: ``snow leopard``, ``trusty``
-declare os_VENDOR os_RELEASE os_UPDATE os_PACKAGE os_CODENAME
+os_VENDOR=""
+os_RELEASE=""
+os_UPDATE=""
+os_PACKAGE=""
+os_CODENAME=""
 
 # GetOSVersion
 function GetOSVersion {
@@ -577,8 +591,7 @@
 # Set global ``RECLONE=yes`` to simulate a clone when dest-dir exists
 # Set global ``ERROR_ON_CLONE=True`` to abort execution with an error if the git repo
 # does not exist (default is False, meaning the repo will be cloned).
-# Set global ``GIT_DEPTH=<number>`` to limit the history depth of the git clone
-# Uses globals ``ERROR_ON_CLONE``, ``OFFLINE``, ``RECLONE``, ``GIT_DEPTH``
+# Uses globals ``ERROR_ON_CLONE``, ``OFFLINE``, ``RECLONE``
 # git_clone remote dest-dir branch
 function git_clone {
     local git_remote=$1
@@ -587,8 +600,7 @@
     local orig_dir=$(pwd)
     local git_clone_flags=""
 
-    RECLONE=$(trueorfalse False $RECLONE)
-
+    RECLONE=$(trueorfalse False RECLONE)
     if [[ -n "${GIT_DEPTH}" ]]; then
         git_clone_flags="$git_clone_flags --depth $GIT_DEPTH"
     fi
@@ -814,7 +826,7 @@
 # Gets or creates a domain
 # Usage: get_or_create_domain <name> <description>
 function get_or_create_domain {
-    local os_url="$KEYSTONE_SERVICE_URI/v3"
+    local os_url="$KEYSTONE_SERVICE_URI_V3"
     # Gets domain id
     local domain_id=$(
         # Gets domain id
@@ -830,6 +842,23 @@
     echo $domain_id
 }
 
+# Gets or creates group
+# Usage: get_or_create_group <groupname> [<domain> <description>]
+function get_or_create_group {
+    local domain=${2:+--domain ${2}}
+    local desc="${3:-}"
+    local os_url="$KEYSTONE_SERVICE_URI_V3"
+    # Gets group id
+    local group_id=$(
+        # Creates new group with --or-show
+        openstack --os-token=$OS_TOKEN --os-url=$os_url \
+            --os-identity-api-version=3 group create $1 \
+            $domain --description "$desc" --or-show \
+            -f value -c id
+    )
+    echo $group_id
+}
+
 # Gets or creates user
 # Usage: get_or_create_user <username> <password> <project> [<email> [<domain>]]
 function get_or_create_user {
@@ -842,7 +871,7 @@
     local domain=""
     if [[ ! -z "$5" ]]; then
         domain="--domain=$5"
-        os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI/v3 --os-identity-api-version=3"
+        os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI_V3 --os-identity-api-version=3"
     fi
     # Gets user id
     local user_id=$(
@@ -867,7 +896,7 @@
     local domain=""
     if [[ ! -z "$2" ]]; then
         domain="--domain=$2"
-        os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI/v3 --os-identity-api-version=3"
+        os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI_V3 --os-identity-api-version=3"
     fi
     local project_id=$(
         # Creates new project with --or-show
@@ -978,9 +1007,10 @@
     [[ "$(id -u)" = "0" ]] && sudo="env"
 
     $xtrace
+
     $sudo DEBIAN_FRONTEND=noninteractive \
-        http_proxy=$http_proxy https_proxy=$https_proxy \
-        no_proxy=$no_proxy \
+        http_proxy=${http_proxy:-} https_proxy=${https_proxy:-} \
+        no_proxy=${no_proxy:-} \
         apt-get --option "Dpkg::Options::=--force-confold" --assume-yes "$@"
 }
 
@@ -999,10 +1029,10 @@
     set +o xtrace
     local services=$@
     local package_dir=$(_get_package_dir)
-    local file_to_parse
-    local service
+    local file_to_parse=""
+    local service=""
 
-    INSTALL_TESTONLY_PACKAGES=$(trueorfalse False $INSTALL_TESTONLY_PACKAGES)
+    INSTALL_TESTONLY_PACKAGES=$(trueorfalse False INSTALL_TESTONLY_PACKAGES)
 
     if [[ -z "$package_dir" ]]; then
         echo "No package directory supplied"
@@ -1112,6 +1142,10 @@
 # Uses globals ``NO_UPDATE_REPOS``, ``REPOS_UPDATED``, ``RETRY_UPDATE``
 # install_package package [package ...]
 function update_package_repo {
+    NO_UPDATE_REPOS=${NO_UPDATE_REPOS:-False}
+    REPOS_UPDATED=${REPOS_UPDATED:-False}
+    RETRY_UPDATE=${RETRY_UPDATE:-False}
+
     if [[ "$NO_UPDATE_REPOS" = "True" ]]; then
         return 0
     fi
@@ -1321,7 +1355,7 @@
 
     SCREEN_NAME=${SCREEN_NAME:-stack}
     SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
-    USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+    USE_SCREEN=$(trueorfalse True USE_SCREEN)
 
     # Append the process to the screen rc file
     screen_rc "$name" "$command"
@@ -1394,7 +1428,7 @@
 
     SCREEN_NAME=${SCREEN_NAME:-stack}
     SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
-    USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+    USE_SCREEN=$(trueorfalse True USE_SCREEN)
 
     if is_service_enabled $service; then
         # Clean up the screen window
@@ -1412,7 +1446,7 @@
     local service=$1
 
     SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
-    USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+    USE_SCREEN=$(trueorfalse True USE_SCREEN)
 
     if is_service_enabled $service; then
         # Kill via pid if we have one available
@@ -1462,7 +1496,7 @@
     local name=$1
     local logfile=$2
 
-    USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+    USE_SCREEN=$(trueorfalse True USE_SCREEN)
     if [[ "$USE_SCREEN" = "True" ]]; then
         screen_process "$name" "sudo tail -f $logfile"
     fi
@@ -1572,7 +1606,8 @@
 function pip_install {
     local xtrace=$(set +o | grep xtrace)
     set +o xtrace
-    if [[ "$OFFLINE" = "True" || -z "$@" ]]; then
+    local offline=${OFFLINE:-False}
+    if [[ "$offline" == "True" || -z "$@" ]]; then
         $xtrace
         return
     fi
@@ -1601,20 +1636,20 @@
 
     $xtrace
     $sudo_pip \
-        http_proxy=$http_proxy \
-        https_proxy=$https_proxy \
-        no_proxy=$no_proxy \
+        http_proxy=${http_proxy:-} \
+        https_proxy=${https_proxy:-} \
+        no_proxy=${no_proxy:-} \
         $cmd_pip install \
         $@
 
-    INSTALL_TESTONLY_PACKAGES=$(trueorfalse False $INSTALL_TESTONLY_PACKAGES)
+    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
             $sudo_pip \
-                http_proxy=$http_proxy \
-                https_proxy=$https_proxy \
-                no_proxy=$no_proxy \
+                http_proxy=${http_proxy:-} \
+                https_proxy=${https_proxy:-} \
+                no_proxy=${no_proxy:-} \
                 $cmd_pip install \
                 -r $test_req
         fi
@@ -2084,13 +2119,13 @@
 #     http_proxy=http://proxy.example.com:3128/ no_proxy=repo.example.net ./stack.sh
 
 function export_proxy_variables {
-    if [[ -n "$http_proxy" ]]; then
+    if isset http_proxy ; then
         export http_proxy=$http_proxy
     fi
-    if [[ -n "$https_proxy" ]]; then
+    if isset https_proxy ; then
         export https_proxy=$https_proxy
     fi
-    if [[ -n "$no_proxy" ]]; then
+    if isset no_proxy ; then
         export no_proxy=$no_proxy
     fi
 }
diff --git a/lib/ceilometer b/lib/ceilometer
index f6280d9..98a7e8f 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -75,11 +75,14 @@
 CEILOMETER_SERVICE_PROTOCOL=http
 CEILOMETER_SERVICE_HOST=$SERVICE_HOST
 CEILOMETER_SERVICE_PORT=${CEILOMETER_SERVICE_PORT:-8777}
-CEILOMETER_USE_MOD_WSGI=$(trueorfalse False $CEILOMETER_USE_MOD_WSGI)
+CEILOMETER_USE_MOD_WSGI=$(trueorfalse False CEILOMETER_USE_MOD_WSGI)
 
 # To enable OSprofiler change value of this variable to "notifications,profiler"
 CEILOMETER_NOTIFICATION_TOPICS=${CEILOMETER_NOTIFICATION_TOPICS:-notifications}
 
+CEILOMETER_COORDINATION_URL=${CEILOMETER_COORDINATION_URL:-}
+CEILOMETER_PIPELINE_INTERVAL=${CEILOMETER_PIPELINE_INTERVAL:-}
+
 # Tell Tempest this project is present
 TEMPEST_SERVICES+=,ceilometer
 
diff --git a/lib/cinder b/lib/cinder
index 9d27daa..7fc6949 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -68,7 +68,7 @@
 # Maintain this here for backward-compatibility with the old configuration
 # DEPRECATED: Use CINDER_ENABLED_BACKENDS instead
 # Support for multi lvm backend configuration (default is no support)
-CINDER_MULTI_LVM_BACKEND=$(trueorfalse False $CINDER_MULTI_LVM_BACKEND)
+CINDER_MULTI_LVM_BACKEND=$(trueorfalse False CINDER_MULTI_LVM_BACKEND)
 
 # Default backends
 # The backend format is type:name where type is one of the supported backend
@@ -85,7 +85,7 @@
 # Should cinder perform secure deletion of volumes?
 # Defaults to true, can be set to False to avoid this bug when testing:
 # https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1023755
-CINDER_SECURE_DELETE=`trueorfalse True $CINDER_SECURE_DELETE`
+CINDER_SECURE_DELETE=$(trueorfalse True CINDER_SECURE_DELETE)
 
 # Cinder reports allocations back to the scheduler on periodic intervals
 # it turns out we can get an "out of space" issue when we run tests too
@@ -211,6 +211,8 @@
 
     cp -p $CINDER_DIR/etc/cinder/policy.json $CINDER_CONF_DIR
 
+    rm -f $CINDER_CONF
+
     configure_cinder_rootwrap
 
     cp $CINDER_DIR/etc/cinder/api-paste.ini $CINDER_API_PASTE_INI
diff --git a/lib/cinder_backends/lvm b/lib/cinder_backends/lvm
index a3ab5bf..280baf7 100644
--- a/lib/cinder_backends/lvm
+++ b/lib/cinder_backends/lvm
@@ -33,6 +33,7 @@
 # If ``VOLUME_GROUP`` is set, use it, otherwise we'll build a VG name based
 # on ``VOLUME_GROUP_NAME`` that includes the backend name
 # Grenade doesn't use ``VOLUME_GROUP2`` so it is left out
+VOLUME_GROUP=${VOLUME_GROUP:-}
 VOLUME_GROUP_NAME=${VOLUME_GROUP:-${VOLUME_GROUP_NAME:-stack-volumes}}
 
 # TODO: resurrect backing device...need to know how to set values
diff --git a/lib/database b/lib/database
index 366d2b3..896b8e1 100644
--- a/lib/database
+++ b/lib/database
@@ -23,6 +23,7 @@
 XTRACE=$(set +o | grep xtrace)
 set +o xtrace
 
+DATABASE_BACKENDS=""
 
 # Register a database backend
 #
@@ -30,7 +31,7 @@
 #
 # This is required to be defined before the specific database scripts are sourced
 function register_database {
-    [ -z "$DATABASE_BACKENDS" ] && DATABASE_BACKENDS=$1 || DATABASE_BACKENDS+=" $1"
+    DATABASE_BACKENDS+=" $1"
 }
 
 # Sourcing the database libs sets DATABASE_BACKENDS with the available list
diff --git a/lib/databases/mysql b/lib/databases/mysql
index 1c74c03..45484c1 100644
--- a/lib/databases/mysql
+++ b/lib/databases/mysql
@@ -14,12 +14,22 @@
 
 register_database mysql
 
+# Linux distros, thank you for being incredibly consistent
+MYSQL=mysql
+if is_fedora; then
+    if [[ $DISTRO =~ (rhel6) ]]; then
+        MYSQL=mysqld
+    else
+        MYSQL=mariadb
+    fi
+fi
 
 # Functions
 # ---------
 
 # Get rid of everything enough to cleanly change database backends
 function cleanup_database_mysql {
+    stop_service $MYSQL
     if is_ubuntu; then
         # Get ruthless with mysql
         stop_service $MYSQL
diff --git a/lib/dib b/lib/dib
index 177f4c1..809217b 100644
--- a/lib/dib
+++ b/lib/dib
@@ -26,7 +26,7 @@
 # NOTE: Setting DIB_APT_SOURCES assumes you will be building
 # Debian/Ubuntu based images. Leave unset for other flavors.
 DIB_APT_SOURCES=${DIB_APT_SOURCES:-""}
-DIB_BUILD_OFFLINE=$(trueorfalse False $DIB_BUILD_OFFLINE)
+DIB_BUILD_OFFLINE=$(trueorfalse False DIB_BUILD_OFFLINE)
 DIB_IMAGE_CACHE=$DATA_DIR/diskimage-builder/image-create
 DIB_PIP_REPO=$DATA_DIR/diskimage-builder/pip-repo
 DIB_PIP_REPO_PORT=${DIB_PIP_REPO_PORT:-8899}
diff --git a/lib/dstat b/lib/dstat
index 8f456a8..73ca279 100644
--- a/lib/dstat
+++ b/lib/dstat
@@ -26,7 +26,7 @@
 # start_dstat() - Start running processes, including screen
 function start_dstat {
     # A better kind of sysstat, with the top process per time slice
-    DSTAT_OPTS="-tcmndrylpg --top-cpu-adv"
+    DSTAT_OPTS="-tcmndrylpg --top-cpu-adv --top-io-adv"
     if [[ -n ${SCREEN_LOGDIR} ]]; then
         screen_it dstat "cd $TOP_DIR; dstat $DSTAT_OPTS | tee $SCREEN_LOGDIR/$DSTAT_FILE"
     else
diff --git a/lib/heat b/lib/heat
index 544ec45..ee3f502 100644
--- a/lib/heat
+++ b/lib/heat
@@ -37,13 +37,13 @@
 HEAT_CFNTOOLS_DIR=$DEST/heat-cfntools
 HEAT_TEMPLATES_REPO_DIR=$DEST/heat-templates
 HEAT_AUTH_CACHE_DIR=${HEAT_AUTH_CACHE_DIR:-/var/cache/heat}
-HEAT_STANDALONE=`trueorfalse False $HEAT_STANDALONE`
-HEAT_ENABLE_ADOPT_ABANDON=`trueorfalse False $HEAT_ENABLE_ADOPT_ABANDON`
+HEAT_STANDALONE=$(trueorfalse False HEAT_STANDALONE)
+HEAT_ENABLE_ADOPT_ABANDON=$(trueorfalse False HEAT_ENABLE_ADOPT_ABANDON)
 HEAT_CONF_DIR=/etc/heat
 HEAT_CONF=$HEAT_CONF_DIR/heat.conf
 HEAT_ENV_DIR=$HEAT_CONF_DIR/environment.d
 HEAT_TEMPLATES_DIR=$HEAT_CONF_DIR/templates
-HEAT_STACK_DOMAIN=`trueorfalse True $HEAT_STACK_DOMAIN`
+HEAT_STACK_DOMAIN=$(trueorfalse True HEAT_STACK_DOMAIN)
 HEAT_API_HOST=${HEAT_API_HOST:-$HOST_IP}
 HEAT_API_PORT=${HEAT_API_PORT:-8004}
 
@@ -265,23 +265,21 @@
     if [[ "$HEAT_STACK_DOMAIN" == "True" ]]; then
         # Note we have to pass token/endpoint here because the current endpoint and
         # version negotiation in OSC means just --os-identity-api-version=3 won't work
-        local ks_endpoint_v3="$KEYSTONE_SERVICE_URI/v3"
-
-        D_ID=$(openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+        D_ID=$(openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
             --os-identity-api-version=3 domain list | grep ' heat ' | get_field 1)
 
         if [[ -z "$D_ID" ]]; then
-            D_ID=$(openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+            D_ID=$(openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
                 --os-identity-api-version=3 domain create heat \
                 --description "Owns users and projects created by heat" \
                 | grep ' id ' | get_field 2)
             iniset $HEAT_CONF DEFAULT stack_user_domain_id ${D_ID}
 
-            openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+            openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
                 --os-identity-api-version=3 user create --password $SERVICE_PASSWORD \
                 --domain $D_ID heat_domain_admin \
                 --description "Manages users and projects created by heat"
-            openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+            openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
                 --os-identity-api-version=3 role add \
                 --user heat_domain_admin --domain ${D_ID} admin
             iniset $HEAT_CONF DEFAULT stack_domain_admin heat_domain_admin
diff --git a/lib/horizon b/lib/horizon
index fee2ef0..aa70bd5 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -173,7 +173,7 @@
     # Apache installation, because we mark it NOPRIME
     install_apache_wsgi
 
-    git_clone $HORIZON_REPO $HORIZON_DIR $HORIZON_BRANCH $HORIZON_TAG
+    git_clone $HORIZON_REPO $HORIZON_DIR $HORIZON_BRANCH
 }
 
 # start_horizon() - Start running processes, including screen
diff --git a/lib/ironic b/lib/ironic
index 55272b9..d5afa30 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -42,6 +42,9 @@
 IRONIC_ROOTWRAP_CONF=$IRONIC_CONF_DIR/rootwrap.conf
 IRONIC_POLICY_JSON=$IRONIC_CONF_DIR/policy.json
 
+# Deploy callback timeout can be changed from its default (1800), if required.
+IRONIC_CALLBACK_TIMEOUT=${IRONIC_CALLBACK_TIMEOUT:-}
+
 # Deploy to hardware platform
 IRONIC_HW_NODE_CPU=${IRONIC_HW_NODE_CPU:-1}
 IRONIC_HW_NODE_RAM=${IRONIC_HW_NODE_RAM:-512}
@@ -57,7 +60,7 @@
 # Set up defaults for functional / integration testing
 IRONIC_SCRIPTS_DIR=${IRONIC_SCRIPTS_DIR:-$TOP_DIR/tools/ironic/scripts}
 IRONIC_TEMPLATES_DIR=${IRONIC_TEMPLATES_DIR:-$TOP_DIR/tools/ironic/templates}
-IRONIC_BAREMETAL_BASIC_OPS=$(trueorfalse False $IRONIC_BAREMETAL_BASIC_OPS)
+IRONIC_BAREMETAL_BASIC_OPS=$(trueorfalse False IRONIC_BAREMETAL_BASIC_OPS)
 IRONIC_ENABLED_DRIVERS=${IRONIC_ENABLED_DRIVERS:-fake,pxe_ssh,pxe_ipmitool}
 IRONIC_SSH_USERNAME=${IRONIC_SSH_USERNAME:-`whoami`}
 IRONIC_SSH_KEY_DIR=${IRONIC_SSH_KEY_DIR:-$IRONIC_DATA_DIR/ssh_keys}
@@ -84,7 +87,7 @@
 IRONIC_VM_LOG_DIR=${IRONIC_VM_LOG_DIR:-$IRONIC_DATA_DIR/logs/}
 
 # Use DIB to create deploy ramdisk and kernel.
-IRONIC_BUILD_DEPLOY_RAMDISK=`trueorfalse True $IRONIC_BUILD_DEPLOY_RAMDISK`
+IRONIC_BUILD_DEPLOY_RAMDISK=$(trueorfalse True IRONIC_BUILD_DEPLOY_RAMDISK)
 # If not use DIB, these files are used as deploy ramdisk/kernel.
 # (The value must be a absolute path)
 IRONIC_DEPLOY_RAMDISK=${IRONIC_DEPLOY_RAMDISK:-}
@@ -113,7 +116,7 @@
 TEMPEST_SERVICES+=,ironic
 
 # Enable iPXE
-IRONIC_IPXE_ENABLED=$(trueorfalse False $IRONIC_IPXE_ENABLED)
+IRONIC_IPXE_ENABLED=$(trueorfalse False IRONIC_IPXE_ENABLED)
 IRONIC_HTTP_DIR=${IRONIC_HTTP_DIR:-$IRONIC_DATA_DIR/httpboot}
 IRONIC_HTTP_SERVER=${IRONIC_HTTP_SERVER:-$HOST_IP}
 IRONIC_HTTP_PORT=${IRONIC_HTTP_PORT:-8088}
@@ -300,6 +303,9 @@
     iniset $IRONIC_CONF_FILE DEFAULT rootwrap_config $IRONIC_ROOTWRAP_CONF
     iniset $IRONIC_CONF_FILE DEFAULT enabled_drivers $IRONIC_ENABLED_DRIVERS
     iniset $IRONIC_CONF_FILE conductor api_url $IRONIC_SERVICE_PROTOCOL://$HOST_IP:$IRONIC_SERVICE_PORT
+    if [[ -n "$IRONIC_CALLBACK_TIMEOUT" ]]; then
+        iniset $IRONIC_CONF_FILE conductor deploy_callback_timeout $IRONIC_CALLBACK_TIMEOUT
+    fi
     iniset $IRONIC_CONF_FILE pxe tftp_server $IRONIC_TFTPSERVER_IP
     iniset $IRONIC_CONF_FILE pxe tftp_root $IRONIC_TFTPBOOT_DIR
     iniset $IRONIC_CONF_FILE pxe tftp_master_path $IRONIC_TFTPBOOT_DIR/master_images
diff --git a/lib/keystone b/lib/keystone
index 1599fa5..9bde597 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -71,6 +71,7 @@
 
 # Select Keystone's token format
 # Choose from 'UUID', 'PKI', or 'PKIZ'
+KEYSTONE_TOKEN_FORMAT=${KEYSTONE_TOKEN_FORMAT:-}
 KEYSTONE_TOKEN_FORMAT=$(echo ${KEYSTONE_TOKEN_FORMAT} | tr '[:upper:]' '[:lower:]')
 
 # Set Keystone interface configuration
@@ -106,6 +107,10 @@
 KEYSTONE_AUTH_URI=${KEYSTONE_AUTH_PROTOCOL}://${KEYSTONE_AUTH_HOST}:${KEYSTONE_AUTH_PORT}
 KEYSTONE_SERVICE_URI=${KEYSTONE_SERVICE_PROTOCOL}://${KEYSTONE_SERVICE_HOST}:${KEYSTONE_SERVICE_PORT}
 
+# V3 URIs
+KEYSTONE_AUTH_URI_V3=$KEYSTONE_AUTH_URI/v3
+KEYSTONE_SERVICE_URI_V3=$KEYSTONE_SERVICE_URI/v3
+
 # Functions
 # ---------
 # cleanup_keystone() - Remove residual data files, anything left over from previous
@@ -394,6 +399,9 @@
     get_or_add_user_role $another_role $demo_user $demo_tenant
     get_or_add_user_role $member_role $demo_user $invis_tenant
 
+    get_or_create_group "developers" "default" "openstack developers"
+    get_or_create_group "testers" "default"
+
     # Keystone
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
diff --git a/lib/nova b/lib/nova
index 5ac9ff1..0e9282b 100644
--- a/lib/nova
+++ b/lib/nova
@@ -106,7 +106,7 @@
 
 # $NOVA_VNC_ENABLED can be used to forcibly enable vnc configuration.
 # In multi-node setups allows compute hosts to not run n-novnc.
-NOVA_VNC_ENABLED=$(trueorfalse False $NOVA_VNC_ENABLED)
+NOVA_VNC_ENABLED=$(trueorfalse False NOVA_VNC_ENABLED)
 
 # Get hypervisor configuration
 # ----------------------------
@@ -145,11 +145,11 @@
 # ``MULTI_HOST`` is a mode where each compute node runs its own network node.  This
 # allows network operations and routing for a VM to occur on the server that is
 # running the VM - removing a SPOF and bandwidth bottleneck.
-MULTI_HOST=`trueorfalse False $MULTI_HOST`
+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`
+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
@@ -657,7 +657,7 @@
 
     if is_service_enabled n-novnc; then
         # a websockets/html5 or flash powered VNC console for vm instances
-        NOVNC_FROM_PACKAGE=`trueorfalse False $NOVNC_FROM_PACKAGE`
+        NOVNC_FROM_PACKAGE=$(trueorfalse False NOVNC_FROM_PACKAGE)
         if [ "$NOVNC_FROM_PACKAGE" = "True" ]; then
             NOVNC_WEB_DIR=/usr/share/novnc
             install_package novnc
@@ -669,7 +669,7 @@
 
     if is_service_enabled n-spice; then
         # a websockets/html5 or flash powered SPICE console for vm instances
-        SPICE_FROM_PACKAGE=`trueorfalse True $SPICE_FROM_PACKAGE`
+        SPICE_FROM_PACKAGE=$(trueorfalse True SPICE_FROM_PACKAGE)
         if [ "$SPICE_FROM_PACKAGE" = "True" ]; then
             SPICE_WEB_DIR=/usr/share/spice-html5
             install_package spice-html5
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index 4601eea..c136e35 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -15,7 +15,7 @@
 # --------
 
 # if we should turn on massive libvirt debugging
-DEBUG_LIBVIRT=$(trueorfalse False $DEBUG_LIBVIRT)
+DEBUG_LIBVIRT=$(trueorfalse False DEBUG_LIBVIRT)
 
 # Installs required distro-specific libvirt packages.
 function install_libvirt {
diff --git a/lib/nova_plugins/hypervisor-libvirt b/lib/nova_plugins/hypervisor-libvirt
index b1b4400..4d1eb6c 100644
--- a/lib/nova_plugins/hypervisor-libvirt
+++ b/lib/nova_plugins/hypervisor-libvirt
@@ -54,7 +54,7 @@
         iniset $NOVA_CONF DEFAULT vnc_enabled "false"
     fi
 
-    ENABLE_FILE_INJECTION=$(trueorfalse False $ENABLE_FILE_INJECTION)
+    ENABLE_FILE_INJECTION=$(trueorfalse False ENABLE_FILE_INJECTION)
     if [[ "$ENABLE_FILE_INJECTION" = "True" ]] ; then
         # When libguestfs is available for file injection, enable using
         # libguestfs to inspect the image and figure out the proper
diff --git a/lib/rpc_backend b/lib/rpc_backend
index 778d466..d87d620 100644
--- a/lib/rpc_backend
+++ b/lib/rpc_backend
@@ -21,6 +21,11 @@
 XTRACE=$(set +o | grep xtrace)
 set +o xtrace
 
+RPC_MESSAGING_PROTOCOL=${RPC_MESSAGING_PROTOCOL:-0.9}
+
+# TODO(sdague): RPC backend selection is super wonky because we treat
+# messaging server as a service, which it really isn't for multi host
+QPID_HOST=${QPID_HOST:-}
 
 # Functions
 # ---------
@@ -68,9 +73,6 @@
 function cleanup_rpc_backend {
     if is_service_enabled rabbit; then
         # Obliterate rabbitmq-server
-        if [ -n "$RABBIT_USERID" ]; then
-            sudo rabbitmqctl delete_user "$RABBIT_USERID"
-        fi
         uninstall_package rabbitmq-server
         sudo killall epmd || sudo killall -9 epmd
         if is_ubuntu; then
diff --git a/lib/swift b/lib/swift
index e9043b3..b5577d8 100644
--- a/lib/swift
+++ b/lib/swift
@@ -82,7 +82,7 @@
 
 # Set ``SWIFT_EXTRAS_MIDDLEWARE_LAST`` to extras middlewares that need to be at
 # the end of the pipeline.
-SWIFT_EXTRAS_MIDDLEWARE_LAST=${SWIFT_EXTRAS_MIDDLEWARE_LAST}
+SWIFT_EXTRAS_MIDDLEWARE_LAST=${SWIFT_EXTRAS_MIDDLEWARE_LAST:-}
 
 # Set ``SWIFT_EXTRAS_MIDDLEWARE_NO_AUTH`` to extras middlewares that need to be at
 # the beginning of the pipeline, before authentication middlewares.
@@ -127,7 +127,7 @@
 
 # Enable tempurl feature
 SWIFT_ENABLE_TEMPURLS=${SWIFT_ENABLE_TEMPURLS:-False}
-SWIFT_TEMPURL_KEY=${SWIFT_TEMPURL_KEY}
+SWIFT_TEMPURL_KEY=${SWIFT_TEMPURL_KEY:-}
 
 # Tell Tempest this project is present
 TEMPEST_SERVICES+=,swift
diff --git a/lib/tempest b/lib/tempest
index d31119b..1ae9457 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -75,8 +75,8 @@
 TEMPEST_STORAGE_PROTOCOL=${TEMPEST_STORAGE_PROTOCOL:-$TEMPEST_DEFAULT_STORAGE_PROTOCOL}
 
 # Neutron/Network variables
-IPV6_ENABLED=$(trueorfalse True $IPV6_ENABLED)
-IPV6_SUBNET_ATTRIBUTES_ENABLED=$(trueorfalse True $IPV6_SUBNET_ATTRIBUTES_ENABLED)
+IPV6_ENABLED=$(trueorfalse True IPV6_ENABLED)
+IPV6_SUBNET_ATTRIBUTES_ENABLED=$(trueorfalse True IPV6_SUBNET_ATTRIBUTES_ENABLED)
 
 # Functions
 # ---------
@@ -278,7 +278,7 @@
 
     # Identity
     iniset $TEMPEST_CONFIG identity uri "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:5000/v2.0/"
-    iniset $TEMPEST_CONFIG identity uri_v3 "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:5000/v3/"
+    iniset $TEMPEST_CONFIG identity uri_v3 "$KEYSTONE_SERVICE_URI_V3"
     iniset $TEMPEST_CONFIG identity username $TEMPEST_USERNAME
     iniset $TEMPEST_CONFIG identity password "$password"
     iniset $TEMPEST_CONFIG identity tenant_name $TEMPEST_TENANT_NAME
diff --git a/stack.sh b/stack.sh
index 696dc24..585d1ce 100755
--- a/stack.sh
+++ b/stack.sh
@@ -40,6 +40,12 @@
 # Keep track of the devstack directory
 TOP_DIR=$(cd $(dirname "$0") && pwd)
 
+# Check for uninitialized variables, a big cause of bugs
+NOUNSET=${NOUNSET:-}
+if [[ -n "$NOUNSET" ]]; then
+    set -o nounset
+fi
+
 # Sanity Checks
 # -------------
 
@@ -79,6 +85,9 @@
 # Prepare the environment
 # -----------------------
 
+# Initialize variables:
+LAST_SPINNER_PID=""
+
 # Import common functions
 source $TOP_DIR/functions
 
@@ -172,12 +181,12 @@
 disable_negated_services
 
 # Look for obsolete stuff
-if [[ ,${ENABLED_SERVICES}, =~ ,"swift", ]]; then
-    echo "FATAL: 'swift' is not supported as a service name"
-    echo "FATAL: Use the actual swift service names to enable them as required:"
-    echo "FATAL: s-proxy s-object s-container s-account"
-    exit 1
-fi
+# if [[ ,${ENABLED_SERVICES}, =~ ,"swift", ]]; then
+#     echo "FATAL: 'swift' is not supported as a service name"
+#     echo "FATAL: Use the actual swift service names to enable them as required:"
+#     echo "FATAL: s-proxy s-object s-container s-account"
+#     exit 1
+# fi
 
 # Configure sudo
 # --------------
@@ -311,7 +320,7 @@
 # -----------------
 
 # Set up logging level
-VERBOSE=$(trueorfalse True $VERBOSE)
+VERBOSE=$(trueorfalse True VERBOSE)
 
 # Draw a spinner so the user knows something is happening
 function spinner {
@@ -482,47 +491,6 @@
 # an error.  It is also useful for following along as the install occurs.
 set -o xtrace
 
-
-# Common Configuration
-# --------------------
-
-# Set ``OFFLINE`` to ``True`` to configure ``stack.sh`` to run cleanly without
-# Internet access. ``stack.sh`` must have been previously run with Internet
-# access to install prerequisites and fetch repositories.
-OFFLINE=`trueorfalse False $OFFLINE`
-
-# Set ``ERROR_ON_CLONE`` to ``True`` to configure ``stack.sh`` to exit if
-# the destination git repository does not exist during the ``git_clone``
-# operation.
-ERROR_ON_CLONE=`trueorfalse False $ERROR_ON_CLONE`
-
-# Whether to enable the debug log level in OpenStack services
-ENABLE_DEBUG_LOG_LEVEL=`trueorfalse True $ENABLE_DEBUG_LOG_LEVEL`
-
-# Set fixed and floating range here so we can make sure not to use addresses
-# from either range when attempting to guess the IP to use for the host.
-# Note that setting FIXED_RANGE may be necessary when running DevStack
-# in an OpenStack cloud that uses either of these address ranges internally.
-FLOATING_RANGE=${FLOATING_RANGE:-172.24.4.0/24}
-FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24}
-FIXED_NETWORK_SIZE=${FIXED_NETWORK_SIZE:-256}
-
-HOST_IP=$(get_default_host_ip $FIXED_RANGE $FLOATING_RANGE "$HOST_IP_IFACE" "$HOST_IP")
-if [ "$HOST_IP" == "" ]; then
-    die $LINENO "Could not determine host ip address.  See local.conf for suggestions on setting HOST_IP."
-fi
-
-# Allow the use of an alternate hostname (such as localhost/127.0.0.1) for service endpoints.
-SERVICE_HOST=${SERVICE_HOST:-$HOST_IP}
-
-# Configure services to use syslog instead of writing to individual log files
-SYSLOG=`trueorfalse False $SYSLOG`
-SYSLOG_HOST=${SYSLOG_HOST:-$HOST_IP}
-SYSLOG_PORT=${SYSLOG_PORT:-516}
-
-# Use color for logging output (only available if syslog is not used)
-LOG_COLOR=`trueorfalse True $LOG_COLOR`
-
 # Reset the bundle of CA certificates
 SSL_BUNDLE_FILE="$DATA_DIR/ca-bundle.pem"
 rm -f $SSL_BUNDLE_FILE
@@ -535,9 +503,6 @@
 # and the specified rpc backend is available on your platform.
 check_rpc_backend
 
-# Use native SSL for servers in SSL_ENABLED_SERVICES
-USE_SSL=$(trueorfalse False $USE_SSL)
-
 # Service to enable with SSL if USE_SSL is True
 SSL_ENABLED_SERVICES="key,nova,cinder,glance,s-proxy,neutron"
 
@@ -708,7 +673,20 @@
 
 # Configure an appropriate python environment
 if [[ "$OFFLINE" != "True" ]]; then
-    PYPI_ALTERNATIVE_URL=$PYPI_ALTERNATIVE_URL $TOP_DIR/tools/install_pip.sh
+    PYPI_ALTERNATIVE_URL=${PYPI_ALTERNATIVE_URL:-""} $TOP_DIR/tools/install_pip.sh
+fi
+
+TRACK_DEPENDS=${TRACK_DEPENDS:-False}
+
+# Install python packages into a virtualenv so that we can track them
+if [[ $TRACK_DEPENDS = True ]]; then
+    echo_summary "Installing Python packages into a virtualenv $DEST/.venv"
+    pip_install -U virtualenv
+
+    rm -rf $DEST/.venv
+    virtualenv --system-site-packages $DEST/.venv
+    source $DEST/.venv/bin/activate
+    $DEST/.venv/bin/pip freeze > $DEST/requires-pre-pip
 fi
 
 # Do the ugly hacks for broken packages and distros
@@ -731,19 +709,6 @@
     install_neutron_agent_packages
 fi
 
-TRACK_DEPENDS=${TRACK_DEPENDS:-False}
-
-# Install python packages into a virtualenv so that we can track them
-if [[ $TRACK_DEPENDS = True ]]; then
-    echo_summary "Installing Python packages into a virtualenv $DEST/.venv"
-    pip_install -U virtualenv
-
-    rm -rf $DEST/.venv
-    virtualenv --system-site-packages $DEST/.venv
-    source $DEST/.venv/bin/activate
-    $DEST/.venv/bin/pip freeze > $DEST/requires-pre-pip
-fi
-
 # Check Out and Install Source
 # ----------------------------
 
@@ -944,13 +909,14 @@
 # Configure screen
 # ----------------
 
-USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+USE_SCREEN=$(trueorfalse True USE_SCREEN)
 if [[ "$USE_SCREEN" == "True" ]]; then
     # Create a new named screen to run processes in
     screen -d -m -S $SCREEN_NAME -t shell -s /bin/bash
     sleep 1
 
     # Set a reasonable status bar
+    SCREEN_HARDSTATUS=${SCREEN_HARDSTATUS-:}
     if [ -z "$SCREEN_HARDSTATUS" ]; then
         SCREEN_HARDSTATUS='%{= .} %-Lw%{= .}%> %n%f %t*%{= .}%+Lw%< %-=%{g}(%{d}%H/%l%{g})'
     fi
diff --git a/stackrc b/stackrc
index 355c0dc..2568202 100644
--- a/stackrc
+++ b/stackrc
@@ -43,9 +43,17 @@
 #  enable_service q-meta
 #  # Optional, to enable tempest configuration as part of devstack
 #  enable_service tempest
+function isset {
+    local nounset=$(set +o | grep nounset)
+    set +o nounset
+    [[ -n "${!1+x}" ]]
+    result=$?
+    $nounset
+    return $result
+}
 
 # this allows us to pass ENABLED_SERVICES
-if [[ -z "$ENABLED_SERVICES" ]]; then
+if ! isset ENABLED_SERVICES ; then
     # core compute (glance / keystone / nova (+ nova-network))
     ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,n-sch,n-xvnc,n-cauth
     # cinder
@@ -106,7 +114,7 @@
 
 # This can be used to turn database query logging on and off
 # (currently only implemented for MySQL backend)
-DATABASE_QUERY_LOGGING=$(trueorfalse True $DATABASE_QUERY_LOGGING)
+DATABASE_QUERY_LOGGING=$(trueorfalse True DATABASE_QUERY_LOGGING)
 
 # Set a timeout for git operations.  If git is still running when the
 # timeout expires, the command will be retried up to 3 times.  This is
@@ -593,7 +601,7 @@
 
 # Staging Area for New Images, have them here for at least 24hrs for nodepool
 # to cache them otherwise the failure rates in the gate are too high
-PRECACHE_IMAGES=$(trueorfalse False $PRECACHE_IMAGES)
+PRECACHE_IMAGES=$(trueorfalse False PRECACHE_IMAGES)
 if [[ "$PRECACHE_IMAGES" == "True" ]]; then
     # staging in update for nodepool
     IMAGE_URL="https://download.fedoraproject.org/pub/alt/openstack/20/x86_64/Fedora-x86_64-20-20140618-sda.qcow2"
@@ -646,6 +654,54 @@
 # till we get to the point we need to handle this automatically
 YUM=${YUM:-yum}
 
+# Common Configuration
+# --------------------
+
+# Set ``OFFLINE`` to ``True`` to configure ``stack.sh`` to run cleanly without
+# Internet access. ``stack.sh`` must have been previously run with Internet
+# access to install prerequisites and fetch repositories.
+OFFLINE=$(trueorfalse False OFFLINE)
+
+# Set ``ERROR_ON_CLONE`` to ``True`` to configure ``stack.sh`` to exit if
+# the destination git repository does not exist during the ``git_clone``
+# operation.
+ERROR_ON_CLONE=$(trueorfalse False ERROR_ON_CLONE)
+
+# Whether to enable the debug log level in OpenStack services
+ENABLE_DEBUG_LOG_LEVEL=$(trueorfalse True ENABLE_DEBUG_LOG_LEVEL)
+
+# Set fixed and floating range here so we can make sure not to use addresses
+# from either range when attempting to guess the IP to use for the host.
+# Note that setting FIXED_RANGE may be necessary when running DevStack
+# in an OpenStack cloud that uses either of these address ranges internally.
+FLOATING_RANGE=${FLOATING_RANGE:-172.24.4.0/24}
+FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24}
+FIXED_NETWORK_SIZE=${FIXED_NETWORK_SIZE:-256}
+HOST_IP_IFACE=${HOST_IP_IFACE:-}
+HOST_IP=${HOST_IP:-}
+
+HOST_IP=$(get_default_host_ip $FIXED_RANGE $FLOATING_RANGE "$HOST_IP_IFACE" "$HOST_IP")
+if [ "$HOST_IP" == "" ]; then
+    die $LINENO "Could not determine host ip address.  See local.conf for suggestions on setting HOST_IP."
+fi
+
+# Allow the use of an alternate hostname (such as localhost/127.0.0.1) for service endpoints.
+SERVICE_HOST=${SERVICE_HOST:-$HOST_IP}
+
+# Configure services to use syslog instead of writing to individual log files
+SYSLOG=$(trueorfalse False SYSLOG)
+SYSLOG_HOST=${SYSLOG_HOST:-$HOST_IP}
+SYSLOG_PORT=${SYSLOG_PORT:-516}
+
+# Use color for logging output (only available if syslog is not used)
+LOG_COLOR=$(trueorfalse True LOG_COLOR)
+
+# Set global ``GIT_DEPTH=<number>`` to limit the history depth of the git clone
+GIT_DEPTH=${GIT_DEPTH:-1}
+
+# Use native SSL for servers in SSL_ENABLED_SERVICES
+USE_SSL=$(trueorfalse False USE_SSL)
+
 # Following entries need to be last items in file
 
 # Local variables:
diff --git a/tests/test_functions.sh b/tests/test_functions.sh
new file mode 100755
index 0000000..e57948a
--- /dev/null
+++ b/tests/test_functions.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+# Tests for DevStack meta-config functions
+
+TOP=$(cd $(dirname "$0")/.. && pwd)
+
+# Import common functions
+source $TOP/functions
+source $TOP/tests/unittest.sh
+
+function test_truefalse {
+    local one=1
+    local captrue=True
+    local lowtrue=true
+    local abrevtrue=t
+    local zero=0
+    local capfalse=False
+    local lowfalse=false
+    local abrevfalse=f
+    for against in True False; do
+        for name in one captrue lowtrue abrevtrue; do
+            assert_equal "True" $(trueorfalse $against $name) "\$(trueorfalse $against $name)"
+        done
+    done
+    for against in True False; do
+        for name in zero capfalse lowfalse abrevfalse; do
+            assert_equal "False" $(trueorfalse $against $name) "\$(trueorfalse $against $name)"
+        done
+    done
+}
+
+test_truefalse
+
+report_results
diff --git a/tests/unittest.sh b/tests/unittest.sh
new file mode 100644
index 0000000..435cc3a
--- /dev/null
+++ b/tests/unittest.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# we always start with no errors
+ERROR=0
+FAILED_FUNCS=""
+
+function assert_equal {
+    local lineno=`caller 0 | awk '{print $1}'`
+    local function=`caller 0 | awk '{print $2}'`
+    local msg=$3
+    if [[ "$1" != "$2" ]]; then
+        FAILED_FUNCS+="$function:L$lineno\n"
+        echo "ERROR: $1 != $2 in $function:L$lineno!"
+        echo "  $msg"
+        ERROR=1
+    else
+        echo "$function:L$lineno - ok"
+    fi
+}
+
+function report_results {
+    if [[ $ERROR -eq 1 ]]; then
+        echo "Tests FAILED"
+        echo $FAILED_FUNCS
+        exit 1
+    fi
+}
diff --git a/tools/install_prereqs.sh b/tools/install_prereqs.sh
index 9651083..303cc63 100755
--- a/tools/install_prereqs.sh
+++ b/tools/install_prereqs.sh
@@ -8,9 +8,15 @@
 #
 # -f        Force an install run now
 
-if [[ -n "$1" &&  "$1" = "-f" ]]; then
-    FORCE_PREREQ=1
-fi
+FORCE_PREREQ=0
+
+while getopts ":f" opt; do
+    case $opt in
+        f)
+            FORCE_PREREQ=1
+            ;;
+    esac
+done
 
 # If TOP_DIR is set we're being sourced rather than running stand-alone
 # or in a sub-shell
diff --git a/unstack.sh b/unstack.sh
index ea45da9..bff01d1 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -6,11 +6,22 @@
 # mysql and rabbit are left running as OpenStack code refreshes
 # do not require them to be restarted.
 #
-# Stop all processes by setting ``UNSTACK_ALL`` or specifying ``--all``
+# Stop all processes by setting ``UNSTACK_ALL`` or specifying ``-a``
 # on the command line
 
+UNSTACK_ALL=""
+
+while getopts ":a" opt; do
+    case $opt in
+        a)
+            UNSTACK_ALL=""
+            ;;
+    esac
+done
+
 # Keep track of the current devstack directory.
 TOP_DIR=$(cd $(dirname "$0") && pwd)
+FILES=$TOP_DIR/files
 
 # Import common functions
 source $TOP_DIR/functions
@@ -19,7 +30,7 @@
 source $TOP_DIR/lib/database
 
 # Load local configuration
-source $TOP_DIR/stackrc
+source $TOP_DIR/openrc
 
 # Destination path for service data
 DATA_DIR=${DATA_DIR:-${DEST}/data}
@@ -72,10 +83,6 @@
 # ``os_RELEASE``, ``os_UPDATE``, ``os_PACKAGE``, ``os_CODENAME``
 GetOSVersion
 
-if [[ "$1" == "--all" ]]; then
-    UNSTACK_ALL=${UNSTACK_ALL:-1}
-fi
-
 # Run extras
 # ==========