Merge "install euca2ools for tempest preparation"
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..082aff2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,104 @@
+# DevStack Makefile of Sanity
+
+# Interesting targets:
+# ds-remote - Create a Git remote for use by ds-push and ds-pull targets
+#             DS_REMOTE_URL must be set on the command line
+#
+# ds-push - Merge a list of branches taken from .ds-test and push them
+#           to the ds-remote repo in ds-test branch
+#
+# ds-pull - Pull the remote ds-test branch into a fresh local branch
+#
+# refresh - Performs a sequence of unstack, refresh and stack
+
+# Duplicated from stackrc for now
+DEST=/opt/stack
+WHEELHOUSE=$(DEST)/.wheelhouse
+
+all:
+	echo "This just saved you from a terrible mistake!"
+
+# Do Some Work
+stack:
+	./stack.sh
+
+unstack:
+	./unstack.sh
+
+wheels:
+	WHEELHOUSE=$(WHEELHOUSE) tools/build-wheels.sh
+
+docs:
+	tox -edocs
+
+# Just run the shocco source formatting build
+docs-build:
+	INSTALL_SHOCCO=True tools/build_docs.sh
+
+# Just run the Sphinx docs build
+docs-rst:
+	python setup.py build_sphinx
+
+# Run the bashate test
+bashate:
+	tox -ebashate
+
+# Run the function tests
+test:
+	tests/test_ini_config.sh
+	tests/test_meta_config.sh
+	tests/test_ip.sh
+	tests/test_refs.sh
+
+# Spiff up the place a bit
+clean:
+	./clean.sh
+	rm -rf accrc doc/build test*-e *.egg-info
+
+# Clean out the cache too
+realclean: clean
+	rm -rf files/cirros*.tar.gz files/Fedora*.qcow2 $(WHEELHOUSE)
+
+# Repo stuffs
+
+pull:
+	git pull
+
+
+# These repo targets are used to maintain a branch in a remote repo that
+# consists of one or more local branches merged and pushed to the remote.
+# This is most useful for iterative testing on multiple or remote servers
+# while keeping the working repo local.
+#
+# It requires:
+# * a remote pointing to a remote repo, often GitHub is used for this
+# * a branch name to be used on the remote
+# * a local file containing the list of local branches to be merged into
+#   the remote branch
+
+GIT_REMOTE_NAME=ds-test
+GIT_REMOTE_BRANCH=ds-test
+
+# Push the current branch to a remote named ds-test
+ds-push:
+	git checkout master
+	git branch -D $(GIT_REMOTE_BRANCH) || true
+	git checkout -b $(GIT_REMOTE_BRANCH)
+	for i in $(shell cat .$(GIT_REMOTE_BRANCH) | grep -v "^#" | grep "[^ ]"); do \
+	  git merge --no-edit $$i; \
+	done
+	git push -f $(GIT_REMOTE_NAME) HEAD:$(GIT_REMOTE_BRANCH)
+
+# Pull the ds-test branch
+ds-pull:
+	git checkout master
+	git branch -D $(GIT_REMOTE_BRANCH) || true
+	git pull $(GIT_REMOTE_NAME) $(GIT_REMOTE_BRANCH)
+	git checkout $(GIT_REMOTE_BRANCH)
+
+# Add the remote - set DS_REMOTE_URL=htps://example.com/ on the command line
+ds-remote:
+	git remote add $(GIT_REMOTE_NAME) $(DS_REMOTE_URL)
+
+# Refresh the current DevStack checkout nd re-initialize
+refresh: unstack ds-pull stack
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index a0d0840..1cc7083 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -95,6 +95,8 @@
 fragment and MUST conform to the shell requirements, specifically no
 whitespace around ``=`` (equals).
 
+.. _minimal-configuration:
+
 Minimal Configuration
 =====================
 
diff --git a/doc/source/index.rst b/doc/source/index.rst
index c31287c..4435b49 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -41,8 +41,7 @@
 
 #. Configure
 
-   We recommend at least a :doc:`minimal
-   configuration <configuration>` be set up.
+   We recommend at least a :ref:`minimal-configuration` be set up.
 
 #. Start the install
 
diff --git a/files/debs/swift b/files/debs/swift
index b32b439..0089d27 100644
--- a/files/debs/swift
+++ b/files/debs/swift
@@ -1,4 +1,5 @@
 curl
+make
 memcached
 # NOTE python-nose only exists because of swift functional job, we should probably
 # figure out a more consistent way of installing this from test-requirements.txt instead
diff --git a/functions b/functions
index 5bc8456..4dc20e7 100644
--- a/functions
+++ b/functions
@@ -15,6 +15,7 @@
 source ${FUNC_DIR}/functions-common
 source ${FUNC_DIR}/inc/ini-config
 source ${FUNC_DIR}/inc/python
+source ${FUNC_DIR}/inc/rootwrap
 
 # Save trace setting
 XTRACE=$(set +o | grep xtrace)
diff --git a/functions-common b/functions-common
index 21c74c6..f442211 100644
--- a/functions-common
+++ b/functions-common
@@ -768,6 +768,27 @@
     echo $user_role_id
 }
 
+# Gets or adds group role to project
+# Usage: get_or_add_group_project_role <role> <group> <project>
+function get_or_add_group_project_role {
+    # Gets group role id
+    local group_role_id=$(openstack role list \
+        --group $2 \
+        --project $3 \
+        --column "ID" \
+        --column "Name" \
+        | grep " $1 " | get_field 1)
+    if [[ -z "$group_role_id" ]]; then
+        # Adds role to group
+        group_role_id=$(openstack role add \
+            $1 \
+            --group $2 \
+            --project $3 \
+            | grep " id " | get_field 2)
+    fi
+    echo $group_role_id
+}
+
 # Gets or creates service
 # Usage: get_or_create_service <name> <type> <description>
 function get_or_create_service {
diff --git a/inc/rootwrap b/inc/rootwrap
new file mode 100644
index 0000000..bac8e1e
--- /dev/null
+++ b/inc/rootwrap
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# **inc/rootwrap** - Rootwrap functions
+#
+# Handle rootwrap's foibles
+
+# Uses: ``STACK_USER``
+# Defines: ``SUDO_SECURE_PATH_FILE``
+
+# Save trace setting
+INC_ROOT_TRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+# Accumulate all additions to sudo's ``secure_path`` in one file read last
+# so they all work in a venv configuration
+SUDO_SECURE_PATH_FILE=${SUDO_SECURE_PATH_FILE:-/etc/sudoers.d/zz-secure-path}
+
+# Add a directory to the common sudo ``secure_path``
+# add_sudo_secure_path dir
+function add_sudo_secure_path {
+    local dir=$1
+    local line
+
+    # This is pretty simplistic for now - assume only the first line is used
+    if [[ -r SUDO_SECURE_PATH_FILE ]]; then
+        line=$(head -1 $SUDO_SECURE_PATH_FILE)
+    else
+        line="Defaults:$STACK_USER secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/usr/bin:/bin"
+    fi
+
+    # Only add ``dir`` if it is not already present
+    if [[ $line =~ $dir ]]; then
+        echo "${line}:$dir" | sudo tee $SUDO_SECURE_PATH_FILE
+        sudo chmod 400 $SUDO_SECURE_PATH_FILE
+        sudo chown root:root $SUDO_SECURE_PATH_FILE
+    fi
+}
+
+# Configure rootwrap
+# Make a load of assumptions otherwise we'll have 6 arguments
+# configure_rootwrap project bin conf-src-dir
+function configure_rootwrap {
+    local project=$1                    # xx
+    local rootwrap_bin=$2               # /opt/stack/xx.venv/bin/xx-rootwrap
+    local rootwrap_conf_src_dir=$3      # /opt/stack/xx/etc/xx
+
+    # Start fresh with rootwrap filters
+    sudo rm -rf /etc/${project}/rootwrap.d
+    sudo install -d -o root -g root -m 755 /etc/${project}/rootwrap.d
+    sudo install -o root -g root -m 644 $rootwrap_conf_src_dir/rootwrap.d/*.filters /etc/${project}/rootwrap.d
+
+    # Set up rootwrap.conf, pointing to /etc/*/rootwrap.d
+    sudo install -o root -g root -m 644 $rootwrap_conf_src_dir/rootwrap.conf /etc/${project}/rootwrap.conf
+    sudo sed -e "s:^filters_path=.*$:filters_path=/etc/${project}/rootwrap.d:" -i /etc/${project}/rootwrap.conf
+
+    # Specify rootwrap.conf as first parameter to rootwrap
+    rootwrap_sudo_cmd="$rootwrap_bin /etc/${project}/rootwrap.conf *"
+
+    # Set up the rootwrap sudoers
+    local tempfile=$(mktemp)
+    echo "$STACK_USER ALL=(root) NOPASSWD: $rootwrap_sudo_cmd" >$tempfile
+    chmod 0440 $tempfile
+    sudo chown root:root $tempfile
+    sudo mv $tempfile /etc/sudoers.d/${project}-rootwrap
+
+    # Add bin dir to sudo's secure_path because rootwrap is being called
+    # without a path because BROKEN.
+    add_sudo_secure_path $(dirname $rootwrap_bin)
+}
+
+
+# Restore xtrace
+$INC_ROOT_TRACE
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/lib/cinder b/lib/cinder
index 27fd692..de41bc5 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -171,42 +171,6 @@
     fi
 }
 
-# Deploy new rootwrap filters files and configure sudo
-# configure_cinder_rootwrap() - configure Cinder's rootwrap
-function configure_cinder_rootwrap {
-    local cinder_rootwrap=$CINDER_BIN_DIR/cinder-rootwrap
-
-    # Wipe any existing rootwrap.d files first
-    if [[ -d $CINDER_CONF_DIR/rootwrap.d ]]; then
-        sudo rm -rf $CINDER_CONF_DIR/rootwrap.d
-    fi
-
-    # Deploy filters to /etc/cinder/rootwrap.d
-    sudo install -d -o root -g root -m 755 $CINDER_CONF_DIR/rootwrap.d
-    sudo install -o root -g root -m 644 $CINDER_DIR/etc/cinder/rootwrap.d/*.filters $CINDER_CONF_DIR/rootwrap.d
-
-    # Set up rootwrap.conf, pointing to /etc/cinder/rootwrap.d
-    sudo install -o root -g root -m 644 $CINDER_DIR/etc/cinder/rootwrap.conf $CINDER_CONF_DIR
-    sudo sed -e "s:^filters_path=.*$:filters_path=$CINDER_CONF_DIR/rootwrap.d:" -i $CINDER_CONF_DIR/rootwrap.conf
-
-    # Specify rootwrap.conf as first parameter to rootwrap
-    ROOTWRAP_CSUDOER_CMD="$cinder_rootwrap $CINDER_CONF_DIR/rootwrap.conf *"
-
-    # Set up the rootwrap sudoers for cinder
-    local tempfile=`mktemp`
-    echo "Defaults:$STACK_USER secure_path=$CINDER_BIN_DIR:/sbin:/usr/sbin:/usr/bin:/bin:/usr/local/sbin:/usr/local/bin" >$tempfile
-    echo "$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_CSUDOER_CMD" >>$tempfile
-    chmod 0440 $tempfile
-    sudo chown root:root $tempfile
-    sudo mv $tempfile /etc/sudoers.d/cinder-rootwrap
-
-    # So rootwrap and PATH are broken beyond belief.  WTF relies on a SECURE operation
-    # to blindly follow PATH???  We learned that was a bad idea in the 80's!
-    # So to fix this in a venv, we must exploit the very hole we want to close by dropping
-    # a copy of the venv rootwrap binary into /usr/local/bin.
-    #sudo cp -p $cinder_rootwrap /usr/local/bin
-}
-
 # configure_cinder() - Set config files, create data dirs, etc
 function configure_cinder {
     sudo install -d -o $STACK_USER -m 755 $CINDER_CONF_DIR
@@ -215,7 +179,7 @@
 
     rm -f $CINDER_CONF
 
-    configure_cinder_rootwrap
+    configure_rootwrap cinder $CINDER_BIN_DIR/cinder-rootwrap $CINDER_DIR/etc/cinder
 
     cp $CINDER_DIR/etc/cinder/api-paste.ini $CINDER_API_PASTE_INI
 
diff --git a/lib/glance b/lib/glance
old mode 100755
new mode 100644
diff --git a/lib/heat b/lib/heat
index 0930776..5cb0dbf 100644
--- a/lib/heat
+++ b/lib/heat
@@ -335,6 +335,7 @@
     " -i $heat_pip_repo_apache_conf
     enable_apache_site heat_pip_repo
     restart_apache_server
+    sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $HEAT_PIP_REPO_PORT -j ACCEPT || true
 }
 
 # Restore xtrace
diff --git a/lib/ironic b/lib/ironic
index fcf1a54..c8481ab 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -370,6 +370,7 @@
         iniset $IRONIC_CONF_FILE glance swift_container glance
         iniset $IRONIC_CONF_FILE glance swift_temp_url_duration 3600
         iniset $IRONIC_CONF_FILE agent heartbeat_timeout 30
+        iniset $IRONIC_CONF_FILE agent agent_erase_devices_priority 0
     fi
 
     if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
diff --git a/lib/keystone b/lib/keystone
index 1e39ab6..7b41812 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -366,6 +366,12 @@
 # demo                 demo       Member, anotherrole
 # invisible_to_admin   demo       Member
 
+# Group                Users      Roles                 Tenant
+# ------------------------------------------------------------------
+# admins               admin      admin                 admin
+# nonadmin             demo       Member, anotherrole   demo
+
+
 # Migrated from keystone_data.sh
 function create_keystone_accounts {
 
@@ -407,8 +413,14 @@
     get_or_add_user_project_role $another_role $demo_user $demo_tenant
     get_or_add_user_project_role $member_role $demo_user $invis_tenant
 
-    get_or_create_group "developers" "default" "openstack developers"
-    get_or_create_group "testers" "default"
+    local admin_group=$(get_or_create_group "admins" \
+        "default" "openstack admin group")
+    local non_admin_group=$(get_or_create_group "nonadmins" \
+        "default" "non-admin group")
+
+    get_or_add_group_project_role $member_role $non_admin_group $demo_tenant
+    get_or_add_group_project_role $another_role $non_admin_group $demo_tenant
+    get_or_add_group_project_role $admin_role $admin_group $admin_tenant
 
     # Keystone
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
diff --git a/lib/neutron-legacy b/lib/neutron-legacy
old mode 100755
new mode 100644
index d3dd8dd..c6d9296
--- a/lib/neutron-legacy
+++ b/lib/neutron-legacy
@@ -801,7 +801,7 @@
         fi
 
         if [[ "$add_ovs_port" == "True" ]]; then
-            ADD_OVS_PORT="sudo ovs-vsctl add-port $to_intf $from_intf"
+            ADD_OVS_PORT="sudo ovs-vsctl --may-exist add-port $to_intf $from_intf"
         fi
 
         sudo ip addr del $IP_BRD dev $from_intf; sudo ip addr add $IP_BRD dev $to_intf; $ADD_OVS_PORT; $ADD_DEFAULT_ROUTE
diff --git a/lib/nova b/lib/nova
index 385da4e..807dfce 100644
--- a/lib/nova
+++ b/lib/nova
@@ -223,42 +223,6 @@
     #fi
 }
 
-# Deploy new rootwrap filters files and configure sudo
-# configure_nova_rootwrap() - configure Nova's rootwrap
-function configure_nova_rootwrap {
-    nova_rootwrap=$NOVA_BIN_DIR/nova-rootwrap
-
-    # Wipe any existing rootwrap.d files first
-    if [[ -d $NOVA_CONF_DIR/rootwrap.d ]]; then
-        sudo rm -rf $NOVA_CONF_DIR/rootwrap.d
-    fi
-
-    # Deploy filters to /etc/nova/rootwrap.d
-    sudo install -d -o root -g root -m 755 $NOVA_CONF_DIR/rootwrap.d
-    sudo install -o root -g root -m 644  $NOVA_DIR/etc/nova/rootwrap.d/*.filters $NOVA_CONF_DIR/rootwrap.d
-
-    # Set up rootwrap.conf, pointing to /etc/nova/rootwrap.d
-    sudo install -o root -g root -m 644 $NOVA_DIR/etc/nova/rootwrap.conf $NOVA_CONF_DIR
-    sudo sed -e "s:^filters_path=.*$:filters_path=$NOVA_CONF_DIR/rootwrap.d:" -i $NOVA_CONF_DIR/rootwrap.conf
-
-    # Specify rootwrap.conf as first parameter to nova-rootwrap
-    local rootwrap_sudoer_cmd="$nova_rootwrap $NOVA_CONF_DIR/rootwrap.conf *"
-
-    # Set up the rootwrap sudoers for nova
-    local tempfile=`mktemp`
-    echo "Defaults:$STACK_USER secure_path=$NOVA_BIN_DIR:/sbin:/usr/sbin:/usr/bin:/bin:/usr/local/sbin:/usr/local/bin" >$tempfile
-    echo "$STACK_USER ALL=(root) NOPASSWD: $rootwrap_sudoer_cmd" >>$tempfile
-    chmod 0440 $tempfile
-    sudo chown root:root $tempfile
-    sudo mv $tempfile /etc/sudoers.d/nova-rootwrap
-
-    # So rootwrap and PATH are broken beyond belief.  WTF relies on a SECURE operation
-    # to blindly follow PATH???  We learned that was a bad idea in the 80's!
-    # So to fix this in a venv, we must exploit the very hole we want to close by dropping
-    # a copy of the venv rootwrap binary into /usr/local/bin.
-    #sudo cp -p $nova_rootwrap /usr/local/bin
-}
-
 # configure_nova() - Set config files, create data dirs, etc
 function configure_nova {
     # Put config files in ``/etc/nova`` for everyone to find
@@ -266,7 +230,7 @@
 
     install_default_policy nova
 
-    configure_nova_rootwrap
+    configure_rootwrap nova $NOVA_BIN_DIR/nova-rootwrap $NOVA_DIR/etc/nova
 
     if [[ "$ENABLED_SERVICES" =~ "n-api" ]]; then
         # Get the sample configuration file in place
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index 05ef605..fa3666e 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -14,8 +14,11 @@
 # Defaults
 # --------
 
-# if we should turn on massive libvirt debugging
-DEBUG_LIBVIRT=$(trueorfalse False DEBUG_LIBVIRT)
+# Turn on selective debug log filters for libvirt.
+# (NOTE: Enabling this by default, because the log filters enabled in
+# 'configure_libvirt' function further below are _selective_ and not
+# extremely verbose.)
+DEBUG_LIBVIRT=$(trueorfalse True DEBUG_LIBVIRT)
 
 # Installs required distro-specific libvirt packages.
 function install_libvirt {
@@ -112,10 +115,18 @@
         fi
     fi
 
+    # Update the libvirt cpu map with a gate64 cpu model. This enables nova
+    # live migration for 64bit guest OSes on heterogenous cloud "hardware".
+    if [[ -f /usr/share/libvirt/cpu_map.xml ]] ; then
+        sudo $TOP_DIR/tools/cpu_map_update.py /usr/share/libvirt/cpu_map.xml
+    fi
+
     # libvirt detects various settings on startup, as we potentially changed
     # the system configuration (modules, filesystems), we need to restart
-    # libvirt to detect those changes.
-    restart_service $LIBVIRT_DAEMON
+    # libvirt to detect those changes. Use a stop start as otherwise the new
+    # cpu_map is not loaded properly on some systems (Ubuntu).
+    stop_service $LIBVIRT_DAEMON
+    start_service $LIBVIRT_DAEMON
 }
 
 
diff --git a/lib/tempest b/lib/tempest
index d8692dc..e8e9e0b 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -271,6 +271,15 @@
         fi
     fi
 
+    EC2_URL=$(openstack endpoint show -f value -c publicurl ec2 || true)
+    if [[ -z $EC2_URL ]]; then
+        EC2_URL="$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/"
+    fi
+    S3_URL=$(openstack endpoint show -f value -c publicurl s3 || true)
+    if [[ -z $S3_URL ]]; then
+        S3_URL="http://$SERVICE_HOST:${S3_SERVICE_PORT:-3333}"
+    fi
+
     iniset $TEMPEST_CONFIG DEFAULT use_syslog $SYSLOG
 
     # Oslo
@@ -375,8 +384,8 @@
     iniset $TEMPEST_CONFIG network-feature-enabled api_extensions $network_api_extensions
 
     # boto
-    iniset $TEMPEST_CONFIG boto ec2_url "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/"
-    iniset $TEMPEST_CONFIG boto s3_url "http://$SERVICE_HOST:${S3_SERVICE_PORT:-3333}"
+    iniset $TEMPEST_CONFIG boto ec2_url "$EC2_URL"
+    iniset $TEMPEST_CONFIG boto s3_url "$S3_URL"
     iniset $TEMPEST_CONFIG boto s3_materials_path "$BOTO_MATERIALS_PATH"
     iniset $TEMPEST_CONFIG boto ari_manifest cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-initrd.manifest.xml
     iniset $TEMPEST_CONFIG boto ami_manifest cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-blank.img.manifest.xml
@@ -401,10 +410,12 @@
     fi
 
     # Scenario
-    iniset $TEMPEST_CONFIG scenario img_dir "$FILES/images/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec"
+    SCENARIO_IMAGE_DIR=${SCENARIO_IMAGE_DIR:-$FILES/images/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec}
+    iniset $TEMPEST_CONFIG scenario img_dir $SCENARIO_IMAGE_DIR
     iniset $TEMPEST_CONFIG scenario ami_img_file "cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-blank.img"
     iniset $TEMPEST_CONFIG scenario ari_img_file "cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-initrd"
     iniset $TEMPEST_CONFIG scenario aki_img_file "cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-vmlinuz"
+    iniset $TEMPEST_CONFIG scenario img_file "cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-disk.img"
 
     # Large Ops Number
     iniset $TEMPEST_CONFIG scenario large_ops_number ${TEMPEST_LARGE_OPS_NUMBER:-0}
@@ -469,6 +480,7 @@
     # Baremetal
     if [ "$VIRT_DRIVER" = "ironic" ] ; then
         iniset $TEMPEST_CONFIG baremetal driver_enabled True
+        iniset $TEMPEST_CONFIG baremetal unprovision_timeout 300
         iniset $TEMPEST_CONFIG compute-feature-enabled change_password False
         iniset $TEMPEST_CONFIG compute-feature-enabled console_output False
         iniset $TEMPEST_CONFIG compute-feature-enabled interface_attach False
diff --git a/stack.sh b/stack.sh
index 8ab8234..adcaa21 100755
--- a/stack.sh
+++ b/stack.sh
@@ -1214,6 +1214,9 @@
 elif is_service_enabled $DATABASE_BACKENDS && is_service_enabled n-net; then
     NM_CONF=${NOVA_CONF}
     if is_service_enabled n-cell; then
+        # Create a small network in the API cell
+        $NOVA_BIN_DIR/nova-manage --config-file $NM_CONF network create "$PRIVATE_NETWORK_NAME" $FIXED_RANGE 1 $FIXED_NETWORK_SIZE $NETWORK_CREATE_ARGS
+        # Everything else should go in the child cell
         NM_CONF=${NOVA_CELLS_CONF}
     fi
 
diff --git a/stackrc b/stackrc
index c27ead3..abedb00 100644
--- a/stackrc
+++ b/stackrc
@@ -46,16 +46,18 @@
 
 # This allows us to pass ``ENABLED_SERVICES``
 if ! isset ENABLED_SERVICES ; then
-    # Compute (Glance / Keystone / Nova (+ nova-network))
-    ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,n-sch,n-novnc,n-xvnc,n-cauth
+    # Keystone - nothing works without keystone
+    ENABLED_SERVICES=key
+    # Nova - services to support libvirt based openstack clouds
+    ENABLED_SERVICES+=,n-api,n-cpu,n-net,n-cond,n-sch,n-novnc
+    # Glance services needed for Nova
+    ENABLED_SERVICES+=,g-api,g-reg
     # Cinder
     ENABLED_SERVICES+=,c-sch,c-api,c-vol
-    # Heat
-    ENABLED_SERVICES+=,h-eng,h-api,h-api-cfn,h-api-cw
     # Dashboard
     ENABLED_SERVICES+=,horizon
     # Additional services
-    ENABLED_SERVICES+=,rabbit,tempest,mysql
+    ENABLED_SERVICES+=,rabbit,tempest,mysql,dstat
 fi
 
 # SQLAlchemy supports multiple database drivers for each database server
diff --git a/tools/cpu_map_update.py b/tools/cpu_map_update.py
new file mode 100755
index 0000000..1938793
--- /dev/null
+++ b/tools/cpu_map_update.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# This small script updates the libvirt CPU map to add a gate64 cpu model
+# that can be used to enable a common 64bit capable feature set across
+# devstack nodes so that features like nova live migration work.
+
+import sys
+import xml.etree.ElementTree as ET
+from xml.dom import minidom
+
+
+def update_cpu_map(tree):
+    root = tree.getroot()
+    cpus = root#.find("cpus")
+    x86 = None
+    for arch in cpus.findall("arch"):
+        if arch.get("name") == "x86":
+            x86 = arch
+            break
+    if x86 is not None:
+        # Create a gate64 cpu model that is core2duo less monitor and pse36
+        gate64 = ET.SubElement(x86, "model")
+        gate64.set("name", "gate64")
+        ET.SubElement(gate64, "vendor").set("name", "Intel")
+        ET.SubElement(gate64, "feature").set("name", "fpu")
+        ET.SubElement(gate64, "feature").set("name", "de")
+        ET.SubElement(gate64, "feature").set("name", "pse")
+        ET.SubElement(gate64, "feature").set("name", "tsc")
+        ET.SubElement(gate64, "feature").set("name", "msr")
+        ET.SubElement(gate64, "feature").set("name", "pae")
+        ET.SubElement(gate64, "feature").set("name", "mce")
+        ET.SubElement(gate64, "feature").set("name", "cx8")
+        ET.SubElement(gate64, "feature").set("name", "apic")
+        ET.SubElement(gate64, "feature").set("name", "sep")
+        ET.SubElement(gate64, "feature").set("name", "pge")
+        ET.SubElement(gate64, "feature").set("name", "cmov")
+        ET.SubElement(gate64, "feature").set("name", "pat")
+        ET.SubElement(gate64, "feature").set("name", "mmx")
+        ET.SubElement(gate64, "feature").set("name", "fxsr")
+        ET.SubElement(gate64, "feature").set("name", "sse")
+        ET.SubElement(gate64, "feature").set("name", "sse2")
+        ET.SubElement(gate64, "feature").set("name", "vme")
+        ET.SubElement(gate64, "feature").set("name", "mtrr")
+        ET.SubElement(gate64, "feature").set("name", "mca")
+        ET.SubElement(gate64, "feature").set("name", "clflush")
+        ET.SubElement(gate64, "feature").set("name", "pni")
+        ET.SubElement(gate64, "feature").set("name", "nx")
+        ET.SubElement(gate64, "feature").set("name", "ssse3")
+        ET.SubElement(gate64, "feature").set("name", "syscall")
+        ET.SubElement(gate64, "feature").set("name", "lm")
+
+
+def format_xml(root):
+    # Adapted from http://pymotw.com/2/xml/etree/ElementTree/create.html
+    # thank you dhellmann
+    rough_string = ET.tostring(root, encoding="UTF-8")
+    dom_parsed = minidom.parseString(rough_string)
+    return dom_parsed.toprettyxml("  ", encoding="UTF-8")
+
+
+def main():
+    if len(sys.argv) != 2:
+        raise Exception("Must pass path to cpu_map.xml to update")
+    cpu_map = sys.argv[1]
+    tree = ET.parse(cpu_map)
+    for model in tree.getroot().iter("model"):
+        if model.get("name") == "gate64":
+            # gate64 model is already present
+            return
+    update_cpu_map(tree)
+    pretty_xml = format_xml(tree.getroot())
+    with open(cpu_map, 'w') as f:
+        f.write(pretty_xml)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tox.ini b/tox.ini
index bc84928..279dcd4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -20,7 +20,7 @@
           -name \*.sh -or                     \
           -name \*rc -or                      \
           -name functions\* -or               \
-          -wholename \*/inc/\*                \ # /inc files and
+          -wholename \*/inc/\* -or            \ # /inc files and
           -wholename \*/lib/\*                \ # /lib files are shell, but
          \)                                   \ #   have no extension
          -print0 | xargs -0 bashate -v"