Merge "Teach pip_install() about virtual envs"
diff --git a/README.md b/README.md
index 7eacebd..40060a7 100644
--- a/README.md
+++ b/README.md
@@ -215,21 +215,6 @@
     [[post-config|/$Q_PLUGIN_CONF_FILE]]
     [linuxbridge]   # or [ovs]
 
-* ``Q_AGENT_EXTRA_AGENT_OPTS``:
-
-    [[post-config|/$Q_PLUGIN_CONF_FILE]]
-    [agent]
-
-* ``Q_AGENT_EXTRA_SRV_OPTS``:
-
-    [[post-config|/$Q_PLUGIN_CONF_FILE]]
-    [linuxbridge]   # or [ovs]
-
-* ``Q_SRV_EXTRA_DEFAULT_OPTS``:
-
-    [[post-config|$NEUTRON_CONF]]
-    [DEFAULT]
-
 Example extra config in `local.conf`:
 
     [[post-config|/$Q_PLUGIN_CONF_FILE]]
diff --git a/clean.sh b/clean.sh
index edbd04a..50d414c 100755
--- a/clean.sh
+++ b/clean.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/bash
 
 # **clean.sh**
 
@@ -83,7 +83,10 @@
 fi
 
 # Clean projects
-cleanup_cinder
+
+# BUG: cinder tgt doesn't exit cleanly if it's not running.
+cleanup_cinder || /bin/true
+
 cleanup_glance
 cleanup_keystone
 cleanup_nova
diff --git a/doc/source/guides/devstack-with-nested-kvm.rst b/doc/source/guides/devstack-with-nested-kvm.rst
index 2538c8d..58ec3d3 100644
--- a/doc/source/guides/devstack-with-nested-kvm.rst
+++ b/doc/source/guides/devstack-with-nested-kvm.rst
@@ -19,7 +19,7 @@
 Configure Nested KVM for Intel-based Machines
 ---------------------------------------------
 
-Procedure to enable nested KVM virtualization on AMD-based machines.
+Procedure to enable nested KVM virtualization on Intel-based machines.
 
 Check if the nested KVM Kernel parameter is enabled:
 
diff --git a/files/default_catalog.templates b/files/default_catalog.templates
index a18d38f..4aab416 100644
--- a/files/default_catalog.templates
+++ b/files/default_catalog.templates
@@ -30,9 +30,9 @@
 catalog.RegionOne.volumev2.name = Volume Service V2
 
 
-catalog.RegionOne.ec2.publicURL = http://%SERVICE_HOST%:8773/services/Cloud
-catalog.RegionOne.ec2.adminURL = http://%SERVICE_HOST%:8773/services/Admin
-catalog.RegionOne.ec2.internalURL = http://%SERVICE_HOST%:8773/services/Cloud
+catalog.RegionOne.ec2.publicURL = http://%SERVICE_HOST%:8773/
+catalog.RegionOne.ec2.adminURL = http://%SERVICE_HOST%:8773/
+catalog.RegionOne.ec2.internalURL = http://%SERVICE_HOST%:8773/
 catalog.RegionOne.ec2.name = EC2 Service
 
 
diff --git a/gate/updown.sh b/gate/updown.sh
new file mode 100755
index 0000000..d2d7351
--- /dev/null
+++ b/gate/updown.sh
@@ -0,0 +1,24 @@
+#!/bin/bash -xe
+#
+# An up / down test for gate functional testing
+#
+# Note: this is expected to start running as jenkins
+
+# Step 1: give back sudoers permissions to devstack
+TEMPFILE=`mktemp`
+echo "stack ALL=(root) NOPASSWD:ALL" >$TEMPFILE
+chmod 0440 $TEMPFILE
+sudo chown root:root $TEMPFILE
+sudo mv $TEMPFILE /etc/sudoers.d/51_stack_sh
+
+# TODO: do something to start a guest to create crud that should
+# disappear
+
+# Step 2: unstack
+echo "Running unstack.sh"
+sudo -H -u stack stdbuf -oL -eL bash -ex ./unstack.sh
+
+# Step 3: clean
+echo "Running clean.sh"
+sudo -H -u stack stdbuf -oL -eL bash -ex ./clean.sh
+
diff --git a/inc/python b/inc/python
index 4a107ac..d9451b4 100644
--- a/inc/python
+++ b/inc/python
@@ -108,6 +108,17 @@
     $xtrace
 }
 
+# get version of a package from global requirements file
+# get_from_global_requirements <package>
+function get_from_global_requirements {
+    local package=$1
+    local required_pkg=$(grep -h ${package} $REQUIREMENTS_DIR/global-requirements.txt | cut -d\# -f1)
+    if [[ $required_pkg == ""  ]]; then
+        die $LINENO "Can't find package $package in requirements"
+    fi
+    echo $required_pkg
+}
+
 # should we use this library from their git repo, or should we let it
 # get pulled in via pip dependencies.
 function use_library_from_git {
diff --git a/lib/ceilometer b/lib/ceilometer
index 698e8b0..f509788 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -57,6 +57,7 @@
 
 # Set up default directories
 GITDIR["python-ceilometerclient"]=$DEST/python-ceilometerclient
+GITDIR["ceilometermiddleware"]=$DEST/ceilometermiddleware
 
 CEILOMETER_DIR=$DEST/ceilometer
 CEILOMETER_CONF_DIR=/etc/ceilometer
@@ -108,7 +109,7 @@
     # Ceilometer
     if [[ "$ENABLED_SERVICES" =~ "ceilometer-api" ]]; then
 
-        create_service_user "ceilometer"
+        create_service_user "ceilometer" "admin"
 
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
             local ceilometer_service=$(get_or_create_service "ceilometer" \
@@ -303,6 +304,14 @@
     fi
 }
 
+# install_ceilometermiddleware() - Collect source and prepare
+function install_ceilometermiddleware {
+    if use_library_from_git "ceilometermiddleware"; then
+        git_clone_by_name "ceilometermiddleware"
+        setup_dev_lib "ceilometermiddleware"
+    fi
+}
+
 # start_ceilometer() - Start running processes, including screen
 function start_ceilometer {
     run_process ceilometer-acentral "ceilometer-agent-central --config-file $CEILOMETER_CONF"
diff --git a/lib/cinder b/lib/cinder
index 17a0cc3..0d157dd 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -65,21 +65,14 @@
 fi
 
 
-# Maintain this here for backward-compatibility with the old configuration
-# DEPRECATED: Use CINDER_ENABLED_BACKENDS instead
-# Support for multi lvm backend configuration (default is no support)
-CINDER_MULTI_LVM_BACKEND=$(trueorfalse False CINDER_MULTI_LVM_BACKEND)
-
 # Default backends
 # The backend format is type:name where type is one of the supported backend
 # types (lvm, nfs, etc) and name is the identifier used in the Cinder
 # configuration and for the volume type name.  Multiple backends are
 # comma-separated.
-if [[ $CINDER_MULTI_LVM_BACKEND == "False" ]]; then
-    CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:${DEFAULT_VOLUME_GROUP_NAME##*-}}
-else
-    CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:${DEFAULT_VOLUME_GROUP_NAME##*-},lvm:cinder}
-fi
+# The old ``CINDER_MULTI_LVM_BACKEND=True`` setting had a default of:
+# CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:lvmdriver-1,lvm:lvmdriver-2}
+CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:lvmdriver-1}
 
 
 # Should cinder perform secure deletion of volumes?
diff --git a/lib/cinder_backends/lvm b/lib/cinder_backends/lvm
index 4b9d8dc..d83c31a 100644
--- a/lib/cinder_backends/lvm
+++ b/lib/cinder_backends/lvm
@@ -77,6 +77,7 @@
     local line
 
     for pv_info in $(sudo pvs --noheadings -o name,vg_name --separator ';'); do
+        echo_summary "Evaluate PV info for Cinder lvm.conf: $pv_info"
         IFS=';' read pv vg <<< $pv_info
         for line in ${conf_entries}; do
             IFS='=' read label group <<< $line
diff --git a/lib/databases/mysql b/lib/databases/mysql
index 72c0f82..c8ceec2 100644
--- a/lib/databases/mysql
+++ b/lib/databases/mysql
@@ -28,17 +28,14 @@
     stop_service $MYSQL
     if is_ubuntu; then
         # Get ruthless with mysql
-        stop_service $MYSQL
         apt_get purge -y mysql* mariadb*
         sudo rm -rf /var/lib/mysql
         sudo rm -rf /etc/mysql
         return
     elif is_fedora; then
-        stop_service mariadb
         uninstall_package mariadb-server
         sudo rm -rf /var/lib/mysql
     elif is_suse; then
-        stop_service mysql
         uninstall_package mysql-community-server
         sudo rm -rf /var/lib/mysql
     else
diff --git a/lib/dstat b/lib/dstat
index 8165e5c..740e48f 100644
--- a/lib/dstat
+++ b/lib/dstat
@@ -40,7 +40,10 @@
 
 # stop_dstat() stop dstat process
 function stop_dstat {
-    screen_stop dstat
+    # dstat runs as a console, not as a service, and isn't trackable
+    # via the normal mechanisms for devstack. So lets just do a
+    # killall and move on.
+    killall dstat || /bin/true
 }
 
 # Restore xtrace
diff --git a/lib/glance b/lib/glance
index 5bd0b8c..eb1df2e 100755
--- a/lib/glance
+++ b/lib/glance
@@ -183,6 +183,12 @@
         iniset $GLANCE_API_CONF DEFAULT registry_client_protocol https
     fi
 
+    # Format logging
+    if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
+        setup_colorized_logging $GLANCE_API_CONF DEFAULT "project_id" "user_id"
+        setup_colorized_logging $GLANCE_REGISTRY_CONF DEFAULT "project_id" "user_id"
+    fi
+
     cp -p $GLANCE_DIR/etc/glance-registry-paste.ini $GLANCE_REGISTRY_PASTE_INI
 
     cp -p $GLANCE_DIR/etc/glance-api-paste.ini $GLANCE_API_PASTE_INI
diff --git a/lib/horizon b/lib/horizon
index 122d516..a8e83f9 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -182,8 +182,7 @@
 # NOTE: It can be moved to common functions, but it is only used by compilation
 # of django_openstack_auth catalogs at the moment.
 function _prepare_message_catalog_compilation {
-    local babel_package=$(grep ^Babel $REQUIREMENTS_DIR/global-requirements.txt)
-    pip_install "$babel_package"
+    pip_install $(get_from_global_requirements Babel)
 }
 
 
diff --git a/lib/ironic b/lib/ironic
index bed816e..7ffa6a5 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -362,7 +362,8 @@
     if [[ "$ENABLED_SERVICES" =~ "ir-api" ]]; then
         # Get ironic user if exists
 
-        create_service_user "ironic"
+        # NOTE(Shrews): This user MUST have admin level privileges!
+        create_service_user "ironic" "admin"
 
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
diff --git a/lib/lvm b/lib/lvm
index ed24487..c183f09 100644
--- a/lib/lvm
+++ b/lib/lvm
@@ -86,7 +86,7 @@
     local backing_file=$DATA_DIR/$vg$BACKING_FILE_SUFFIX
     if ! sudo vgs $vg; then
         # Only create if the file doesn't already exists
-        [[ -f $DATA_DIR/$backing_file ]] || truncate -s $size $backing_file
+        [[ -f $backing_file ]] || truncate -s $size $backing_file
         local vg_dev=`sudo losetup -f --show $backing_file`
 
         # Only create volume group if it doesn't already exist
diff --git a/lib/neutron b/lib/neutron
index 15a5f00..8d27feb 100755
--- a/lib/neutron
+++ b/lib/neutron
@@ -552,7 +552,7 @@
         sudo ip link set $PUBLIC_INTERFACE up
     else
         NET_ID=$(neutron net-create --tenant-id $TENANT_ID "$PRIVATE_NETWORK_NAME" | grep ' id ' | get_field 2)
-        die_if_not_set $LINENO NET_ID "Failure creating NET_ID for $PHYSICAL_NETWORK $TENANT_ID"
+        die_if_not_set $LINENO NET_ID "Failure creating NET_ID for $PRIVATE_NETWORK_NAME $TENANT_ID"
 
         if [[ "$IP_VERSION" =~ 4.* ]]; then
             # Create IPv4 private subnet
diff --git a/lib/neutron_plugins/linuxbridge_agent b/lib/neutron_plugins/linuxbridge_agent
index ec17c01..c9ea1ca 100644
--- a/lib/neutron_plugins/linuxbridge_agent
+++ b/lib/neutron_plugins/linuxbridge_agent
@@ -50,20 +50,6 @@
     fi
     AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-linuxbridge-agent"
     iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
-    # Define extra "AGENT" configuration options when q-agt is configured by defining
-    # the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
-    # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
-    for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE agent ${I/=/ }
-    done
-    # Define extra "LINUX_BRIDGE" configuration options when q-agt is configured by defining
-    # the array ``Q_AGENT_EXTRA_SRV_OPTS``.
-    # For Example: ``Q_AGENT_EXTRA_SRV_OPTS=(foo=true bar=2)``
-    for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE linux_bridge ${I/=/ }
-    done
 }
 
 function neutron_plugin_setup_interface_driver {
diff --git a/lib/neutron_plugins/ofagent_agent b/lib/neutron_plugins/ofagent_agent
index 915badc..0bc9bff 100644
--- a/lib/neutron_plugins/ofagent_agent
+++ b/lib/neutron_plugins/ofagent_agent
@@ -1,107 +1,4 @@
 #!/bin/bash
-#
-# OpenFlow Agent plugin
-# ----------------------
 
-# Save trace setting
-OFA_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-source $TOP_DIR/lib/neutron_plugins/ovs_base
-source $TOP_DIR/lib/neutron_thirdparty/ryu  # for RYU_DIR, install_ryu, etc
-
-function neutron_plugin_create_nova_conf {
-    _neutron_ovs_base_configure_nova_vif_driver
-}
-
-function neutron_plugin_install_agent_packages {
-    _neutron_ovs_base_install_agent_packages
-
-    # This agent uses ryu to talk with switches
-    install_package $(get_packages "ryu")
-    install_ryu
-}
-
-function neutron_plugin_configure_debug_command {
-    _neutron_ovs_base_configure_debug_command
-}
-
-function neutron_plugin_configure_dhcp_agent {
-    iniset $Q_DHCP_CONF_FILE DEFAULT dhcp_agent_manager neutron.agent.dhcp_agent.DhcpAgentWithStateReport
-}
-
-function neutron_plugin_configure_l3_agent {
-    _neutron_ovs_base_configure_l3_agent
-    iniset $Q_L3_CONF_FILE DEFAULT l3_agent_manager neutron.agent.l3_agent.L3NATAgentWithStateReport
-}
-
-function _neutron_ofagent_configure_firewall_driver {
-    if [[ "$Q_USE_SECGROUP" == "True" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
-    else
-        iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
-    fi
-}
-
-function neutron_plugin_configure_plugin_agent {
-    # Set up integration bridge
-    _neutron_ovs_base_setup_bridge $OVS_BRIDGE
-    _neutron_ofagent_configure_firewall_driver
-
-    # Check a supported openflow version
-    OF_VERSION=`ovs-ofctl --version | grep "OpenFlow versions" | awk '{print $3}' | cut -d':' -f2`
-    if [ `vercmp_numbers "$OF_VERSION" "0x3"` -lt "0" ]; then
-        die $LINENO "This agent requires OpenFlow 1.3+ capable switch."
-    fi
-
-    # Enable tunnel networks if selected
-    if [[ "$OVS_ENABLE_TUNNELING" == "True" ]]; then
-        # Verify tunnels are supported
-        # REVISIT - also check kernel module support for GRE and patch ports
-        OVS_VERSION=`ovs-vsctl --version | head -n 1 | grep -E -o "[0-9]+\.[0-9]+"`
-        if [ `vercmp_numbers "$OVS_VERSION" "1.4"` -lt "0" ]; then
-            die $LINENO "You are running OVS version $OVS_VERSION. OVS 1.4+ is required for tunneling between multiple hosts."
-        fi
-        iniset /$Q_PLUGIN_CONF_FILE ovs local_ip $TUNNEL_ENDPOINT_IP
-    fi
-
-    # Setup physical network bridge mappings.  Override
-    # ``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
-
-        # Configure bridge manually with physical interface as port for multi-node
-        sudo ovs-vsctl --no-wait -- --may-exist add-br $OVS_PHYSICAL_BRIDGE
-    fi
-    if [[ "$OVS_BRIDGE_MAPPINGS" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE ovs bridge_mappings $OVS_BRIDGE_MAPPINGS
-    fi
-    if [[ "$OFAGENT_PHYSICAL_INTERFACE_MAPPINGS" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE agent physical_interface_mappings \
-            $OFAGENT_PHYSICAL_INTERFACE_MAPPINGS
-    fi
-    AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-ofagent-agent"
-
-    iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
-    # Define extra "AGENT" configuration options when q-agt is configured by defining
-    # defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
-    # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
-    for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE agent ${I/=/ }
-    done
-}
-
-function neutron_plugin_setup_interface_driver {
-    local conf_file=$1
-    iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.OVSInterfaceDriver
-    iniset $conf_file DEFAULT ovs_use_veth True
-}
-
-function neutron_plugin_check_adv_test_requirements {
-    is_service_enabled q-agt && is_service_enabled q-dhcp && return 0
-}
-
-# Restore xtrace
-$OFA_XTRACE
+# REVISIT(yamamoto): This file is intentionally left empty
+# in order to keep Q_AGENT=ofagent_agent work.
diff --git a/lib/neutron_plugins/openvswitch_agent b/lib/neutron_plugins/openvswitch_agent
index 2ab61b0..1d24f3b 100644
--- a/lib/neutron_plugins/openvswitch_agent
+++ b/lib/neutron_plugins/openvswitch_agent
@@ -104,20 +104,6 @@
         iniset "/$Q_PLUGIN_CONF_FILE.domU" agent root_helper "$Q_RR_COMMAND"
     fi
     iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
-    # Define extra "AGENT" configuration options when q-agt is configured by defining
-    # defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
-    # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
-    for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE agent ${I/=/ }
-    done
-    # Define extra "OVS" configuration options when q-agt is configured by defining
-    # defining the array ``Q_AGENT_EXTRA_SRV_OPTS``.
-    # For Example: ``Q_AGENT_EXTRA_SRV_OPTS=(foo=true bar=2)``
-    for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        iniset /$Q_PLUGIN_CONF_FILE ovs ${I/=/ }
-    done
 }
 
 function neutron_plugin_setup_interface_driver {
diff --git a/lib/neutron_thirdparty/ryu b/lib/neutron_thirdparty/ryu
deleted file mode 100644
index 1f78a21..0000000
--- a/lib/neutron_thirdparty/ryu
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Ryu SDN Framework
-# -----------------
-
-# Used by ofagent.
-# TODO(yamamoto): Switch to pip_install once the development was settled
-
-# Save trace setting
-RYU3_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-RYU_DIR=$DEST/ryu
-
-# Make this function idempotent and avoid cloning same repo many times
-# with RECLONE=yes
-_RYU_INSTALLED=${_RYU_INSTALLED:-False}
-function install_ryu {
-    if [[ "$_RYU_INSTALLED" == "False" ]]; then
-        git_clone $RYU_REPO $RYU_DIR $RYU_BRANCH
-        export PYTHONPATH=$RYU_DIR:$PYTHONPATH
-        pip_install $(cat $RYU_DIR/tools/pip-requires)
-        _RYU_INSTALLED=True
-    fi
-}
-
-# Restore xtrace
-$RYU3_XTRACE
diff --git a/lib/nova b/lib/nova
index a5033f7..74a3411 100644
--- a/lib/nova
+++ b/lib/nova
@@ -395,9 +395,9 @@
                 "ec2" "EC2 Compatibility Layer")
             get_or_create_endpoint $ec2_service \
                 "$REGION_NAME" \
-                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Cloud" \
-                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Admin" \
-                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Cloud"
+                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/" \
+                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/" \
+                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/"
         fi
     fi
 
diff --git a/lib/rpc_backend b/lib/rpc_backend
index 30d300b..899748c 100644
--- a/lib/rpc_backend
+++ b/lib/rpc_backend
@@ -74,7 +74,8 @@
     if is_service_enabled rabbit; then
         # Obliterate rabbitmq-server
         uninstall_package rabbitmq-server
-        sudo killall epmd || sudo killall -9 epmd
+        # in case it's not actually running, /bin/true at the end
+        sudo killall epmd || sudo killall -9 epmd || /bin/true
         if is_ubuntu; then
             # And the Erlang runtime too
             apt_get purge -y erlang*
diff --git a/lib/sahara b/lib/sahara
index db200cc..da4fbcd 100644
--- a/lib/sahara
+++ b/lib/sahara
@@ -203,7 +203,7 @@
 # stop_sahara() - Stop running processes
 function stop_sahara {
     # Kill the Sahara screen windows
-    screen -S $SCREEN_NAME -p sahara -X kill
+    stop_process sahara
 }
 
 
diff --git a/lib/swift b/lib/swift
index e4d8b5f..56baa12 100644
--- a/lib/swift
+++ b/lib/swift
@@ -603,7 +603,9 @@
 
     local another_role=$(openstack role list | awk "/ anotherrole / { print \$2 }")
 
-    create_service_user "swift"
+    # NOTE(jroll): Swift doesn't need the admin role here, however Ironic uses
+    # temp urls, which break when uploaded by a non-admin role
+    create_service_user "swift" "admin"
 
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
diff --git a/lib/tempest b/lib/tempest
index 5ca217e..d3b40aa 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -29,7 +29,6 @@
 # - ``USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION``
 # - ``DEFAULT_INSTANCE_TYPE``
 # - ``DEFAULT_INSTANCE_USER``
-# - ``CINDER_MULTI_LVM_BACKEND``
 # - ``CINDER_ENABLED_BACKENDS``
 #
 # ``stack.sh`` calls the entry points in this order:
@@ -96,7 +95,8 @@
 # configure_tempest() - Set config files, create data dirs, etc
 function configure_tempest {
     # install testr since its used to process tempest logs
-    pip_install `grep -h testrepository $REQUIREMENTS_DIR/global-requirements.txt | cut -d\# -f1`
+    pip_install $(get_from_global_requirements testrepository)
+
     local image_lines
     local images
     local num_images
@@ -364,7 +364,7 @@
     iniset $TEMPEST_CONFIG network-feature-enabled api_extensions $network_api_extensions
 
     # boto
-    iniset $TEMPEST_CONFIG boto ec2_url "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Cloud"
+    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 s3_materials_path "$BOTO_MATERIALS_PATH"
     iniset $TEMPEST_CONFIG boto ari_manifest cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-initrd.manifest.xml
diff --git a/lib/trove b/lib/trove
index e1b307a..d437718 100644
--- a/lib/trove
+++ b/lib/trove
@@ -34,7 +34,13 @@
 
 TROVE_DIR=$DEST/trove
 TROVE_CONF_DIR=/etc/trove
+TROVE_CONF=$TROVE_CONF_DIR/trove.conf
+TROVE_TASKMANAGER_CONF=$TROVE_CONF_DIR/trove-taskmanager.conf
+TROVE_CONDUCTOR_CONF=$TROVE_CONF_DIR/trove-conductor.conf
+TROVE_API_PASTE_INI=$TROVE_CONF_DIR/api-paste.ini
+
 TROVE_LOCAL_CONF_DIR=$TROVE_DIR/etc/trove
+TROVE_LOCAL_API_PASTE_INI=$TROVE_LOCAL_CONF_DIR/api-paste.ini
 TROVE_AUTH_CACHE_DIR=${TROVE_AUTH_CACHE_DIR:-/var/cache/trove}
 TROVE_DATASTORE_TYPE=${TROVE_DATASTORE_TYPE:-"mysql"}
 TROVE_DATASTORE_VERSION=${TROVE_DATASTORE_VERSION:-"5.5"}
@@ -46,6 +52,7 @@
 else
     TROVE_BIN_DIR=$(get_python_exec_prefix)
 fi
+TROVE_MANAGE=$TROVE_BIN_DIR/trove-manage
 
 # Tell Tempest this project is present
 TEMPEST_SERVICES+=,trove
@@ -119,48 +126,48 @@
     sudo chown -R $STACK_USER: ${TROVE_AUTH_CACHE_DIR}
 
     # Copy api-paste file over to the trove conf dir
-    cp $TROVE_LOCAL_CONF_DIR/api-paste.ini $TROVE_CONF_DIR/api-paste.ini
+    cp $TROVE_LOCAL_API_PASTE_INI $TROVE_API_PASTE_INI
 
     # (Re)create trove conf files
-    rm -f $TROVE_CONF_DIR/trove.conf
-    rm -f $TROVE_CONF_DIR/trove-taskmanager.conf
-    rm -f $TROVE_CONF_DIR/trove-conductor.conf
+    rm -f $TROVE_CONF
+    rm -f $TROVE_TASKMANAGER_CONF
+    rm -f $TROVE_CONDUCTOR_CONF
 
-    iniset $TROVE_CONF_DIR/trove.conf DEFAULT rabbit_userid $RABBIT_USERID
-    iniset $TROVE_CONF_DIR/trove.conf DEFAULT rabbit_password $RABBIT_PASSWORD
-    iniset $TROVE_CONF_DIR/trove.conf DEFAULT sql_connection `database_connection_url trove`
-    iniset $TROVE_CONF_DIR/trove.conf DEFAULT default_datastore $TROVE_DATASTORE_TYPE
-    setup_trove_logging $TROVE_CONF_DIR/trove.conf
-    iniset $TROVE_CONF_DIR/trove.conf DEFAULT trove_api_workers "$API_WORKERS"
+    iniset $TROVE_CONF DEFAULT rabbit_userid $RABBIT_USERID
+    iniset $TROVE_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
+    iniset $TROVE_CONF DEFAULT sql_connection `database_connection_url trove`
+    iniset $TROVE_CONF DEFAULT default_datastore $TROVE_DATASTORE_TYPE
+    setup_trove_logging $TROVE_CONF
+    iniset $TROVE_CONF DEFAULT trove_api_workers "$API_WORKERS"
 
-    configure_auth_token_middleware $TROVE_CONF_DIR/trove.conf trove $TROVE_AUTH_CACHE_DIR
+    configure_auth_token_middleware $TROVE_CONF trove $TROVE_AUTH_CACHE_DIR
 
     # (Re)create trove taskmanager conf file if needed
     if is_service_enabled tr-tmgr; then
         TROVE_AUTH_ENDPOINT=$KEYSTONE_AUTH_URI/v$IDENTITY_API_VERSION
 
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT rabbit_userid $RABBIT_USERID
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT rabbit_password $RABBIT_PASSWORD
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT sql_connection `database_connection_url trove`
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT taskmanager_manager trove.taskmanager.manager.Manager
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT nova_proxy_admin_user radmin
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT nova_proxy_admin_tenant_name trove
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT nova_proxy_admin_pass $RADMIN_USER_PASS
-        iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT trove_auth_url $TROVE_AUTH_ENDPOINT
-        setup_trove_logging $TROVE_CONF_DIR/trove-taskmanager.conf
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT rabbit_userid $RABBIT_USERID
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT sql_connection `database_connection_url trove`
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT taskmanager_manager trove.taskmanager.manager.Manager
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT nova_proxy_admin_user radmin
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT nova_proxy_admin_tenant_name trove
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT nova_proxy_admin_pass $RADMIN_USER_PASS
+        iniset $TROVE_TASKMANAGER_CONF DEFAULT trove_auth_url $TROVE_AUTH_ENDPOINT
+        setup_trove_logging $TROVE_TASKMANAGER_CONF
     fi
 
     # (Re)create trove conductor conf file if needed
     if is_service_enabled tr-cond; then
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT rabbit_userid $RABBIT_USERID
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT rabbit_password $RABBIT_PASSWORD
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT sql_connection `database_connection_url trove`
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT nova_proxy_admin_user radmin
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT nova_proxy_admin_tenant_name trove
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT nova_proxy_admin_pass $RADMIN_USER_PASS
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT trove_auth_url $TROVE_AUTH_ENDPOINT
-        iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT control_exchange trove
-        setup_trove_logging $TROVE_CONF_DIR/trove-conductor.conf
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT rabbit_userid $RABBIT_USERID
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT sql_connection `database_connection_url trove`
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT nova_proxy_admin_user radmin
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT nova_proxy_admin_tenant_name trove
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT nova_proxy_admin_pass $RADMIN_USER_PASS
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT trove_auth_url $TROVE_AUTH_ENDPOINT
+        iniset $TROVE_CONDUCTOR_CONF DEFAULT control_exchange trove
+        setup_trove_logging $TROVE_CONDUCTOR_CONF
     fi
 
     # Set up Guest Agent conf
@@ -197,7 +204,7 @@
     recreate_database trove
 
     # Initialize the trove database
-    $TROVE_BIN_DIR/trove-manage db_sync
+    $TROVE_MANAGE db_sync
 
     # If no guest image is specified, skip remaining setup
     [ -z "$TROVE_GUEST_IMAGE_URL" ] && return 0
@@ -214,19 +221,19 @@
     fi
 
     # Now that we have the guest image id, initialize appropriate datastores / datastore versions
-    $TROVE_BIN_DIR/trove-manage datastore_update "$TROVE_DATASTORE_TYPE" ""
-    $TROVE_BIN_DIR/trove-manage datastore_version_update "$TROVE_DATASTORE_TYPE" "$TROVE_DATASTORE_VERSION" "$TROVE_DATASTORE_TYPE" \
+    $TROVE_MANAGE datastore_update "$TROVE_DATASTORE_TYPE" ""
+    $TROVE_MANAGE datastore_version_update "$TROVE_DATASTORE_TYPE" "$TROVE_DATASTORE_VERSION" "$TROVE_DATASTORE_TYPE" \
         "$TROVE_GUEST_IMAGE_ID" "$TROVE_DATASTORE_PACKAGE" 1
-    $TROVE_BIN_DIR/trove-manage datastore_version_update "$TROVE_DATASTORE_TYPE" "inactive_version" "inactive_manager" "$TROVE_GUEST_IMAGE_ID" "" 0
-    $TROVE_BIN_DIR/trove-manage datastore_update "$TROVE_DATASTORE_TYPE" "$TROVE_DATASTORE_VERSION"
-    $TROVE_BIN_DIR/trove-manage datastore_update "Inactive_Datastore" ""
+    $TROVE_MANAGE datastore_version_update "$TROVE_DATASTORE_TYPE" "inactive_version" "inactive_manager" "$TROVE_GUEST_IMAGE_ID" "" 0
+    $TROVE_MANAGE datastore_update "$TROVE_DATASTORE_TYPE" "$TROVE_DATASTORE_VERSION"
+    $TROVE_MANAGE datastore_update "Inactive_Datastore" ""
 }
 
 # start_trove() - Start running processes, including screen
 function start_trove {
-    run_process tr-api "$TROVE_BIN_DIR/trove-api --config-file=$TROVE_CONF_DIR/trove.conf --debug"
-    run_process tr-tmgr "$TROVE_BIN_DIR/trove-taskmanager --config-file=$TROVE_CONF_DIR/trove-taskmanager.conf --debug"
-    run_process tr-cond "$TROVE_BIN_DIR/trove-conductor --config-file=$TROVE_CONF_DIR/trove-conductor.conf --debug"
+    run_process tr-api "$TROVE_BIN_DIR/trove-api --config-file=$TROVE_CONF --debug"
+    run_process tr-tmgr "$TROVE_BIN_DIR/trove-taskmanager --config-file=$TROVE_TASKMANAGER_CONF --debug"
+    run_process tr-cond "$TROVE_BIN_DIR/trove-conductor --config-file=$TROVE_CONDUCTOR_CONF --debug"
 }
 
 # stop_trove() - Stop running processes
diff --git a/stack.sh b/stack.sh
index 3f97919..43cb991 100755
--- a/stack.sh
+++ b/stack.sh
@@ -584,7 +584,7 @@
 # The available database backends are listed in ``DATABASE_BACKENDS`` after
 # ``lib/database`` is sourced. ``mysql`` is the default.
 
-initialize_database_backends && echo "Using $DATABASE_TYPE database backend" || die $LINENO "No database enabled"
+initialize_database_backends && echo "Using $DATABASE_TYPE database backend" || echo "No database enabled"
 
 
 # Queue Configuration
@@ -1329,57 +1329,6 @@
     echo_summary "WARNING: $DEPRECATED_TEXT"
 fi
 
-if is_service_enabled neutron; then
-    # TODO(dtroyer): Remove Q_AGENT_EXTRA_AGENT_OPTS after stable/juno branch is cut
-    if [[ -n "$Q_AGENT_EXTRA_AGENT_OPTS" ]]; then
-        echo ""
-        echo_summary "WARNING: Q_AGENT_EXTRA_AGENT_OPTS is used"
-        echo "You are using Q_AGENT_EXTRA_AGENT_OPTS to pass configuration into $NEUTRON_CONF."
-        echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
-        echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
-        echo "
-[[post-config|/\$Q_PLUGIN_CONF_FILE]]
-[DEFAULT]
-"
-        for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
-            # Replace the first '=' with ' ' for iniset syntax
-            echo ${I}
-        done
-    fi
-
-    # TODO(dtroyer): Remove Q_AGENT_EXTRA_SRV_OPTS after stable/juno branch is cut
-    if [[ -n "$Q_AGENT_EXTRA_SRV_OPTS" ]]; then
-        echo ""
-        echo_summary "WARNING: Q_AGENT_EXTRA_SRV_OPTS is used"
-        echo "You are using Q_AGENT_EXTRA_SRV_OPTS to pass configuration into $NEUTRON_CONF."
-        echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
-        echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
-        echo "
-[[post-config|/\$Q_PLUGIN_CONF_FILE]]
-[DEFAULT]
-"
-        for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
-            # Replace the first '=' with ' ' for iniset syntax
-            echo ${I}
-        done
-    fi
-fi
-
-if is_service_enabled cinder; then
-    # TODO(dtroyer): Remove CINDER_MULTI_LVM_BACKEND after stable/juno branch is cut
-    if [[ "$CINDER_MULTI_LVM_BACKEND" = "True" ]]; then
-        echo ""
-        echo_summary "WARNING: CINDER_MULTI_LVM_BACKEND is used"
-        echo "You are using CINDER_MULTI_LVM_BACKEND to configure Cinder's multiple LVM backends"
-        echo "Please convert that configuration in local.conf to use CINDER_ENABLED_BACKENDS."
-        echo "CINDER_MULTI_LVM_BACKEND will be removed early in the 'K' development cycle"
-        echo "
-[[local|localrc]]
-CINDER_ENABLED_BACKENDS=lvm:lvmdriver-1,lvm:lvmdriver-2
-"
-    fi
-fi
-
 # Indicate how long this took to run (bash maintained variable ``SECONDS``)
 echo_summary "stack.sh completed in $SECONDS seconds."
 
diff --git a/stackrc b/stackrc
index 7bb4cc2..386c5d5 100644
--- a/stackrc
+++ b/stackrc
@@ -55,7 +55,7 @@
 # this allows us to pass ENABLED_SERVICES
 if ! isset ENABLED_SERVICES ; then
     # core compute (glance / keystone / nova (+ nova-network))
-    ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,n-sch,n-xvnc,n-cauth
+    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
     # cinder
     ENABLED_SERVICES+=,c-sch,c-api,c-vol
     # heat
@@ -164,7 +164,7 @@
 #
 ##############
 
-# metering service
+# telemetry service
 CEILOMETER_REPO=${CEILOMETER_REPO:-${GIT_BASE}/openstack/ceilometer.git}
 CEILOMETER_BRANCH=${CEILOMETER_BRANCH:-master}
 
@@ -407,6 +407,10 @@
 SWIFT3_REPO=${SWIFT3_REPO:-${GIT_BASE}/stackforge/swift3.git}
 SWIFT3_BRANCH=${SWIFT3_BRANCH:-master}
 
+# ceilometer middleware
+GITREPO["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_REPO:-${GIT_BASE}/openstack/ceilometermiddleware.git}
+GITBRANCH["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_BRANCH:-master}
+
 
 ##################
 #
diff --git a/tests/test_libs_from_pypi.sh b/tests/test_libs_from_pypi.sh
index 6e1b515..cce0203 100755
--- a/tests/test_libs_from_pypi.sh
+++ b/tests/test_libs_from_pypi.sh
@@ -29,7 +29,7 @@
     fi
 done
 
-ALL_LIBS="python-novaclient oslo.config pbr oslo.context python-troveclient python-keystoneclient taskflow oslo.middleware pycadf python-glanceclient python-ironicclient tempest-lib oslo.messaging oslo.log cliff python-heatclient stevedore python-cinderclient glance_store oslo.concurrency oslo.db oslo.vmware keystonemiddleware oslo.serialization python-saharaclient django_openstack_auth python-openstackclient oslo.rootwrap oslo.i18n python-ceilometerclient oslo.utils python-swiftclient python-neutronclient tooz"
+ALL_LIBS="python-novaclient oslo.config pbr oslo.context python-troveclient python-keystoneclient taskflow oslo.middleware pycadf python-glanceclient python-ironicclient tempest-lib oslo.messaging oslo.log cliff python-heatclient stevedore python-cinderclient glance_store oslo.concurrency oslo.db oslo.vmware keystonemiddleware oslo.serialization python-saharaclient django_openstack_auth python-openstackclient oslo.rootwrap oslo.i18n python-ceilometerclient oslo.utils python-swiftclient python-neutronclient tooz ceilometermiddleware"
 
 # Generate the above list with
 # echo ${!GITREPO[@]}
diff --git a/tools/create_userrc.sh b/tools/create_userrc.sh
index b43fd88..f067ed1 100755
--- a/tools/create_userrc.sh
+++ b/tools/create_userrc.sh
@@ -131,7 +131,7 @@
 
 EC2_URL=$(openstack endpoint show -f value -c publicurl ec2 || true)
 if [[ -z $EC2_URL ]]; then
-    EC2_URL=http://localhost:8773/services/Cloud
+    EC2_URL=http://localhost:8773/
 fi
 
 S3_URL=$(openstack endpoint show -f value -c publicurl s3 || true)
diff --git a/tools/xen/devstackubuntu_latecommand.sh b/tools/xen/devstackubuntu_latecommand.sh
new file mode 100644
index 0000000..2afbe2c
--- /dev/null
+++ b/tools/xen/devstackubuntu_latecommand.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -eux
+
+# Need to set barrier=0 to avoid a Xen bug
+# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/824089
+sed -i -e 's/errors=/barrier=0,errors=/' /etc/fstab
+
+# Allow root to login with a password
+sed -i -e 's/.*PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config
+
+# Install the XenServer tools so IP addresses are reported
+wget --no-proxy @XS_TOOLS_URL@ -O /root/tools.deb
+dpkg -i /root/tools.deb
+rm /root/tools.deb
diff --git a/tools/xen/devstackubuntupreseed.cfg b/tools/xen/devstackubuntupreseed.cfg
index 94e6e96..80f334b 100644
--- a/tools/xen/devstackubuntupreseed.cfg
+++ b/tools/xen/devstackubuntupreseed.cfg
@@ -331,10 +331,11 @@
 tasksel tasksel/first multiselect openssh-server
 
 # Individual additional packages to install
-#d-i pkgsel/include string openssh-server build-essential
+d-i pkgsel/include string cracklib-runtime curl wget ssh openssh-server tcpdump ethtool git sudo python-netaddr coreutils
+
 # Whether to upgrade packages after debootstrap.
 # Allowed values: none, safe-upgrade, full-upgrade
-#d-i pkgsel/upgrade select none
+d-i pkgsel/upgrade select safe-upgrade
 
 # Language pack selection
 #d-i pkgsel/language-packs multiselect de, en, zh
@@ -467,4 +468,4 @@
 # still a usable /target directory. You can chroot to /target and use it
 # directly, or use the apt-install and in-target commands to easily install
 # packages and run commands in the target system.
-#d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh
+d-i preseed/late_command string
diff --git a/tools/xen/install_os_domU.sh b/tools/xen/install_os_domU.sh
index 546ead6..082c27e 100755
--- a/tools/xen/install_os_domU.sh
+++ b/tools/xen/install_os_domU.sh
@@ -149,12 +149,10 @@
 
 function wait_for_VM_to_halt {
     set +x
-    echo "Waiting for the VM to halt.  Progress in-VM can be checked with vncviewer:"
+    echo "Waiting for the VM to halt.  Progress in-VM can be checked with XenCenter or xl console:"
     mgmt_ip=$(echo $XENAPI_CONNECTION_URL | tr -d -c '1234567890.')
     domid=$(get_domid "$GUEST_NAME")
-    sleep 20 # Wait for the vnc-port to be written
-    port=$(xenstore-read /local/domain/$domid/console/vnc-port)
-    echo "vncviewer -via root@$mgmt_ip localhost:${port:2}"
+    echo "ssh root@$mgmt_ip \"xl console $domid\""
     while true; do
         state=$(xe_min vm-list name-label="$GUEST_NAME" power-state=halted)
         if [ -n "$state" ]; then
@@ -178,12 +176,32 @@
     PRESEED_URL=${PRESEED_URL:-""}
     if [ -z "$PRESEED_URL" ]; then
         PRESEED_URL="${HOST_IP}/devstackubuntupreseed.cfg"
+
         HTTP_SERVER_LOCATION="/opt/xensource/www"
         if [ ! -e $HTTP_SERVER_LOCATION ]; then
             HTTP_SERVER_LOCATION="/var/www/html"
             mkdir -p $HTTP_SERVER_LOCATION
         fi
+
+        # Copy the tools DEB to the XS web server
+        XS_TOOLS_URL="https://github.com/downloads/citrix-openstack/warehouse/xe-guest-utilities_5.6.100-651_amd64.deb"
+        ISO_DIR="/opt/xensource/packages/iso"
+        XS_TOOLS_FILE_NAME="xs-tools.deb"
+        XS_TOOLS_PATH="/root/$XS_TOOLS_FILE_NAME"
+        if [ -e "$ISO_DIR" ]; then
+            TOOLS_ISO=$(ls -1 $ISO_DIR/xs-tools-*.iso | head -1)
+            TMP_DIR=/tmp/temp.$RANDOM
+            mkdir -p $TMP_DIR
+            mount -o loop $TOOLS_ISO $TMP_DIR
+            DEB_FILE=$(ls $TMP_DIR/Linux/*amd64.deb)
+            cp $DEB_FILE $HTTP_SERVER_LOCATION
+            umount $TMP_DIR
+            rmdir $TMP_DIR
+            XS_TOOLS_URL=${HOST_IP}/$(basename $DEB_FILE)
+        fi
+
         cp -f $THIS_DIR/devstackubuntupreseed.cfg $HTTP_SERVER_LOCATION
+        cp -f $THIS_DIR/devstackubuntu_latecommand.sh $HTTP_SERVER_LOCATION/latecommand.sh
 
         sed \
             -e "s,\(d-i mirror/http/hostname string\).*,\1 $UBUNTU_INST_HTTP_HOSTNAME,g" \
@@ -191,7 +209,12 @@
             -e "s,\(d-i mirror/http/proxy string\).*,\1 $UBUNTU_INST_HTTP_PROXY,g" \
             -e "s,\(d-i passwd/root-password password\).*,\1 $GUEST_PASSWORD,g" \
             -e "s,\(d-i passwd/root-password-again password\).*,\1 $GUEST_PASSWORD,g" \
+            -e "s,\(d-i preseed/late_command string\).*,\1 in-target mkdir -p /tmp; in-target wget --no-proxy ${HOST_IP}/latecommand.sh -O /root/latecommand.sh; in-target bash /root/latecommand.sh,g" \
             -i "${HTTP_SERVER_LOCATION}/devstackubuntupreseed.cfg"
+
+        sed \
+            -e "s,@XS_TOOLS_URL@,$XS_TOOLS_URL,g" \
+            -i "${HTTP_SERVER_LOCATION}/latecommand.sh"
     fi
 
     # Update the template
@@ -233,6 +256,7 @@
 #
 # Prepare VM for DevStack
 #
+xe vm-param-set other-config:os-vpx=true uuid="$vm_uuid"
 
 # Install XenServer tools, and other such things
 $THIS_DIR/prepare_guest_template.sh "$GUEST_NAME"
diff --git a/tools/xen/prepare_guest.sh b/tools/xen/prepare_guest.sh
index 7fe032a..6de1afc 100755
--- a/tools/xen/prepare_guest.sh
+++ b/tools/xen/prepare_guest.sh
@@ -16,9 +16,8 @@
 
 # Configurable nuggets
 GUEST_PASSWORD="$1"
-XS_TOOLS_PATH="$2"
-STACK_USER="$3"
-DOMZERO_USER="$4"
+STACK_USER="$2"
+DOMZERO_USER="$3"
 
 
 function setup_domzero_user {
@@ -70,16 +69,6 @@
 
 }
 
-# Install basics
-apt-get update
-apt-get install -y cracklib-runtime curl wget ssh openssh-server tcpdump ethtool
-apt-get install -y git sudo python-netaddr coreutils
-
-# Install XenServer guest utilities
-dpkg -i $XS_TOOLS_PATH
-update-rc.d -f xe-linux-distribution remove
-update-rc.d xe-linux-distribution defaults
-
 # Make a small cracklib dictionary, so that passwd still works, but we don't
 # have the big dictionary.
 mkdir -p /usr/share/cracklib
diff --git a/tools/xen/prepare_guest_template.sh b/tools/xen/prepare_guest_template.sh
index 6cb2ca7..6cdddda 100755
--- a/tools/xen/prepare_guest_template.sh
+++ b/tools/xen/prepare_guest_template.sh
@@ -46,28 +46,6 @@
     exit 1
 fi
 
-# Copy XenServer tools deb into the VM
-ISO_DIR="/opt/xensource/packages/iso"
-XS_TOOLS_FILE_NAME="xs-tools.deb"
-XS_TOOLS_PATH="/root/$XS_TOOLS_FILE_NAME"
-if [ -e "$ISO_DIR" ]; then
-    TOOLS_ISO=$(ls -1 $ISO_DIR/xs-tools-*.iso | head -1)
-    TMP_DIR=/tmp/temp.$RANDOM
-    mkdir -p $TMP_DIR
-    mount -o loop $TOOLS_ISO $TMP_DIR
-    DEB_FILE=$(ls $TMP_DIR/Linux/*amd64.deb)
-    echo "Copying XenServer tools into VM from: $DEB_FILE"
-    cp $DEB_FILE "${STAGING_DIR}${XS_TOOLS_PATH}"
-    umount $TMP_DIR
-    rm -rf $TMP_DIR
-else
-    echo "WARNING: no XenServer tools found, falling back to 5.6 tools"
-    TOOLS_URL="https://github.com/downloads/citrix-openstack/warehouse/xe-guest-utilities_5.6.100-651_amd64.deb"
-    curl --no-sessionid -L -o "$XS_TOOLS_FILE_NAME" $TOOLS_URL
-    cp $XS_TOOLS_FILE_NAME "${STAGING_DIR}${XS_TOOLS_PATH}"
-    rm -rf $XS_TOOLS_FILE_NAME
-fi
-
 # Copy prepare_guest.sh to VM
 mkdir -p $STAGING_DIR/opt/stack/
 cp $TOP_DIR/prepare_guest.sh $STAGING_DIR/opt/stack/prepare_guest.sh
@@ -79,14 +57,10 @@
 cat <<EOF >$STAGING_DIR/etc/rc.local
 #!/bin/sh -e
 bash /opt/stack/prepare_guest.sh \\
-    "$GUEST_PASSWORD" "$XS_TOOLS_PATH" "$STACK_USER" "$DOMZERO_USER" \\
+    "$GUEST_PASSWORD" "$STACK_USER" "$DOMZERO_USER" \\
     > /opt/stack/prepare_guest.log 2>&1
 EOF
 
-# Need to set barrier=0 to avoid a Xen bug
-# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/824089
-sed -i -e 's/errors=/barrier=0,errors=/' $STAGING_DIR/etc/fstab
-
 # Update ubuntu repositories
 cat > $STAGING_DIR/etc/apt/sources.list << EOF
 deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE} main restricted
diff --git a/tools/xen/scripts/install-os-vpx.sh b/tools/xen/scripts/install-os-vpx.sh
index b9b65fd..1ebbeaf 100755
--- a/tools/xen/scripts/install-os-vpx.sh
+++ b/tools/xen/scripts/install-os-vpx.sh
@@ -131,5 +131,4 @@
 destroy_vifs "$vm_uuid"
 set_auto_start "$vm_uuid"
 create_vif "$vm_uuid"
-xe vm-param-set other-config:os-vpx=true uuid="$vm_uuid"
 xe vm-param-set actions-after-reboot=Destroy uuid="$vm_uuid"
diff --git a/unstack.sh b/unstack.sh
index 4364e58..6deeba2 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/bash
 
 # **unstack.sh**
 
@@ -138,10 +138,13 @@
 
 SCSI_PERSIST_DIR=$CINDER_STATE_PATH/volumes/*
 
+# BUG: tgt likes to exit 1 on service stop if everything isn't
+# perfect, we should clean up cinder stop paths.
+
 # Get the iSCSI volumes
 if is_service_enabled cinder; then
-    stop_cinder
-    cleanup_cinder
+    stop_cinder || /bin/true
+    cleanup_cinder || /bin/true
 fi
 
 if [[ -n "$UNSTACK_ALL" ]]; then
@@ -181,4 +184,5 @@
     fi
 fi
 
-clean_lvm_volume_group $DEFAULT_VOLUME_GROUP_NAME
+# BUG: maybe it doesn't exist? We should isolate this further down.
+clean_lvm_volume_group $DEFAULT_VOLUME_GROUP_NAME || /bin/true