Merge "remove hard tabs from keystone_data.sh"
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 183efa6..4c2f279 100755
--- a/exercises/boot_from_volume.sh
+++ b/exercises/boot_from_volume.sh
@@ -32,12 +32,18 @@
 # Import configuration
 source $TOP_DIR/openrc
 
+# Import quantum functions if needed
+if is_service_enabled quantum; then
+    source $TOP_DIR/lib/quantum
+    setup_quantum
+fi
+
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
-# If cinder or n-vol are not enabled we exit with exitcode 55 so that
+# If cinder is not enabled we exit with exitcode 55 so that
 # the exercise is skipped
-is_service_enabled cinder n-vol || exit 55
+is_service_enabled cinder || exit 55
 
 # Boot this image, use first AMI image if unset
 DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ami}
@@ -95,7 +101,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 +118,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:
@@ -139,16 +145,10 @@
 nova add-floating-ip $VOL_VM_UUID $FLOATING_IP
 
 # Test we can ping our floating ip within ASSOCIATE_TIMEOUT seconds
-if ! timeout $ASSOCIATE_TIMEOUT sh -c "while ! ping -c1 -w1 $FLOATING_IP; do sleep 1; done"; then
-    echo "Couldn't ping volume-backed server with floating ip"
-    exit 1
-fi
+ping_check "$PUBLIC_NETWORK_NAME" $FLOATING_IP $ASSOCIATE_TIMEOUT
 
 # Make sure our volume-backed instance launched
-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
-fi
+ssh_check "$PUBLIC_NETWORK_NAME" $KEY_FILE $FLOATING_IP $DEFAULT_INSTANCE_USER $ACTIVE_TIMEOUT
 
 # Remove floating ip from volume-backed instance
 nova remove-floating-ip $VOL_VM_UUID $FLOATING_IP
@@ -158,13 +158,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
@@ -174,6 +174,10 @@
 # Delete a secgroup
 nova secgroup-delete $SECGROUP || die "Failure deleting security group $SECGROUP"
 
+if is_service_enabled quantum; then
+    teardown_quantum
+fi
+
 set +o xtrace
 echo "*********************************************************************"
 echo "SUCCESS: End DevStack Exercise: $0"
diff --git a/exercises/euca.sh b/exercises/euca.sh
index 58b5d91..c307a06 100755
--- a/exercises/euca.sh
+++ b/exercises/euca.sh
@@ -33,6 +33,12 @@
 # Import EC2 configuration
 source $TOP_DIR/eucarc
 
+# Import quantum functions if needed
+if is_service_enabled quantum; then
+    source $TOP_DIR/lib/quantum
+    setup_quantum
+fi
+
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
@@ -73,7 +79,7 @@
 
 # Volumes
 # -------
-if [[ "$ENABLED_SERVICES" =~ "n-vol" || "$ENABLED_SERVICES" =~ "c-vol" ]]; then
+if [[ "$ENABLED_SERVICES" =~ "c-vol" ]]; then
    VOLUME_ZONE=`euca-describe-availability-zones | head -n1 | cut -f2`
    die_if_not_set VOLUME_ZONE "Failure to find zone for volume"
 
@@ -130,10 +136,7 @@
     die "Failure authorizing rule in $SECGROUP"
 
 # Test we can ping our floating ip within ASSOCIATE_TIMEOUT seconds
-if ! timeout $ASSOCIATE_TIMEOUT sh -c "while ! ping -c1 -w1 $FLOATING_IP; do sleep 1; done"; then
-    echo "Couldn't ping server with floating ip"
-    exit 1
-fi
+ping_check "$PUBLIC_NETWORK_NAME" $FLOATING_IP $ASSOCIATE_TIMEOUT
 
 # Revoke pinging
 euca-revoke -P icmp -s 0.0.0.0/0 -t -1:-1 $SECGROUP || \
@@ -172,6 +175,10 @@
 # Delete group
 euca-delete-group $SECGROUP || die "Failure deleting security group $SECGROUP"
 
+if is_service_enabled quantum; then
+    teardown_quantum
+fi
+
 set +o xtrace
 echo "*********************************************************************"
 echo "SUCCESS: End DevStack Exercise: $0"
diff --git a/exercises/floating_ips.sh b/exercises/floating_ips.sh
index 77f020e..ae5691f 100755
--- a/exercises/floating_ips.sh
+++ b/exercises/floating_ips.sh
@@ -31,6 +31,12 @@
 # Import configuration
 source $TOP_DIR/openrc
 
+# Import quantum functions if needed
+if is_service_enabled quantum; then
+    source $TOP_DIR/lib/quantum
+    setup_quantum
+fi
+
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
@@ -118,23 +124,10 @@
 fi
 
 # get the IP of the server
-IP=`nova show $VM_UUID | grep "private network" | get_field 2`
+IP=`nova show $VM_UUID | grep "$PRIVATE_NETWORK_NAME" | get_field 2`
 die_if_not_set IP "Failure retrieving IP address"
 
-# for single node deployments, we can ping private ips
-MULTI_HOST=`trueorfalse False $MULTI_HOST`
-if [ "$MULTI_HOST" = "False" ]; then
-    # sometimes the first ping fails (10 seconds isn't enough time for the VM's
-    # network to respond?), so let's ping for a default of 15 seconds with a
-    # timeout of a second for each ping.
-    if ! timeout $BOOT_TIMEOUT sh -c "while ! ping -c1 -w1 $IP; do sleep 1; done"; then
-        echo "Couldn't ping server"
-        exit 1
-    fi
-else
-    # On a multi-host system, without vm net access, do a sleep to wait for the boot
-    sleep $BOOT_TIMEOUT
-fi
+ping_check "$PRIVATE_NETWORK_NAME" $IP $BOOT_TIMEOUT
 
 # Security Groups & Floating IPs
 # ------------------------------
@@ -166,19 +159,18 @@
     die "Failure adding floating IP $FLOATING_IP to $NAME"
 
 # test we can ping our floating ip within ASSOCIATE_TIMEOUT seconds
-if ! timeout $ASSOCIATE_TIMEOUT sh -c "while ! ping -c1 -w1 $FLOATING_IP; do sleep 1; done"; then
-    echo "Couldn't ping server with floating ip"
-    exit 1
-fi
+ping_check "$PUBLIC_NETWORK_NAME" $FLOATING_IP $ASSOCIATE_TIMEOUT
 
-# Allocate an IP from second floating pool
-TEST_FLOATING_IP=`nova floating-ip-create $TEST_FLOATING_POOL | grep $TEST_FLOATING_POOL | get_field 1`
-die_if_not_set TEST_FLOATING_IP "Failure creating floating IP in $TEST_FLOATING_POOL"
+if ! is_service_enabled quantum; then
+    # Allocate an IP from second floating pool
+    TEST_FLOATING_IP=`nova floating-ip-create $TEST_FLOATING_POOL | grep $TEST_FLOATING_POOL | get_field 1`
+    die_if_not_set TEST_FLOATING_IP "Failure creating floating IP in $TEST_FLOATING_POOL"
 
-# list floating addresses
-if ! timeout $ASSOCIATE_TIMEOUT sh -c "while ! nova floating-ip-list | grep $TEST_FLOATING_POOL | grep -q $TEST_FLOATING_IP; do sleep 1; done"; then
-    echo "Floating IP not allocated"
-    exit 1
+    # list floating addresses
+    if ! timeout $ASSOCIATE_TIMEOUT sh -c "while ! nova floating-ip-list | grep $TEST_FLOATING_POOL | grep -q $TEST_FLOATING_IP; do sleep 1; done"; then
+        echo "Floating IP not allocated"
+        exit 1
+     fi
 fi
 
 # dis-allow icmp traffic (ping)
@@ -187,19 +179,17 @@
 # FIXME (anthony): make xs support security groups
 if [ "$VIRT_DRIVER" != "xenserver" -a "$VIRT_DRIVER" != "openvz" ]; then
     # test we can aren't able to ping our floating ip within ASSOCIATE_TIMEOUT seconds
-    if ! timeout $ASSOCIATE_TIMEOUT sh -c "while ping -c1 -w1 $FLOATING_IP; do sleep 1; done"; then
-        print "Security group failure - ping should not be allowed!"
-        echo "Couldn't ping server with floating ip"
-        exit 1
-    fi
+    ping_check "$PUBLIC_NETWORK_NAME" $FLOATING_IP $ASSOCIATE_TIMEOUT Fail
+fi
+
+if ! is_service_enabled quantum; then
+    # Delete second floating IP
+    nova floating-ip-delete $TEST_FLOATING_IP || die "Failure deleting floating IP $TEST_FLOATING_IP"
 fi
 
 # de-allocate the floating ip
 nova floating-ip-delete $FLOATING_IP || die "Failure deleting floating IP $FLOATING_IP"
 
-# Delete second floating IP
-nova floating-ip-delete $TEST_FLOATING_IP || die "Failure deleting floating IP $TEST_FLOATING_IP"
-
 # Shutdown the server
 nova delete $VM_UUID || die "Failure deleting instance $NAME"
 
@@ -212,6 +202,10 @@
 # Delete a secgroup
 nova secgroup-delete $SECGROUP || die "Failure deleting security group $SECGROUP"
 
+if is_service_enabled quantum; then
+    teardown_quantum
+fi
+
 set +o xtrace
 echo "*********************************************************************"
 echo "SUCCESS: End DevStack Exercise: $0"
diff --git a/exercises/quantum-adv-test.sh b/exercises/quantum-adv-test.sh
index 8f15b63..2ee82ff 100755
--- a/exercises/quantum-adv-test.sh
+++ b/exercises/quantum-adv-test.sh
@@ -52,13 +52,17 @@
 # Import configuration
 source $TOP_DIR/openrc
 
-# Import exercise configuration
-source $TOP_DIR/exerciserc
-
 # If quantum is not enabled we exit with exitcode 55 which mean
 # exercise is skipped.
 is_service_enabled quantum && is_service_enabled q-agt && is_service_enabled q-dhcp || exit 55
 
+# Import quantum fucntions
+source $TOP_DIR/lib/quantum
+setup_quantum
+
+# Import exercise configuration
+source $TOP_DIR/exerciserc
+
 #------------------------------------------------------------------------------
 # Test settings for quantum
 #------------------------------------------------------------------------------
@@ -76,14 +80,14 @@
 DEMO2_NUM_NET=2
 
 PUBLIC_NET1_CIDR="200.0.0.0/24"
-DEMO1_NET1_CIDR="10.1.0.0/24"
-DEMO2_NET1_CIDR="10.2.0.0/24"
-DEMO2_NET2_CIDR="10.2.1.0/24"
+DEMO1_NET1_CIDR="10.10.0.0/24"
+DEMO2_NET1_CIDR="10.20.0.0/24"
+DEMO2_NET2_CIDR="10.20.1.0/24"
 
 PUBLIC_NET1_GATEWAY="200.0.0.1"
-DEMO1_NET1_GATEWAY="10.1.0.1"
-DEMO2_NET1_GATEWAY="10.2.0.1"
-DEMO2_NET2_GATEWAY="10.2.1.1"
+DEMO1_NET1_GATEWAY="10.10.0.1"
+DEMO2_NET1_GATEWAY="10.20.0.1"
+DEMO2_NET2_GATEWAY="10.20.1.1"
 
 PUBLIC_NUM_VM=1
 DEMO1_NUM_VM=1
@@ -188,7 +192,7 @@
 
 function confirm_server_active {
     local VM_UUID=$1
-    if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova  --no_cache show $VM_UUID | grep status | grep -q ACTIVE; do sleep 1; done"; then
+    if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova show $VM_UUID | grep status | grep -q ACTIVE; do sleep 1; done"; then
     echo "server '$VM_UUID' did not become active!"
     false
 fi
@@ -232,6 +236,7 @@
     source $TOP_DIR/openrc admin admin
     add_tenant demo1 demo1 demo1
     add_tenant demo2 demo2 demo2
+    source $TOP_DIR/openrc demo demo
 }
 
 function delete_tenants_and_users {
@@ -241,6 +246,7 @@
     remove_user demo2
     remove_tenant demo2
     echo "removed all tenants"
+    source $TOP_DIR/openrc demo demo
 }
 
 function create_network {
@@ -256,12 +262,8 @@
     source $TOP_DIR/openrc $TENANT $TENANT
     local NET_ID=$(quantum net-create --tenant_id $TENANT_ID $NET_NAME $EXTRA| grep ' id ' | awk '{print $4}' )
     quantum subnet-create --ip_version 4 --tenant_id $TENANT_ID --gateway $GATEWAY $NET_ID $CIDR
-    #T0DO(nati) comment out until l3-agent is merged
-    #local ROUTER_ID=$($QUANTUM router-create --tenant_id $TENANT_ID $ROUTER_NAME| grep ' id ' | awk '{print $4}' )
-    #for NET_NAME in ${NET_NAMES//,/ };do
-    #    SUBNET_ID=`get_subnet_id $NET_NAME`
-    #    $QUANTUM router-interface-create $NAME --subnet_id $SUBNET_ID
-    #done
+    quantum-debug probe-create $NET_ID
+    source $TOP_DIR/openrc demo demo
 }
 
 function create_networks {
@@ -285,7 +287,7 @@
     done
     #TODO (nati) Add multi-nic test
     #TODO (nati) Add public-net test
-    local VM_UUID=`nova --no_cache boot --flavor $(get_flavor_id m1.tiny) \
+    local VM_UUID=`nova boot --flavor $(get_flavor_id m1.tiny) \
         --image $(get_image_id) \
         $NIC \
         $TENANT-server$NUM | grep ' id ' | cut -d"|" -f3 | sed 's/ //g'`
@@ -301,32 +303,26 @@
    # Test agent connection.  Assumes namespaces are disabled, and
    # that DHCP is in use, but not L3
    local VM_NAME=$1
-   IP=`nova  --no_cache show $VM_NAME | grep 'network' | awk '{print $5}'`
-   if ! timeout $BOOT_TIMEOUT sh -c "while ! ping -c1 -w1 $IP; do sleep 1; done"; then
-        echo "Could not ping $VM_NAME"
-        false
-   fi
+   local NET_NAME=$2
+   IP=`nova show $VM_NAME | grep 'network' | awk '{print $5}'`
+   ping_check $NET_NAME $IP $BOOT_TIMEOUT
 }
 
 function check_vm {
     local TENANT=$1
     local NUM=$2
     local VM_NAME="$TENANT-server$NUM"
+    local NET_NAME=$3
     source $TOP_DIR/openrc $TENANT $TENANT
-    ping_ip $VM_NAME
+    ping_ip $VM_NAME $NET_NAME
     # TODO (nati) test ssh connection
     # TODO (nati) test inter connection between vm
-    # TODO (nati) test namespace dhcp
     # TODO (nati) test dhcp host routes
     # TODO (nati) test multi-nic
-    # TODO (nati) use test-agent
-    # TODO (nati) test L3 forwarding
-    # TODO (nati) test floating ip
-    # TODO (nati) test security group
 }
 
 function check_vms {
-    foreach_tenant_vm 'check_vm ${%TENANT%_NAME} %NUM%'
+    foreach_tenant_vm 'check_vm ${%TENANT%_NAME} %NUM% ${%TENANT%_VM%NUM%_NET}'
 }
 
 function shutdown_vm {
@@ -334,12 +330,12 @@
     local NUM=$2
     source $TOP_DIR/openrc $TENANT $TENANT
     VM_NAME=${TENANT}-server$NUM
-    nova --no_cache delete $VM_NAME
+    nova delete $VM_NAME
 }
 
 function shutdown_vms {
     foreach_tenant_vm 'shutdown_vm ${%TENANT%_NAME} %NUM%'
-    if ! timeout $TERMINATE_TIMEOUT sh -c "while nova --no_cache list | grep -q ACTIVE; do sleep 1; done"; then
+    if ! timeout $TERMINATE_TIMEOUT sh -c "while nova list | grep -q ACTIVE; do sleep 1; done"; then
         echo "Some VMs failed to shutdown"
         false
     fi
@@ -347,17 +343,22 @@
 
 function delete_network {
     local TENANT=$1
+    local NUM=$2
+    local NET_NAME="${TENANT}-net$NUM"
     source $TOP_DIR/openrc admin admin
     local TENANT_ID=$(get_tenant_id $TENANT)
     #TODO(nati) comment out until l3-agent merged
     #for res in port subnet net router;do
-    for res in port subnet net;do
-        quantum ${res}-list -F id -F tenant_id | grep $TENANT_ID | awk '{print $2}' | xargs -I % quantum ${res}-delete %
+    for net_id in `quantum net-list -c id -c name | grep $NET_NAME | awk '{print $2}'`;do
+        delete_probe $net_id
+        quantum subnet-list | grep $net_id | awk '{print $2}' | xargs -I% quantum subnet-delete %
+        quantum net-delete $net_id
     done
+    source $TOP_DIR/openrc demo demo
 }
 
 function delete_networks {
-   foreach_tenant 'delete_network ${%TENANT%_NAME}'
+   foreach_tenant_net 'delete_network ${%TENANT%_NAME} ${%NUM%}'
    #TODO(nati) add secuirty group check after it is implemented
    # source $TOP_DIR/openrc demo1 demo1
    # nova secgroup-delete-rule default icmp -1 -1 0.0.0.0/0
@@ -474,6 +475,7 @@
 }
 
 
+teardown_quantum
 #-------------------------------------------------------------------------------
 # Kick off script.
 #-------------------------------------------------------------------------------
diff --git a/exercises/volumes.sh b/exercises/volumes.sh
index ffa12c4..3432763 100755
--- a/exercises/volumes.sh
+++ b/exercises/volumes.sh
@@ -2,14 +2,14 @@
 
 # **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"
 echo "*********************************************************************"
 
 # This script exits on an error so that errors don't compound and you see
-# only the first error that occured.
+# only the first error that occurred.
 set -o errexit
 
 # Print the commands being run so that we can see the command that triggers
@@ -30,12 +30,18 @@
 # Import configuration
 source $TOP_DIR/openrc
 
+# Import quantum functions if needed
+if is_service_enabled quantum; then
+    source $TOP_DIR/lib/quantum
+    setup_quantum
+fi
+
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
-# If cinder or n-vol are not enabled we exit with exitcode 55 which mean
+# If cinder is not enabled we exit with exitcode 55 which mean
 # exercise is skipped.
-is_service_enabled cinder n-vol || exit 55
+is_service_enabled cinder || exit 55
 
 # Instance type to create
 DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
@@ -119,23 +125,11 @@
 fi
 
 # get the IP of the server
-IP=`nova show $VM_UUID | grep "private network" | get_field 2`
+IP=`nova show $VM_UUID | grep "$PRIVATE_NETWORK_NAME" | get_field 2`
 die_if_not_set IP "Failure retrieving IP address"
 
 # for single node deployments, we can ping private ips
-MULTI_HOST=`trueorfalse False $MULTI_HOST`
-if [ "$MULTI_HOST" = "False" ]; then
-    # sometimes the first ping fails (10 seconds isn't enough time for the VM's
-    # network to respond?), so let's ping for a default of 15 seconds with a
-    # timeout of a second for each ping.
-    if ! timeout $BOOT_TIMEOUT sh -c "while ! ping -c1 -w1 $IP; do sleep 1; done"; then
-        echo "Couldn't ping server"
-        exit 1
-    fi
-else
-    # On a multi-host system, without vm net access, do a sleep to wait for the boot
-    sleep $BOOT_TIMEOUT
-fi
+ping_check "$PRIVATE_NETWORK_NAME" $IP $BOOT_TIMEOUT
 
 # Volumes
 # -------
@@ -143,28 +137,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
@@ -172,14 +166,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"
@@ -189,7 +183,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
@@ -198,13 +192,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"
@@ -218,6 +212,10 @@
 # Delete a secgroup
 nova secgroup-delete $SECGROUP || die "Failure deleting security group $SECGROUP"
 
+if is_service_enabled quantum; then
+    teardown_quantum
+fi
+
 set +o xtrace
 echo "*********************************************************************"
 echo "SUCCESS: End DevStack Exercise: $0"
diff --git a/files/apts/horizon b/files/apts/horizon
index 2161ccd..2c2faf1 100644
--- a/files/apts/horizon
+++ b/files/apts/horizon
@@ -21,4 +21,5 @@
 python-cherrypy3 # why?
 python-migrate
 nodejs
+nodejs-legacy # dist:quantal
 python-netaddr
diff --git a/files/apts/n-cpu b/files/apts/n-cpu
index 06c21a2..a40b659 100644
--- a/files/apts/n-cpu
+++ b/files/apts/n-cpu
@@ -2,3 +2,4 @@
 lvm2
 open-iscsi
 open-iscsi-utils
+genisoimage
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/apts/quantum b/files/apts/quantum
index 39f4561..64fc1bf 100644
--- a/files/apts/quantum
+++ b/files/apts/quantum
@@ -1,6 +1,7 @@
 ebtables
 iptables
 iputils-ping
+iputils-arping
 mysql-server #NOPRIME
 sudo
 python-boto
diff --git a/files/apts/swift b/files/apts/swift
index f298377..c52c68b 100644
--- a/files/apts/swift
+++ b/files/apts/swift
@@ -1,6 +1,6 @@
 curl
 gcc
-memcached # NOPRIME
+memcached
 python-configobj
 python-coverage
 python-dev
diff --git a/files/keystone_data.sh b/files/keystone_data.sh
index 9b07d0b..066d4ae 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
 #
@@ -105,7 +106,7 @@
 fi
 
 # Nova
-if [[ "$ENABLED_SERVICES" =~ "n-cpu" ]]; then
+if [[ "$ENABLED_SERVICES" =~ "n-api" ]]; then
     NOVA_USER=$(get_id keystone user-create \
         --name=nova \
         --pass="$SERVICE_PASSWORD" \
@@ -139,22 +140,6 @@
         --role_id $RESELLER_ROLE
 fi
 
-# Volume
-if [[ "$ENABLED_SERVICES" =~ "n-vol" ]]; then
-    if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
-        VOLUME_SERVICE=$(get_id keystone service-create \
-            --name=volume \
-            --type=volume \
-            --description="Volume Service")
-        keystone endpoint-create \
-            --region RegionOne \
-            --service_id $VOLUME_SERVICE \
-            --publicurl "http://$SERVICE_HOST:8776/v1/\$(tenant_id)s" \
-            --adminurl "http://$SERVICE_HOST:8776/v1/\$(tenant_id)s" \
-            --internalurl "http://$SERVICE_HOST:8776/v1/\$(tenant_id)s"
-    fi
-fi
-
 # Heat
 if [[ "$ENABLED_SERVICES" =~ "heat" ]]; then
     HEAT_USER=$(get_id keystone user-create --name=heat \
@@ -262,6 +247,28 @@
     fi
 fi
 
+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 \
+            --type=metering \
+            --description="Ceilometer Service")
+        keystone endpoint-create \
+            --region RegionOne \
+            --service_id $CEILOMETER_SERVICE \
+            --publicurl "http://$SERVICE_HOST:8777/" \
+            --adminurl "http://$SERVICE_HOST:8777/" \
+            --internalurl "http://$SERVICE_HOST:8777/"
+    fi
+fi
+
 # EC2
 if [[ "$ENABLED_SERVICES" =~ "n-api" ]]; then
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -330,4 +337,3 @@
             --internalurl "http://$SERVICE_HOST:8776/v1/\$(tenant_id)s"
     fi
 fi
-
diff --git a/files/rpms/n-cpu b/files/rpms/n-cpu
index 1996a98..f7054e8 100644
--- a/files/rpms/n-cpu
+++ b/files/rpms/n-cpu
@@ -1,3 +1,4 @@
 # Stuff for diablo volumes
 iscsi-initiator-utils
 lvm2
+genisoimage
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/files/rpms/swift b/files/rpms/swift
index c9d49e9..ce41ceb 100644
--- a/files/rpms/swift
+++ b/files/rpms/swift
@@ -1,6 +1,6 @@
 curl
 gcc
-memcached # NOPRIME
+memcached
 python-configobj
 python-coverage
 python-devel
diff --git a/functions b/functions
index 0d0df51..f2b12e2 100644
--- a/functions
+++ b/functions
@@ -7,6 +7,7 @@
 # ``GLANCE_HOSTPORT``
 # ``OFFLINE``
 # ``PIP_DOWNLOAD_CACHE``
+# ``PIP_USE_MIRRORS``
 # ``RECLONE``
 # ``TRACK_DEPENDS``
 # ``http_proxy``, ``https_proxy``, ``no_proxy``
@@ -154,6 +155,10 @@
             if [[ ! $file_to_parse =~ keystone ]]; then
                 file_to_parse="${file_to_parse} keystone"
             fi
+        elif [[ $service == q-* ]]; then
+            if [[ ! $file_to_parse =~ quantum ]]; then
+                file_to_parse="${file_to_parse} quantum"
+            fi
         fi
     done
 
@@ -222,6 +227,12 @@
         os_UPDATE=""
         if [[ "Debian,Ubuntu" =~ $os_VENDOR ]]; then
             os_PACKAGE="deb"
+        elif [[ "SUSE LINUX" =~ $os_VENDOR ]]; then
+            lsb_release -d -s | grep -q openSUSE
+            if [[ $? -eq 0 ]]; then
+                os_VENDOR="openSUSE"
+            fi
+            os_PACKAGE="rpm"
         else
             os_PACKAGE="rpm"
         fi
@@ -245,6 +256,23 @@
             os_VENDOR=""
         done
         os_PACKAGE="rpm"
+    elif [[ -r /etc/SuSE-release ]]; then
+        for r in openSUSE "SUSE Linux"; do
+            if [[ "$r" = "SUSE Linux" ]]; then
+                os_VENDOR="SUSE LINUX"
+            else
+                os_VENDOR=$r
+            fi
+
+            if [[ -n "`grep \"$r\" /etc/SuSE-release`" ]]; then
+                os_CODENAME=`grep "CODENAME = " /etc/SuSE-release | sed 's:.* = ::g'`
+                os_RELEASE=`grep "VERSION = " /etc/SuSE-release | sed 's:.* = ::g'`
+                os_UPDATE=`grep "PATCHLEVEL = " /etc/SuSE-release | sed 's:.* = ::g'`
+                break
+            fi
+            os_VENDOR=""
+        done
+        os_PACKAGE="rpm"
     fi
     export os_VENDOR os_RELEASE os_UPDATE os_PACKAGE os_CODENAME
 }
@@ -296,6 +324,15 @@
     elif [[ "$os_VENDOR" =~ (Fedora) ]]; then
         # For Fedora, just use 'f' and the release
         DISTRO="f$os_RELEASE"
+    elif [[ "$os_VENDOR" =~ (openSUSE) ]]; then
+        DISTRO="opensuse-$os_RELEASE"
+    elif [[ "$os_VENDOR" =~ (SUSE LINUX) ]]; then
+        # For SLE, also use the service pack
+        if [[ -z "$os_UPDATE" ]]; then
+            DISTRO="sle${os_RELEASE}"
+        else
+            DISTRO="sle${os_RELEASE}sp${os_UPDATE}"
+        fi
     else
         # Catch-all for now is Vendor + Release + Update
         DISTRO="$os_VENDOR-$os_RELEASE.$os_UPDATE"
@@ -304,6 +341,19 @@
 }
 
 
+# Determine if current distribution is a SUSE-based distribution
+# (openSUSE, SLE).
+# is_suse
+function is_suse {
+    if [[ -z "$os_VENDOR" ]]; then
+        GetOSVersion
+    fi
+
+    [[ "$os_VENDOR" = "openSUSE" || "$os_VENDOR" = "SUSE LINUX" ]]
+    return $?
+}
+
+
 # git clone only if directory doesn't exist already.  Since ``DEST`` might not
 # be owned by the installation user, we create the directory and change the
 # ownership to the proper user.
@@ -369,7 +419,7 @@
     local file=$1
     local section=$2
     local option=$3
-    sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=.*$\)|#\1|" $file
+    sed -i -e "/^\[ *$section *\]/,/^\[.*\]/ s|^\($option[ \t]*=.*$\)|#\1|" $file
 }
 
 # Uncomment an option in an INI file
@@ -378,7 +428,7 @@
     local file=$1
     local section=$2
     local option=$3
-    sed -i -e "/^\[$section\]/,/^\[.*\]/ s|[^ \t]*#[ \t]*\($option[ \t]*=.*$\)|\1|" $file
+    sed -i -e "/^\[ *$section *\]/,/^\[.*\]/ s|[^ \t]*#[ \t]*\($option[ \t]*=.*$\)|\1|" $file
 }
 
 
@@ -389,7 +439,7 @@
     local section=$2
     local option=$3
     local line
-    line=$(sed -ne "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" $file)
+    line=$(sed -ne "/^\[ *$section *\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" $file)
     echo ${line#*=}
 }
 
@@ -401,18 +451,18 @@
     local section=$2
     local option=$3
     local value=$4
-    if ! grep -q "^\[$section\]" $file; then
+    if ! grep -q "^\[ *$section *\]" $file; then
         # Add section at the end
         echo -e "\n[$section]" >>$file
     fi
     if [[ -z "$(iniget $file $section $option)" ]]; then
         # Add it
-        sed -i -e "/^\[$section\]/ a\\
+        sed -i -e "/^\[ *$section *\]/ a\\
 $option = $value
 " $file
     else
         # Replace it
-        sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=[ \t]*\).*$|\1$value|" $file
+        sed -i -e "/^\[ *$section *\]/,/^\[.*\]/ s|^\($option[ \t]*=[ \t]*\).*$|\1$value|" $file
     fi
 }
 
@@ -461,7 +511,7 @@
 # ``ENABLED_SERVICES`` list, if they are not already present.
 #
 # For example:
-#   enable_service n-vol
+#   enable_service qpid
 #
 # This function does not know about the special cases
 # for nova, glance, and quantum built into is_service_enabled().
@@ -483,7 +533,7 @@
 # ``ENABLED_SERVICES`` list, if they are present.
 #
 # For example:
-#   disable_service n-vol
+#   disable_service rabbit
 #
 # This function does not know about the special cases
 # for nova, glance, and quantum built into is_service_enabled().
@@ -512,8 +562,8 @@
 
 
 # Remove all services starting with '-'.  For example, to install all default
-# services except nova-volume (n-vol) set in ``localrc``:
-# ENABLED_SERVICES+=",-n-vol"
+# services except rabbit (rabbit) set in ``localrc``:
+# ENABLED_SERVICES+=",-rabbit"
 # Uses global ``ENABLED_SERVICES``
 # disable_negated_services
 function disable_negated_services() {
@@ -541,7 +591,11 @@
 
         apt_get install "$@"
     else
-        yum_install "$@"
+        if is_suse; then
+            zypper_install "$@"
+        else
+            yum_install "$@"
+        fi
     fi
 }
 
@@ -570,7 +624,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
@@ -578,7 +632,8 @@
 
 
 # Wrapper for ``pip install`` to set cache and proxy environment variables
-# Uses globals ``OFFLINE``, ``PIP_DOWNLOAD_CACHE``, ``TRACK_DEPENDES``, ``*_proxy`
+# Uses globals ``OFFLINE``, ``PIP_DOWNLOAD_CACHE``, ``PIP_USE_MIRRORS``,
+#   ``TRACK_DEPENDS``, ``*_proxy`
 # pip_install package [package ...]
 function pip_install {
     [[ "$OFFLINE" = "True" || -z "$@" ]] && return
@@ -591,17 +646,20 @@
         SUDO_PIP="env"
     else
         SUDO_PIP="sudo"
-        if [[ "$os_PACKAGE" = "deb" ]]; then
+        if [[ "$os_PACKAGE" = "deb" || is_suse ]]; then
             CMD_PIP=/usr/bin/pip
         else
             CMD_PIP=/usr/bin/pip-python
         fi
     fi
+    if [[ "$PIP_USE_MIRRORS" != "False" ]]; then
+        PIP_MIRROR_OPT="--use-mirrors"
+    fi
     $SUDO_PIP PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE:-/var/cache/pip} \
         HTTP_PROXY=$http_proxy \
         HTTPS_PROXY=$https_proxy \
         NO_PROXY=$no_proxy \
-        $CMD_PIP install --use-mirrors $@
+        $CMD_PIP install $PIP_MIRROR_OPT $@
 }
 
 
@@ -836,6 +894,37 @@
     fi
 }
 
+# Set the database backend to use
+# When called from stackrc/localrc DATABASE_BACKENDS has not been
+# initialized yet, just save the configuration selection and call back later
+# to validate it.
+#  $1 The name of the database backend to use (mysql, postgresql, ...)
+function use_database {
+    if [[ -z "$DATABASE_BACKENDS" ]]; then
+        # The backends haven't initialized yet, just save the selection for now
+        DATABASE_TYPE=$1
+        return
+    fi
+    use_exclusive_service DATABASE_BACKENDS DATABASE_TYPE $1 && return 0
+    ret=$?
+    return $ret
+}
+
+# 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`
@@ -849,6 +938,128 @@
         yum install -y "$@"
 }
 
+# ping check
+# Uses globals ``ENABLED_SERVICES``
+function ping_check() {
+    if is_service_enabled quantum; then
+        _ping_check_quantum  "$1" $2 $3 $4
+        return
+    fi
+    _ping_check_novanet "$1" $2 $3 $4
+}
+
+# ping check for nova
+# Uses globals ``MULTI_HOST``, ``PRIVATE_NETWORK``
+function _ping_check_novanet() {
+    local from_net=$1
+    local ip=$2
+    local boot_timeout=$3
+    local expected=${4:-"True"}
+    local check_command=""
+    MULTI_HOST=`trueorfalse False $MULTI_HOST`
+    if [[ "$MULTI_HOST" = "True" && "$from_net" = "$PRIVATE_NETWORK_NAME" ]]; then
+        sleep $boot_timeout
+        return
+    fi
+    if [[ "$expected" = "True" ]]; then
+        check_command="while ! ping -c1 -w1 $ip; do sleep 1; done"
+    else
+        check_command="while ping -c1 -w1 $ip; do sleep 1; done"
+    fi
+    if ! timeout $boot_timeout sh -c "$check_command"; then
+        if [[ "$expected" = "True" ]]; then
+            echo "[Fail] Couldn't ping server"
+        else
+            echo "[Fail] Could ping server"
+        fi
+        exit 1
+    fi
+}
+
+# ssh check
+
+function ssh_check() {
+    if is_service_enabled quantum; then
+        _ssh_check_quantum  "$1" $2 $3 $4 $5
+        return
+    fi
+    _ssh_check_novanet "$1" $2 $3 $4 $5
+}
+
+function _ssh_check_novanet() {
+    local NET_NAME=$1
+    local KEY_FILE=$2
+    local FLOATING_IP=$3
+    local DEFAULT_INSTANCE_USER=$4
+    local ACTIVE_TIMEOUT=$5
+    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
+    fi
+}
+
+
+# zypper wrapper to set arguments correctly
+# zypper_install package [package ...]
+function zypper_install() {
+    [[ "$OFFLINE" = "True" ]] && return
+    local sudo="sudo"
+    [[ "$(id -u)" = "0" ]] && sudo="env"
+    $sudo http_proxy=$http_proxy https_proxy=$https_proxy \
+        zypper --non-interactive install --auto-agree-with-licenses "$@"
+}
+
+
+# Add a user to a group.
+# add_user_to_group user group
+function add_user_to_group() {
+    local user=$1
+    local group=$2
+
+    if [[ -z "$os_VENDOR" ]]; then
+        GetOSVersion
+    fi
+
+    # SLE11 and openSUSE 12.2 don't have the usual usermod
+    if ! is_suse || [[ "$os_VENDOR" = "openSUSE" && "$os_RELEASE" != "12.2" ]]; then
+        sudo usermod -a -G "$group" "$user"
+    else
+        sudo usermod -A "$group" "$user"
+    fi
+}
+
+
+# Get the location of the $module-rootwrap executables, where module is cinder
+# or nova.
+# get_rootwrap_location module
+function get_rootwrap_location() {
+    local module=$1
+
+    if [[ -z "$os_PACKAGE" ]]; then
+        GetOSVersion
+    fi
+
+    if [[ "$os_PACKAGE" = "deb" || is_suse ]]; then
+        echo "/usr/local/bin/$module-rootwrap"
+    else
+        echo "/usr/bin/$module-rootwrap"
+    fi
+}
+
+
+# Check if qpid can be used on the current distro.
+# qpid_is_supported
+function qpid_is_supported() {
+    if [[ -z "$DISTRO" ]]; then
+        GetDistro
+    fi
+
+    # Qpid was introduced to Ubuntu in precise, disallow it on oneiric; it is
+    # not in openSUSE either right now.
+    [[ "$DISTRO" = "oneiric" || is_suse ]]
+    return $?
+}
 
 # Restore xtrace
 $XTRACE
diff --git a/lib/ceilometer b/lib/ceilometer
index 7154ccb..aa1b396 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -6,9 +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
-# - NOVA_CONF set to the nova configuration file
+# - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api
 
 # stack.sh
 # ---------
@@ -36,15 +36,13 @@
     CEILOMETER_BIN_DIR=/usr/local/bin
 fi
 CEILOMETER_CONF_DIR=/etc/ceilometer
-CEILOMETER_AGENT_CONF=$CEILOMETER_CONF_DIR/ceilometer-agent.conf
-CEILOMETER_COLLECTOR_CONF=$CEILOMETER_CONF_DIR/ceilometer-collector.conf
+CEILOMETER_CONF=$CEILOMETER_CONF_DIR/ceilometer.conf
 CEILOMETER_API_LOG_DIR=/var/log/ceilometer-api
 
 # cleanup_ceilometer() - Remove residual data files, anything left over from previous
 # runs that a clean run would need to clean up
 function cleanup_ceilometer() {
-    # This function intentionally left blank
-    :
+    mongo ceilometer --eval "db.dropDatabase();"
 }
 
 # configure_ceilometer() - Set config files, create data dirs, etc
@@ -57,13 +55,30 @@
     [ ! -d $CEILOMETER_API_LOG_DIR ] &&  sudo mkdir -m 755 -p $CEILOMETER_API_LOG_DIR
     sudo chown $USER $CEILOMETER_API_LOG_DIR
 
-    # ceilometer confs are copy of /etc/nova/nova.conf which must exist first
-    grep -v format_string $NOVA_CONF > $CEILOMETER_AGENT_CONF
-    iniset $CEILOMETER_AGENT_CONF DEFAULT rpc_backend 'ceilometer.openstack.common.rpc.impl_kombu'
+    iniset $CEILOMETER_CONF DEFAULT rpc_backend 'ceilometer.openstack.common.rpc.impl_kombu'
+    iniset $CEILOMETER_CONF DEFAULT notification_topics 'notifications,glance_notifications'
+    iniset $CEILOMETER_CONF DEFAULT verbose True
+    iniset $CEILOMETER_CONF DEFAULT rabbit_host $RABBIT_HOST
+    iniset $CEILOMETER_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
+    iniset $CEILOMETER_CONF DEFAULT sql_connection $BASE_SQL_CONN/nova?charset=utf8
 
-    grep -v format_string $NOVA_CONF > $CEILOMETER_COLLECTOR_CONF
-    iniset $CEILOMETER_COLLECTOR_CONF DEFAULT rpc_backend 'ceilometer.openstack.common.rpc.impl_kombu'
-    iniset $CEILOMETER_COLLECTOR_CONF DEFAULT notification_topics 'notifications,glance_notifications'
+    # 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
+
+    # the compute and central agents need these credentials in order to
+    # call out to the public nova and glance APIs
+    iniset $CEILOMETER_CONF DEFAULT os_username ceilometer
+    iniset $CEILOMETER_CONF DEFAULT os_password $SERVICE_PASSWORD
+    iniset $CEILOMETER_CONF DEFAULT os_tenant_name $SERVICE_TENANT_NAME
+    iniset $CEILOMETER_CONF DEFAULT os_auth_url $OS_AUTH_URL
+
+    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
 }
 
 # install_ceilometer() - Collect source and prepare
@@ -73,10 +88,10 @@
 
 # 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_AGENT_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_AGENT_CONF"
-    screen_it ceilometer-collector "cd $CEILOMETER_DIR && $CEILOMETER_BIN_DIR/ceilometer-collector --config-file $CEILOMETER_COLLECTOR_CONF"
-    screen_it ceilometer-api "cd $CEILOMETER_DIR && $CEILOMETER_BIN_DIR/ceilometer-api -d -v --log-dir=$CEILOMETER_API_LOG_DIR"
+    screen_it ceilometer-acompute "cd $CEILOMETER_DIR && sg libvirtd \"$CEILOMETER_BIN_DIR/ceilometer-agent-compute --config-file $CEILOMETER_CONF\""
+    screen_it ceilometer-acentral "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"
 }
 
 # stop_ceilometer() - Stop running processes
diff --git a/lib/cinder b/lib/cinder
index 08c840e..058fcc2 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
@@ -62,11 +63,7 @@
     cp -p $CINDER_DIR/etc/cinder/policy.json $CINDER_CONF_DIR
 
     # Set the paths of certain binaries
-    if [[ "$os_PACKAGE" = "deb" ]]; then
-        CINDER_ROOTWRAP=/usr/local/bin/cinder-rootwrap
-    else
-        CINDER_ROOTWRAP=/usr/bin/cinder-rootwrap
-    fi
+    CINDER_ROOTWRAP=$(get_rootwrap_location cinder)
 
     # If Cinder ships the new rootwrap filters files, deploy them
     # (owned by root) and add a parameter to $CINDER_ROOTWRAP
@@ -106,18 +103,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 +134,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 +152,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 +196,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..07e37ae
--- /dev/null
+++ b/lib/database
@@ -0,0 +1,94 @@
+# 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
+}
+
+# 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..fc6a3b7
--- /dev/null
+++ b/lib/databases/mysql
@@ -0,0 +1,97 @@
+# 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
+        if is_suse; then
+            MYSQL=mysql
+        else
+            MYSQL=mysqld
+        fi
+    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..d9c2f00
--- /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
+        sudo [ -e $PG_HBA ] || sudo postgresql-setup initdb
+    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
+    # 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 root sudo -u postgres -i psql -c "CREATE ROLE $DATABASE_USER WITH SUPERUSER LOGIN PASSWORD '$DATABASE_PASSWORD'" || \
+    sudo -u root 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..60026d5 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
@@ -68,6 +70,13 @@
     setup_develop $GLANCECLIENT_DIR
 }
 
+# durable_glance_queues() - Determine if RabbitMQ queues are durable or not
+function durable_glance_queues() {
+    test `rabbitmqctl list_queues name durable | grep true | wc -l` -gt 0 && return 0
+    test `rabbitmqctl list_exchanges name durable | grep true | wc -l` -gt 0 && return 0
+    return 1
+}
+
 # configure_glance() - Set config files, create data dirs, etc
 function configure_glance() {
     setup_develop $GLANCE_DIR
@@ -81,7 +90,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 +102,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/
@@ -113,6 +127,15 @@
         iniset $GLANCE_API_CONF DEFAULT notifier_strategy rabbit
         iniset $GLANCE_API_CONF DEFAULT rabbit_host $RABBIT_HOST
         iniset $GLANCE_API_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
+        if [[ durable_glance_queues -eq 0 ]]; then
+            # This gets around https://bugs.launchpad.net/glance/+bug/1074132
+            # that results in a g-api server becoming unresponsive during
+            # startup...
+            iniset $GLANCE_API_CONF DEFAULT rabbit_durable_queues True
+        fi
+    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 +172,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 80e3f7a..396c8a0 100644
--- a/lib/heat
+++ b/lib/heat
@@ -23,7 +23,7 @@
 # Defaults
 # --------
 HEAT_DIR=$DEST/heat
-
+HEATCLIENT_DIR=$DEST/python-heatclient
 # set up default directories
 
 # cleanup_heat() - Remove residual data files, anything left over from previous
@@ -33,6 +33,11 @@
     :
 }
 
+# configure_heatclient() - Set config files, create data dirs, etc
+function configure_heatclient() {
+    setup_develop $HEATCLIENT_DIR
+}
+
 # configure_heat() - Set config files, create data dirs, etc
 function configure_heat() {
     setup_develop $HEAT_DIR
@@ -120,7 +125,12 @@
     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
+    iniset $HEAT_ENGINE_CONF DEFAULT heat_metadata_server_url http://$HEAT_API_CFN_HOST:$HEAT_API_CFN_PORT
+    iniset $HEAT_ENGINE_CONF DEFAULT heat_waitcondition_server_url http://$HEAT_METADATA_HOST:$HEAT_METADATA_PORT
+    iniset $HEAT_ENGINE_CONF DEFAULT heat_watch_server_url http://$HEAT_API_CW_HOST:$HEAT_API_CW_PORT
+    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,10 +195,15 @@
 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
+}
+
+# install_heatclient() - Collect source and prepare
+function install_heatclient() {
+    git_clone $HEATCLIENT_REPO $HEATCLIENT_DIR $HEATCLIENT_BRANCH
 }
 
 # install_heat() - Collect source and prepare
diff --git a/lib/horizon b/lib/horizon
new file mode 100644
index 0000000..6173042
--- /dev/null
+++ b/lib/horizon
@@ -0,0 +1,143 @@
+# lib/horizon
+# Functions to control the configuration and operation of the horizon service
+# <do not include this template file in ``stack.sh``!>
+
+# Dependencies:
+# ``functions`` file
+# ``SERVICE_{TENANT_NAME|PASSWORD}`` must be defined
+# <list other global vars that are assumed to be defined>
+
+# ``stack.sh`` calls the entry points in this order:
+#
+# install_horizon
+# configure_horizon
+# init_horizon
+# start_horizon
+# stop_horizon
+# cleanup_horizon
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Defaults
+# --------
+
+# <define global variables here that belong to this project>
+
+# Set up default directories
+HORIZON_DIR=$DEST/horizon
+
+# Allow overriding the default Apache user and group, default both to
+# current user.
+APACHE_USER=${APACHE_USER:-$USER}
+APACHE_GROUP=${APACHE_GROUP:-$APACHE_USER}
+
+
+# Entry Points
+# ------------
+
+# cleanup_horizon() - Remove residual data files, anything left over from previous
+# runs that a clean run would need to clean up
+function cleanup_horizon() {
+    # kill instances (nova)
+    # delete image files (glance)
+    # This function intentionally left blank
+    :
+}
+
+# configure_horizon() - Set config files, create data dirs, etc
+function configure_horizon() {
+    setup_develop $HORIZON_DIR
+}
+
+# init_horizon() - Initialize databases, etc.
+function init_horizon() {
+    # Remove stale session database.
+    rm -f $HORIZON_DIR/openstack_dashboard/local/dashboard_openstack.sqlite3
+
+    # ``local_settings.py`` is used to override horizon default settings.
+    local_settings=$HORIZON_DIR/openstack_dashboard/local/local_settings.py
+    cp $FILES/horizon_settings.py $local_settings
+
+    # Initialize the horizon database (it stores sessions and notices shown to
+    # users).  The user system is external (keystone).
+    cd $HORIZON_DIR
+    python manage.py syncdb --noinput
+    cd $TOP_DIR
+
+    # Create an empty directory that apache uses as docroot
+    sudo mkdir -p $HORIZON_DIR/.blackhole
+
+
+    if [[ "$os_PACKAGE" = "deb" ]]; then
+        APACHE_NAME=apache2
+        APACHE_CONF=sites-available/horizon
+        # Clean up the old config name
+        sudo rm -f /etc/apache2/sites-enabled/000-default
+        # Be a good citizen and use the distro tools here
+        sudo touch /etc/$APACHE_NAME/$APACHE_CONF
+        sudo a2ensite horizon
+        # WSGI doesn't enable by default, enable it
+        sudo a2enmod wsgi
+    else
+        # Install httpd, which is NOPRIME'd
+        if is_suse; then
+            APACHE_NAME=apache2
+            APACHE_CONF=vhosts.d/horizon.conf
+            # Append wsgi to the list of modules to load
+            grep -q "^APACHE_MODULES=.*wsgi" /etc/sysconfig/apache2 ||
+                sudo sed '/^APACHE_MODULES=/s/^\(.*\)"$/\1 wsgi"/' -i /etc/sysconfig/apache2
+        else
+            APACHE_NAME=httpd
+            APACHE_CONF=conf.d/horizon.conf
+            sudo sed '/^Listen/s/^.*$/Listen 0.0.0.0:80/' -i /etc/httpd/conf/httpd.conf
+        fi
+    fi
+
+    # Configure apache to run horizon
+    sudo sh -c "sed -e \"
+        s,%USER%,$APACHE_USER,g;
+        s,%GROUP%,$APACHE_GROUP,g;
+        s,%HORIZON_DIR%,$HORIZON_DIR,g;
+        s,%APACHE_NAME%,$APACHE_NAME,g;
+        s,%DEST%,$DEST,g;
+    \" $FILES/apache-horizon.template >/etc/$APACHE_NAME/$APACHE_CONF"
+
+}
+
+# install_horizon() - Collect source and prepare
+function install_horizon() {
+    # Apache installation, because we mark it NOPRIME
+    if [[ "$os_PACKAGE" = "deb" ]]; then
+        # Install apache2, which is NOPRIME'd
+        install_package apache2 libapache2-mod-wsgi
+    else
+        sudo rm -f /etc/httpd/conf.d/000-*
+        install_package httpd mod_wsgi
+    fi
+
+    # NOTE(sdague) quantal changed the name of the node binary
+    if [[ "$os_PACKAGE" = "deb" ]]; then
+        if [[ ! -e "/usr/bin/node" ]]; then
+            install_package nodejs-legacy
+        fi
+    fi
+
+    git_clone $HORIZON_REPO $HORIZON_DIR $HORIZON_BRANCH $HORIZON_TAG
+}
+
+# start_horizon() - Start running processes, including screen
+function start_horizon() {
+    restart_service $APACHE_NAME
+    screen_it horizon "cd $HORIZON_DIR && sudo tail -f /var/log/$APACHE_NAME/horizon_error.log"
+}
+
+# stop_horizon() - Stop running processes (non-screen)
+function stop_horizon() {
+    stop_service apache2
+}
+
+# Restore xtrace
+$XTRACE
diff --git a/lib/keystone b/lib/keystone
index 06920f8..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}
@@ -81,8 +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 sql connection "$BASE_SQL_CONN/keystone?charset=utf8"
+    iniset $KEYSTONE_CONF signing token_format "$KEYSTONE_TOKEN_FORMAT"
+    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|;
@@ -139,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/n-vol b/lib/n-vol
deleted file mode 100644
index db53582..0000000
--- a/lib/n-vol
+++ /dev/null
@@ -1,126 +0,0 @@
-# lib/n-vol
-# Install and start Nova volume service
-
-# Dependencies:
-# - functions
-# - DATA_DIR must be defined
-# - KEYSTONE_AUTH_* must be defined
-# - NOVA_DIR, NOVA_BIN_DIR, NOVA_STATE_PATH must be defined
-# SERVICE_{TENANT_NAME|PASSWORD} must be defined
-# _configure_tgt_for_config_d() from lib/cinder
-
-# stack.sh
-# ---------
-# install_nvol
-# configure_nvol
-# init_nvol
-# start_nvol
-# stop_nvol
-# cleanup_nvol
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Defaults
-# --------
-
-# 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-}
-
-
-# cleanup_nvol() - Remove residual data files, anything left over from previous
-# runs that a clean run would need to clean up
-function cleanup_nvol() {
-    # kill instances (nova)
-    # delete image files (glance)
-    # This function intentionally left blank
-    :
-}
-
-# configure_nvol() - Set config files, create data dirs, etc
-function configure_nvol() {
-    # sudo python setup.py deploy
-    # iniset $XXX_CONF ...
-    # This function intentionally left blank
-    :
-}
-
-# init_nvol() - Initialize databases, etc.
-function init_nvol() {
-    # Configure a default volume group called '`stack-volumes`' for the volume
-    # service if it does not yet exist.  If you don't wish to use a file backed
-    # volume group, create your own volume group called ``stack-volumes`` before
-    # invoking ``stack.sh``.
-    #
-    # By default, the backing file is 5G in size, and is stored in ``/opt/stack/data``.
-
-    if ! sudo vgs $VOLUME_GROUP; then
-        VOLUME_BACKING_FILE=${VOLUME_BACKING_FILE:-$DATA_DIR/${VOLUME_GROUP}-backing-file}
-        # Only create if the file doesn't already exists
-        [[ -f $VOLUME_BACKING_FILE ]] || truncate -s $VOLUME_BACKING_FILE_SIZE $VOLUME_BACKING_FILE
-        DEV=`sudo losetup -f --show $VOLUME_BACKING_FILE`
-        # Only create if the loopback device doesn't contain $VOLUME_GROUP
-        if ! sudo vgs $VOLUME_GROUP; then sudo vgcreate $VOLUME_GROUP $DEV; fi
-    fi
-
-    mkdir -p $NOVA_STATE_PATH/volumes
-
-    if sudo vgs $VOLUME_GROUP; then
-        if [[ "$os_PACKAGE" = "rpm" ]]; then
-            # RPM doesn't start the service
-            start_service tgtd
-        fi
-
-        # Remove nova iscsi targets
-        sudo tgtadm --op show --mode target | grep $VOLUME_NAME_PREFIX | grep Target | cut -f3 -d ' ' | sudo xargs -n1 tgt-admin --delete || true
-        # Clean out existing volumes
-        for lv in `sudo lvs --noheadings -o lv_name $VOLUME_GROUP`; do
-            # ``VOLUME_NAME_PREFIX`` prefixes the LVs we want
-            if [[ "${lv#$VOLUME_NAME_PREFIX}" != "$lv" ]]; then
-                sudo lvremove -f $VOLUME_GROUP/$lv
-            fi
-        done
-    fi
-}
-
-# install_nvol() - Collect source and prepare
-function install_nvol() {
-    # git clone xxx
-    # Install is handled when installing Nova
-    :
-}
-
-# start_nvol() - Start running processes, including screen
-function start_nvol() {
-    # Setup the tgt configuration file
-    if [[ ! -f /etc/tgt/conf.d/nova.conf ]]; then
-        _configure_tgt_for_config_d
-       sudo mkdir -p /etc/tgt/conf.d
-       echo "include $NOVA_STATE_PATH/volumes/*" | sudo tee /etc/tgt/conf.d/nova.conf
-    fi
-
-    if [[ "$os_PACKAGE" = "deb" ]]; then
-        # tgt in oneiric doesn't restart properly if tgtd isn't running
-        # do it in two steps
-        sudo stop tgt || true
-        sudo start tgt
-    else
-        restart_service tgtd
-    fi
-
-    screen_it n-vol "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-volume"
-}
-
-# stop_nvol() - Stop running processes
-function stop_nvol() {
-    # Kill the nova volume screen window
-    screen -S $SCREEN_NAME -p n-vol -X kill
-
-    stop_service tgt
-}
-
-# Restore xtrace
-$XTRACE
diff --git a/lib/nova b/lib/nova
index 95d5d87..d15d9e3 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
@@ -45,11 +47,7 @@
 fi
 
 # Set the paths of certain binaries
-if [[ "$os_PACKAGE" = "deb" ]]; then
-    NOVA_ROOTWRAP=/usr/local/bin/nova-rootwrap
-else
-    NOVA_ROOTWRAP=/usr/bin/nova-rootwrap
-fi
+NOVA_ROOTWRAP=$(get_rootwrap_location nova)
 
 # Allow rate limiting to be turned off for testing, like for Tempest
 # NOTE: Set API_RATE_LIMIT="False" to turn OFF rate limiting
@@ -174,6 +172,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
@@ -246,7 +248,7 @@
 
         # The user that nova runs as needs to be member of **libvirtd** group otherwise
         # nova-compute will be unable to use libvirt.
-        sudo usermod -a -G libvirtd `whoami`
+        add_user_to_group `whoami` libvirtd
 
         # libvirt detects various settings on startup, as we potentially changed
         # the system configuration (modules, filesystems), we need to restart
@@ -296,7 +298,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"
@@ -304,13 +308,6 @@
     if is_service_enabled n-api; then
         add_nova_opt "enabled_apis=$NOVA_ENABLED_APIS"
     fi
-    if is_service_enabled n-vol; then
-        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"
-        # oneiric no longer supports ietadm
-        add_nova_opt "iscsi_helper=tgtadm"
-    fi
     if is_service_enabled cinder; then
         add_nova_opt "volume_api_class=nova.volume.cinder.API"
     fi
@@ -370,19 +367,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
@@ -428,14 +428,15 @@
     screen_it n-net "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-network"
     screen_it n-sch "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-scheduler"
     screen_it n-novnc "cd $NOVNC_DIR && ./utils/nova-novncproxy --config-file $NOVA_CONF --web ."
-    screen_it n-xvnc "cd $NOVA_DIR && ./bin/nova-xvpvncproxy --config-file $NOVA_CONF"
-    screen_it n-cauth "cd $NOVA_DIR && ./bin/nova-consoleauth"
+    screen_it n-xvnc "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-xvpvncproxy --config-file $NOVA_CONF"
+    screen_it n-cauth "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-consoleauth"
+    screen_it n-cond "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-conductor"
 }
 
 # stop_nova() - Stop running processes (non-screen)
 function stop_nova() {
     # Kill the nova screen windows
-    for serv in n-api n-cpu n-crt n-net n-sch n-novnc n-xvnc n-cauth; do
+    for serv in n-api n-cpu n-crt n-net n-sch n-novnc n-xvnc n-cauth n-cond; do
         screen -S $SCREEN_NAME -p $serv -X kill
     done
 }
diff --git a/lib/quantum b/lib/quantum
index 0b26622..373d521 100644
--- a/lib/quantum
+++ b/lib/quantum
@@ -5,6 +5,9 @@
 XTRACE=$(set +o | grep xtrace)
 set +o xtrace
 
+export QUANTUM_TEST_CONFIG_FILE=${QUANTUM_TEST_CONFIG_FILE:-"/etc/quantum/debug.ini"}
+QUANTUM_AUTH_CACHE_DIR=${QUANTUM_AUTH_CACHE_DIR:-/var/cache/quantum}
+
 # Configures keystone integration for quantum service and agents
 function quantum_setup_keystone() {
     local conf_file=$1
@@ -20,6 +23,12 @@
     iniset $conf_file $section admin_tenant_name $SERVICE_TENANT_NAME
     iniset $conf_file $section admin_user $Q_ADMIN_USERNAME
     iniset $conf_file $section admin_password $SERVICE_PASSWORD
+    if [[ "$KEYSTONE_TOKEN_FORMAT" == "PKI" ]]; then
+        iniset $conf_file $section signing_dir $QUANTUM_AUTH_CACHE_DIR
+        # Create cache dir
+        sudo mkdir -p $QUANTUM_AUTH_CACHE_DIR
+        sudo chown `whoami` $QUANTUM_AUTH_CACHE_DIR
+    fi
 }
 
 function quantum_setup_ovs_bridge() {
@@ -50,12 +59,79 @@
 }
 
 function is_quantum_ovs_base_plugin() {
-    local plguin=$1
+    local plugin=$1
     if [[ ",openvswitch,ryu," =~ ,${plugin}, ]]; then
         return 0
     fi
     return 1
 }
 
+function _get_net_id() {
+    quantum --os-tenant-name admin --os-username admin --os-password $ADMIN_PASSWORD net-list | grep $1 | awk '{print $2}'
+}
+
+function _get_probe_cmd_prefix() {
+    local from_net="$1"
+    net_id=`_get_net_id $from_net`
+    probe_id=`quantum-debug --os-tenant-name admin --os-username admin --os-password $ADMIN_PASSWORD probe-list -c id -c network_id | grep $net_id | awk '{print $2}' | head -n 1`
+    echo "sudo ip netns exec qprobe-$probe_id"
+}
+
+function delete_probe() {
+    local from_net="$1"
+    net_id=`_get_net_id $from_net`
+    probe_id=`quantum-debug --os-tenant-name admin --os-username admin --os-password $ADMIN_PASSWORD probe-list -c id -c network_id | grep $net_id | awk '{print $2}'`
+    quantum-debug --os-tenant-name admin --os-username admin probe-delete $probe_id
+}
+
+function _ping_check_quantum() {
+    local from_net=$1
+    local ip=$2
+    local timeout_sec=$3
+    local expected=${4:-"True"}
+    local check_command=""
+    probe_cmd=`_get_probe_cmd_prefix $from_net`
+    if [[ "$expected" = "True" ]]; then
+        check_command="while ! $probe_cmd ping -c1 -w1 $ip; do sleep 1; done"
+    else
+        check_command="while $probe_cmd ping -c1 -w1 $ip; do sleep 1; done"
+    fi
+    if ! timeout $timeout_sec sh -c "$check_command"; then
+        if [[ "$expected" = "True" ]]; then
+            echo "[Fail] Couldn't ping server"
+        else
+            echo "[Fail] Could ping server"
+        fi
+        exit 1
+    fi
+}
+
+# ssh check
+function _ssh_check_quantum() {
+    local from_net=$1
+    local key_file=$2
+    local ip=$3
+    local user=$4
+    local timeout_sec=$5
+    local probe_cmd = ""
+    probe_cmd=`_get_probe_cmd_prefix $from_net`
+    if ! timeout $timeout_sec sh -c "while ! $probe_cmd ssh -o StrictHostKeyChecking=no -i $key_file ${user}@$ip echo success ; do sleep 1; done"; then
+        echo "server didn't become ssh-able!"
+        exit 1
+    fi
+}
+
+function setup_quantum() {
+    public_net_id=`_get_net_id $PUBLIC_NETWORK_NAME`
+    quantum-debug --os-tenant-name admin --os-username admin --os-password $ADMIN_PASSWORD probe-create $public_net_id
+    private_net_id=`_get_net_id $PRIVATE_NETWORK_NAME`
+    quantum-debug --os-tenant-name admin --os-username admin --os-password $ADMIN_PASSWORD probe-create $private_net_id
+}
+
+function teardown_quantum() {
+    delete_probe $PUBLIC_NETWORK_NAME
+    delete_probe $PRIVATE_NETWORK_NAME
+}
+
 # Restore xtrace
 $XTRACE
diff --git a/lib/tempest b/lib/tempest
index 115c911..871e9e7 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -49,6 +49,10 @@
 # install_tempest() - Collect source and prepare
 function install_tempest() {
     git_clone $TEMPEST_REPO $TEMPEST_DIR $TEMPEST_BRANCH
+
+    # Tempest doesn't satisfy its dependencies on its own, so
+    # install them here instead.
+    sudo pip install -r $TEMPEST_DIR/tools/pip-requires
 }
 
 
diff --git a/openrc b/openrc
index 08ef98b..4b6b9b2 100644
--- a/openrc
+++ b/openrc
@@ -72,3 +72,6 @@
 # set log level to DEBUG (helps debug issues)
 # export KEYSTONECLIENT_DEBUG=1
 # export NOVACLIENT_DEBUG=1
+
+# set quantum debug command
+export QUANTUM_TEST_CONFIG_FILE=${QUANTUM_TEST_CONFIG_FILE:-"/etc/quantum/debug.ini"}
diff --git a/stack.sh b/stack.sh
index 80860ab..70f4610 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)
 
@@ -31,21 +30,22 @@
 GetDistro
 
 
+
 # Settings
 # ========
 
 # ``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
@@ -90,8 +90,16 @@
 # Sanity Check
 # ============
 
+# Import database configuration
+source $TOP_DIR/lib/database
+
+# Validate database selection
+# Since DATABASE_BACKENDS is now set, this also gets ENABLED_SERVICES
+# properly configured for the database selection.
+use_database $DATABASE_TYPE || echo "Invalid database '$DATABASE_TYPE'"
+
 # Remove services which were negated in ENABLED_SERVICES
-# using the "-" prefix (e.g., "-n-vol") instead of
+# using the "-" prefix (e.g., "-rabbit") instead of
 # calling disable_service().
 disable_negated_services
 
@@ -105,10 +113,8 @@
     fi
 fi
 
-# Disallow qpid 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."
+if is_service_enabled qpid && ! qpid_is_supported; then
+    echo "Qpid support is not available for this version of your distribution."
     exit 1
 fi
 
@@ -153,12 +159,6 @@
 fi
 unset rpc_backend_cnt
 
-# Make sure we only have one volume service enabled.
-if is_service_enabled cinder && is_service_enabled n-vol; then
-    echo "ERROR: n-vol and cinder must not be enabled at the same time"
-    exit 1
-fi
-
 # Set up logging level
 VERBOSE=$(trueorfalse True $VERBOSE)
 
@@ -305,11 +305,11 @@
 # ==================
 
 # Get project function libraries
+source $TOP_DIR/lib/horizon
 source $TOP_DIR/lib/keystone
 source $TOP_DIR/lib/glance
 source $TOP_DIR/lib/nova
 source $TOP_DIR/lib/cinder
-source $TOP_DIR/lib/n-vol
 source $TOP_DIR/lib/ceilometer
 source $TOP_DIR/lib/heat
 source $TOP_DIR/lib/quantum
@@ -330,7 +330,7 @@
 # Default Quantum Port
 Q_PORT=${Q_PORT:-9696}
 # Default Quantum Host
-Q_HOST=${Q_HOST:-localhost}
+Q_HOST=${Q_HOST:-$HOST_IP}
 # Which Quantum API nova should use
 # Default admin username
 Q_ADMIN_USERNAME=${Q_ADMIN_USERNAME:-quantum}
@@ -341,6 +341,8 @@
 Q_USE_ROOTWRAP=${Q_USE_ROOTWRAP:-True}
 # Meta data IP
 Q_META_DATA_IP=${Q_META_DATA_IP:-$HOST_IP}
+# Use quantum-debug command
+Q_USE_DEBUG_COMMAND=${Q_USE_DEBUG_COMMAND:-False}
 
 RYU_DIR=$DEST/ryu
 # Ryu API Host
@@ -354,6 +356,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 +453,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 +480,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 +545,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 +560,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,24 +569,13 @@
 SERVICE_TENANT_NAME=${SERVICE_TENANT_NAME:-service}
 
 
-
-# Horizon
-# -------
-
-# Allow overriding the default Apache user and group, default both to
-# current user.
-APACHE_USER=${APACHE_USER:-$USER}
-APACHE_GROUP=${APACHE_GROUP:-$APACHE_USER}
-
-
 # Log files
 # ---------
 
 # 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 +630,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,48 +744,14 @@
     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
-fi
-
-if is_service_enabled horizon; then
-    if [[ "$os_PACKAGE" = "deb" ]]; then
-        # Install apache2, which is NOPRIME'd
-        install_package apache2 libapache2-mod-wsgi
-    else
-        sudo rm -f /etc/httpd/conf.d/000-*
-        install_package httpd mod_wsgi
-    fi
+if is_service_enabled $DATABASE_BACKENDS; then
+    install_database
 fi
 
 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
@@ -802,11 +766,6 @@
     fi
 fi
 
-if is_service_enabled swift; then
-    # Install memcached for swift.
-    install_package memcached
-fi
-
 TRACK_DEPENDS=${TRACK_DEPENDS:-False}
 
 # Install python packages into a virtualenv so that we can track them
@@ -830,6 +789,7 @@
 
 echo_summary "Installing OpenStack project source"
 
+# Grab clients first
 install_keystoneclient
 install_glanceclient
 install_novaclient
@@ -865,8 +825,8 @@
     git_clone $NOVNC_REPO $NOVNC_DIR $NOVNC_BRANCH
 fi
 if is_service_enabled horizon; then
-    # django powered web control panel for openstack
-    git_clone $HORIZON_REPO $HORIZON_DIR $HORIZON_BRANCH $HORIZON_TAG
+    # dashboard
+    install_horizon
 fi
 if is_service_enabled quantum; then
     git_clone $QUANTUM_CLIENT_REPO $QUANTUM_CLIENT_DIR $QUANTUM_CLIENT_BRANCH
@@ -877,6 +837,7 @@
 fi
 if is_service_enabled heat; then
     install_heat
+    install_heatclient
 fi
 if is_service_enabled cinder; then
     install_cinder
@@ -891,6 +852,7 @@
     git_clone $RYU_REPO $RYU_DIR $RYU_BRANCH
 fi
 
+
 # Initialization
 # ==============
 
@@ -923,7 +885,7 @@
     configure_nova
 fi
 if is_service_enabled horizon; then
-    setup_develop $HORIZON_DIR
+    configure_horizon
 fi
 if is_service_enabled quantum; then
     setup_develop $QUANTUM_CLIENT_DIR
@@ -931,13 +893,11 @@
 fi
 if is_service_enabled heat; then
     configure_heat
+    configure_heatclient
 fi
 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,55 +953,31 @@
 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
 
+# Clear screen rc file
+SCREENRC=$TOP_DIR/$SCREEN_NAME-screenrc
+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"
 
@@ -1085,48 +1021,8 @@
 
 if is_service_enabled horizon; then
     echo_summary "Configuring and starting Horizon"
-
-    # Remove stale session database.
-    rm -f $HORIZON_DIR/openstack_dashboard/local/dashboard_openstack.sqlite3
-
-    # ``local_settings.py`` is used to override horizon default settings.
-    local_settings=$HORIZON_DIR/openstack_dashboard/local/local_settings.py
-    cp $FILES/horizon_settings.py $local_settings
-
-    # Initialize the horizon database (it stores sessions and notices shown to
-    # users).  The user system is external (keystone).
-    cd $HORIZON_DIR
-    python manage.py syncdb --noinput
-    cd $TOP_DIR
-
-    # Create an empty directory that apache uses as docroot
-    sudo mkdir -p $HORIZON_DIR/.blackhole
-
-    if [[ "$os_PACKAGE" = "deb" ]]; then
-        APACHE_NAME=apache2
-        APACHE_CONF=sites-available/horizon
-        # Clean up the old config name
-        sudo rm -f /etc/apache2/sites-enabled/000-default
-        # Be a good citizen and use the distro tools here
-        sudo touch /etc/$APACHE_NAME/$APACHE_CONF
-        sudo a2ensite horizon
-    else
-        # Install httpd, which is NOPRIME'd
-        APACHE_NAME=httpd
-        APACHE_CONF=conf.d/horizon.conf
-        sudo sed '/^Listen/s/^.*$/Listen 0.0.0.0:80/' -i /etc/httpd/conf/httpd.conf
-    fi
-
-    # Configure apache to run horizon
-    sudo sh -c "sed -e \"
-        s,%USER%,$APACHE_USER,g;
-        s,%GROUP%,$APACHE_GROUP,g;
-        s,%HORIZON_DIR%,$HORIZON_DIR,g;
-        s,%APACHE_NAME%,$APACHE_NAME,g;
-        s,%DEST%,$DEST,g;
-    \" $FILES/apache-horizon.template >/etc/$APACHE_NAME/$APACHE_CONF"
-
-    restart_service $APACHE_NAME
+    init_horizon
+    start_horizon
 fi
 
 
@@ -1151,6 +1047,7 @@
 
 # Ryu
 # ---
+
 # Ryu is not a part of OpenStack project. Please ignore following block if
 # you are not interested in Ryu.
 # launch ryu manager
@@ -1177,11 +1074,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
@@ -1209,7 +1105,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
@@ -1218,7 +1114,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,
@@ -1228,7 +1124,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,
@@ -1236,13 +1132,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
@@ -1266,8 +1162,10 @@
         Q_PLUGIN_CONF_FILENAME=ryu.ini
         Q_DB_NAME="ovs_quantum"
         Q_PLUGIN_CLASS="quantum.plugins.ryu.ryu_quantum_plugin.RyuQuantumPluginV2"
-    else
-        echo "Unknown Quantum plugin '$Q_PLUGIN'.. exiting"
+    fi
+
+    if [[ $Q_PLUGIN_CONF_PATH == '' || $Q_PLUGIN_CONF_FILENAME == '' || $Q_PLUGIN_CLASS == '' ]]; then
+        echo "Quantum plugin not set.. exiting"
         exit 1
     fi
 
@@ -1276,7 +1174,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
@@ -1302,12 +1202,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
@@ -1327,7 +1226,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
@@ -1350,7 +1249,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
@@ -1390,7 +1289,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
@@ -1404,7 +1303,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
@@ -1503,8 +1402,18 @@
         iniset $Q_CONF_FILE DEFAULT rabbit_host $RABBIT_HOST
         iniset $Q_CONF_FILE DEFAULT rabbit_password $RABBIT_PASSWORD
     fi
+    if [[ "$Q_USE_DEBUG_COMMAND" == "True" ]]; then
+        Q_DEBUG_CONF_FILE=/etc/quantum/debug.ini
+        cp $QUANTUM_DIR/etc/l3_agent.ini $Q_DEBUG_CONF_FILE
+        iniset $Q_L3_CONF_FILE DEFAULT verbose False
+        iniset $Q_L3_CONF_FILE DEFAULT debug False
+        iniset $Q_L3_CONF_FILE DEFAULT metadata_ip $Q_META_DATA_IP
+        iniset $Q_L3_CONF_FILE DEFAULT use_namespaces $Q_USE_NAMESPACE
+        iniset $Q_L3_CONF_FILE DEFAULT root_helper "sudo"
+    fi
 fi
 
+
 # Nova
 # ----
 
@@ -1786,9 +1695,6 @@
 if is_service_enabled cinder; then
     echo_summary "Configuring Cinder"
     init_cinder
-elif is_service_enabled n-vol; then
-    echo_summary "Configuring Nova volumes"
-    init_nvol
 fi
 
 if is_service_enabled nova; then
@@ -1796,106 +1702,98 @@
     # Rebuild the config file from scratch
     create_nova_conf
     init_nova
-fi
 
-# Additional Nova configuration that is dependent on other services
-if is_service_enabled quantum; then
-    add_nova_opt "network_api_class=nova.network.quantumv2.api.API"
-    add_nova_opt "quantum_admin_username=$Q_ADMIN_USERNAME"
-    add_nova_opt "quantum_admin_password=$SERVICE_PASSWORD"
-    add_nova_opt "quantum_admin_auth_url=$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_AUTH_PORT/v2.0"
-    add_nova_opt "quantum_auth_strategy=$Q_AUTH_STRATEGY"
-    add_nova_opt "quantum_admin_tenant_name=$SERVICE_TENANT_NAME"
-    add_nova_opt "quantum_url=http://$Q_HOST:$Q_PORT"
+    # Additional Nova configuration that is dependent on other services
+    if is_service_enabled quantum; then
+        add_nova_opt "network_api_class=nova.network.quantumv2.api.API"
+        add_nova_opt "quantum_admin_username=$Q_ADMIN_USERNAME"
+        add_nova_opt "quantum_admin_password=$SERVICE_PASSWORD"
+        add_nova_opt "quantum_admin_auth_url=$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_AUTH_PORT/v2.0"
+        add_nova_opt "quantum_auth_strategy=$Q_AUTH_STRATEGY"
+        add_nova_opt "quantum_admin_tenant_name=$SERVICE_TENANT_NAME"
+        add_nova_opt "quantum_url=http://$Q_HOST:$Q_PORT"
 
-    if [[ "$Q_PLUGIN" = "openvswitch" ]]; then
-        NOVA_VIF_DRIVER="nova.virt.libvirt.vif.LibvirtHybridOVSBridgeDriver"
-    elif [[ "$Q_PLUGIN" = "linuxbridge" ]]; then
-        NOVA_VIF_DRIVER="nova.virt.libvirt.vif.QuantumLinuxBridgeVIFDriver"
-    elif [[ "$Q_PLUGIN" = "ryu" ]]; then
-        NOVA_VIF_DRIVER="quantum.plugins.ryu.nova.vif.LibvirtOpenVswitchOFPRyuDriver"
-        add_nova_opt "libvirt_ovs_integration_bridge=$OVS_BRIDGE"
-        add_nova_opt "linuxnet_ovs_ryu_api_host=$RYU_API_HOST:$RYU_API_PORT"
-        add_nova_opt "libvirt_ovs_ryu_api_host=$RYU_API_HOST:$RYU_API_PORT"
+        if [[ "$Q_PLUGIN" = "openvswitch" ]]; then
+            NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtHybridOVSBridgeDriver"}
+        elif [[ "$Q_PLUGIN" = "linuxbridge" ]]; then
+            NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.QuantumLinuxBridgeVIFDriver"}
+        elif [[ "$Q_PLUGIN" = "ryu" ]]; then
+            NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"quantum.plugins.ryu.nova.vif.LibvirtOpenVswitchOFPRyuDriver"}
+            add_nova_opt "libvirt_ovs_integration_bridge=$OVS_BRIDGE"
+            add_nova_opt "linuxnet_ovs_ryu_api_host=$RYU_API_HOST:$RYU_API_PORT"
+            add_nova_opt "libvirt_ovs_ryu_api_host=$RYU_API_HOST:$RYU_API_PORT"
+        fi
+        add_nova_opt "libvirt_vif_driver=$NOVA_VIF_DRIVER"
+        add_nova_opt "linuxnet_interface_driver=$LINUXNET_VIF_DRIVER"
+    elif is_service_enabled n-net; then
+        add_nova_opt "network_manager=nova.network.manager.$NET_MAN"
+        add_nova_opt "public_interface=$PUBLIC_INTERFACE"
+        add_nova_opt "vlan_interface=$VLAN_INTERFACE"
+        add_nova_opt "flat_network_bridge=$FLAT_NETWORK_BRIDGE"
+        if [ -n "$FLAT_INTERFACE" ]; then
+            add_nova_opt "flat_interface=$FLAT_INTERFACE"
+        fi
     fi
-    add_nova_opt "libvirt_vif_driver=$NOVA_VIF_DRIVER"
-    add_nova_opt "linuxnet_interface_driver=$LINUXNET_VIF_DRIVER"
-else
-    add_nova_opt "network_manager=nova.network.manager.$NET_MAN"
-    add_nova_opt "public_interface=$PUBLIC_INTERFACE"
-    add_nova_opt "vlan_interface=$VLAN_INTERFACE"
-    add_nova_opt "flat_network_bridge=$FLAT_NETWORK_BRIDGE"
-    if [ -n "$FLAT_INTERFACE" ]; then
-        add_nova_opt "flat_interface=$FLAT_INTERFACE"
+    # All nova-compute workers need to know the vnc configuration options
+    # These settings don't hurt anything if n-xvnc and n-novnc are disabled
+    if is_service_enabled n-cpu; then
+        NOVNCPROXY_URL=${NOVNCPROXY_URL:-"http://$SERVICE_HOST:6080/vnc_auto.html"}
+        add_nova_opt "novncproxy_base_url=$NOVNCPROXY_URL"
+        XVPVNCPROXY_URL=${XVPVNCPROXY_URL:-"http://$SERVICE_HOST:6081/console"}
+        add_nova_opt "xvpvncproxy_base_url=$XVPVNCPROXY_URL"
     fi
-fi
-# All nova-compute workers need to know the vnc configuration options
-# These settings don't hurt anything if n-xvnc and n-novnc are disabled
-if is_service_enabled n-cpu; then
-    NOVNCPROXY_URL=${NOVNCPROXY_URL:-"http://$SERVICE_HOST:6080/vnc_auto.html"}
-    add_nova_opt "novncproxy_base_url=$NOVNCPROXY_URL"
-    XVPVNCPROXY_URL=${XVPVNCPROXY_URL:-"http://$SERVICE_HOST:6081/console"}
-    add_nova_opt "xvpvncproxy_base_url=$XVPVNCPROXY_URL"
-fi
-if [ "$VIRT_DRIVER" = 'xenserver' ]; then
-    VNCSERVER_PROXYCLIENT_ADDRESS=${VNCSERVER_PROXYCLIENT_ADDRESS=169.254.0.1}
-else
-    VNCSERVER_PROXYCLIENT_ADDRESS=${VNCSERVER_PROXYCLIENT_ADDRESS=127.0.0.1}
-fi
-# Address on which instance vncservers will listen on compute hosts.
-# For multi-host, this should be the management ip of the compute host.
-VNCSERVER_LISTEN=${VNCSERVER_LISTEN=127.0.0.1}
-add_nova_opt "vncserver_listen=$VNCSERVER_LISTEN"
-add_nova_opt "vncserver_proxyclient_address=$VNCSERVER_PROXYCLIENT_ADDRESS"
-add_nova_opt "ec2_dmz_host=$EC2_DMZ_HOST"
-if is_service_enabled zeromq; then
-    add_nova_opt "rpc_backend=nova.openstack.common.rpc.impl_zmq"
-elif is_service_enabled qpid; then
-    add_nova_opt "rpc_backend=nova.rpc.impl_qpid"
-elif [ -n "$RABBIT_HOST" ] &&  [ -n "$RABBIT_PASSWORD" ]; then
-    add_nova_opt "rabbit_host=$RABBIT_HOST"
-    add_nova_opt "rabbit_password=$RABBIT_PASSWORD"
-fi
-add_nova_opt "glance_api_servers=$GLANCE_HOSTPORT"
-
-# XenServer
-# ---------
-
-if [ "$VIRT_DRIVER" = 'xenserver' ]; then
-    echo_summary "Using XenServer virtualization driver"
-    read_password XENAPI_PASSWORD "ENTER A PASSWORD TO USE FOR XEN."
-    add_nova_opt "compute_driver=xenapi.XenAPIDriver"
-    XENAPI_CONNECTION_URL=${XENAPI_CONNECTION_URL:-"http://169.254.0.1"}
-    XENAPI_USER=${XENAPI_USER:-"root"}
-    add_nova_opt "xenapi_connection_url=$XENAPI_CONNECTION_URL"
-    add_nova_opt "xenapi_connection_username=$XENAPI_USER"
-    add_nova_opt "xenapi_connection_password=$XENAPI_PASSWORD"
-    add_nova_opt "flat_injected=False"
-    # Need to avoid crash due to new firewall support
-    XEN_FIREWALL_DRIVER=${XEN_FIREWALL_DRIVER:-"nova.virt.firewall.IptablesFirewallDriver"}
-    add_nova_opt "firewall_driver=$XEN_FIREWALL_DRIVER"
-elif [ "$VIRT_DRIVER" = 'openvz' ]; then
-    echo_summary "Using OpenVZ virtualization driver"
-    # TODO(deva): OpenVZ driver does not yet work if compute_driver is set here.
-    #             Replace connection_type when this is fixed.
-    #             add_nova_opt "compute_driver=openvz.connection.OpenVzConnection"
-    add_nova_opt "connection_type=openvz"
-    LIBVIRT_FIREWALL_DRIVER=${LIBVIRT_FIREWALL_DRIVER:-"nova.virt.libvirt.firewall.IptablesFirewallDriver"}
-    add_nova_opt "firewall_driver=$LIBVIRT_FIREWALL_DRIVER"
-else
-    echo_summary "Using libvirt virtualization driver"
-    add_nova_opt "compute_driver=libvirt.LibvirtDriver"
-    LIBVIRT_FIREWALL_DRIVER=${LIBVIRT_FIREWALL_DRIVER:-"nova.virt.libvirt.firewall.IptablesFirewallDriver"}
-    add_nova_opt "firewall_driver=$LIBVIRT_FIREWALL_DRIVER"
-fi
+    if [ "$VIRT_DRIVER" = 'xenserver' ]; then
+        VNCSERVER_PROXYCLIENT_ADDRESS=${VNCSERVER_PROXYCLIENT_ADDRESS=169.254.0.1}
+    else
+        VNCSERVER_PROXYCLIENT_ADDRESS=${VNCSERVER_PROXYCLIENT_ADDRESS=127.0.0.1}
+    fi
+    # Address on which instance vncservers will listen on compute hosts.
+    # For multi-host, this should be the management ip of the compute host.
+    VNCSERVER_LISTEN=${VNCSERVER_LISTEN=127.0.0.1}
+    add_nova_opt "vncserver_listen=$VNCSERVER_LISTEN"
+    add_nova_opt "vncserver_proxyclient_address=$VNCSERVER_PROXYCLIENT_ADDRESS"
+    add_nova_opt "ec2_dmz_host=$EC2_DMZ_HOST"
+    if is_service_enabled zeromq; then
+        add_nova_opt "rpc_backend=nova.openstack.common.rpc.impl_zmq"
+    elif is_service_enabled qpid; then
+        add_nova_opt "rpc_backend=nova.rpc.impl_qpid"
+    elif [ -n "$RABBIT_HOST" ] &&  [ -n "$RABBIT_PASSWORD" ]; then
+        add_nova_opt "rabbit_host=$RABBIT_HOST"
+        add_nova_opt "rabbit_password=$RABBIT_PASSWORD"
+    fi
+    add_nova_opt "glance_api_servers=$GLANCE_HOSTPORT"
 
 
-# Heat
-# ----
+    # XenServer
+    # ---------
 
-if is_service_enabled heat; then
-    echo_summary "Configuring Heat"
-    init_heat
+    if [ "$VIRT_DRIVER" = 'xenserver' ]; then
+        echo_summary "Using XenServer virtualization driver"
+        read_password XENAPI_PASSWORD "ENTER A PASSWORD TO USE FOR XEN."
+        add_nova_opt "compute_driver=xenapi.XenAPIDriver"
+        XENAPI_CONNECTION_URL=${XENAPI_CONNECTION_URL:-"http://169.254.0.1"}
+        XENAPI_USER=${XENAPI_USER:-"root"}
+        add_nova_opt "xenapi_connection_url=$XENAPI_CONNECTION_URL"
+        add_nova_opt "xenapi_connection_username=$XENAPI_USER"
+        add_nova_opt "xenapi_connection_password=$XENAPI_PASSWORD"
+        add_nova_opt "flat_injected=False"
+        # Need to avoid crash due to new firewall support
+        XEN_FIREWALL_DRIVER=${XEN_FIREWALL_DRIVER:-"nova.virt.firewall.IptablesFirewallDriver"}
+        add_nova_opt "firewall_driver=$XEN_FIREWALL_DRIVER"
+    elif [ "$VIRT_DRIVER" = 'openvz' ]; then
+        echo_summary "Using OpenVZ virtualization driver"
+        # TODO(deva): OpenVZ driver does not yet work if compute_driver is set here.
+        #             Replace connection_type when this is fixed.
+        #             add_nova_opt "compute_driver=openvz.connection.OpenVzConnection"
+        add_nova_opt "connection_type=openvz"
+        LIBVIRT_FIREWALL_DRIVER=${LIBVIRT_FIREWALL_DRIVER:-"nova.virt.libvirt.firewall.IptablesFirewallDriver"}
+        add_nova_opt "firewall_driver=$LIBVIRT_FIREWALL_DRIVER"
+    else
+        echo_summary "Using libvirt virtualization driver"
+        add_nova_opt "compute_driver=libvirt.LibvirtDriver"
+        LIBVIRT_FIREWALL_DRIVER=${LIBVIRT_FIREWALL_DRIVER:-"nova.virt.libvirt.firewall.IptablesFirewallDriver"}
+        add_nova_opt "firewall_driver=$LIBVIRT_FIREWALL_DRIVER"
+    fi
 fi
 
 
@@ -1953,14 +1851,14 @@
     # Create a small network
     # Since quantum command is executed in admin context at this point,
     # ``--tenant_id`` needs to be specified.
-    NET_ID=$(quantum net-create --tenant_id $TENANT_ID net1 | grep ' id ' | get_field 2)
+    NET_ID=$(quantum net-create --tenant_id $TENANT_ID "$PRIVATE_NETWORK_NAME" | grep ' id ' | get_field 2)
     SUBNET_ID=$(quantum subnet-create --tenant_id $TENANT_ID --ip_version 4 --gateway $NETWORK_GATEWAY $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
     if is_service_enabled q-l3; then
         # Create a router, and add the private subnet as one of its interfaces
         ROUTER_ID=$(quantum router-create --tenant_id $TENANT_ID router1 | grep ' id ' | get_field 2)
         quantum router-interface-add $ROUTER_ID $SUBNET_ID
         # Create an external network, and a subnet. Configure the external network as router gw
-        EXT_NET_ID=$(quantum net-create ext_net -- --router:external=True | grep ' id ' | get_field 2)
+        EXT_NET_ID=$(quantum net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True | grep ' id ' | get_field 2)
         EXT_GW_IP=$(quantum subnet-create --ip_version 4 $EXT_NET_ID $FLOATING_RANGE -- --enable_dhcp=False | grep 'gateway_ip' | get_field 2)
         quantum router-gateway-set $ROUTER_ID $EXT_NET_ID
         if is_quantum_ovs_base_plugin "$Q_PLUGIN" && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
@@ -1976,12 +1874,12 @@
         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 $FIXED_RANGE 1 $FIXED_NETWORK_SIZE $NETWORK_CREATE_ARGS
+    $NOVA_BIN_DIR/nova-manage network create "$PRIVATE_NETWORK_NAME" $FIXED_RANGE 1 $FIXED_NETWORK_SIZE $NETWORK_CREATE_ARGS
 
     # Create some floating ips
-    $NOVA_BIN_DIR/nova-manage floating create $FLOATING_RANGE
+    $NOVA_BIN_DIR/nova-manage floating create $FLOATING_RANGE --pool=$PUBLIC_NETWORK
 
     # Create a second pool
     $NOVA_BIN_DIR/nova-manage floating create --ip_range=$TEST_FLOATING_RANGE --pool=$TEST_FLOATING_POOL
@@ -1996,10 +1894,6 @@
     echo_summary "Starting Nova"
     start_nova
 fi
-if is_service_enabled n-vol; then
-    echo_summary "Starting Nova volumes"
-    start_nvol
-fi
 if is_service_enabled cinder; then
     echo_summary "Starting Cinder"
     start_cinder
@@ -2010,7 +1904,7 @@
     echo_summary "Starting Ceilometer"
     start_ceilometer
 fi
-screen_it horizon "cd $HORIZON_DIR && sudo tail -f /var/log/$APACHE_NAME/horizon_error.log"
+
 screen_it swift "cd $SWIFT_DIR && $SWIFT_DIR/bin/swift-proxy-server ${SWIFT_CONFIG_DIR}/proxy-server.conf -v"
 
 # Starting the nova-objectstore only if swift3 service is not enabled.
@@ -2018,8 +1912,12 @@
 is_service_enabled swift3 || \
     screen_it n-obj "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-objectstore"
 
-# launch heat engine, api and metadata
+
+# Configure and launch heat engine, api and metadata
 if is_service_enabled heat; then
+    # Initialize heat, including replacing nova flavors
+    echo_summary "Configuring Heat"
+    init_heat
     echo_summary "Starting Heat"
     start_heat
 fi
@@ -2040,11 +1938,11 @@
 
 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
-        IMAGE_URLS="${IMAGE_URLS:+${IMAGE_URLS},}http://images.ansolabs.com/tty.tgz"
+        IMAGE_URLS="${IMAGE_URLS:+${IMAGE_URLS},}https://github.com/downloads/citrix-openstack/warehouse/tty.tgz"
     fi
 
     for image_url in ${IMAGE_URLS//,/ }; do
@@ -2053,6 +1951,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
 # ================
 
@@ -2091,6 +1996,11 @@
     echo "Horizon is now available at http://$SERVICE_HOST/"
 fi
 
+# Warn that the default flavors have been changed by Heat
+if is_service_enabled heat; then
+    echo "Heat has replaced the default flavors. View by running: nova flavor-list"
+fi
+
 # If Keystone is present you can point ``nova`` cli to this server
 if is_service_enabled key; then
     echo "Keystone is serving at $KEYSTONE_AUTH_PROTOCOL://$SERVICE_HOST:$KEYSTONE_API_PORT/v2.0/"
diff --git a/stackrc b/stackrc
index f9a41bd..9588cf9 100644
--- a/stackrc
+++ b/stackrc
@@ -6,19 +6,18 @@
 # Destination path for installation
 DEST=/opt/stack
 
+# Select the default database
+DATABASE_TYPE=mysql
+
 # Specify which services to launch.  These generally correspond to
 # screen tabs. To change the default list, use the ``enable_service`` and
 # ``disable_service`` functions in ``localrc``.
 # For example, to enable Swift add this to ``localrc``:
 # enable_service swift
-#
-# And to disable Cinder and use Nova Volumes instead:
-# disable_service c-api c-sch c-vol cinder
-# enable_service n-vol
-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
+ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,cinder,c-sch,c-api,c-vol,n-sch,n-novnc,n-xvnc,n-cauth,horizon,rabbit,$DATABASE_TYPE
 
 # Set the default Nova APIs to enable
-NOVA_ENABLED_APIS=ec2,osapi_compute,osapi_volume,metadata
+NOVA_ENABLED_APIS=ec2,osapi_compute,metadata
 
 # Repositories
 # ------------
@@ -28,7 +27,7 @@
 GIT_BASE=https://github.com
 
 # metering service
-CEILOMETER_REPO=https://github.com/stackforge/ceilometer.git
+CEILOMETER_REPO=${GIT_BASE}/openstack/ceilometer.git
 CEILOMETER_BRANCH=master
 
 # volume service
@@ -101,6 +100,10 @@
 HEAT_REPO=${GIT_BASE}/heat-api/heat.git
 HEAT_BRANCH=master
 
+# python heat client library
+HEATCLIENT_REPO=${GIT_BASE}/heat-api/python-heatclient.git
+HEATCLIENT_BRANCH=master
+
 # ryu service
 RYU_REPO=https://github.com/osrg/ryu.git
 RYU_BRANCH=master
@@ -136,10 +139,10 @@
 #IMAGE_URLS="http://launchpad.net/cirros/trunk/0.3.0/+download/cirros-0.3.0-x86_64-disk.img" # cirros full disk image
 
 # Set default image based on ``VIRT_DRIVER`` and ``LIBVIRT_TYPE``, either of
-# which may be set in ``localrc``.  Also allow ``DEFAULT_IMAGE_NAME`` and 
+# which may be set in ``localrc``.  Also allow ``DEFAULT_IMAGE_NAME`` and
 # ``IMAGE_URLS`` to be set directly in ``localrc``.
 case "$VIRT_DRIVER" in
-    openvz) 
+    openvz)
         DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ubuntu-11.10-x86_64}
         IMAGE_URLS=${IMAGE_URLS:-"http://download.openvz.org/template/precreated/ubuntu-11.10-x86_64.tar.gz"};;
     libvirt)
@@ -159,3 +162,6 @@
 
 # 5Gb default volume backing file size
 VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE:-5130M}
+
+PRIVATE_NETWORK_NAME=${PRIVATE_NETWORK_NAME:-"private"}
+PUBLIC_NETWORK_NAME=${PUBLIC_NETWORK_NAME:-"nova"}
diff --git a/tests/functions.sh b/tests/functions.sh
index 3a0f319..d2cc5c4 100755
--- a/tests/functions.sh
+++ b/tests/functions.sh
@@ -54,6 +54,9 @@
 
 [bbb]
 handlers=ee,ff
+
+[ ccc ]
+spaces  =  yes
 EOF
 
 # Test with spaces
@@ -74,6 +77,14 @@
     echo "iniget failed: $VAL"
 fi
 
+# Test with spaces in section header
+
+VAL=$(iniget test.ini ccc spaces)
+if [[ "$VAL" == "yes" ]]; then
+    echo "OK: $VAL"
+else
+    echo "iniget failed: $VAL"
+fi
 
 # Test without spaces, end of file
 
@@ -112,7 +123,6 @@
     echo "iniget failed: $VAL"
 fi
 
-
 # Test option not exist
 
 VAL=$(iniget test.ini aaa debug)
diff --git a/tools/build_usb_boot.sh b/tools/build_usb_boot.sh
index f64b7b6..8566229 100755
--- a/tools/build_usb_boot.sh
+++ b/tools/build_usb_boot.sh
@@ -11,7 +11,6 @@
 
 DEST_DIR=${1:-/tmp/syslinux-boot}
 PXEDIR=${PXEDIR:-/opt/ramstack/pxe}
-PROGDIR=`dirname $0`
 
 # Clean up any resources that may be in use
 cleanup() {
@@ -81,7 +80,7 @@
 # Get image into place
 if [ ! -r $PXEDIR/stack-initrd.img ]; then
     cd $TOP_DIR
-    $PROGDIR/build_uec_ramdisk.sh $PXEDIR/stack-initrd.img
+    $TOOLS_DIR/build_uec_ramdisk.sh $PXEDIR/stack-initrd.img
 fi
 if [ ! -r $PXEDIR/stack-initrd.gz ]; then
     gzip -1 -c $PXEDIR/stack-initrd.img >$PXEDIR/stack-initrd.gz
diff --git a/tools/configure_tempest.sh b/tools/configure_tempest.sh
index 1e35036..03dc683 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
@@ -57,6 +58,9 @@
 CONFIG_DIR=$TEMPEST_DIR/etc
 TEMPEST_CONF=$CONFIG_DIR/tempest.conf
 
+DATABASE_TYPE=${DATABASE_TYPE:-mysql}
+initialize_database_backends
+
 # Use the GUEST_IP unless an explicit IP is set by ``HOST_IP``
 HOST_IP=${HOST_IP:-$GUEST_IP}
 # Use the first IP if HOST_IP still is not set
@@ -124,7 +128,7 @@
 # OS_USERNAME et all should be defined in openrc.
 OS_USERNAME=${OS_USERNAME:-demo}
 OS_TENANT_NAME=${OS_TENANT_NAME:-demo}
-OS_PASSWORD=${OS_PASSWORD:$ADMIN_PASSWORD}
+OS_PASSWORD=${OS_PASSWORD:-$ADMIN_PASSWORD}
 
 # See files/keystone_data.sh where alt_demo user
 # and tenant are set up...
@@ -186,7 +190,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}
@@ -205,6 +209,24 @@
 LIVE_MIGRATION_AVAILABLE=${LIVE_MIGRATION_AVAILABLE:-False}
 USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION=${USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION:-False}
 
+# Object Storage
+OBJECT_CATALOG_TYPE="object-store"
+
+# EC2 and S3 test configuration
+BOTO_EC2_URL="http://$IDENTITY_HOST:8773/services/Cloud"
+BOTO_S3_URL="http://$IDENTITY_HOST:3333"
+BOTO_AWS_ACCESS=""  # Created in tempest...
+BOTO_AWS_SECRET=""  # Created in tempest...
+BOTO_AWS_REGION="RegionOne"
+BOTO_S3_MATERIALS_PATH=$DEST/devstack/files/images/s3-materials/cirros-0.3.0
+BOTO_ARI_MANIFEST=cirros-0.3.0-x86_64-initrd.manifest.xml
+BOTO_AMI_MANIFEST=cirros-0.3.0-x86_64-blank.img.manifest.xml
+BOTO_AKI_MANIFEST=cirros-0.3.0-x86_64-vmlinuz.manifest.xml
+BOTO_FLAVOR_NAME=m1.tiny
+BOTO_SOCKET_TIMEOUT=5
+BOTO_BUILD_TIMEOUT=${COMPUTE_BUILD_TIMEOUT:-400}
+BOTO_BUILD_INTERVAL=${COMPUTE_BUILD_INTERVAL:-3}
+
 sed -e "
     s,%IDENTITY_USE_SSL%,$IDENTITY_USE_SSL,g;
     s,%IDENTITY_HOST%,$IDENTITY_HOST,g;
@@ -262,6 +284,20 @@
     s,%VOLUME_BUILD_TIMEOUT%,$VOLUME_BUILD_TIMEOUT,g;
     s,%LIVE_MIGRATION_AVAILABLE%,$LIVE_MIGRATION_AVAILABLE,g;
     s,%USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION%,$USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION,g;
+    s,%OBJECT_CATALOG_TYPE%,$OBJECT_CATALOG_TYPE,g;
+    s,%BOTO_EC2_URL%,$BOTO_EC2_URL,g;
+    s,%BOTO_S3_URL%,$BOTO_S3_URL,g;
+    s,%BOTO_AWS_ACCESS%,$BOTO_AWS_ACCESS,g;
+    s,%BOTO_AWS_SECRET%,$BOTO_AWS_SECRET,g;
+    s,%BOTO_AWS_REGION%,$BOTO_AWS_REGION,g;
+    s,%BOTO_S3_MATERIALS_PATH%,$BOTO_S3_MATERIALS_PATH,g;
+    s,%BOTO_ARI_MANIFEST%,$BOTO_ARI_MANIFEST,g;
+    s,%BOTO_AMI_MANIFEST%,$BOTO_AMI_MANIFEST,g;
+    s,%BOTO_AKI_MANIFEST%,$BOTO_AKI_MANIFEST,g;
+    s,%BOTO_FLAVOR_NAME%,$BOTO_FLAVOR_NAME,g;
+    s,%BOTO_SOCKET_TIMEOUT%,$BOTO_SOCKET_TIMEOUT,g;
+    s,%BOTO_BUILD_TIMEOUT%,$BOTO_BUILD_TIMEOUT,g;
+    s,%BOTO_BUILD_INTERVAL%,$BOTO_BUILD_INTERVAL,g;
 " -i $TEMPEST_CONF
 
 echo "Created tempest configuration file:"
diff --git a/tools/get_uec_image.sh b/tools/get_uec_image.sh
index ca74a03..156fd43 100755
--- a/tools/get_uec_image.sh
+++ b/tools/get_uec_image.sh
@@ -5,7 +5,7 @@
 # Download and prepare Ubuntu UEC images
 
 CACHEDIR=${CACHEDIR:-/opt/stack/cache}
-ROOTSIZE=${ROOTSIZE:-2000}
+ROOTSIZE=${ROOTSIZE:-2000M}
 
 # Keep track of the current directory
 TOOLS_DIR=$(cd $(dirname "$0") && pwd)
@@ -24,7 +24,7 @@
     echo "$0 [-r rootsize] release imagefile [kernel]"
     echo ""
     echo "-r size   - root fs size (min 2000MB)"
-    echo "release   - Ubuntu release: jaunty - oneric"
+    echo "release   - Ubuntu release: lucid - quantal"
     echo "imagefile - output image file"
     echo "kernel    - output kernel"
     exit 1
@@ -64,6 +64,8 @@
 KERNEL=$3
 
 case $DIST_NAME in
+    quantal)    ;;
+    percise)    ;;
     oneiric)    ;;
     natty)      ;;
     maverick)   ;;
@@ -90,7 +92,7 @@
 
 # Get the UEC image
 UEC_NAME=$DIST_NAME-server-cloudimg-amd64
-if [ ! -d $CACHEDIR ]; then
+if [ ! -d $CACHEDIR/$DIST_NAME ]; then
     mkdir -p $CACHEDIR/$DIST_NAME
 fi
 if [ ! -e $CACHEDIR/$DIST_NAME/$UEC_NAME.tar.gz ]; then
diff --git a/tools/xen/install_os_domU.sh b/tools/xen/install_os_domU.sh
index 0bb6ac8..c78c6f2 100755
--- a/tools/xen/install_os_domU.sh
+++ b/tools/xen/install_os_domU.sh
@@ -57,8 +57,8 @@
 fi
 
 # get nova
-nova_zipball=$(echo $NOVA_REPO | sed "s:\.git$::;s:$:/zipball/$NOVA_BRANCH:g")
-wget $nova_zipball -O nova-zipball --no-check-certificate
+NOVA_ZIPBALL_URL=${NOVA_ZIPBALL_URL:-$(echo $NOVA_REPO | sed "s:\.git$::;s:$:/zipball/$NOVA_BRANCH:g")}
+wget $NOVA_ZIPBALL_URL -O nova-zipball --no-check-certificate
 unzip -o nova-zipball  -d ./nova
 
 # install xapi plugins
diff --git a/tools/xen/prepare_guest_template.sh b/tools/xen/prepare_guest_template.sh
index baf9c3a..19bd2f8 100755
--- a/tools/xen/prepare_guest_template.sh
+++ b/tools/xen/prepare_guest_template.sh
@@ -60,7 +60,7 @@
     rm -rf $TMP_DIR
 else
     echo "WARNING: no XenServer tools found, falling back to 5.6 tools"
-    TOOLS_URL="http://images.ansolabs.com/xen/xe-guest-utilities_5.6.100-651_amd64.deb"
+    TOOLS_URL="https://github.com/downloads/citrix-openstack/warehouse/xe-guest-utilities_5.6.100-651_amd64.deb"
     wget $TOOLS_URL -O $XS_TOOLS_FILE_NAME
     cp $XS_TOOLS_FILE_NAME "${STAGING_DIR}${XS_TOOLS_PATH}"
     rm -rf $XS_TOOLS_FILE_NAME
diff --git a/tools/xen/scripts/install_ubuntu_template.sh b/tools/xen/scripts/install_ubuntu_template.sh
index f67547b..43b6dec 100755
--- a/tools/xen/scripts/install_ubuntu_template.sh
+++ b/tools/xen/scripts/install_ubuntu_template.sh
@@ -45,6 +45,7 @@
 # Clone built-in template to create new template
 new_uuid=$(xe vm-clone uuid=$builtin_uuid \
     new-name-label="$UBUNTU_INST_TEMPLATE_NAME")
+disk_size=$(($OSDOMU_VDI_GB * 1024 * 1024 * 1024))
 
 # Some of these settings can be found in example preseed files
 # however these need to be answered before the netinstall
@@ -73,6 +74,7 @@
     PV-args="$pvargs" \
     other-config:debian-release="$UBUNTU_INST_RELEASE" \
     other-config:default_template=true \
+    other-config:disks='<provision><disk device="0" size="'$disk_size'" sr="" bootable="true" type="system"/></provision>' \
     other-config:install-arch="$UBUNTU_INST_ARCH"
 
 echo "Ubuntu template installed uuid:$new_uuid"
diff --git a/tools/xen/xenrc b/tools/xen/xenrc
index 0365a25..1a5a2a9 100644
--- a/tools/xen/xenrc
+++ b/tools/xen/xenrc
@@ -11,6 +11,7 @@
 # Size of image
 VDI_MB=${VDI_MB:-5000}
 OSDOMU_MEM_MB=1024
+OSDOMU_VDI_GB=8
 
 # VM Password
 GUEST_PASSWORD=${GUEST_PASSWORD:-secrete}
diff --git a/unstack.sh b/unstack.sh
index 42cb7af..0040cf1 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
 
@@ -23,7 +26,7 @@
 
 # Get project function libraries
 source $TOP_DIR/lib/cinder
-source $TOP_DIR/lib/n-vol
+source $TOP_DIR/lib/horizon
 
 # Determine what system we are running on.  This provides ``os_VENDOR``,
 # ``os_RELEASE``, ``os_UPDATE``, ``os_PACKAGE``, ``os_CODENAME``
@@ -49,17 +52,13 @@
 
 # Apache has the WSGI processes
 if is_service_enabled horizon; then
-    stop_service apache2
+    stop_horizon
 fi
 
 SCSI_PERSIST_DIR=$CINDER_STATE_PATH/volumes/*
 
 # Get the iSCSI volumes
-if is_service_enabled cinder n-vol; then
-    if is_service_enabled n-vol; then
-        SCSI_PERSIST_DIR=$NOVA_STATE_PATH/volumes/*
-    fi
-
+if is_service_enabled cinder; then
     TARGETS=$(sudo tgtadm --op show --mode target)
     if [ $? -ne 0 ]; then
         # If tgt driver isn't running this won't work obviously
@@ -85,10 +84,6 @@
         sudo rm -rf $CINDER_STATE_PATH/volumes/*
     fi
 
-    if is_service_enabled n-vol; then
-        sudo rm -rf $NOVA_STATE_PATH/volumes/*
-    fi
-
     if [[ "$os_PACKAGE" = "deb" ]]; then
         stop_service tgt
     else
@@ -102,6 +97,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