Merge "Added options to improve offline usage."
diff --git a/README.md b/README.md
index 872b16b..9310758 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,15 @@
 
 You can override environment variables used in `stack.sh` by creating file name `localrc`.  It is likely that you will need to do this to tweak your networking configuration should you need to access your cloud from a different host.
 
+# Database Backend
+
+Multiple database backends are available. The available databases are defined in the lib/databases directory.
+To choose a database backend, add a line to your `localrc` like:
+
+    use_database postgresql
+
+By default, the mysql database backend is used.
+
 # RPC Backend
 
 Multiple RPC backends are available. Currently, this
diff --git a/exercises/boot_from_volume.sh b/exercises/boot_from_volume.sh
index 460b50c..b06c8dd 100755
--- a/exercises/boot_from_volume.sh
+++ b/exercises/boot_from_volume.sh
@@ -95,7 +95,7 @@
 chmod 600 $KEY_FILE
 
 # Delete the old volume
-nova volume-delete $VOL_NAME || true
+cinder delete $VOL_NAME || true
 
 # Free every floating ips - setting FREE_ALL_FLOATING_IPS=True in localrc will make life easier for testers
 if [ "$FREE_ALL_FLOATING_IPS" = "True" ]; then
@@ -112,15 +112,15 @@
 fi
 
 # Create the bootable volume
-nova volume-create --display_name=$VOL_NAME --image-id $IMAGE 1
+cinder create --display_name=$VOL_NAME --image-id $IMAGE 1
 
 # Wait for volume to activate
-if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova volume-list | grep $VOL_NAME | grep available; do sleep 1; done"; then
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! cinder list | grep $VOL_NAME | grep available; do sleep 1; done"; then
     echo "Volume $VOL_NAME not created"
     exit 1
 fi
 
-VOLUME_ID=`nova volume-list | grep $VOL_NAME  | get_field 1`
+VOLUME_ID=`cinder list | grep $VOL_NAME  | get_field 1`
 
 # Boot instance from volume!  This is done with the --block_device_mapping param.
 # The format of mapping is:
@@ -152,13 +152,13 @@
     die "Failure deleting instance volume $VOL_INSTANCE_NAME"
 
 # Wait till our volume is no longer in-use
-if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova volume-list | grep $VOL_NAME | grep available; do sleep 1; done"; then
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! cinder list | grep $VOL_NAME | grep available; do sleep 1; done"; then
     echo "Volume $VOL_NAME not created"
     exit 1
 fi
 
 # Delete the volume
-nova volume-delete $VOL_NAME || \
+cinder delete $VOL_NAME || \
     die "Failure deleting volume $VOLUME_NAME"
 
 # De-allocate the floating ip
diff --git a/exercises/volumes.sh b/exercises/volumes.sh
index 1c73786..72c8729 100755
--- a/exercises/volumes.sh
+++ b/exercises/volumes.sh
@@ -2,7 +2,7 @@
 
 # **volumes.sh**
 
-# Test nova volumes with the nova command from python-novaclient
+# Test cinder volumes with the cinder command from python-cinderclient
 
 echo "*********************************************************************"
 echo "Begin DevStack Exercise: $0"
@@ -131,28 +131,28 @@
 VOL_NAME="myvol-$(openssl rand -hex 4)"
 
 # Verify it doesn't exist
-if [[ -n "`nova volume-list | grep $VOL_NAME | head -1 | get_field 2`" ]]; then
+if [[ -n "`cinder list | grep $VOL_NAME | head -1 | get_field 2`" ]]; then
     echo "Volume $VOL_NAME already exists"
     exit 1
 fi
 
 # Create a new volume
-nova volume-create --display_name $VOL_NAME --display_description "test volume: $VOL_NAME" 1
+cinder create --display_name $VOL_NAME --display_description "test volume: $VOL_NAME" 1
 if [[ $? != 0 ]]; then
     echo "Failure creating volume $VOL_NAME"
     exit 1
 fi
 
 start_time=`date +%s`
-if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova volume-list | grep $VOL_NAME | grep available; do sleep 1; done"; then
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! cinder list | grep $VOL_NAME | grep available; do sleep 1; done"; then
     echo "Volume $VOL_NAME not created"
     exit 1
 fi
 end_time=`date +%s`
-echo "Completed volume-create in $((end_time - start_time)) seconds"
+echo "Completed cinder create in $((end_time - start_time)) seconds"
 
 # Get volume ID
-VOL_ID=`nova volume-list | grep $VOL_NAME | head -1 | get_field 1`
+VOL_ID=`cinder list | grep $VOL_NAME | head -1 | get_field 1`
 die_if_not_set VOL_ID "Failure retrieving volume ID for $VOL_NAME"
 
 # Attach to server
@@ -160,14 +160,14 @@
 start_time=`date +%s`
 nova volume-attach $VM_UUID $VOL_ID $DEVICE || \
     die "Failure attaching volume $VOL_NAME to $NAME"
-if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova volume-list | grep $VOL_NAME | grep in-use; do sleep 1; done"; then
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! cinder list | grep $VOL_NAME | grep in-use; do sleep 1; done"; then
     echo "Volume $VOL_NAME not attached to $NAME"
     exit 1
 fi
 end_time=`date +%s`
 echo "Completed volume-attach in $((end_time - start_time)) seconds"
 
-VOL_ATTACH=`nova volume-list | grep $VOL_NAME | head -1 | get_field -1`
+VOL_ATTACH=`cinder list | grep $VOL_NAME | head -1 | get_field -1`
 die_if_not_set VOL_ATTACH "Failure retrieving $VOL_NAME status"
 if [[ "$VOL_ATTACH" != $VM_UUID ]]; then
     echo "Volume not attached to correct instance"
@@ -177,7 +177,7 @@
 # Detach volume
 start_time=`date +%s`
 nova volume-detach $VM_UUID $VOL_ID || die "Failure detaching volume $VOL_NAME from $NAME"
-if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova volume-list | grep $VOL_NAME | grep available; do sleep 1; done"; then
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! cinder list | grep $VOL_NAME | grep available; do sleep 1; done"; then
     echo "Volume $VOL_NAME not detached from $NAME"
     exit 1
 fi
@@ -186,13 +186,13 @@
 
 # Delete volume
 start_time=`date +%s`
-nova volume-delete $VOL_ID || die "Failure deleting volume $VOL_NAME"
-if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova volume-list | grep $VOL_NAME; do sleep 1; done"; then
+cinder delete $VOL_ID || die "Failure deleting volume $VOL_NAME"
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! cinder list | grep $VOL_NAME; do sleep 1; done"; then
     echo "Volume $VOL_NAME not deleted"
     exit 1
 fi
 end_time=`date +%s`
-echo "Completed volume-delete in $((end_time - start_time)) seconds"
+echo "Completed cinder delete in $((end_time - start_time)) seconds"
 
 # Shutdown the server
 nova delete $VM_UUID || die "Failure deleting instance $NAME"
diff --git a/files/apts/postgresql b/files/apts/postgresql
new file mode 100644
index 0000000..bf19d39
--- /dev/null
+++ b/files/apts/postgresql
@@ -0,0 +1 @@
+python-psycopg2
diff --git a/files/keystone_data.sh b/files/keystone_data.sh
index 9520b17..3da11bf 100755
--- a/files/keystone_data.sh
+++ b/files/keystone_data.sh
@@ -2,18 +2,19 @@
 #
 # Initial data for Keystone using python-keystoneclient
 #
-# Tenant               User      Roles
+# Tenant               User       Roles
 # ------------------------------------------------------------------
-# admin                admin     admin
-# service              glance    admin
-# service              nova      admin, [ResellerAdmin (swift only)]
-# service              quantum   admin        # if enabled
-# service              swift     admin        # if enabled
-# service              cinder    admin        # if enabled
-# service              heat      admin        # if enabled
-# demo                 admin     admin
-# demo                 demo      Member, anotherrole
-# invisible_to_admin   demo      Member
+# admin                admin      admin
+# service              glance     admin
+# service              nova       admin, [ResellerAdmin (swift only)]
+# service              quantum    admin        # if enabled
+# service              swift      admin        # if enabled
+# service              cinder     admin        # if enabled
+# service              heat       admin        # if enabled
+# service              ceilometer admin        # if enabled
+# demo                 admin      admin
+# demo                 demo       Member, anotherrole
+# invisible_to_admin   demo       Member
 # Tempest Only:
 # alt_demo             alt_demo  Member
 #
@@ -262,7 +263,14 @@
     fi
 fi
 
-if [[ "$ENABLED_SERVICES" =~ "ceilometer-api" ]]; then
+if [[ "$ENABLED_SERVICES" =~ "ceilometer" ]]; then
+    CEILOMETER_USER=$(get_id keystone user-create --name=ceilometer \
+                                              --pass="$SERVICE_PASSWORD" \
+                                              --tenant_id $SERVICE_TENANT \
+                                              --email=ceilometer@example.com)
+    keystone user-role-add --tenant_id $SERVICE_TENANT \
+                           --user_id $CEILOMETER_USER \
+                           --role_id $ADMIN_ROLE
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
         CEILOMETER_SERVICE=$(get_id keystone service-create \
             --name=ceilometer \
@@ -345,4 +353,3 @@
             --internalurl "http://$SERVICE_HOST:8776/v1/\$(tenant_id)s"
     fi
 fi
-
diff --git a/files/rpms/postgresql b/files/rpms/postgresql
new file mode 100644
index 0000000..bf19d39
--- /dev/null
+++ b/files/rpms/postgresql
@@ -0,0 +1 @@
+python-psycopg2
diff --git a/functions b/functions
index 8008500..c7f65db 100644
--- a/functions
+++ b/functions
@@ -571,7 +571,7 @@
 # is_set env-var
 function is_set() {
     local var=\$"$1"
-    if eval "[ -z $var ]"; then
+    if eval "[ -z \"$var\" ]"; then
         return 1
     fi
     return 0
@@ -841,6 +841,21 @@
     fi
 }
 
+# Toggle enable/disable_service for services that must run exclusive of each other
+#  $1 The name of a variable containing a space-separated list of services
+#  $2 The name of a variable in which to store the enabled service's name
+#  $3 The name of the service to enable
+function use_exclusive_service {
+    local options=${!1}
+    local selection=$3
+    out=$2
+    [ -z $selection ] || [[ ! "$options" =~ "$selection" ]] && return 1
+    for opt in $options;do
+        [[ "$opt" = "$selection" ]] && enable_service $opt || disable_service $opt
+    done
+    eval "$out=$selection"
+    return 0
+}
 
 # Wrapper for ``yum`` to set proxy environment variables
 # Uses globals ``OFFLINE``, ``*_proxy`
@@ -884,7 +899,7 @@
     local FLOATING_IP=$3
     local DEFAULT_INSTANCE_USER=$4
     local ACTIVE_TIMEOUT=$5
-    local probe_cmd = ""
+    local probe_cmd=""
     if ! timeout $ACTIVE_TIMEOUT sh -c "while ! ssh -o StrictHostKeyChecking=no -i $KEY_FILE ${DEFAULT_INSTANCE_USER}@$FLOATING_IP echo success ; do sleep 1; done"; then
         echo "server didn't become ssh-able!"
         exit 1
diff --git a/lib/ceilometer b/lib/ceilometer
index b0f0377..2b014b0 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -6,8 +6,9 @@
 
 # Dependencies:
 # - functions
-# - OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL set for admin credentials
+# - OS_AUTH_URL for auth in api
 # - DEST set to the destination directory
+# - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api
 
 # stack.sh
 # ---------
@@ -61,7 +62,15 @@
     iniset $CEILOMETER_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
     iniset $CEILOMETER_CONF DEFAULT sql_connection $BASE_SQL_CONN/nova?charset=utf8
 
+    # Install the policy file for the API server
+    cp $CEILOMETER_DIR/etc/ceilometer/policy.json $CEILOMETER_CONF_DIR
+    iniset $CEILOMETER_CONF DEFAULT policy_file $CEILOMETER_CONF_DIR/policy.json
+
     iniset $CEILOMETER_CONF keystone_authtoken auth_protocol http
+    iniset $CEILOMETER_CONF keystone_authtoken admin_user ceilometer
+    iniset $CEILOMETER_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
+    iniset $CEILOMETER_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
+
     cleanup_ceilometer
 }
 
@@ -73,7 +82,7 @@
 # start_ceilometer() - Start running processes, including screen
 function start_ceilometer() {
     screen_it ceilometer-acompute "cd $CEILOMETER_DIR && sg libvirtd \"$CEILOMETER_BIN_DIR/ceilometer-agent-compute --config-file $CEILOMETER_CONF\""
-    screen_it ceilometer-acentral "export OS_USERNAME=$OS_USERNAME OS_PASSWORD=$OS_PASSWORD OS_TENANT_NAME=$OS_TENANT_NAME OS_AUTH_URL=$OS_AUTH_URL && cd $CEILOMETER_DIR && $CEILOMETER_BIN_DIR/ceilometer-agent-central --config-file $CEILOMETER_CONF"
+    screen_it ceilometer-acentral "export OS_USERNAME=ceilometer OS_PASSWORD=$SERVICE_PASSWORD OS_TENANT_NAME=$SERVICE_TENANT_NAME OS_AUTH_URL=$OS_AUTH_URL && cd $CEILOMETER_DIR && $CEILOMETER_BIN_DIR/ceilometer-agent-central --config-file $CEILOMETER_CONF"
     screen_it ceilometer-collector "cd $CEILOMETER_DIR && $CEILOMETER_BIN_DIR/ceilometer-collector --config-file $CEILOMETER_CONF"
     screen_it ceilometer-api "cd $CEILOMETER_DIR && $CEILOMETER_BIN_DIR/ceilometer-api -d -v --log-dir=$CEILOMETER_API_LOG_DIR --config-file $CEILOMETER_CONF"
 }
diff --git a/lib/cinder b/lib/cinder
index 08c840e..c2cf15b 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -4,8 +4,8 @@
 # Dependencies:
 # - functions
 # - DEST, DATA_DIR must be defined
-# - KEYSTONE_AUTH_* must be defined
 # SERVICE_{TENANT_NAME|PASSWORD} must be defined
+# ``KEYSTONE_TOKEN_FORMAT`` must be defined
 
 # stack.sh
 # ---------
@@ -30,6 +30,7 @@
 CINDER_STATE_PATH=${CINDER_STATE_PATH:=$DATA_DIR/cinder}
 CINDER_CONF_DIR=/etc/cinder
 CINDER_CONF=$CINDER_CONF_DIR/cinder.conf
+CINDER_AUTH_CACHE_DIR=${CINDER_AUTH_CACHE_DIR:-/var/cache/cinder}
 
 # Support entry points installation of console scripts
 if [[ -d $CINDER_DIR/bin ]]; then
@@ -106,18 +107,28 @@
     iniset $CINDER_API_PASTE_INI filter:authtoken admin_user cinder
     iniset $CINDER_API_PASTE_INI filter:authtoken admin_password $SERVICE_PASSWORD
 
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        iniset $CINDER_API_PASTE_INI filter:authtoken signing_dir $CINDER_AUTH_CACHE_DIR
+    fi
+
     cp $CINDER_DIR/etc/cinder/cinder.conf.sample $CINDER_CONF
     iniset $CINDER_CONF DEFAULT auth_strategy keystone
     iniset $CINDER_CONF DEFAULT verbose True
     iniset $CINDER_CONF DEFAULT volume_group $VOLUME_GROUP
     iniset $CINDER_CONF DEFAULT volume_name_template ${VOLUME_NAME_PREFIX}%s
     iniset $CINDER_CONF DEFAULT iscsi_helper tgtadm
-    iniset $CINDER_CONF DEFAULT sql_connection $BASE_SQL_CONN/cinder?charset=utf8
+    local dburl
+    database_connection_url dburl cinder
+    iniset $CINDER_CONF DEFAULT sql_connection $dburl
     iniset $CINDER_CONF DEFAULT api_paste_config $CINDER_API_PASTE_INI
     iniset $CINDER_CONF DEFAULT root_helper "sudo ${CINDER_ROOTWRAP}"
     iniset $CINDER_CONF DEFAULT osapi_volume_extension cinder.api.openstack.volume.contrib.standard_extensions
     iniset $CINDER_CONF DEFAULT state_path $CINDER_STATE_PATH
 
+    if [ "$SYSLOG" != "False" ]; then
+        iniset $CINDER_CONF DEFAULT use_syslog True
+    fi
+
     if is_service_enabled qpid ; then
         iniset $CINDER_CONF DEFAULT rpc_backend cinder.openstack.common.rpc.impl_qpid
     elif is_service_enabled zeromq; then
@@ -127,6 +138,10 @@
         iniset $CINDER_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
     fi
 
+    if [[ "$CINDER_SECURE_DELETE" == "False" ]]; then
+        iniset $CINDER_CONF DEFAULT secure_delete False
+    fi
+
     if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
         # Add color to logging output
         iniset $CINDER_CONF DEFAULT logging_context_format_string "%(asctime)s %(color)s%(levelname)s %(name)s [%(request_id)s %(user_id)s %(project_id)s%(color)s] %(instance)s%(color)s%(message)s"
@@ -141,10 +156,9 @@
     # Force nova volumes off
     NOVA_ENABLED_APIS=$(echo $NOVA_ENABLED_APIS | sed "s/osapi_volume,//")
 
-    if is_service_enabled mysql; then
+    if is_service_enabled $DATABASE_BACKENDS; then
         # (re)create cinder database
-        mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'DROP DATABASE IF EXISTS cinder;'
-        mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'CREATE DATABASE cinder;'
+        recreate_database cinder utf8
 
         # (re)create cinder database
         $CINDER_BIN_DIR/cinder-manage db sync
@@ -186,6 +200,12 @@
             done
         fi
     fi
+
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        # Create cache dir
+        sudo mkdir -p $CINDER_AUTH_CACHE_DIR
+        sudo chown `whoami` $CINDER_AUTH_CACHE_DIR
+    fi
 }
 
 # install_cinder() - Collect source and prepare
diff --git a/lib/database b/lib/database
new file mode 100644
index 0000000..66fb36f
--- /dev/null
+++ b/lib/database
@@ -0,0 +1,103 @@
+# lib/database
+# Interface for interacting with different database backends
+
+# Dependencies:
+# DATABASE_BACKENDS variable must contain a list of available database backends
+# DATABASE_TYPE variable must be set
+
+# Each database must implement four functions:
+#   recreate_database_$DATABASE_TYPE
+#   install_database_$DATABASE_TYPE
+#   configure_database_$DATABASE_TYPE
+#   database_connection_url_$DATABASE_TYPE
+#
+# and call register_database $DATABASE_TYPE
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+# Register a database backend
+#  $1 The name of the database backend
+function register_database {
+    [ -z "$DATABASE_BACKENDS" ] && DATABASE_BACKENDS=$1 || DATABASE_BACKENDS+=" $1"
+}
+
+for f in $TOP_DIR/lib/databases/*; do source $f; done
+
+# Set the database type based on the configuration
+function initialize_database_backends {
+    for backend in $DATABASE_BACKENDS; do
+        is_service_enabled $backend && DATABASE_TYPE=$backend
+    done
+
+    [ -z "$DATABASE_TYPE" ] && return 1
+
+    # For backward-compatibility, read in the MYSQL_HOST/USER variables and use
+    # them as the default values for the DATABASE_HOST/USER variables.
+    MYSQL_HOST=${MYSQL_HOST:-localhost}
+    MYSQL_USER=${MYSQL_USER:-root}
+
+    DATABASE_HOST=${DATABASE_HOST:-${MYSQL_HOST}}
+    DATABASE_USER=${DATABASE_USER:-${MYSQL_USER}}
+
+    if [ -n "$MYSQL_PASSWORD" ]; then
+        DATABASE_PASSWORD=$MYSQL_PASSWORD
+    else
+        read_password DATABASE_PASSWORD "ENTER A PASSWORD TO USE FOR THE DATABASE."
+    fi
+
+    # We configure Nova, Horizon, Glance and Keystone to use MySQL as their
+    # database server.  While they share a single server, each has their own
+    # database and tables.
+
+    # By default this script will install and configure MySQL.  If you want to
+    # use an existing server, you can pass in the user/password/host parameters.
+    # You will need to send the same ``DATABASE_PASSWORD`` to every host if you are doing
+    # a multi-node DevStack installation.
+
+    # NOTE: Don't specify ``/db`` in this string so we can use it for multiple services
+    BASE_SQL_CONN=${BASE_SQL_CONN:-${DATABASE_TYPE}://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST}
+
+    return 0
+}
+
+# Set the database backend to use
+#  $1 The name of the database backend to use (mysql, postgresql, ...)
+function use_database {
+    use_exclusive_service DATABASE_BACKENDS DATABASE_TYPE $1 && return 0
+    ret=$?
+    echo "Invalid database '$1'"
+    return $ret
+}
+
+# Recreate a given database
+#  $1 The name of the database
+#  $2 The character set/encoding of the database
+function recreate_database {
+    local db=$1
+    local charset=$2
+    recreate_database_$DATABASE_TYPE $db $charset
+}
+
+# Install the database
+function install_database {
+    install_database_$DATABASE_TYPE
+}
+
+# Configure and start the database
+function configure_database {
+    configure_database_$DATABASE_TYPE
+}
+
+# Generate an SQLAlchemy connection URL and store it in a variable
+#  $1 The variable name in which to store the connection URL
+#  $2 The name of the database
+function database_connection_url {
+    local var=$1
+    local db=$2
+    database_connection_url_$DATABASE_TYPE $var $db
+}
+
+# Restore xtrace
+$XTRACE
diff --git a/lib/databases/mysql b/lib/databases/mysql
new file mode 100644
index 0000000..ed59290
--- /dev/null
+++ b/lib/databases/mysql
@@ -0,0 +1,93 @@
+# lib/mysql
+# Functions to control the configuration and operation of the MySQL database backend
+
+# Dependencies:
+# DATABASE_{HOST,USER,PASSWORD} must be defined
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+register_database mysql
+
+function recreate_database_mysql {
+    local db=$1
+    local charset=$2
+    mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -e "DROP DATABASE IF EXISTS $db;"
+    mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -e "CREATE DATABASE $db CHARACTER SET $charset;"
+}
+
+function configure_database_mysql {
+    echo_summary "Configuring and starting MySQL"
+
+    if [[ "$os_PACKAGE" = "deb" ]]; then
+        MY_CONF=/etc/mysql/my.cnf
+        MYSQL=mysql
+    else
+        MY_CONF=/etc/my.cnf
+        MYSQL=mysqld
+    fi
+
+    # Start mysql-server
+    if [[ "$os_PACKAGE" = "rpm" ]]; then
+        # RPM doesn't start the service
+        start_service $MYSQL
+        # Set the root password - only works the first time
+        sudo mysqladmin -u root password $DATABASE_PASSWORD || true
+    fi
+    # Update the DB to give user ‘$DATABASE_USER’@’%’ full control of the all databases:
+    sudo mysql -uroot -p$DATABASE_PASSWORD -h127.0.0.1 -e "GRANT ALL PRIVILEGES ON *.* TO '$DATABASE_USER'@'%' identified by '$DATABASE_PASSWORD';"
+
+    # Now update ``my.cnf`` for some local needs and restart the mysql service
+
+    # Change ‘bind-address’ from localhost (127.0.0.1) to any (0.0.0.0)
+    sudo sed -i '/^bind-address/s/127.0.0.1/0.0.0.0/g' $MY_CONF
+
+    # Set default db type to InnoDB
+    if sudo grep -q "default-storage-engine" $MY_CONF; then
+        # Change it
+        sudo bash -c "source $TOP_DIR/functions; iniset $MY_CONF mysqld default-storage-engine InnoDB"
+    else
+        # Add it
+        sudo sed -i -e "/^\[mysqld\]/ a \
+default-storage-engine = InnoDB" $MY_CONF
+    fi
+
+    restart_service $MYSQL
+}
+
+function install_database_mysql {
+    if [[ "$os_PACKAGE" = "deb" ]]; then
+        # Seed configuration with mysql password so that apt-get install doesn't
+        # prompt us for a password upon install.
+        cat <<MYSQL_PRESEED | sudo debconf-set-selections
+mysql-server-5.1 mysql-server/root_password password $DATABASE_PASSWORD
+mysql-server-5.1 mysql-server/root_password_again password $DATABASE_PASSWORD
+mysql-server-5.1 mysql-server/start_on_boot boolean true
+MYSQL_PRESEED
+    fi
+
+    # while ``.my.cnf`` is not needed for OpenStack to function, it is useful
+    # as it allows you to access the mysql databases via ``mysql nova`` instead
+    # of having to specify the username/password each time.
+    if [[ ! -e $HOME/.my.cnf ]]; then
+        cat <<EOF >$HOME/.my.cnf
+[client]
+user=$DATABASE_USER
+password=$DATABASE_PASSWORD
+host=$DATABASE_HOST
+EOF
+        chmod 0600 $HOME/.my.cnf
+    fi
+    # Install mysql-server
+    install_package mysql-server
+}
+
+function database_connection_url_mysql {
+    local output=$1
+    local db=$2
+    eval "$output=$BASE_SQL_CONN/$db?charset=utf8"
+}
+
+# Restore xtrace
+$XTRACE
diff --git a/lib/databases/postgresql b/lib/databases/postgresql
new file mode 100644
index 0000000..81989f2
--- /dev/null
+++ b/lib/databases/postgresql
@@ -0,0 +1,70 @@
+# lib/postgresql
+# Functions to control the configuration and operation of the PostgreSQL database backend
+
+# Dependencies:
+# DATABASE_{HOST,USER,PASSWORD} must be defined
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+register_database postgresql
+
+function recreate_database_postgresql {
+    local db=$1
+    local charset=$2
+    # Avoid unsightly error when calling dropdb when the database doesn't exist
+    psql -h$DATABASE_HOST -U$DATABASE_USER -dtemplate1 -c "DROP DATABASE IF EXISTS $db"
+    createdb -h $DATABASE_HOST -U$DATABASE_USER -l C -T template0 -E $charset $db
+}
+
+function configure_database_postgresql {
+    echo_summary "Configuring and starting PostgreSQL"
+    if [[ "$os_PACKAGE" = "rpm" ]]; then
+        PG_HBA=/var/lib/pgsql/data/pg_hba.conf
+        PG_CONF=/var/lib/pgsql/data/postgresql.conf
+    else
+        PG_DIR=`find /etc/postgresql -name pg_hba.conf|xargs dirname`
+        PG_HBA=$PG_DIR/pg_hba.conf
+        PG_CONF=$PG_DIR/postgresql.conf
+    fi
+    sudo [ -e /var/lib/pgsql/data ] || sudo postgresql-setup initdb
+    # Listen on all addresses
+    sudo sed -i "/listen_addresses/s/.*/listen_addresses = '*'/" $PG_CONF
+    # Do password auth from all IPv4 clients
+    sudo sed -i "/^host/s/all\s\+127.0.0.1\/32\s\+ident/$DATABASE_USER\t0.0.0.0\/0\tpassword/" $PG_HBA
+    # Do password auth for all IPv6 clients
+    sudo sed -i "/^host/s/all\s\+::1\/128\s\+ident/$DATABASE_USER\t::0\/0\tpassword/" $PG_HBA
+    start_service postgresql
+
+    # If creating the role fails, chances are it already existed. Try to alter it.
+    sudo -u postgres -i psql -c "CREATE ROLE $DATABASE_USER WITH SUPERUSER LOGIN PASSWORD '$DATABASE_PASSWORD'" || \
+    sudo -u postgres -i psql -c "ALTER ROLE $DATABASE_USER WITH SUPERUSER LOGIN PASSWORD '$DATABASE_PASSWORD'"
+}
+
+function install_database_postgresql {
+    echo_summary "Installing postgresql"
+    PGPASS=$HOME/.pgpass
+    if [[ ! -e $PGPASS ]]; then
+        cat <<EOF > $PGPASS
+*:*:*:$DATABASE_USER:$DATABASE_PASSWORD
+EOF
+        chmod 0600 $PGPASS
+    else
+        sed -i "s/:root:\w\+/:root:$DATABASE_PASSWORD/" $PGPASS
+    fi
+    if [[ "$os_PACKAGE" = "rpm" ]]; then
+        install_package postgresql-server
+    else
+        install_package postgresql
+    fi
+}
+
+function database_connection_url_postgresql {
+    local output=$1
+    local db=$2
+    eval "$output=$BASE_SQL_CONN/$db?client_encoding=utf8"
+}
+
+# Restore xtrace
+$XTRACE
diff --git a/lib/glance b/lib/glance
index 070c80d..b02a4b6 100644
--- a/lib/glance
+++ b/lib/glance
@@ -6,6 +6,7 @@
 # ``DEST``, ``DATA_DIR`` must be defined
 # ``SERVICE_{TENANT_NAME|PASSWORD}`` must be defined
 # ``SERVICE_HOST``
+# ``KEYSTONE_TOKEN_FORMAT`` must be defined
 
 # ``stack.sh`` calls the entry points in this order:
 #
@@ -31,6 +32,7 @@
 GLANCECLIENT_DIR=$DEST/python-glanceclient
 GLANCE_CACHE_DIR=${GLANCE_CACHE_DIR:=$DATA_DIR/glance/cache}
 GLANCE_IMAGE_DIR=${GLANCE_IMAGE_DIR:=$DATA_DIR/glance/images}
+GLANCE_AUTH_CACHE_DIR=${GLANCE_AUTH_CACHE_DIR:-/var/cache/glance}
 
 GLANCE_CONF_DIR=${GLANCE_CONF_DIR:-/etc/glance}
 GLANCE_REGISTRY_CONF=$GLANCE_CONF_DIR/glance-registry.conf
@@ -81,7 +83,9 @@
     cp $GLANCE_DIR/etc/glance-registry.conf $GLANCE_REGISTRY_CONF
     iniset $GLANCE_REGISTRY_CONF DEFAULT debug True
     inicomment $GLANCE_REGISTRY_CONF DEFAULT log_file
-    iniset $GLANCE_REGISTRY_CONF DEFAULT sql_connection $BASE_SQL_CONN/glance?charset=utf8
+    local dburl
+    database_connection_url dburl glance
+    iniset $GLANCE_REGISTRY_CONF DEFAULT sql_connection $dburl
     iniset $GLANCE_REGISTRY_CONF DEFAULT use_syslog $SYSLOG
     iniset $GLANCE_REGISTRY_CONF paste_deploy flavor keystone
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST
@@ -91,11 +95,14 @@
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_user glance
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        iniset $GLANCE_REGISTRY_CONF keystone_authtoken signing_dir $GLANCE_AUTH_CACHE_DIR/registry
+    fi
 
     cp $GLANCE_DIR/etc/glance-api.conf $GLANCE_API_CONF
     iniset $GLANCE_API_CONF DEFAULT debug True
     inicomment $GLANCE_API_CONF DEFAULT log_file
-    iniset $GLANCE_API_CONF DEFAULT sql_connection $BASE_SQL_CONN/glance?charset=utf8
+    iniset $GLANCE_API_CONF DEFAULT sql_connection $dburl
     iniset $GLANCE_API_CONF DEFAULT use_syslog $SYSLOG
     iniset $GLANCE_API_CONF DEFAULT filesystem_store_datadir $GLANCE_IMAGE_DIR/
     iniset $GLANCE_API_CONF DEFAULT image_cache_dir $GLANCE_CACHE_DIR/
@@ -114,6 +121,9 @@
         iniset $GLANCE_API_CONF DEFAULT rabbit_host $RABBIT_HOST
         iniset $GLANCE_API_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
     fi
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        iniset $GLANCE_API_CONF keystone_authtoken signing_dir $GLANCE_AUTH_CACHE_DIR/api
+    fi
 
     cp -p $GLANCE_DIR/etc/glance-registry-paste.ini $GLANCE_REGISTRY_PASTE_INI
 
@@ -149,10 +159,17 @@
     mkdir -p $GLANCE_CACHE_DIR
 
     # (re)create glance database
-    mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'DROP DATABASE IF EXISTS glance;'
-    mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'CREATE DATABASE glance CHARACTER SET utf8;'
+    recreate_database glance utf8
 
     $GLANCE_BIN_DIR/glance-manage db_sync
+
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        # Create cache dir
+        sudo mkdir -p $GLANCE_AUTH_CACHE_DIR/api
+        sudo chown `whoami` $GLANCE_AUTH_CACHE_DIR/api
+        sudo mkdir -p $GLANCE_AUTH_CACHE_DIR/registry
+        sudo chown `whoami` $GLANCE_AUTH_CACHE_DIR/registry
+    fi
 }
 
 # install_glanceclient() - Collect source and prepare
diff --git a/lib/heat b/lib/heat
index 7fb5fcc..d1f1c7c 100644
--- a/lib/heat
+++ b/lib/heat
@@ -120,7 +120,9 @@
     iniset $HEAT_ENGINE_CONF DEFAULT use_syslog $SYSLOG
     iniset $HEAT_ENGINE_CONF DEFAULT bind_host $HEAT_ENGINE_HOST
     iniset $HEAT_ENGINE_CONF DEFAULT bind_port $HEAT_ENGINE_PORT
-    iniset $HEAT_ENGINE_CONF DEFAULT sql_connection $BASE_SQL_CONN/heat?charset=utf8
+    local dburl
+    database_connection_url dburl heat
+    iniset $HEAT_ENGINE_CONF DEFAULT sql_connection $dburl
     iniset $HEAT_ENGINE_CONF DEFAULT auth_encryption_key `hexdump -n 16 -v -e '/1 "%02x"' /dev/random`
 
     if is_service_enabled rabbit; then
@@ -185,8 +187,7 @@
 function init_heat() {
 
     # (re)create heat database
-    mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'DROP DATABASE IF EXISTS heat;'
-    mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'CREATE DATABASE heat CHARACTER SET utf8;'
+    recreate_database heat utf8
 
     $HEAT_DIR/bin/heat-db-setup $os_PACKAGE -r $MYSQL_PASSWORD
     $HEAT_DIR/tools/nova_create_flavors.sh
diff --git a/lib/keystone b/lib/keystone
index 73d82c5..ae89056 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -32,13 +32,18 @@
 KEYSTONE_DIR=$DEST/keystone
 KEYSTONE_CONF_DIR=${KEYSTONE_CONF_DIR:-/etc/keystone}
 KEYSTONE_CONF=$KEYSTONE_CONF_DIR/keystone.conf
+KEYSTONE_AUTH_CACHE_DIR=${KEYSTONE_AUTH_CACHE_DIR:-/var/cache/keystone}
 
 KEYSTONECLIENT_DIR=$DEST/python-keystoneclient
 
-# Select the backend for Keystopne's service catalog
+# Select the backend for Keystone's service catalog
 KEYSTONE_CATALOG_BACKEND=${KEYSTONE_CATALOG_BACKEND:-sql}
 KEYSTONE_CATALOG=$KEYSTONE_CONF_DIR/default_catalog.templates
 
+# Select Keystone's token format
+# Choose from 'UUID' and 'PKI'
+KEYSTONE_TOKEN_FORMAT=${KEYSTONE_TOKEN_FORMAT:-PKI}
+
 # Set Keystone interface configuration
 KEYSTONE_API_PORT=${KEYSTONE_API_PORT:-5000}
 KEYSTONE_AUTH_HOST=${KEYSTONE_AUTH_HOST:-$SERVICE_HOST}
@@ -47,7 +52,6 @@
 KEYSTONE_SERVICE_HOST=${KEYSTONE_SERVICE_HOST:-$SERVICE_HOST}
 KEYSTONE_SERVICE_PORT=${KEYSTONE_SERVICE_PORT:-5000}
 KEYSTONE_SERVICE_PROTOCOL=${KEYSTONE_SERVICE_PROTOCOL:-http}
-KEYSTONE_TOKEN_FORMAT=${KEYSTONE_TOKEN_FORMAT:-PKI}
 
 
 # Entry Points
@@ -82,9 +86,11 @@
     fi
 
     # Rewrite stock ``keystone.conf``
+    local dburl
+    database_connection_url dburl keystone
     iniset $KEYSTONE_CONF DEFAULT admin_token "$SERVICE_TOKEN"
     iniset $KEYSTONE_CONF signing token_format "$KEYSTONE_TOKEN_FORMAT"
-    iniset $KEYSTONE_CONF sql connection "$BASE_SQL_CONN/keystone?charset=utf8"
+    iniset $KEYSTONE_CONF sql connection $dburl
     iniset $KEYSTONE_CONF ec2 driver "keystone.contrib.ec2.backends.sql.Ec2"
     sed -e "
         /^pipeline.*ec2_extension crud_/s|ec2_extension crud_extension|ec2_extension s3_extension crud_extension|;
@@ -141,14 +147,19 @@
 # init_keystone() - Initialize databases, etc.
 function init_keystone() {
     # (Re)create keystone database
-    mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'DROP DATABASE IF EXISTS keystone;'
-    mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'CREATE DATABASE keystone CHARACTER SET utf8;'
+    recreate_database keystone utf8
 
     # Initialize keystone database
     $KEYSTONE_DIR/bin/keystone-manage db_sync
 
-    # Set up certificates
-    $KEYSTONE_DIR/bin/keystone-manage pki_setup
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        # Set up certificates
+        $KEYSTONE_DIR/bin/keystone-manage pki_setup
+
+        # Create cache dir
+        sudo mkdir -p $KEYSTONE_AUTH_CACHE_DIR
+        sudo chown `whoami` $KEYSTONE_AUTH_CACHE_DIR
+    fi
 }
 
 # install_keystoneclient() - Collect source and prepare
diff --git a/lib/nova b/lib/nova
index 95d5d87..2c1413d 100644
--- a/lib/nova
+++ b/lib/nova
@@ -7,6 +7,7 @@
 # ``SERVICE_{TENANT_NAME|PASSWORD}`` must be defined
 # ``LIBVIRT_TYPE`` must be defined
 # ``INSTANCE_NAME_PREFIX``, ``VOLUME_NAME_PREFIX`` must be defined
+# ``KEYSTONE_TOKEN_FORMAT`` must be defined
 
 # ``stack.sh`` calls the entry points in this order:
 #
@@ -32,6 +33,7 @@
 NOVA_STATE_PATH=${NOVA_STATE_PATH:=$DATA_DIR/nova}
 # INSTANCES_PATH is the previous name for this
 NOVA_INSTANCES_PATH=${NOVA_INSTANCES_PATH:=${INSTANCES_PATH:=$NOVA_STATE_PATH/instances}}
+NOVA_AUTH_CACHE_DIR=${NOVA_AUTH_CACHE_DIR:-/var/cache/nova}
 
 NOVA_CONF_DIR=/etc/nova
 NOVA_CONF=$NOVA_CONF_DIR/nova.conf
@@ -174,6 +176,10 @@
         " -i $NOVA_API_PASTE_INI
     fi
 
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        iniset $NOVA_API_PASTE_INI filter:authtoken signing_dir $NOVA_AUTH_CACHE_DIR
+    fi
+
     if is_service_enabled n-cpu; then
         # Force IP forwarding on, just on case
         sudo sysctl -w net.ipv4.ip_forward=1
@@ -296,7 +302,9 @@
     add_nova_opt "s3_port=$S3_SERVICE_PORT"
     add_nova_opt "osapi_compute_extension=nova.api.openstack.compute.contrib.standard_extensions"
     add_nova_opt "my_ip=$HOST_IP"
-    add_nova_opt "sql_connection=$BASE_SQL_CONN/nova?charset=utf8"
+    local dburl
+    database_connection_url dburl nova
+    add_nova_opt "sql_connection=$dburl"
     add_nova_opt "libvirt_type=$LIBVIRT_TYPE"
     add_nova_opt "libvirt_cpu_mode=none"
     add_nova_opt "instance_name_template=${INSTANCE_NAME_PREFIX}%08x"
@@ -305,6 +313,8 @@
         add_nova_opt "enabled_apis=$NOVA_ENABLED_APIS"
     fi
     if is_service_enabled n-vol; then
+        NOVA_ENABLED_APIS="${NOVA_ENABLED_APIS},osapi_volume"
+        iniset $NOVA_CONF DEFAULT enabled_apis $NOVA_ENABLED_APIS
         add_nova_opt "volume_api_class=nova.volume.api.API"
         add_nova_opt "volume_group=$VOLUME_GROUP"
         add_nova_opt "volume_name_template=${VOLUME_NAME_PREFIX}%s"
@@ -370,19 +380,22 @@
     # All nova components talk to a central database.  We will need to do this step
     # only once for an entire cluster.
 
-    if is_service_enabled mysql && is_service_enabled nova; then
+    if is_service_enabled $DATABASE_BACKENDS && is_service_enabled nova; then
         # (Re)create nova database
-        mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'DROP DATABASE IF EXISTS nova;'
-
         # Explicitly use latin1: to avoid lp#829209, nova expects the database to
         # use latin1 by default, and then upgrades the database to utf8 (see the
         # 082_essex.py in nova)
-        mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'CREATE DATABASE nova CHARACTER SET latin1;'
+        recreate_database nova latin1
 
         # (Re)create nova database
         $NOVA_BIN_DIR/nova-manage db sync
     fi
 
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        # Create cache dir
+        sudo mkdir -p $NOVA_AUTH_CACHE_DIR
+        sudo chown `whoami` $NOVA_AUTH_CACHE_DIR
+    fi
 }
 
 # install_novaclient() - Collect source and prepare
diff --git a/stack.sh b/stack.sh
index 29e4953..04037e8 100755
--- a/stack.sh
+++ b/stack.sh
@@ -12,13 +12,12 @@
 # developer install.
 
 # To keep this script simple we assume you are running on a recent **Ubuntu**
-# (11.10 Oneiric or 12.04 Precise) or **Fedora** (F16 or F17) machine.  It
+# (11.10 Oneiric or newer) or **Fedora** (F16 or newer) machine.  It
 # should work in a VM or physical server.  Additionally we put the list of
 # ``apt`` and ``rpm`` dependencies and other configuration files in this repo.
 
 # Learn more and get the most recent version at http://devstack.org
 
-
 # Keep track of the devstack directory
 TOP_DIR=$(cd $(dirname "$0") && pwd)
 
@@ -30,6 +29,9 @@
 # and ``DISTRO``
 GetDistro
 
+# Import database library (must be loaded before stackrc which sources localrc)
+source $TOP_DIR/lib/database
+
 
 # Settings
 # ========
@@ -37,15 +39,15 @@
 # ``stack.sh`` is customizable through setting environment variables.  If you
 # want to override a setting you can set and export it::
 #
-#     export MYSQL_PASSWORD=anothersecret
+#     export DATABASE_PASSWORD=anothersecret
 #     ./stack.sh
 #
-# You can also pass options on a single line ``MYSQL_PASSWORD=simple ./stack.sh``
+# You can also pass options on a single line ``DATABASE_PASSWORD=simple ./stack.sh``
 #
 # Additionally, you can put any local variables into a ``localrc`` file::
 #
-#     MYSQL_PASSWORD=anothersecret
-#     MYSQL_USER=hellaroot
+#     DATABASE_PASSWORD=anothersecret
+#     DATABASE_USER=hellaroot
 #
 # We try to have sensible defaults, so you should be able to run ``./stack.sh``
 # in most cases.  ``localrc`` is not distributed with DevStack and will never
@@ -105,9 +107,8 @@
     fi
 fi
 
-# Disallow qpid on oneiric
+# Qpid was introduced to Ubuntu in precise, disallow it on oneiric
 if [ "${DISTRO}" = "oneiric" ] && is_service_enabled qpid ; then
-    # Qpid was introduced in precise
     echo "You must use Ubuntu Precise or newer for Qpid support."
     exit 1
 fi
@@ -354,6 +355,11 @@
 # Ryu Applications
 RYU_APPS=${RYU_APPS:-ryu.app.simple_isolation,ryu.app.rest}
 
+# 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`
+
 # Name of the LVM volume group to use/create for iscsi volumes
 VOLUME_GROUP=${VOLUME_GROUP:-stack-volumes}
 VOLUME_NAME_PREFIX=${VOLUME_NAME_PREFIX:-volume-}
@@ -446,14 +452,16 @@
 # fail.
 #
 # If you are running on a single node and don't need to access the VMs from
-# devices other than that node, you can set FLAT_INTERFACE=
-# This will stop nova from bridging any interfaces into FLAT_NETWORK_BRIDGE.
+# devices other than that node, you can set ``FLAT_INTERFACE=``
+# This will stop nova from bridging any interfaces into ``FLAT_NETWORK_BRIDGE``.
 FLAT_INTERFACE=${FLAT_INTERFACE-$GUEST_INTERFACE_DEFAULT}
 
 ## FIXME(ja): should/can we check that FLAT_INTERFACE is sane?
 
-# Using Quantum networking:
-#
+
+# Quantum Networking
+# ------------------
+
 # Make sure that quantum is enabled in ENABLED_SERVICES.  If you want
 # to run Quantum on this host, make sure that q-svc is also in
 # ENABLED_SERVICES.
@@ -471,24 +479,23 @@
 # With Quantum networking the NET_MAN variable is ignored.
 
 
-# MySQL & (RabbitMQ or Qpid)
+# Database Configuration
+# ----------------------
+
+# To select between database backends, add a line to localrc like:
+#
+#  use_database postgresql
+#
+# The available database backends are defined in the ``DATABASE_BACKENDS``
+# variable defined in stackrc. By default, MySQL is enabled as the database
+# backend.
+
+initialize_database_backends && echo "Using $DATABASE_TYPE database backend" || echo "No database enabled"
+
+
+# RabbitMQ or Qpid
 # --------------------------
 
-# We configure Nova, Horizon, Glance and Keystone to use MySQL as their
-# database server.  While they share a single server, each has their own
-# database and tables.
-
-# By default this script will install and configure MySQL.  If you want to
-# use an existing server, you can pass in the user/password/host parameters.
-# You will need to send the same ``MYSQL_PASSWORD`` to every host if you are doing
-# a multi-node DevStack installation.
-MYSQL_HOST=${MYSQL_HOST:-localhost}
-MYSQL_USER=${MYSQL_USER:-root}
-read_password MYSQL_PASSWORD "ENTER A PASSWORD TO USE FOR MYSQL."
-
-# NOTE: Don't specify ``/db`` in this string so we can use it for multiple services
-BASE_SQL_CONN=${BASE_SQL_CONN:-mysql://$MYSQL_USER:$MYSQL_PASSWORD@$MYSQL_HOST}
-
 # Rabbit connection info
 if is_service_enabled rabbit; then
     RABBIT_HOST=${RABBIT_HOST:-localhost}
@@ -537,7 +544,7 @@
         S3_SERVICE_PORT=${S3_SERVICE_PORT:-8080}
     fi
     # We only ask for Swift Hash if we have enabled swift service.
-    # SWIFT_HASH is a random unique string for a swift cluster that
+    # ``SWIFT_HASH`` is a random unique string for a swift cluster that
     # can never change.
     read_password SWIFT_HASH "ENTER A RANDOM SWIFT HASH."
 fi
@@ -552,7 +559,7 @@
 # The ``SERVICE_TOKEN`` is used to bootstrap the Keystone database.  It is
 # just a string and is not a 'real' Keystone token.
 read_password SERVICE_TOKEN "ENTER A SERVICE_TOKEN TO USE FOR THE SERVICE ADMIN TOKEN."
-# Services authenticate to Identity with servicename/SERVICE_PASSWORD
+# Services authenticate to Identity with servicename/``SERVICE_PASSWORD``
 read_password SERVICE_PASSWORD "ENTER A SERVICE_PASSWORD TO USE FOR THE SERVICE AUTHENTICATION."
 # Horizon currently truncates usernames and passwords at 20 characters
 read_password ADMIN_PASSWORD "ENTER A PASSWORD TO USE FOR HORIZON AND KEYSTONE (20 CHARS OR LESS)."
@@ -561,7 +568,6 @@
 SERVICE_TENANT_NAME=${SERVICE_TENANT_NAME:-service}
 
 
-
 # Horizon
 # -------
 
@@ -575,10 +581,9 @@
 # ---------
 
 # Draw a spinner so the user knows something is happening
-function spinner()
-{
+function spinner() {
     local delay=0.75
-    local spinstr='|/-\'
+    local spinstr='/-\|'
     printf "..." >&3
     while [ true ]; do
         local temp=${spinstr#?}
@@ -633,6 +638,7 @@
     SUMFILE=$LOGFILE.${CURRENT_LOG_TIME}.summary
 
     # Redirect output according to config
+
     # Copy stdout to fd 3
     exec 3>&1
     if [[ "$VERBOSE" == "True" ]]; then
@@ -746,32 +752,8 @@
     fi
 fi
 
-if is_service_enabled mysql; then
-
-    if [[ "$os_PACKAGE" = "deb" ]]; then
-        # Seed configuration with mysql password so that apt-get install doesn't
-        # prompt us for a password upon install.
-        cat <<MYSQL_PRESEED | sudo debconf-set-selections
-mysql-server-5.1 mysql-server/root_password password $MYSQL_PASSWORD
-mysql-server-5.1 mysql-server/root_password_again password $MYSQL_PASSWORD
-mysql-server-5.1 mysql-server/start_on_boot boolean true
-MYSQL_PRESEED
-    fi
-
-    # while ``.my.cnf`` is not needed for OpenStack to function, it is useful
-    # as it allows you to access the mysql databases via ``mysql nova`` instead
-    # of having to specify the username/password each time.
-    if [[ ! -e $HOME/.my.cnf ]]; then
-        cat <<EOF >$HOME/.my.cnf
-[client]
-user=$MYSQL_USER
-password=$MYSQL_PASSWORD
-host=$MYSQL_HOST
-EOF
-        chmod 0600 $HOME/.my.cnf
-    fi
-    # Install mysql-server
-    install_package mysql-server
+if is_service_enabled $DATABASE_BACKENDS; then
+    install_database
 fi
 
 if is_service_enabled horizon; then
@@ -787,7 +769,7 @@
 if is_service_enabled q-agt; then
     if is_quantum_ovs_base_plugin "$Q_PLUGIN"; then
         # Install deps
-        # FIXME add to files/apts/quantum, but don't install if not needed!
+        # FIXME add to ``files/apts/quantum``, but don't install if not needed!
         if [[ "$os_PACKAGE" = "deb" ]]; then
             kernel_version=`cat /proc/version | cut -d " " -f3`
             install_package make fakeroot dkms openvswitch-switch openvswitch-datapath-dkms linux-headers-$kernel_version
@@ -830,6 +812,7 @@
 
 echo_summary "Installing OpenStack project source"
 
+# Grab clients first
 install_keystoneclient
 install_glanceclient
 install_novaclient
@@ -891,6 +874,7 @@
     git_clone $RYU_REPO $RYU_DIR $RYU_BRANCH
 fi
 
+
 # Initialization
 # ==============
 
@@ -935,9 +919,6 @@
 if is_service_enabled cinder; then
     configure_cinder
 fi
-if is_service_enabled tempest; then
-    configure_tempest
-fi
 if is_service_enabled ryu || (is_service_enabled quantum && [[ "$Q_PLUGIN" = "ryu" ]]); then
     setup_develop $RYU_DIR
 fi
@@ -993,48 +974,17 @@
 fi
 
 
-# Mysql
-# -----
+# Configure database
+# ------------------
 
-if is_service_enabled mysql; then
-    echo_summary "Configuring and starting MySQL"
-
-    if [[ "$os_PACKAGE" = "deb" ]]; then
-        MY_CONF=/etc/mysql/my.cnf
-        MYSQL=mysql
-    else
-        MY_CONF=/etc/my.cnf
-        MYSQL=mysqld
-    fi
-
-    # Start mysql-server
-    if [[ "$os_PACKAGE" = "rpm" ]]; then
-        # RPM doesn't start the service
-        start_service $MYSQL
-        # Set the root password - only works the first time
-        sudo mysqladmin -u root password $MYSQL_PASSWORD || true
-    fi
-    # Update the DB to give user ‘$MYSQL_USER’@’%’ full control of the all databases:
-    sudo mysql -uroot -p$MYSQL_PASSWORD -h127.0.0.1 -e "GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_USER'@'%' identified by '$MYSQL_PASSWORD';"
-
-    # Now update ``my.cnf`` for some local needs and restart the mysql service
-
-    # Change ‘bind-address’ from localhost (127.0.0.1) to any (0.0.0.0)
-    sudo sed -i '/^bind-address/s/127.0.0.1/0.0.0.0/g' $MY_CONF
-
-    # Set default db type to InnoDB
-    if sudo grep -q "default-storage-engine" $MY_CONF; then
-        # Change it
-        sudo bash -c "source $TOP_DIR/functions; iniset $MY_CONF mysqld default-storage-engine InnoDB"
-    else
-        # Add it
-        sudo sed -i -e "/^\[mysqld\]/ a \
-default-storage-engine = InnoDB" $MY_CONF
-    fi
-
-    restart_service $MYSQL
+if is_service_enabled $DATABASE_BACKENDS; then
+    configure_database
 fi
 
+
+# Configure screen
+# ----------------
+
 if [ -z "$SCREEN_HARDSTATUS" ]; then
     SCREEN_HARDSTATUS='%{= .} %-Lw%{= .}%> %n%f %t*%{= .}%+Lw%< %-=%{g}(%{d}%H/%l%{g})'
 fi
@@ -1044,9 +994,11 @@
 if [[ -e $SCREENRC ]]; then
     echo -n > $SCREENRC
 fi
+
 # 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 -r $SCREEN_NAME -X hardstatus alwayslastline "$SCREEN_HARDSTATUS"
 
@@ -1156,6 +1108,7 @@
 
 # Ryu
 # ---
+
 # Ryu is not a part of OpenStack project. Please ignore following block if
 # you are not interested in Ryu.
 # launch ryu manager
@@ -1182,11 +1135,10 @@
 # Quantum
 # -------
 
+# Quantum Network Configuration
 if is_service_enabled quantum; then
     echo_summary "Configuring Quantum"
-    #
-    # Quantum Network Configuration
-    #
+
     # The following variables control the Quantum openvswitch and
     # linuxbridge plugins' allocation of tenant networks and
     # availability of provider networks. If these are not configured
@@ -1214,7 +1166,7 @@
     # allocated. An external network switch must be configured to
     # trunk these VLANs between hosts for multi-host connectivity.
     #
-    # Example: TENANT_VLAN_RANGE=1000:1999
+    # Example: ``TENANT_VLAN_RANGE=1000:1999``
     TENANT_VLAN_RANGE=${TENANT_VLAN_RANGE:-}
 
     # If using VLANs for tenant networks, or if using flat or VLAN
@@ -1223,7 +1175,7 @@
     # openvswitch agent or LB_PHYSICAL_INTERFACE for the linuxbridge
     # agent, as described below.
     #
-    # Example: PHYSICAL_NETWORK=default
+    # Example: ``PHYSICAL_NETWORK=default``
     PHYSICAL_NETWORK=${PHYSICAL_NETWORK:-}
 
     # With the openvswitch plugin, if using VLANs for tenant networks,
@@ -1233,7 +1185,7 @@
     # physical interface must be manually added to the bridge as a
     # port for external connectivity.
     #
-    # Example: OVS_PHYSICAL_BRIDGE=br-eth1
+    # Example: ``OVS_PHYSICAL_BRIDGE=br-eth1``
     OVS_PHYSICAL_BRIDGE=${OVS_PHYSICAL_BRIDGE:-}
 
     # With the linuxbridge plugin, if using VLANs for tenant networks,
@@ -1241,13 +1193,13 @@
     # the name of the network interface to use for the physical
     # network.
     #
-    # Example: LB_PHYSICAL_INTERFACE=eth1
+    # Example: ``LB_PHYSICAL_INTERFACE=eth1``
     LB_PHYSICAL_INTERFACE=${LB_PHYSICAL_INTERFACE:-}
 
     # With the openvswitch plugin, set to True in localrc to enable
-    # provider GRE tunnels when ENABLE_TENANT_TUNNELS is False.
+    # provider GRE tunnels when ``ENABLE_TENANT_TUNNELS`` is False.
     #
-    # Example: OVS_ENABLE_TUNNELING=True
+    # Example: ``OVS_ENABLE_TUNNELING=True``
     OVS_ENABLE_TUNNELING=${OVS_ENABLE_TUNNELING:-$ENABLE_TENANT_TUNNELS}
 
     # Put config files in ``/etc/quantum`` for everyone to find
@@ -1283,7 +1235,9 @@
     Q_PLUGIN_CONF_FILE=$Q_PLUGIN_CONF_PATH/$Q_PLUGIN_CONF_FILENAME
     cp $QUANTUM_DIR/$Q_PLUGIN_CONF_FILE /$Q_PLUGIN_CONF_FILE
 
-    iniset /$Q_PLUGIN_CONF_FILE DATABASE sql_connection mysql:\/\/$MYSQL_USER:$MYSQL_PASSWORD@$MYSQL_HOST\/$Q_DB_NAME?charset=utf8
+    database_connection_url dburl $Q_DB_NAME
+    iniset /$Q_PLUGIN_CONF_FILE DATABASE sql_connection $dburl
+    unset dburl
 
     Q_CONF_FILE=/etc/quantum/quantum.conf
     cp $QUANTUM_DIR/etc/quantum.conf $Q_CONF_FILE
@@ -1309,12 +1263,11 @@
     cp $QUANTUM_DIR/etc/api-paste.ini $Q_API_PASTE_FILE
     cp $QUANTUM_DIR/etc/policy.json $Q_POLICY_FILE
 
-    if is_service_enabled mysql; then
-            mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e "DROP DATABASE IF EXISTS $Q_DB_NAME;"
-            mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e "CREATE DATABASE IF NOT EXISTS $Q_DB_NAME CHARACTER SET utf8;"
-        else
-            echo "mysql must be enabled in order to use the $Q_PLUGIN Quantum plugin."
-            exit 1
+    if is_service_enabled $DATABASE_BACKENDS; then
+        recreate_database $Q_DB_NAME utf8
+    else
+        echo "A database must be enabled in order to use the $Q_PLUGIN Quantum plugin."
+        exit 1
     fi
 
     # Update either configuration file with plugin
@@ -1334,7 +1287,7 @@
             echo "WARNING - The openvswitch plugin is using local tenant networks, with no connectivity between hosts."
         fi
 
-        # Override OVS_VLAN_RANGES and OVS_BRIDGE_MAPPINGS in localrc
+        # Override ``OVS_VLAN_RANGES`` and ``OVS_BRIDGE_MAPPINGS`` in ``localrc``
         # for more complex physical network configurations.
         if [[ "$OVS_VLAN_RANGES" = "" ]] && [[ "$PHYSICAL_NETWORK" != "" ]]; then
             OVS_VLAN_RANGES=$PHYSICAL_NETWORK
@@ -1357,7 +1310,7 @@
             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
+        # 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
@@ -1397,7 +1350,7 @@
         fi
 
         # Setup physical network bridge mappings.  Override
-        # OVS_VLAN_RANGES and OVS_BRIDGE_MAPPINGS in localrc for more
+        # ``OVS_VLAN_RANGES`` and ``OVS_BRIDGE_MAPPINGS`` in ``localrc`` for more
         # complex physical network configurations.
         if [[ "$OVS_BRIDGE_MAPPINGS" = "" ]] && [[ "$PHYSICAL_NETWORK" != "" ]] && [[ "$OVS_PHYSICAL_BRIDGE" != "" ]]; then
             OVS_BRIDGE_MAPPINGS=$PHYSICAL_NETWORK:$OVS_PHYSICAL_BRIDGE
@@ -1411,7 +1364,7 @@
         AGENT_BINARY="$QUANTUM_DIR/bin/quantum-openvswitch-agent"
     elif [[ "$Q_PLUGIN" = "linuxbridge" ]]; then
         # Setup physical network interface mappings.  Override
-        # LB_VLAN_RANGES and LB_INTERFACE_MAPPINGS in localrc for more
+        # ``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
@@ -1512,6 +1465,7 @@
     fi
 fi
 
+
 # Nova
 # ----
 
@@ -1865,6 +1819,7 @@
 fi
 add_nova_opt "glance_api_servers=$GLANCE_HOSTPORT"
 
+
 # XenServer
 # ---------
 
@@ -1974,7 +1929,7 @@
         fi
    fi
 
-elif is_service_enabled mysql && is_service_enabled nova; then
+elif is_service_enabled $DATABASE_BACKENDS && is_service_enabled n-net; then
     # Create a small network
     $NOVA_BIN_DIR/nova-manage network create "$PRIVATE_NETWORK_NAME" $FIXED_RANGE 1 $FIXED_NETWORK_SIZE $NETWORK_CREATE_ARGS
 
@@ -2042,7 +1997,7 @@
 
 if is_service_enabled g-reg; then
     echo_summary "Uploading images"
-    TOKEN=$(keystone  token-get | grep ' id ' | get_field 2)
+    TOKEN=$(keystone token-get | grep ' id ' | get_field 2)
 
     # Option to upload legacy ami-tty, which works with xenserver
     if [[ -n "$UPLOAD_LEGACY_TTY" ]]; then
@@ -2055,6 +2010,13 @@
 fi
 
 
+# Configure Tempest last to ensure that the runtime configuration of
+# the various OpenStack services can be queried.
+if is_service_enabled tempest; then
+    configure_tempest
+fi
+
+
 # Run local script
 # ================
 
diff --git a/stackrc b/stackrc
index e587efa..283b271 100644
--- a/stackrc
+++ b/stackrc
@@ -18,7 +18,7 @@
 ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,cinder,c-sch,c-api,c-vol,n-sch,n-novnc,n-xvnc,n-cauth,horizon,mysql,rabbit
 
 # Set the default Nova APIs to enable
-NOVA_ENABLED_APIS=ec2,osapi_compute,osapi_volume,metadata
+NOVA_ENABLED_APIS=ec2,osapi_compute,metadata
 
 # Repositories
 # ------------
diff --git a/tools/configure_tempest.sh b/tools/configure_tempest.sh
index 1e35036..070bc0b 100755
--- a/tools/configure_tempest.sh
+++ b/tools/configure_tempest.sh
@@ -47,6 +47,7 @@
 fi
 
 # Source params
+source $TOP_DIR/lib/database
 source $TOP_DIR/openrc
 
 # Where Openstack code lives
@@ -186,7 +187,7 @@
 # TODO(jaypipes): Create the key file here... right now, no whitebox
 # tests actually use a key.
 COMPUTE_PATH_TO_PRIVATE_KEY=$TEMPEST_DIR/id_rsa
-COMPUTE_DB_URI=mysql://root:$MYSQL_PASSWORD@localhost/nova
+COMPUTE_DB_URI=$BASE_SQL_CONN/nova
 
 # Image test configuration options...
 IMAGE_HOST=${IMAGE_HOST:-127.0.0.1}
diff --git a/unstack.sh b/unstack.sh
index 42cb7af..6b34aa3 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -15,6 +15,9 @@
 # Import common functions
 source $TOP_DIR/functions
 
+# Import database library
+source $TOP_DIR/lib/database
+
 # Load local configuration
 source $TOP_DIR/stackrc
 
@@ -102,6 +105,10 @@
         stop_service mysql
     fi
 
+    if is_service_enabled postgresql; then
+        stop_service postgresql
+    fi
+
     # Stop rabbitmq-server
     if is_service_enabled rabbit; then
         stop_service rabbitmq-server