Merge "Cinder needs iscsiadm available"
diff --git a/README.md b/README.md
index 6426e9a..99e9838 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,14 @@
 
 IMPORTANT: Be sure to carefully read `stack.sh` and any other scripts you execute before you run them, as they install software and may alter your networking configuration.  We strongly recommend that you run `stack.sh` in a clean and disposable vm when you are first getting started.
 
-# Devstack on Xenserver
+# DevStack on Xenserver
 
 If you would like to use Xenserver as the hypervisor, please refer to the instructions in `./tools/xen/README.md`.
 
+# DevStack on Docker
+
+If you would like to use Docker as the hypervisor, please refer to the instructions in `./tools/docker/README.md`.
+
 # Versions
 
 The devstack master branch generally points to trunk versions of OpenStack components.  For older, stable versions, look for branches named stable/[release] in the DevStack repo.  For example, you can do the following to create a diablo OpenStack cloud:
diff --git a/clean.sh b/clean.sh
index f7d15df..a443ac8 100755
--- a/clean.sh
+++ b/clean.sh
@@ -64,6 +64,11 @@
 cleanup_neutron
 cleanup_swift
 
+# Do the hypervisor cleanup until this can be moved back into lib/nova
+if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+    cleanup_nova_hypervisor
+fi
+
 # cinder doesn't always clean up the volume group as it might be used elsewhere...
 # clean it up if it is a loop device
 VG_DEV=$(sudo losetup -j $DATA_DIR/${VOLUME_GROUP}-backing-file | awk -F':' '/backing-file/ { print $1}')
diff --git a/exercises/boot_from_volume.sh b/exercises/boot_from_volume.sh
index 36524ed..fe27bd0 100755
--- a/exercises/boot_from_volume.sh
+++ b/exercises/boot_from_volume.sh
@@ -44,6 +44,9 @@
 # the exercise is skipped
 is_service_enabled cinder || exit 55
 
+# Also skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
 # Instance type to create
 DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
 
diff --git a/exercises/docker.sh b/exercises/docker.sh
new file mode 100755
index 0000000..0672bc0
--- /dev/null
+++ b/exercises/docker.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env bash
+
+# **docker**
+
+# Test Docker hypervisor
+
+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 occurred.
+set -o errexit
+
+# Print the commands being run so that we can see the command that triggers
+# an error.  It is also useful for following allowing as the install occurs.
+set -o xtrace
+
+
+# Settings
+# ========
+
+# Keep track of the current directory
+EXERCISE_DIR=$(cd $(dirname "$0") && pwd)
+TOP_DIR=$(cd $EXERCISE_DIR/..; pwd)
+
+# Import common functions
+source $TOP_DIR/functions
+
+# Import configuration
+source $TOP_DIR/openrc
+
+# Import exercise configuration
+source $TOP_DIR/exerciserc
+
+# Skip if the hypervisor is not Docker
+[[ "$VIRT_DRIVER" == "docker" ]] || exit 55
+
+# Import docker functions and declarations
+source $TOP_DIR/lib/nova_plugins/hypervisor-docker
+
+# Image and flavor are ignored but the CLI requires them...
+
+# Instance type to create
+DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
+
+# Boot this image, use first AMI image if unset
+DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ami}
+
+# Instance name
+VM_NAME=ex-docker
+
+
+# Launching a server
+# ==================
+
+# Grab the id of the image to launch
+IMAGE=$(glance image-list | egrep " $DOCKER_IMAGE_NAME:latest " | get_field 1)
+die_if_not_set $LINENO IMAGE "Failure getting image $DOCKER_IMAGE_NAME"
+
+# Select a flavor
+INSTANCE_TYPE=$(nova flavor-list | grep $DEFAULT_INSTANCE_TYPE | get_field 1)
+if [[ -z "$INSTANCE_TYPE" ]]; then
+    # grab the first flavor in the list to launch if default doesn't exist
+   INSTANCE_TYPE=$(nova flavor-list | head -n 4 | tail -n 1 | get_field 1)
+fi
+
+# Clean-up from previous runs
+nova delete $VM_NAME || true
+if ! timeout $ACTIVE_TIMEOUT sh -c "while nova show $VM_NAME; do sleep 1; done"; then
+    die $LINENO "server didn't terminate!"
+fi
+
+# Boot instance
+# -------------
+
+VM_UUID=$(nova boot --flavor $INSTANCE_TYPE --image $IMAGE $VM_NAME | grep ' id ' | get_field 2)
+die_if_not_set $LINENO VM_UUID "Failure launching $VM_NAME"
+
+# Check that the status is active within ACTIVE_TIMEOUT seconds
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova show $VM_UUID | grep status | grep -q ACTIVE; do sleep 1; done"; then
+    die $LINENO "server didn't become active!"
+fi
+
+# Get the instance IP
+IP=$(nova show $VM_UUID | grep "$PRIVATE_NETWORK_NAME" | get_field 2)
+die_if_not_set $LINENO IP "Failure retrieving IP address"
+
+# Private IPs can be pinged in single node deployments
+ping_check "$PRIVATE_NETWORK_NAME" $IP $BOOT_TIMEOUT
+
+# Clean up
+# --------
+
+# Delete instance
+nova delete $VM_UUID || die $LINENO "Failure deleting instance $VM_NAME"
+if ! timeout $TERMINATE_TIMEOUT sh -c "while nova list | grep -q $VM_UUID; do sleep 1; done"; then
+    die $LINENO "Server $VM_NAME not deleted"
+fi
+
+set +o xtrace
+echo "*********************************************************************"
+echo "SUCCESS: End DevStack Exercise: $0"
+echo "*********************************************************************"
+
diff --git a/exercises/euca.sh b/exercises/euca.sh
index b8b283a..64c0014 100755
--- a/exercises/euca.sh
+++ b/exercises/euca.sh
@@ -41,6 +41,9 @@
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
+# Skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
 # Instance type to create
 DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
 
diff --git a/exercises/floating_ips.sh b/exercises/floating_ips.sh
index f93a727..2833b65 100755
--- a/exercises/floating_ips.sh
+++ b/exercises/floating_ips.sh
@@ -38,6 +38,9 @@
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
+# Skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
 # Instance type to create
 DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
 
diff --git a/exercises/sec_groups.sh b/exercises/sec_groups.sh
index 6b67291..7d80570 100755
--- a/exercises/sec_groups.sh
+++ b/exercises/sec_groups.sh
@@ -33,6 +33,9 @@
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
+# Skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
 
 # Testing Security Groups
 # =======================
diff --git a/exercises/volumes.sh b/exercises/volumes.sh
index 028d19b..e536d16 100755
--- a/exercises/volumes.sh
+++ b/exercises/volumes.sh
@@ -42,6 +42,9 @@
 # exercise is skipped.
 is_service_enabled cinder || exit 55
 
+# Also skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
 # Instance type to create
 DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
 
diff --git a/files/keystone_data.sh b/files/keystone_data.sh
index 45f9c81..3f3137c 100755
--- a/files/keystone_data.sh
+++ b/files/keystone_data.sh
@@ -58,9 +58,9 @@
     # Nova needs ResellerAdmin role to download images when accessing
     # swift through the s3 api.
     keystone user-role-add \
-        --tenant_id $SERVICE_TENANT \
-        --user_id $NOVA_USER \
-        --role_id $RESELLER_ROLE
+        --tenant-id $SERVICE_TENANT \
+        --user-id $NOVA_USER \
+        --role-id $RESELLER_ROLE
 fi
 
 # Heat
@@ -69,9 +69,9 @@
                                               --pass="$SERVICE_PASSWORD" \
                                               --tenant_id $SERVICE_TENANT \
                                               --email=heat@example.com)
-    keystone user-role-add --tenant_id $SERVICE_TENANT \
-                           --user_id $HEAT_USER \
-                           --role_id $SERVICE_ROLE
+    keystone user-role-add --tenant-id $SERVICE_TENANT \
+                           --user-id $HEAT_USER \
+                           --role-id $SERVICE_ROLE
     # heat_stack_user role is for users created by Heat
     keystone role-create --name heat_stack_user
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -106,9 +106,9 @@
         --tenant_id $SERVICE_TENANT \
         --email=glance@example.com)
     keystone user-role-add \
-        --tenant_id $SERVICE_TENANT \
-        --user_id $GLANCE_USER \
-        --role_id $ADMIN_ROLE
+        --tenant-id $SERVICE_TENANT \
+        --user-id $GLANCE_USER \
+        --role-id $ADMIN_ROLE
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
         GLANCE_SERVICE=$(get_id keystone service-create \
             --name=glance \
@@ -129,13 +129,13 @@
                                               --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
+    keystone user-role-add --tenant-id $SERVICE_TENANT \
+                           --user-id $CEILOMETER_USER \
+                           --role-id $ADMIN_ROLE
     # Ceilometer needs ResellerAdmin role to access swift account stats.
-    keystone user-role-add --tenant_id $SERVICE_TENANT \
-                           --user_id $CEILOMETER_USER \
-                           --role_id $RESELLER_ROLE
+    keystone user-role-add --tenant-id $SERVICE_TENANT \
+                           --user-id $CEILOMETER_USER \
+                           --role-id $RESELLER_ROLE
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
         CEILOMETER_SERVICE=$(get_id keystone service-create \
             --name=ceilometer \
@@ -192,7 +192,7 @@
         --pass="$ADMIN_PASSWORD" \
         --email=alt_demo@example.com)
     keystone user-role-add \
-        --tenant_id $ALT_DEMO_TENANT \
-        --user_id $ALT_DEMO_USER \
-        --role_id $MEMBER_ROLE
+        --tenant-id $ALT_DEMO_TENANT \
+        --user-id $ALT_DEMO_USER \
+        --role-id $MEMBER_ROLE
 fi
diff --git a/functions b/functions
index f24cc89..df8166a 100644
--- a/functions
+++ b/functions
@@ -1454,7 +1454,6 @@
     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
@@ -1645,6 +1644,37 @@
 }
 
 
+# ``policy_add policy_file policy_name policy_permissions``
+#
+# Add a policy to a policy.json file
+# Do nothing if the policy already exists
+
+function policy_add() {
+    local policy_file=$1
+    local policy_name=$2
+    local policy_perm=$3
+
+    if grep -q ${policy_name} ${policy_file}; then
+        echo "Policy ${policy_name} already exists in ${policy_file}"
+        return
+    fi
+
+    # Add a terminating comma to policy lines without one
+    # Remove the closing '}' and all lines following to the end-of-file
+    local tmpfile=$(mktemp)
+    uniq ${policy_file} | sed -e '
+        s/]$/],/
+        /^[}]/,$d
+    ' > ${tmpfile}
+
+    # Append policy and closing brace
+    echo "    \"${policy_name}\": ${policy_perm}" >>${tmpfile}
+    echo "}" >>${tmpfile}
+
+    mv ${tmpfile} ${policy_file}
+}
+
+
 # Restore xtrace
 $XTRACE
 
diff --git a/lib/cinder b/lib/cinder
index 826b958..b30829f 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -335,9 +335,9 @@
             --email=cinder@example.com \
             | grep " id " | get_field 2)
         keystone user-role-add \
-            --tenant_id $SERVICE_TENANT \
-            --user_id $CINDER_USER \
-            --role_id $ADMIN_ROLE
+            --tenant-id $SERVICE_TENANT \
+            --user-id $CINDER_USER \
+            --role-id $ADMIN_ROLE
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
             CINDER_SERVICE=$(keystone service-create \
                 --name=cinder \
diff --git a/lib/ironic b/lib/ironic
new file mode 100644
index 0000000..2ce5038
--- /dev/null
+++ b/lib/ironic
@@ -0,0 +1,222 @@
+# lib/ironic
+# Functions to control the configuration and operation of the **Ironic** service
+
+# Dependencies:
+# ``functions`` file
+# ``DEST``, ``DATA_DIR``, ``STACK_USER`` 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:
+#
+# install_ironic
+# configure_ironic
+# init_ironic
+# start_ironic
+# stop_ironic
+# cleanup_ironic
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Defaults
+# --------
+
+# Set up default directories
+IRONIC_DIR=$DEST/ironic
+IRONIC_AUTH_CACHE_DIR=${IRONIC_AUTH_CACHE_DIR:-/var/cache/ironic}
+IRONIC_CONF_DIR=${IRONIC_CONF_DIR:-/etc/ironic}
+IRONIC_CONF_FILE=$IRONIC_CONF_DIR/ironic.conf
+IRONIC_ROOTWRAP_CONF=$IRONIC_CONF_DIR/rootwrap.conf
+IRONIC_ROOTWRAP_FILTERS=$IRONIC_CONF_DIR/rootwrap.d
+IRONIC_POLICY_JSON=$IRONIC_CONF_DIR/policy.json
+
+# Support entry points installation of console scripts
+IRONIC_BIN_DIR=$(get_python_exec_prefix)
+
+# Ironic connection info.  Note the port must be specified.
+IRONIC_SERVICE_PROTOCOL=http
+IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST:6385}
+
+
+# Functions
+# ---------
+
+# cleanup_ironic() - Remove residual data files, anything left over from previous
+# runs that would need to clean up.
+function cleanup_ironic() {
+    sudo rm -rf $IRONIC_AUTH_CACHE_DIR
+}
+
+# configure_ironic() - Set config files, create data dirs, etc
+function configure_ironic() {
+    if [[ ! -d $IRONIC_CONF_DIR ]]; then
+        sudo mkdir -p $IRONIC_CONF_DIR
+    fi
+    sudo chown $STACK_USER $IRONIC_CONF_DIR
+
+    # Copy over ironic configuration file and configure common parameters.
+    cp $IRONIC_DIR/etc/ironic/ironic.conf.sample $IRONIC_CONF_FILE
+    iniset $IRONIC_CONF_FILE DEFAULT debug True
+    inicomment $IRONIC_CONF_FILE DEFAULT log_file
+    iniset $IRONIC_CONF_FILE DEFAULT sql_connection `database_connection_url ironic`
+    iniset $IRONIC_CONF_FILE DEFAULT use_syslog $SYSLOG
+
+    # Configure Ironic conductor, if it was enabled.
+    if is_service_enabled ir-cond; then
+        configure_ironic_conductor
+    fi
+
+    # Configure Ironic API, if it was enabled.
+    if is_service_enabled ir-api; then
+        configure_ironic_api
+    fi
+}
+
+# configure_ironic_api() - Is used by configure_ironic(). Performs
+# API specific configuration.
+function configure_ironic_api() {
+    iniset $IRONIC_CONF_FILE keystone_authtoken auth_host $KEYSTONE_AUTH_HOST
+    iniset $IRONIC_CONF_FILE keystone_authtoken auth_port $KEYSTONE_AUTH_PORT
+    iniset $IRONIC_CONF_FILE keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
+    iniset $IRONIC_CONF_FILE keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/
+    iniset $IRONIC_CONF_FILE keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
+    iniset $IRONIC_CONF_FILE keystone_authtoken admin_user ironic
+    iniset $IRONIC_CONF_FILE keystone_authtoken admin_password $SERVICE_PASSWORD
+    if is_service_enabled qpid; then
+        iniset $IRONIC_CONF_FILE DEFAULT notifier_strategy qpid
+    elif [ -n "$RABBIT_HOST" ] &&  [ -n "$RABBIT_PASSWORD" ]; then
+        iniset $IRONIC_CONF_FILE DEFAULT notifier_strategy rabbit
+    fi
+    iniset_rpc_backend ironic $IRONIC_CONF_FILE DEFAULT
+    iniset $IRONIC_CONF_FILE keystone_authtoken signing_dir $IRONIC_AUTH_CACHE_DIR/api
+
+    cp -p $IRONIC_DIR/etc/ironic/policy.json $IRONIC_POLICY_JSON
+}
+
+# configure_ironic_conductor() - Is used by configure_ironic().
+# Sets conductor specific settings.
+function configure_ironic_conductor() {
+    cp $IRONIC_DIR/etc/ironic/rootwrap.conf $IRONIC_ROOTWRAP_CONF
+    cp -r $IRONIC_DIR/etc/ironic/rootwrap.d $IRONIC_ROOTWRAP_FILTERS
+
+    iniset $IRONIC_CONF DEFAULT rootwrap_config $IRONIC_ROOTWRAP_CONF
+}
+
+# create_ironic_cache_dir() - Part of the init_ironic() process
+function create_ironic_cache_dir() {
+    # Create cache dir
+    sudo mkdir -p $IRONIC_AUTH_CACHE_DIR/api
+    sudo chown $STACK_USER $IRONIC_AUTH_CACHE_DIR/api
+    rm -f $IRONIC_AUTH_CACHE_DIR/api/*
+    sudo mkdir -p $IRONIC_AUTH_CACHE_DIR/registry
+    sudo chown $STACK_USER $IRONIC_AUTH_CACHE_DIR/registry
+    rm -f $IRONIC_AUTH_CACHE_DIR/registry/*
+}
+
+# create_ironic_accounts() - Set up common required ironic accounts
+
+# Tenant               User       Roles
+# ------------------------------------------------------------------
+# service              ironic     admin        # if enabled
+create_ironic_accounts() {
+
+    SERVICE_TENANT=$(keystone tenant-list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    ADMIN_ROLE=$(keystone role-list | awk "/ admin / { print \$2 }")
+
+    # Ironic
+    if [[ "$ENABLED_SERVICES" =~ "ir-api" ]]; then
+        IRONIC_USER=$(keystone user-create \
+            --name=ironic \
+            --pass="$SERVICE_PASSWORD" \
+            --tenant_id $SERVICE_TENANT \
+            --email=ironic@example.com \
+            | grep " id " | get_field 2)
+        keystone user-role-add \
+            --tenant_id $SERVICE_TENANT \
+            --user_id $IRONIC_USER \
+            --role_id $ADMIN_ROLE
+        if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
+            IRONIC_SERVICE=$(keystone service-create \
+                --name=ironic \
+                --type=baremetal \
+                --description="Ironic baremetal provisioning service" \
+                | grep " id " | get_field 2)
+            keystone endpoint-create \
+                --region RegionOne \
+                --service_id $IRONIC_SERVICE \
+                --publicurl "$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT/v1/" \
+                --adminurl "$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT/v1/" \
+                --internalurl "$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT/v1/"
+        fi
+    fi
+}
+
+
+# init_ironic() - Initialize databases, etc.
+function init_ironic() {
+    # (Re)create  ironic database
+    recreate_database ironic utf8
+
+    # Migrate ironic database
+    $IRONIC_BIN_DIR/ironic-dbsync
+
+    create_ironic_cache_dir
+
+    # Create keystone artifacts for Ironic.
+    create_ironic_accounts
+}
+
+# install_ironic() - Collect source and prepare
+function install_ironic() {
+    git_clone $IRONIC_REPO $IRONIC_DIR $IRONIC_BRANCH
+    setup_develop $IRONIC_DIR
+}
+
+# start_ironic() - Start running processes, including screen
+function start_ironic() {
+    # Start Ironic API server, if enabled.
+    if is_service_enabled ir-api; then
+        start_ironic_api
+    fi
+
+    # Start Ironic conductor, if enabled.
+    if is_service_enabled ir-cond; then
+        start_ironic_conductor
+    fi
+}
+
+# start_ironic_api() - Used by start_ironic().
+# Starts Ironic API server.
+function start_ironic_api() {
+    screen_it ir-api "cd $IRONIC_DIR; $IRONIC_BIN_DIR/ironic-api --config-file=$IRONIC_CONF_FILE"
+    echo "Waiting for ir-api ($IRONIC_HOSTPORT) to start..."
+    if ! timeout $SERVICE_TIMEOUT sh -c "while ! http_proxy= wget -q -O- http://$IRONIC_HOSTPORT; do sleep 1; done"; then
+      die $LINENO "ir-api did not start"
+    fi
+}
+
+# start_ironic_conductor() - Used by start_ironic().
+# Starts Ironic conductor.
+function start_ironic_conductor() {
+    screen_it ir-cond "cd $IRONIC_DIR; $IRONIC_BIN_DIR/ironic-conductor --config-file=$IRONIC_CONF_FILE"
+    # TODO(romcheg): Find a way to check whether the conductor has started.
+}
+
+# stop_ironic() - Stop running processes
+function stop_ironic() {
+    # Kill the Ironic screen windows
+    screen -S $SCREEN_NAME -p ir-api -X kill
+    screen -S $SCREEN_NAME -p ir-cond -X kill
+}
+
+
+# Restore xtrace
+$XTRACE
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/lib/keystone b/lib/keystone
index 0a35dd5..535710f 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -217,9 +217,9 @@
         --name admin \
         | grep " id " | get_field 2)
     keystone user-role-add \
-        --user_id $ADMIN_USER \
-        --role_id $ADMIN_ROLE \
-        --tenant_id $ADMIN_TENANT
+        --user-id $ADMIN_USER \
+        --role-id $ADMIN_ROLE \
+        --tenant-id $ADMIN_TENANT
 
     # service
     SERVICE_TENANT=$(keystone tenant-create \
@@ -244,10 +244,10 @@
         --pass "$ADMIN_PASSWORD" \
         --email demo@example.com \
         | grep " id " | get_field 2)
-    keystone user-role-add --user_id $DEMO_USER --role_id $MEMBER_ROLE --tenant_id $DEMO_TENANT
-    keystone user-role-add --user_id $ADMIN_USER --role_id $ADMIN_ROLE --tenant_id $DEMO_TENANT
-    keystone user-role-add --user_id $DEMO_USER --role_id $ANOTHER_ROLE --tenant_id $DEMO_TENANT
-    keystone user-role-add --user_id $DEMO_USER --role_id $MEMBER_ROLE --tenant_id $INVIS_TENANT
+    keystone user-role-add --user-id $DEMO_USER --role-id $MEMBER_ROLE --tenant-id $DEMO_TENANT
+    keystone user-role-add --user-id $ADMIN_USER --role-id $ADMIN_ROLE --tenant-id $DEMO_TENANT
+    keystone user-role-add --user-id $DEMO_USER --role-id $ANOTHER_ROLE --tenant-id $DEMO_TENANT
+    keystone user-role-add --user-id $DEMO_USER --role-id $MEMBER_ROLE --tenant-id $INVIS_TENANT
 
     # Keystone
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
diff --git a/lib/neutron b/lib/neutron
index 01fe3ea..dc3c622 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -301,9 +301,9 @@
             --email=neutron@example.com \
             | grep " id " | get_field 2)
         keystone user-role-add \
-            --tenant_id $SERVICE_TENANT \
-            --user_id $NEUTRON_USER \
-            --role_id $ADMIN_ROLE
+            --tenant-id $SERVICE_TENANT \
+            --user-id $NEUTRON_USER \
+            --role-id $ADMIN_ROLE
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
             NEUTRON_SERVICE=$(keystone service-create \
                 --name=neutron \
@@ -521,6 +521,15 @@
         done
     fi
 
+    if [ "$VIRT_DRIVER" = 'fake' ]; then
+        # Disable arbitrary limits
+        iniset $NEUTRON_CONF quotas quota_network -1
+        iniset $NEUTRON_CONF quotas quota_subnet -1
+        iniset $NEUTRON_CONF quotas quota_port -1
+        iniset $NEUTRON_CONF quotas quota_security_group -1
+        iniset $NEUTRON_CONF quotas quota_security_group_rule -1
+    fi
+
     _neutron_setup_rootwrap
 }
 
diff --git a/lib/nova b/lib/nova
index a835386..19093ad 100644
--- a/lib/nova
+++ b/lib/nova
@@ -169,6 +169,13 @@
     fi
 
     sudo rm -rf $NOVA_STATE_PATH $NOVA_AUTH_CACHE_DIR
+
+    # NOTE(dtroyer): This really should be called from here but due to the way
+    #                nova abuses the _cleanup() function we're moving it
+    #                directly into cleanup.sh until this can be fixed.
+    #if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+    #    cleanup_nova_hypervisor
+    #fi
 }
 
 # configure_nova_rootwrap() - configure Nova's rootwrap
@@ -392,9 +399,9 @@
             --email=nova@example.com \
             | grep " id " | get_field 2)
         keystone user-role-add \
-            --tenant_id $SERVICE_TENANT \
-            --user_id $NOVA_USER \
-            --role_id $ADMIN_ROLE
+            --tenant-id $SERVICE_TENANT \
+            --user-id $NOVA_USER \
+            --role-id $ADMIN_ROLE
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
             NOVA_SERVICE=$(keystone service-create \
                 --name=nova \
@@ -652,7 +659,9 @@
 # install_nova() - Collect source and prepare
 function install_nova() {
     if is_service_enabled n-cpu; then
-        if [[ "$VIRT_DRIVER" = 'libvirt' ]]; then
+        if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+            install_nova_hypervisor
+        elif [[ "$VIRT_DRIVER" = 'libvirt' ]]; then
             if is_ubuntu; then
                 install_package kvm
                 install_package libvirt-bin
@@ -731,6 +740,9 @@
            screen_it n-cpu "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-compute --config-file $NOVA_CONF_BOTTOM"
        done
     else
+        if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+            start_nova_hypervisor
+        fi
         screen_it n-cpu "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-compute --config-file $NOVA_CONF_BOTTOM"
     fi
     screen_it n-crt "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-cert"
@@ -757,6 +769,9 @@
     for serv in n-api n-cpu n-crt n-net n-sch n-novnc n-xvnc n-cauth n-spice n-cond n-cond n-cell n-cell n-api-meta; do
         screen -S $SCREEN_NAME -p $serv -X kill
     done
+    if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+        stop_nova_hypervisor
+    fi
 }
 
 
diff --git a/lib/nova_plugins/hypervisor-docker b/lib/nova_plugins/hypervisor-docker
new file mode 100644
index 0000000..4c8fc27
--- /dev/null
+++ b/lib/nova_plugins/hypervisor-docker
@@ -0,0 +1,132 @@
+# lib/nova_plugins/docker
+# Configure the Docker hypervisor
+
+# Enable with:
+# VIRT_DRIVER=docker
+
+# Dependencies:
+# ``functions`` file
+# ``nova`` and ``glance`` configurations
+
+# install_nova_hypervisor - install any external requirements
+# configure_nova_hypervisor - make configuration changes, including those to other services
+# start_nova_hypervisor - start any external services
+# stop_nova_hypervisor - stop any external services
+# cleanup_nova_hypervisor - remove transient data and cache
+
+# Save trace setting
+MY_XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Defaults
+# --------
+
+# Set up default directories
+DOCKER_DIR=$DEST/docker
+DOCKER_REPO=${DOCKER_REPO:-https://github.com/dotcloud/openstack-docker.git}
+DOCKER_BRANCH=${DOCKER_BRANCH:-master}
+
+DOCKER_UNIX_SOCKET=/var/run/docker.sock
+DOCKER_PID_FILE=/var/run/docker.pid
+DOCKER_REGISTRY_PORT=${DOCKER_REGISTRY_PORT:-5042}
+
+DOCKER_IMAGE=${DOCKER_IMAGE:-http://get.docker.io/images/openstack/docker-ut.tar.gz}
+DOCKER_IMAGE_NAME=docker-busybox
+DOCKER_REGISTRY_IMAGE=${DOCKER_REGISTRY_IMAGE:-http://get.docker.io/images/openstack/docker-registry.tar.gz}
+DOCKER_REGISTRY_IMAGE_NAME=docker-registry
+DOCKER_REPOSITORY_NAME=${SERVICE_HOST}:${DOCKER_REGISTRY_PORT}/${DOCKER_IMAGE_NAME}
+
+DOCKER_PACKAGE_VERSION=${DOCKER_PACKAGE_VERSION:-0.6.1}
+DOCKER_APT_REPO=${DOCKER_APT_REPO:-https://get.docker.io/ubuntu}
+
+
+# Entry Points
+# ------------
+
+# clean_nova_hypervisor - Clean up an installation
+function cleanup_nova_hypervisor() {
+    stop_service docker
+
+    # Clean out work area
+    sudo rm -rf /var/lib/docker
+}
+
+# configure_nova_hypervisor - Set config files, create data dirs, etc
+function configure_nova_hypervisor() {
+    git_clone $DOCKER_REPO $DOCKER_DIR $DOCKER_BRANCH
+
+    ln -snf ${DOCKER_DIR}/nova-driver $NOVA_DIR/nova/virt/docker
+
+    iniset $NOVA_CONF DEFAULT compute_driver docker.DockerDriver
+    iniset $GLANCE_API_CONF DEFAULT container_formats ami,ari,aki,bare,ovf,docker
+
+    sudo cp -p ${DOCKER_DIR}/nova-driver/docker.filters $NOVA_CONF_DIR/rootwrap.d
+}
+
+# install_nova_hypervisor() - Install external components
+function install_nova_hypervisor() {
+    # So far this is Ubuntu only
+    if ! is_ubuntu; then
+        die $LINENO "Docker is only supported on Ubuntu at this time"
+    fi
+
+    # Make sure Docker is installed
+    if ! is_package_installed lxc-docker; then
+        die $LINENO "Docker is not installed.  Please run tools/docker/install_docker.sh"
+    fi
+
+    local docker_pid
+    read docker_pid <$DOCKER_PID_FILE
+    if [[ -z $docker_pid ]] || ! ps -p $docker_pid | grep [d]ocker; then
+        die $LINENO "Docker not running"
+    fi
+}
+
+# start_nova_hypervisor - Start any required external services
+function start_nova_hypervisor() {
+    local docker_pid
+    read docker_pid <$DOCKER_PID_FILE
+    if [[ -z $docker_pid ]] || ! ps -p $docker_pid | grep [d]ocker; then
+        die $LINENO "Docker not running, start the daemon"
+    fi
+
+    # Start the Docker registry container
+    docker run -d -p ${DOCKER_REGISTRY_PORT}:5000 \
+        -e SETTINGS_FLAVOR=openstack -e OS_USERNAME=${OS_USERNAME} \
+        -e OS_PASSWORD=${OS_PASSWORD} -e OS_TENANT_NAME=${OS_TENANT_NAME} \
+        -e OS_GLANCE_URL="${SERVICE_PROTOCOL}://${GLANCE_HOSTPORT}" \
+        -e OS_AUTH_URL=${OS_AUTH_URL} \
+        $DOCKER_REGISTRY_IMAGE_NAME ./docker-registry/run.sh
+
+    echo "Waiting for docker registry to start..."
+    DOCKER_REGISTRY=${SERVICE_HOST}:${DOCKER_REGISTRY_PORT}
+    if ! timeout $SERVICE_TIMEOUT sh -c "while ! curl -s $DOCKER_REGISTRY; do sleep 1; done"; then
+        die $LINENO "docker-registry did not start"
+    fi
+
+    # Tag image if not already tagged
+    if ! docker images | grep $DOCKER_REPOSITORY_NAME; then
+        docker tag $DOCKER_IMAGE_NAME $DOCKER_REPOSITORY_NAME
+    fi
+
+    # Make sure we copied the image in Glance
+    DOCKER_IMAGE=$(glance image-list | egrep " $DOCKER_IMAGE_NAME ")
+    if ! is_set DOCKER_IMAGE ; then
+        docker push $DOCKER_REPOSITORY_NAME
+    fi
+}
+
+# stop_nova_hypervisor - Stop any external services
+function stop_nova_hypervisor() {
+    # Stop the docker registry container
+    docker kill $(docker ps | grep docker-registry | cut -d' ' -f1)
+}
+
+
+# Restore xtrace
+$MY_XTRACE
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/lib/swift b/lib/swift
index 8e64152..f72beaf 100644
--- a/lib/swift
+++ b/lib/swift
@@ -464,7 +464,7 @@
 
     SWIFT_USER=$(keystone user-create --name=swift --pass="$SERVICE_PASSWORD" \
         --tenant_id $SERVICE_TENANT --email=swift@example.com | grep " id " | get_field 2)
-    keystone user-role-add --tenant_id $SERVICE_TENANT --user_id $SWIFT_USER --role_id $ADMIN_ROLE
+    keystone user-role-add --tenant-id $SERVICE_TENANT --user-id $SWIFT_USER --role-id $ADMIN_ROLE
 
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
         SWIFT_SERVICE=$(keystone service-create --name=swift --type="object-store" \
@@ -479,14 +479,14 @@
 
     SWIFT_TENANT_TEST1=$(keystone tenant-create --name=swifttenanttest1 | grep " id " | get_field 2)
     SWIFT_USER_TEST1=$(keystone user-create --name=swiftusertest1 --pass=testing --email=test@example.com | grep " id " | get_field 2)
-    keystone user-role-add --user_id $SWIFT_USER_TEST1 --role_id $ADMIN_ROLE --tenant_id $SWIFT_TENANT_TEST1
+    keystone user-role-add --user-id $SWIFT_USER_TEST1 --role-id $ADMIN_ROLE --tenant-id $SWIFT_TENANT_TEST1
 
     SWIFT_USER_TEST3=$(keystone user-create --name=swiftusertest3 --pass=testing3 --email=test3@example.com | grep " id " | get_field 2)
-    keystone user-role-add --user_id $SWIFT_USER_TEST3 --role_id $ANOTHER_ROLE --tenant_id $SWIFT_TENANT_TEST1
+    keystone user-role-add --user-id $SWIFT_USER_TEST3 --role-id $ANOTHER_ROLE --tenant-id $SWIFT_TENANT_TEST1
 
     SWIFT_TENANT_TEST2=$(keystone tenant-create --name=swifttenanttest2 | grep " id " | get_field 2)
     SWIFT_USER_TEST2=$(keystone user-create --name=swiftusertest2 --pass=testing2 --email=test2@example.com | grep " id " | get_field 2)
-    keystone user-role-add --user_id $SWIFT_USER_TEST2 --role_id $ADMIN_ROLE --tenant_id $SWIFT_TENANT_TEST2
+    keystone user-role-add --user-id $SWIFT_USER_TEST2 --role-id $ADMIN_ROLE --tenant-id $SWIFT_TENANT_TEST2
 }
 
 # init_swift() - Initialize rings
diff --git a/stack.sh b/stack.sh
index 8f59328..89e4c24 100755
--- a/stack.sh
+++ b/stack.sh
@@ -318,6 +318,14 @@
 source $TOP_DIR/lib/neutron
 source $TOP_DIR/lib/baremetal
 source $TOP_DIR/lib/ldap
+source $TOP_DIR/lib/ironic
+
+# Look for Nova hypervisor plugin
+NOVA_PLUGINS=$TOP_DIR/lib/nova_plugins
+if is_service_enabled nova && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+    # Load plugin
+    source $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER
+fi
 
 # Set the destination directories for other OpenStack projects
 OPENSTACKCLIENT_DIR=$DEST/python-openstackclient
@@ -589,64 +597,6 @@
 # Do the ugly hacks for borken packages and distros
 $TOP_DIR/tools/fixup_stuff.sh
 
-
-# System-specific preconfigure
-# ============================
-
-if [[ is_fedora && $DISTRO =~ (rhel6) ]]; then
-    # Disable selinux to avoid configuring to allow Apache access
-    # to Horizon files or run nodejs (LP#1175444)
-    if selinuxenabled; then
-        sudo setenforce 0
-    fi
-
-    # The following workarounds break xenserver
-    if [ "$VIRT_DRIVER" != 'xenserver' ]; then
-        # An old version of ``python-crypto`` (2.0.1) may be installed on a
-        # fresh system via Anaconda and the dependency chain
-        # ``cas`` -> ``python-paramiko`` -> ``python-crypto``.
-        # ``pip uninstall pycrypto`` will remove the packaged ``.egg-info``
-        #  file but leave most of the actual library files behind in
-        # ``/usr/lib64/python2.6/Crypto``. Later ``pip install pycrypto``
-        # will install over the packaged files resulting
-        # in a useless mess of old, rpm-packaged files and pip-installed files.
-        # Remove the package so that ``pip install python-crypto`` installs
-        # cleanly.
-        # Note: other RPM packages may require ``python-crypto`` as well.
-        # For example, RHEL6 does not install ``python-paramiko packages``.
-        uninstall_package python-crypto
-
-        # A similar situation occurs with ``python-lxml``, which is required by
-        # ``ipa-client``, an auditing package we don't care about.  The
-        # build-dependencies needed for ``pip install lxml`` (``gcc``,
-        # ``libxml2-dev`` and ``libxslt-dev``) are present in
-        # ``files/rpms/general``.
-        uninstall_package python-lxml
-    fi
-
-    # If the ``dbus`` package was installed by DevStack dependencies the
-    # uuid may not be generated because the service was never started (PR#598200),
-    # causing Nova to stop later on complaining that ``/var/lib/dbus/machine-id``
-    # does not exist.
-    sudo service messagebus restart
-
-    # ``setup.py`` contains a ``setup_requires`` package that is supposed
-    # to be transient.  However, RHEL6 distribute has a bug where
-    # ``setup_requires`` registers entry points that are not cleaned
-    # out properly after the setup-phase resulting in installation failures
-    # (bz#924038).  Pre-install the problem package so the ``setup_requires``
-    # dependency is satisfied and it will not be installed transiently.
-    # Note we do this before the track-depends below.
-    pip_install hgtools
-
-    # RHEL6's version of ``python-nose`` is incompatible with Tempest.
-    # Install nose 1.1 (Tempest-compatible) from EPEL
-    install_package python-nose1.1
-    # Add a symlink for the new nosetests to allow tox for Tempest to
-    # work unmolested.
-    sudo ln -sf /usr/bin/nosetests1.1 /usr/local/bin/nosetests
-fi
-
 install_rpc_backend
 
 if is_service_enabled $DATABASE_BACKENDS; then
@@ -778,6 +728,11 @@
     # don't be naive and add to existing line!
 fi
 
+if is_service_enabled ir-api ir-cond; then
+    install_ironic
+    configure_ironic
+fi
+
 if [[ $TRACK_DEPENDS = True ]]; then
     $DEST/.venv/bin/pip freeze > $DEST/requires-post-pip
     if ! diff -Nru $DEST/requires-pre-pip $DEST/requires-post-pip > $DEST/requires.diff; then
@@ -946,6 +901,15 @@
     init_glance
 fi
 
+# Ironic
+# ------
+
+if is_service_enabled ir-api ir-cond; then
+    echo_summary "Configuring Ironic"
+    init_ironic
+fi
+
+
 
 # Neutron
 # -------
@@ -1013,6 +977,10 @@
     init_cinder
 fi
 
+
+# Compute Service
+# ---------------
+
 if is_service_enabled nova; then
     echo_summary "Configuring Nova"
     # Rebuild the config file from scratch
@@ -1027,10 +995,15 @@
     fi
 
 
+    if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+        # Configure hypervisor plugin
+        configure_nova_hypervisor
+
+
     # XenServer
     # ---------
 
-    if [ "$VIRT_DRIVER" = 'xenserver' ]; then
+    elif [ "$VIRT_DRIVER" = 'xenserver' ]; then
         echo_summary "Using XenServer virtualization driver"
         if [ -z "$XENAPI_CONNECTION_URL" ]; then
             die $LINENO "XENAPI_CONNECTION_URL is not specified"
@@ -1186,6 +1159,12 @@
     start_glance
 fi
 
+# Launch the Ironic services
+if is_service_enabled ir-api ir-cond; then
+    echo_summary "Starting Ironic"
+    start_ironic
+fi
+
 # Create an access key and secret key for nova ec2 register image
 if is_service_enabled key && is_service_enabled swift3 && is_service_enabled nova; then
     NOVA_USER_ID=$(keystone user-list | grep ' nova ' | get_field 1)
diff --git a/stackrc b/stackrc
index 8b97536..f9a977c 100644
--- a/stackrc
+++ b/stackrc
@@ -96,6 +96,10 @@
 HORIZON_REPO=${HORIZON_REPO:-${GIT_BASE}/openstack/horizon.git}
 HORIZON_BRANCH=${HORIZON_BRANCH:-master}
 
+# baremetal provisionint service
+IRONIC_REPO=${IRONIC_REPO:-${GIT_BASE}/openstack/ironic.git}
+IRONIC_BRANCH=${IRONIC_BRANCH:-master}
+
 # unified auth system (manages accounts/tokens)
 KEYSTONE_REPO=${KEYSTONE_REPO:-${GIT_BASE}/openstack/keystone.git}
 KEYSTONE_BRANCH=${KEYSTONE_BRANCH:-master}
diff --git a/tools/docker/README.md b/tools/docker/README.md
new file mode 100644
index 0000000..976111f
--- /dev/null
+++ b/tools/docker/README.md
@@ -0,0 +1,13 @@
+# DevStack on Docker
+
+Using Docker as Nova's hypervisor requries two steps:
+
+* Configure DevStack by adding the following to `localrc`::
+
+    VIRT_DRIVER=docker
+
+* Download and install the Docker service and images::
+
+    tools/docker/install_docker.sh
+
+After this, `stack.sh` should run as normal.
diff --git a/tools/docker/install_docker.sh b/tools/docker/install_docker.sh
new file mode 100755
index 0000000..d659ad1
--- /dev/null
+++ b/tools/docker/install_docker.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+# **install_docker.sh** - Do the initial Docker installation and configuration
+
+# install_docker.sh
+#
+# Install docker package and images
+# * downloads a base busybox image and a glance registry image if necessary
+# * install the images in Docker's image cache
+
+
+# Keep track of the current directory
+SCRIPT_DIR=$(cd $(dirname "$0") && pwd)
+TOP_DIR=$(cd $SCRIPT_DIR/../..; pwd)
+
+# Import common functions
+source $TOP_DIR/functions
+
+# Load local configuration
+source $TOP_DIR/stackrc
+
+FILES=$TOP_DIR/files
+
+# Get our defaults
+source $TOP_DIR/lib/nova_plugins/hypervisor-docker
+
+SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}
+
+
+# Install Docker Service
+# ======================
+
+# Stop the auto-repo updates and do it when required here
+NO_UPDATE_REPOS=True
+
+# Set up home repo
+curl https://get.docker.io/gpg | sudo apt-key add -
+install_package python-software-properties && \
+    sudo sh -c "echo deb $DOCKER_APT_REPO docker main > /etc/apt/sources.list.d/docker.list"
+apt_get update
+install_package --force-yes lxc-docker=${DOCKER_PACKAGE_VERSION}
+
+# Start the daemon - restart just in case the package ever auto-starts...
+restart_service docker
+
+echo "Waiting for docker daemon to start..."
+DOCKER_GROUP=$(groups | cut -d' ' -f1)
+CONFIGURE_CMD="while ! /bin/echo -e 'GET /v1.3/version HTTP/1.0\n\n' | socat - unix-connect:$DOCKER_UNIX_SOCKET | grep -q '200 OK'; do
+    # Set the right group on docker unix socket before retrying
+    sudo chgrp $DOCKER_GROUP $DOCKER_UNIX_SOCKET
+    sudo chmod g+rw $DOCKER_UNIX_SOCKET
+    sleep 1
+done"
+if ! timeout $SERVICE_TIMEOUT sh -c "$CONFIGURE_CMD"; then
+    die $LINENO "docker did not start"
+fi
+
+
+# Get Docker image
+if [[ ! -r $FILES/docker-ut.tar.gz ]]; then
+    (cd $FILES; curl -OR $DOCKER_IMAGE)
+fi
+if [[ ! -r $FILES/docker-ut.tar.gz ]]; then
+    die $LINENO "Docker image unavailable"
+fi
+docker import - $DOCKER_IMAGE_NAME <$FILES/docker-ut.tar.gz
+
+# Get Docker registry image
+if [[ ! -r $FILES/docker-registry.tar.gz ]]; then
+    (cd $FILES; curl -OR $DOCKER_REGISTRY_IMAGE)
+fi
+if [[ ! -r $FILES/docker-registry.tar.gz ]]; then
+    die $LINENO "Docker registry image unavailable"
+fi
+docker import - $DOCKER_REGISTRY_IMAGE_NAME <$FILES/docker-registry.tar.gz
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 60d0f46..371b25f 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -9,10 +9,17 @@
 #   pip 1.4 doesn't fix it (1.3 did)
 # - httplib2 0.8 permissions are 600 in the package and
 #   pip 1.4 doesn't fix it (1.3 did)
+# - RHEL6:
+#   - set selinux not enforcing
+#   - (re)start messagebus daemon
+#   - remove distro packages python-crypto and python-lxml
+#   - pre-install hgtools to work around a bug in RHEL6 distribute
+#   - install nose 1.1 from EPEL
+
 
 # Keep track of the current directory
 TOOLS_DIR=$(cd $(dirname "$0") && pwd)
-TOP_DIR=`cd $TOOLS_DIR/..; pwd`
+TOP_DIR=$(cd $TOOLS_DIR/..; pwd)
 
 # Change dir to top of devstack
 cd $TOP_DIR
@@ -22,6 +29,10 @@
 
 FILES=$TOP_DIR/files
 
+
+# Python Packages
+# ---------------
+
 # Pre-install affected packages so we can fix the permissions
 sudo pip install prettytable
 sudo pip install httplib2
@@ -41,3 +52,65 @@
     fi
 
 done
+
+
+# RHEL6
+# -----
+
+if [[ $DISTRO =~ (rhel6) ]]; then
+
+    # Disable selinux to avoid configuring to allow Apache access
+    # to Horizon files or run nodejs (LP#1175444)
+    # FIXME(dtroyer): see if this can be skipped without node or if Horizon is not enabled
+    if selinuxenabled; then
+        sudo setenforce 0
+    fi
+
+    # If the ``dbus`` package was installed by DevStack dependencies the
+    # uuid may not be generated because the service was never started (PR#598200),
+    # causing Nova to stop later on complaining that ``/var/lib/dbus/machine-id``
+    # does not exist.
+    sudo service messagebus restart
+
+    # The following workarounds break xenserver
+    if [ "$VIRT_DRIVER" != 'xenserver' ]; then
+        # An old version of ``python-crypto`` (2.0.1) may be installed on a
+        # fresh system via Anaconda and the dependency chain
+        # ``cas`` -> ``python-paramiko`` -> ``python-crypto``.
+        # ``pip uninstall pycrypto`` will remove the packaged ``.egg-info``
+        #  file but leave most of the actual library files behind in
+        # ``/usr/lib64/python2.6/Crypto``. Later ``pip install pycrypto``
+        # will install over the packaged files resulting
+        # in a useless mess of old, rpm-packaged files and pip-installed files.
+        # Remove the package so that ``pip install python-crypto`` installs
+        # cleanly.
+        # Note: other RPM packages may require ``python-crypto`` as well.
+        # For example, RHEL6 does not install ``python-paramiko packages``.
+        uninstall_package python-crypto
+
+        # A similar situation occurs with ``python-lxml``, which is required by
+        # ``ipa-client``, an auditing package we don't care about.  The
+        # build-dependencies needed for ``pip install lxml`` (``gcc``,
+        # ``libxml2-dev`` and ``libxslt-dev``) are present in
+        # ``files/rpms/general``.
+        uninstall_package python-lxml
+    fi
+
+    # ``setup.py`` contains a ``setup_requires`` package that is supposed
+    # to be transient.  However, RHEL6 distribute has a bug where
+    # ``setup_requires`` registers entry points that are not cleaned
+    # out properly after the setup-phase resulting in installation failures
+    # (bz#924038).  Pre-install the problem package so the ``setup_requires``
+    # dependency is satisfied and it will not be installed transiently.
+    # Note we do this before the track-depends in ``stack.sh``.
+    pip_install hgtools
+
+
+    # RHEL6's version of ``python-nose`` is incompatible with Tempest.
+    # Install nose 1.1 (Tempest-compatible) from EPEL
+    install_package python-nose1.1
+    # Add a symlink for the new nosetests to allow tox for Tempest to
+    # work unmolested.
+    sudo ln -sf /usr/bin/nosetests1.1 /usr/local/bin/nosetests
+
+fi
diff --git a/tools/xen/install_os_domU.sh b/tools/xen/install_os_domU.sh
index 997644d..a012a08 100755
--- a/tools/xen/install_os_domU.sh
+++ b/tools/xen/install_os_domU.sh
@@ -181,7 +181,7 @@
     mgmt_ip=$(echo $XENAPI_CONNECTION_URL | tr -d -c '1234567890.')
     domid=$(xe vm-list name-label="$GUEST_NAME" params=dom-id minimal=true)
     port=$(xenstore-read /local/domain/$domid/console/vnc-port)
-    echo "vncviewer -via $mgmt_ip localhost:${port:2}"
+    echo "vncviewer -via root@$mgmt_ip localhost:${port:2}"
     while true
     do
         state=$(xe_min vm-list name-label="$GUEST_NAME" power-state=halted)
diff --git a/tools/xen/xenrc b/tools/xen/xenrc
index 03b30ac..f698be1 100644
--- a/tools/xen/xenrc
+++ b/tools/xen/xenrc
@@ -13,7 +13,7 @@
 
 # Size of image
 VDI_MB=${VDI_MB:-5000}
-OSDOMU_MEM_MB=1024
+OSDOMU_MEM_MB=2048
 OSDOMU_VDI_GB=8
 
 # Network mapping. Specify bridge names or network names. Network names may
diff --git a/unstack.sh b/unstack.sh
index 2268b90..38f795b 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -33,6 +33,7 @@
 source $TOP_DIR/lib/horizon
 source $TOP_DIR/lib/swift
 source $TOP_DIR/lib/neutron
+source $TOP_DIR/lib/ironic
 
 # Determine what system we are running on.  This provides ``os_VENDOR``,
 # ``os_RELEASE``, ``os_UPDATE``, ``os_PACKAGE``, ``os_CODENAME``
@@ -65,12 +66,26 @@
     fi
 fi
 
+# Shut down Nova hypervisor plugins after Nova
+NOVA_PLUGINS=$TOP_DIR/lib/nova_plugins
+if is_service_enabled nova && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+    # Load plugin
+    source $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER
+    stop_nova_hypervisor
+fi
+
 # Swift runs daemons
 if is_service_enabled s-proxy; then
     stop_swift
     cleanup_swift
 fi
 
+# Ironic runs daemons
+if is_service_enabled ir-api ir-cond; then
+    stop_ironic
+    cleanup_ironic
+fi
+
 # Apache has the WSGI processes
 if is_service_enabled horizon; then
     stop_horizon