Merge "Actually run all the Cinder cert tests."
diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst
index 8c9e658..b2a219b 100644
--- a/doc/source/contributing.rst
+++ b/doc/source/contributing.rst
@@ -5,7 +5,7 @@
 DevStack uses the standard OpenStack contribution process as outlined in
 `the OpenStack wiki 'How To
 Contribute' <https://wiki.openstack.org/wiki/How_To_Contribute>`__. This
-means that you will need to meet the requirements of the Contribututors
+means that you will need to meet the requirements of the Contributors
 License Agreement (CLA). If you have already done that for another
 OpenStack project you are good to go.
 
diff --git a/doc/source/exerciserc.rst b/doc/source/exerciserc.rst
index f3780c3..dacae2e 100644
--- a/doc/source/exerciserc.rst
+++ b/doc/source/exerciserc.rst
@@ -3,7 +3,7 @@
 ==============================
 
 ``exerciserc`` is used to configure settings for the exercise scripts.
-The values shown below are the default values. Thse can all be
+The values shown below are the default values. These can all be
 overridden by setting them in the ``localrc`` section.
 
 ACTIVE\_TIMEOUT
diff --git a/doc/source/faq.rst b/doc/source/faq.rst
index f39471c..92d7945 100644
--- a/doc/source/faq.rst
+++ b/doc/source/faq.rst
@@ -123,7 +123,7 @@
     [STRIKEOUT:The majority of deployments will use packages to install
     OpenStack that will have distro-based packages as dependencies.
     DevStack installs as many of these Python packages as possible to
-    mimic the expected production environemnt.] Certain Linux
+    mimic the expected production environment.] Certain Linux
     distributions have a 'lack of workaround' in their Python
     configurations that installs vendor packaged Python modules and
     pip-installed modules to the SAME DIRECTORY TREE. This is causing
diff --git a/doc/source/guides/neutron.rst b/doc/source/guides/neutron.rst
index dc2fc71..90d4ca3 100644
--- a/doc/source/guides/neutron.rst
+++ b/doc/source/guides/neutron.rst
@@ -59,6 +59,40 @@
 
 
 
+Disabling Next Generation Firewall Tools
+========================================
+
+Devstack does not properly operate with modern firewall tools.  Specifically
+it will appear as if the guest VM can access the external network via ICMP,
+but UDP and TCP packets will not be delivered to the guest VM.  The root cause
+of the issue is that both ufw (Uncomplicated Firewall) and firewalld (Fedora's
+firewall manager) apply firewall rules to all interfaces in the system, rather
+then per-device.  One solution to this problem is to revert to iptables
+functionality.
+
+To get a functional firewall configuration for Fedora do the following:
+
+::
+
+         sudo service iptables save
+         sudo systemctl disable firewalld
+         sudo systemctl enable iptables
+         sudo systemctl stop firewalld
+         sudo systemctl start iptables
+
+
+To get a functional firewall configuration for distributions containing ufw,
+disable ufw.  Note ufw is generally not enabled by default in Ubuntu.  To
+disable ufw if it was enabled, do the following:
+
+::
+
+        sudo service iptables save
+        sudo ufw disable
+
+
+
+
 Neutron Networking with Open vSwitch
 ====================================
 
diff --git a/doc/source/local.conf.rst b/doc/source/local.conf.rst
index b2f7557..a1ca60a 100644
--- a/doc/source/local.conf.rst
+++ b/doc/source/local.conf.rst
@@ -2,7 +2,7 @@
 local.conf - User Settings
 ==========================
 
-``local.conf`` is a user-maintained setings file that is sourced in
+``local.conf`` is a user-maintained settings file that is sourced in
 ``stackrc``. It contains a section that replaces the historical
 ``localrc`` file. See the description of
 :doc:`local.conf <configuration>` for more details about the mechanics
diff --git a/doc/source/openrc.rst b/doc/source/openrc.rst
index 56ff5c2..0b090c7 100644
--- a/doc/source/openrc.rst
+++ b/doc/source/openrc.rst
@@ -4,7 +4,7 @@
 
 ``openrc`` configures login credentials suitable for use with the
 OpenStack command-line tools. ``openrc`` sources ``stackrc`` at the
-beginning (which in turn sources the ``localrc`` setion of
+beginning (which in turn sources the ``localrc`` section of
 ``local.conf``) in order to pick up ``HOST_IP`` and/or ``SERVICE_HOST``
 to use in the endpoints. The values shown below are the default values.
 
diff --git a/doc/source/plugins.rst b/doc/source/plugins.rst
index 485cd0f..d1f7377 100644
--- a/doc/source/plugins.rst
+++ b/doc/source/plugins.rst
@@ -92,6 +92,45 @@
 -  **clean** - Called by ``clean.sh`` before other services are cleaned,
    but after ``unstack.sh`` has been called.
 
+
+Externally Hosted Plugins
+=========================
+
+Based on the extras.d hooks, DevStack supports a standard mechansim
+for including plugins from external repositories. The plugin interface
+assumes the following:
+
+An external git repository that includes a ``devstack/`` top level
+directory. Inside this directory there can be 2 files.
+
+- ``settings`` - a file containing global variables that will be
+  sourced very early in the process. This is helpful if other plugins
+  might depend on this one, and need access to global variables to do
+  their work.
+- ``plugin.sh`` - the actual plugin. It will be executed by devstack
+  during it's run. The run order will be done in the registration
+  order for these plugins, and will occur immediately after all in
+  tree extras.d dispatch at the phase in question.  The plugin.sh
+  looks like the extras.d dispatcher above **except** it should not
+  include the is_service_enabled conditional. All external plugins are
+  always assumed to be enabled.
+
+Plugins are registered by adding the following to the localrc section
+of ``local.conf``.
+
+They are added in the following format::
+
+  enable_plugin <NAME> <GITURL> [GITREF]
+
+- ``name`` - an arbitrary name. (ex: glustfs, docker, zaqar, congress)
+- ``giturl`` - a valid git url that can be cloned
+- ``gitref`` - an optional git ref (branch / ref / tag) that will be
+  cloned. Defaults to master.
+
+An example would be as follows::
+
+  enable_plugin glusterfs https://github.com/sdague/devstack-plugins glusterfs
+
 Hypervisor
 ==========
 
diff --git a/files/debs/general b/files/debs/general
index 3fe7863..e824d23 100644
--- a/files/debs/general
+++ b/files/debs/general
@@ -1,6 +1,5 @@
 bridge-utils
 pylint
-python-setuptools
 screen
 unzip
 wget
diff --git a/files/debs/neutron b/files/debs/neutron
index a48a800..fd99677 100644
--- a/files/debs/neutron
+++ b/files/debs/neutron
@@ -24,3 +24,4 @@
 sqlite3
 vlan
 radvd # NOPRIME
+uuid-runtime
diff --git a/files/rpms-suse/general b/files/rpms-suse/general
index f1f7e8f..63ef705 100644
--- a/files/rpms-suse/general
+++ b/files/rpms-suse/general
@@ -15,7 +15,6 @@
 psmisc
 python-cmd2 # dist:opensuse-12.3
 python-pylint
-python-setuptools # instead of python-distribute; dist:sle11sp2
 python-unittest2
 screen
 tar
diff --git a/files/rpms/general b/files/rpms/general
index d7ace9b..ee7cc12 100644
--- a/files/rpms/general
+++ b/files/rpms/general
@@ -13,7 +13,6 @@
 libxslt-devel
 psmisc
 pylint
-python-setuptools
 python-prettytable # dist:rhel6 [1]
 python-unittest2
 python-virtualenv
diff --git a/functions-common b/functions-common
index 94ab347..56106b3 100644
--- a/functions-common
+++ b/functions-common
@@ -44,7 +44,6 @@
 declare -A GITBRANCH
 declare -A GITDIR
 
-
 # Config Functions
 # ================
 
@@ -148,6 +147,21 @@
     $xtrace
 }
 
+function inidelete {
+    local xtrace=$(set +o | grep xtrace)
+    set +o xtrace
+    local file=$1
+    local section=$2
+    local option=$3
+
+    [[ -z $section || -z $option ]] && return
+
+    # Remove old values
+    sed -i -e "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ d; }" "$file"
+
+    $xtrace
+}
+
 # Set an option in an INI file
 # iniset config-file section option value
 function iniset {
@@ -1578,7 +1592,7 @@
         local sudo_pip="env"
     else
         local cmd_pip=$(get_pip_command)
-        local sudo_pip="sudo"
+        local sudo_pip="sudo -H"
     fi
 
     $xtrace
@@ -1722,6 +1736,97 @@
     fi
 }
 
+# Plugin Functions
+# =================
+
+DEVSTACK_PLUGINS=${DEVSTACK_PLUGINS:-""}
+
+# enable_plugin <name> <url> [branch]
+#
+# ``name`` is an arbitrary name - (aka: glusterfs, nova-docker, zaqar)
+# ``url`` is a git url
+# ``branch`` is a gitref. If it's not set, defaults to master
+function enable_plugin {
+    local name=$1
+    local url=$2
+    local branch=${3:-master}
+    DEVSTACK_PLUGINS+=",$name"
+    GITREPO[$name]=$url
+    GITDIR[$name]=$DEST/$name
+    GITBRANCH[$name]=$branch
+}
+
+# fetch_plugins
+#
+# clones all plugins
+function fetch_plugins {
+    local plugins="${DEVSTACK_PLUGINS}"
+    local plugin
+
+    # short circuit if nothing to do
+    if [[ -z $plugins ]]; then
+        return
+    fi
+
+    echo "Fetching devstack plugins"
+    for plugin in ${plugins//,/ }; do
+        git_clone_by_name $plugin
+    done
+}
+
+# load_plugin_settings
+#
+# Load settings from plugins in the order that they were registered
+function load_plugin_settings {
+    local plugins="${DEVSTACK_PLUGINS}"
+    local plugin
+
+    # short circuit if nothing to do
+    if [[ -z $plugins ]]; then
+        return
+    fi
+
+    echo "Loading plugin settings"
+    for plugin in ${plugins//,/ }; do
+        local dir=${GITDIR[$plugin]}
+        # source any known settings
+        if [[ -f $dir/devstack/settings ]]; then
+            source $dir/devstack/settings
+        fi
+    done
+}
+
+# run_plugins
+#
+# Run the devstack/plugin.sh in all the plugin directories. These are
+# run in registration order.
+function run_plugins {
+    local mode=$1
+    local phase=$2
+    for plugin in ${plugins//,/ }; do
+        local dir=${GITDIR[$plugin]}
+        if [[ -f $dir/devstack/plugin.sh ]]; then
+            source $dir/devstack/plugin.sh $mode $phase
+        fi
+    done
+}
+
+function run_phase {
+    local mode=$1
+    local phase=$2
+    if [[ -d $TOP_DIR/extras.d ]]; then
+        for i in $TOP_DIR/extras.d/*.sh; do
+            [[ -r $i ]] && source $i $mode $phase
+        done
+    fi
+    # the source phase corresponds to settings loading in plugins
+    if [[ "$mode" == "source" ]]; then
+        load_plugin_settings
+    else
+        run_plugins $mode $phase
+    fi
+}
+
 
 # Service Functions
 # =================
diff --git a/lib/ceilometer b/lib/ceilometer
index 1f480ea..f6280d9 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -272,12 +272,12 @@
 function install_redis {
     if is_ubuntu; then
         install_package redis-server
+        restart_service redis-server
     else
         # This will fail (correctly) where a redis package is unavailable
         install_package redis
+        restart_service redis
     fi
-
-    restart_service redis
 }
 
 # install_ceilometer() - Collect source and prepare
diff --git a/lib/cinder b/lib/cinder
index 930119c..c106424 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -292,7 +292,7 @@
         configure_cinder_driver
     fi
 
-    if [[ is_fedora && $DISTRO =~ (rhel6) ]]; then
+    if is_fedora && [[ $DISTRO =~ (rhel6) ]]; then
         # Cinder clones are slightly larger due to some extra
         # metadata.  RHEL6 will not allow auto-extending of LV's
         # without this, leading to clones giving hard-to-track disk
diff --git a/lib/config b/lib/config
index c0756bf..31c6fa6 100644
--- a/lib/config
+++ b/lib/config
@@ -144,6 +144,7 @@
                     else {
                         # For multiline, invoke the ini routines in the reverse order
                         count = cfg_attr_count[section, attr]
+                        print "inidelete " configfile " " section " " attr
                         print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\""
                         for (l = count -2; l >= 0; l--)
                             print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\""
diff --git a/lib/heat b/lib/heat
index 49ed533..4e72cae 100644
--- a/lib/heat
+++ b/lib/heat
@@ -73,7 +73,6 @@
 
 # configure_heat() - Set config files, create data dirs, etc
 function configure_heat {
-    setup_develop $HEAT_DIR
     if [[ "$HEAT_STANDALONE" = "True" ]]; then
         setup_develop $HEAT_DIR/contrib/heat_keystoneclient_v2
     fi
@@ -195,6 +194,7 @@
 # install_heat() - Collect source and prepare
 function install_heat {
     git_clone $HEAT_REPO $HEAT_DIR $HEAT_BRANCH
+    setup_develop $HEAT_DIR
 }
 
 # install_heat_other() - Collect source and prepare
diff --git a/lib/horizon b/lib/horizon
index edb95e9..fee2ef0 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -72,7 +72,7 @@
 # cleanup_horizon() - Remove residual data files, anything left over from previous
 # runs that a clean run would need to clean up
 function cleanup_horizon {
-    if [[ is_fedora && $DISTRO =~ (rhel6) ]]; then
+    if is_fedora && [[ $DISTRO =~ (rhel6) ]]; then
         # If ``/usr/bin/node`` points into ``$DEST``
         # we installed it via ``install_nodejs``
         if [[ $(readlink -f /usr/bin/node) =~ ($DEST) ]]; then
diff --git a/lib/infra b/lib/infra
index 57df07d..c825b4e 100644
--- a/lib/infra
+++ b/lib/infra
@@ -37,7 +37,9 @@
         git_clone_by_name "pbr"
         setup_lib "pbr"
     else
-        pip_install "pbr"
+        # Always upgrade pbr to latest version as we may have pulled it
+        # in via system packages.
+        pip_install "-U" "pbr"
     fi
 }
 
diff --git a/lib/keystone b/lib/keystone
index 071dc90..1599fa5 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -116,7 +116,7 @@
 
 # _cleanup_keystone_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
 function _cleanup_keystone_apache_wsgi {
-    sudo rm -f $KEYSTONE_WSGI_DIR/*.wsgi
+    sudo rm -f $KEYSTONE_WSGI_DIR/*
     sudo rm -f $(apache_site_config_for keystone)
 }
 
diff --git a/lib/neutron b/lib/neutron
old mode 100644
new mode 100755
index 8517102..5678769
--- a/lib/neutron
+++ b/lib/neutron
@@ -595,6 +595,16 @@
     recreate_database $Q_DB_NAME utf8
     # Run Neutron db migrations
     $NEUTRON_BIN_DIR/neutron-db-manage --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE upgrade head
+    for svc in fwaas lbaas vpnaas; do
+        if [ "$svc" = "vpnaas" ]; then
+            q_svc="q-vpn"
+        else
+            q_svc="q-$svc"
+        fi
+        if is_service_enabled $q_svc; then
+            $NEUTRON_BIN_DIR/neutron-db-manage --service $svc --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE upgrade head
+        fi
+    done
 }
 
 # install_neutron() - Collect source and prepare
@@ -751,7 +761,7 @@
 # cleanup_neutron() - Remove residual data files, anything left over from previous
 # runs that a clean run would need to clean up
 function cleanup_neutron {
-    if [[ is_provider_network && is_ironic_hardware ]]; then
+    if is_provider_network && is_ironic_hardware; then
         for IP in $(ip addr show dev $OVS_PHYSICAL_BRIDGE | grep ' inet ' | awk '{print $2}'); do
             sudo ip addr del $IP dev $OVS_PHYSICAL_BRIDGE
             sudo ip addr add $IP dev $PUBLIC_INTERFACE
@@ -921,7 +931,9 @@
     iniset $Q_META_CONF_FILE DEFAULT nova_metadata_ip $Q_META_DATA_IP
     iniset $Q_META_CONF_FILE DEFAULT root_helper "$Q_RR_COMMAND"
 
-    _neutron_setup_keystone $Q_META_CONF_FILE DEFAULT
+    # Configures keystone for metadata_agent
+    # The third argument "True" sets auth_url needed to communicate with keystone
+    _neutron_setup_keystone $Q_META_CONF_FILE DEFAULT True
 
 }
 
@@ -1066,6 +1078,13 @@
 function _neutron_setup_keystone {
     local conf_file=$1
     local section=$2
+    local use_auth_url=$3
+
+    # Configures keystone for metadata_agent
+    # metadata_agent needs auth_url to communicate with keystone
+    if [[ "$use_auth_url" == "True" ]]; then
+        iniset $conf_file $section auth_url $KEYSTONE_SERVICE_URI/v2.0
+    fi
 
     create_neutron_cache_dir
     configure_auth_token_middleware $conf_file $Q_ADMIN_USERNAME $NEUTRON_AUTH_CACHE_DIR $section
diff --git a/lib/opendaylight b/lib/opendaylight
index 936a17c..2f0f37e 100644
--- a/lib/opendaylight
+++ b/lib/opendaylight
@@ -47,16 +47,16 @@
 ODL_PASSWORD=${ODL_PASSWORD:-admin}
 
 # Short name of ODL package
-ODL_NAME=${ODL_NAME:-distribution-karaf-0.2.1-Helium-SR1}
+ODL_NAME=${ODL_NAME:-distribution-karaf-0.2.1-Helium-SR1.1}
 
 # <define global variables here that belong to this project>
 ODL_DIR=$DEST/opendaylight
 
 # The OpenDaylight Package, currently using 'Hydrogen' release
-ODL_PKG=${ODL_PKG:-distribution-karaf-0.2.1-Helium-SR1.zip}
+ODL_PKG=${ODL_PKG:-distribution-karaf-0.2.1-Helium-SR1.1.zip}
 
 # The OpenDaylight URL
-ODL_URL=${ODL_URL:-https://nexus.opendaylight.org/content/repositories/public/org/opendaylight/integration/distribution-karaf/0.2.1-Helium-SR1/}
+ODL_URL=${ODL_URL:-https://nexus.opendaylight.org/content/repositories/public/org/opendaylight/integration/distribution-karaf/0.2.1-Helium-SR1.1/}
 
 # Default arguments for OpenDaylight. This is typically used to set
 # Java memory options.
diff --git a/lib/rpc_backend b/lib/rpc_backend
index 400204a..172d024 100644
--- a/lib/rpc_backend
+++ b/lib/rpc_backend
@@ -221,9 +221,9 @@
     local file=$2
     local section=$3
     if is_service_enabled zeromq; then
-        iniset $file $section rpc_backend ${package}.openstack.common.rpc.impl_zmq
+        iniset $file $section rpc_backend "zmq"
         iniset $file $section rpc_zmq_matchmaker \
-            ${package}.openstack.common.rpc.matchmaker_redis.MatchMakerRedis
+            oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis
         # Set MATCHMAKER_REDIS_HOST if running multi-node.
         MATCHMAKER_REDIS_HOST=${MATCHMAKER_REDIS_HOST:-127.0.0.1}
         iniset $file matchmaker_redis host $MATCHMAKER_REDIS_HOST
diff --git a/lib/tempest b/lib/tempest
index 6fc157f..d31119b 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -372,7 +372,7 @@
     # Orchestration Tests
     if is_service_enabled heat; then
         if [[ ! -z "$HEAT_CFN_IMAGE_URL" ]]; then
-            iniset $TEMPEST_CONFIG orchestration image_ref $(basename "$HEAT_CFN_IMAGE_URL" ".qcow2")
+            iniset $TEMPEST_CONFIG orchestration image_ref $(basename "${HEAT_CFN_IMAGE_URL%.*}")
         fi
         # build a specialized heat flavor
         available_flavors=$(nova flavor-list)
@@ -464,6 +464,13 @@
         iniset $TEMPEST_CONFIG compute-feature-enabled suspend False
     fi
 
+    # Libvirt-LXC
+    if [ "$VIRT_DRIVER" = "libvirt" ] && [ "$LIBVIRT_TYPE" = "lxc" ]; then
+        iniset $TEMPEST_CONFIG compute-feature-enabled rescue False
+        iniset $TEMPEST_CONFIG compute-feature-enabled resize False
+        iniset $TEMPEST_CONFIG compute-feature-enabled suspend False
+    fi
+
     # service_available
     for service in ${TEMPEST_SERVICES//,/ }; do
         if is_service_enabled $service ; then
diff --git a/lib/trove b/lib/trove
index 8b7b0ef..abf4e87 100644
--- a/lib/trove
+++ b/lib/trove
@@ -123,11 +123,8 @@
     sudo chown -R $STACK_USER: ${TROVE_CONF_DIR}
     sudo chown -R $STACK_USER: ${TROVE_AUTH_CACHE_DIR}
 
-    # Copy api-paste file over to the trove conf dir and configure it
+    # Copy api-paste file over to the trove conf dir
     cp $TROVE_LOCAL_CONF_DIR/api-paste.ini $TROVE_CONF_DIR/api-paste.ini
-    TROVE_API_PASTE_INI=$TROVE_CONF_DIR/api-paste.ini
-
-    configure_auth_token_middleware $TROVE_API_PASTE_INI trove $TROVE_AUTH_CACHE_DIR filter:authtoken
 
     # (Re)create trove conf files
     rm -f $TROVE_CONF_DIR/trove.conf
@@ -141,6 +138,7 @@
     setup_trove_logging $TROVE_CONF_DIR/trove.conf
     iniset $TROVE_CONF_DIR/trove.conf DEFAULT trove_api_workers "$API_WORKERS"
 
+    configure_auth_token_middleware $TROVE_CONF_DIR/trove.conf trove $TROVE_AUTH_CACHE_DIR
 
     # (Re)create trove taskmanager conf file if needed
     if is_service_enabled tr-tmgr; then
@@ -207,7 +205,7 @@
     $TROVE_BIN_DIR/trove-manage db_sync
 
     # If no guest image is specified, skip remaining setup
-    [ -z "$TROVE_GUEST_IMAGE_URL"] && return 0
+    [ -z "$TROVE_GUEST_IMAGE_URL" ] && return 0
 
     # Find the glance id for the trove guest image
     # The image is uploaded by stack.sh -- see $IMAGE_URLS handling
diff --git a/stack.sh b/stack.sh
index 605d3cc..b064c22 100755
--- a/stack.sh
+++ b/stack.sh
@@ -212,17 +212,6 @@
 # Some distros need to add repos beyond the defaults provided by the vendor
 # to pick up required packages.
 
-if is_fedora && [ $DISTRO == "rhel6" ]; then
-    # Installing Open vSwitch on RHEL requires enabling the RDO repo.
-    RHEL6_RDO_REPO_RPM=${RHEL6_RDO_REPO_RPM:-"http://rdo.fedorapeople.org/openstack-icehouse/rdo-release-icehouse.rpm"}
-    RHEL6_RDO_REPO_ID=${RHEL6_RDO_REPO_ID:-"openstack-icehouse"}
-    if ! sudo yum repolist enabled $RHEL6_RDO_REPO_ID | grep -q $RHEL6_RDO_REPO_ID; then
-        echo "RDO repo not detected; installing"
-        yum_install $RHEL6_RDO_REPO_RPM || \
-            die $LINENO "Error installing RDO repo, cannot continue"
-    fi
-fi
-
 if is_fedora && [[ $DISTRO == "rhel6" || $DISTRO == "rhel7" ]]; then
     # RHEL requires EPEL for many Open Stack dependencies
 
@@ -269,6 +258,23 @@
         OPTIONAL_REPO=rhel-6-server-optional-rpms
     fi
     sudo yum-config-manager --enable ${OPTIONAL_REPO}
+
+    # Installing Open vSwitch on RHEL requires enabling the RDO repo.
+    # Note no juno packages for rhel6
+    if [[ $DISTRO == "rhel6" ]]; then
+        RHEL_RDO_REPO_RPM=${RHEL6_RDO_REPO_RPM:-"https://repos.fedorapeople.org/repos/openstack/openstack-icehouse/rdo-release-icehouse-4.noarch.rpm"}
+        RHEL_RDO_REPO_ID=${RHEL6_RDO_REPO_ID:-"openstack-icehouse"}
+    elif [[ $DISTRO == "rhel7" ]]; then
+        RHEL_RDO_REPO_RPM=${RHEL7_RDO_REPO_RPM:-"https://repos.fedorapeople.org/repos/openstack/openstack-juno/rdo-release-juno-1.noarch.rpm"}
+        RHEL_RDO_REPO_ID=${RHEL7_RDO_REPO_ID:-"openstack-juno"}
+    fi
+
+    if ! sudo yum repolist enabled $RHEL_RDO_REPO_ID | grep -q $RHEL_RDO_REPO_ID; then
+        echo "RDO repo not detected; installing"
+        yum_install $RHEL_RDO_REPO_RPM || \
+            die $LINENO "Error installing RDO repo, cannot continue"
+    fi
+
 fi
 
 
@@ -564,15 +570,14 @@
 source $TOP_DIR/lib/ldap
 source $TOP_DIR/lib/dstat
 
+# Clone all external plugins
+fetch_plugins
+
 # Extras Source
 # --------------
 
 # Phase: source
-if [[ -d $TOP_DIR/extras.d ]]; then
-    for i in $TOP_DIR/extras.d/*.sh; do
-        [[ -r $i ]] && source $i source
-    done
-fi
+run_phase source
 
 # Interactive Configuration
 # -------------------------
@@ -714,12 +719,7 @@
 # ------------------
 
 # Phase: pre-install
-if [[ -d $TOP_DIR/extras.d ]]; then
-    for i in $TOP_DIR/extras.d/*.sh; do
-        [[ -r $i ]] && source $i stack pre-install
-    done
-fi
-
+run_phase stack pre-install
 
 install_rpc_backend
 
@@ -865,11 +865,7 @@
 # --------------
 
 # Phase: install
-if [[ -d $TOP_DIR/extras.d ]]; then
-    for i in $TOP_DIR/extras.d/*.sh; do
-        [[ -r $i ]] && source $i stack install
-    done
-fi
+run_phase stack install
 
 if [[ $TRACK_DEPENDS = True ]]; then
     $DEST/.venv/bin/pip freeze > $DEST/requires-post-pip
@@ -1142,11 +1138,7 @@
 # ====================
 
 # Phase: post-config
-if [[ -d $TOP_DIR/extras.d ]]; then
-    for i in $TOP_DIR/extras.d/*.sh; do
-        [[ -r $i ]] && source $i stack post-config
-    done
-fi
+run_phase stack post-config
 
 
 # Local Configuration
@@ -1328,11 +1320,7 @@
 # ==========
 
 # Phase: extra
-if [[ -d $TOP_DIR/extras.d ]]; then
-    for i in $TOP_DIR/extras.d/*.sh; do
-        [[ -r $i ]] && source $i stack extra
-    done
-fi
+run_phase stack extra
 
 # Local Configuration
 # ===================
diff --git a/tests/test_ini.sh b/tests/test_ini.sh
index 598cd57..106cc95 100755
--- a/tests/test_ini.sh
+++ b/tests/test_ini.sh
@@ -34,6 +34,32 @@
 [eee]
 multi = foo1
 multi = foo2
+
+# inidelete(a)
+[del_separate_options]
+a=b
+b=c
+
+# inidelete(a)
+[del_same_option]
+a=b
+a=c
+
+# inidelete(a)
+[del_missing_option]
+b=c
+
+# inidelete(a)
+[del_missing_option_multi]
+b=c
+b=d
+
+# inidelete(a)
+[del_no_options]
+
+# inidelete(a)
+# no section - del_no_section
+
 EOF
 
 # Test with missing arguments
@@ -237,4 +263,33 @@
     echo "iniadd with non-exsting failed: $VAL"
 fi
 
+# Test inidelete
+del_cases="
+    del_separate_options
+    del_same_option
+    del_missing_option
+    del_missing_option_multi
+    del_no_options
+    del_no_section"
+
+for x in $del_cases; do
+    inidelete test.ini $x a
+    VAL=$(iniget_multiline test.ini $x a)
+    if [ -z "$VAL" ]; then
+        echo "OK: inidelete $x"
+    else
+        echo "inidelete $x failed: $VAL"
+    fi
+    if [ "$x" = "del_separate_options" -o \
+        "$x" = "del_missing_option" -o \
+        "$x" = "del_missing_option_multi" ]; then
+        VAL=$(iniget_multiline test.ini $x b)
+        if [ "$VAL" = "c" -o "$VAL" = "c d" ]; then
+            echo "OK: inidelete other_options $x"
+        else
+            echo "inidelete other_option $x failed: $VAL"
+        fi
+    fi
+done
+
 rm test.ini
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index ca46533..26aae82 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -85,7 +85,7 @@
 
 # Fix prettytable 0.7.2 permissions
 # Don't specify --upgrade so we use the existing package if present
-pip_install 'prettytable>0.7'
+pip_install 'prettytable>=0.7'
 PACKAGE_DIR=$(get_package_path prettytable)
 # Only fix version 0.7.2
 dir=$(echo $PACKAGE_DIR/prettytable-0.7.2*)
diff --git a/tools/install_pip.sh b/tools/install_pip.sh
index c9119ae..d57a687 100755
--- a/tools/install_pip.sh
+++ b/tools/install_pip.sh
@@ -46,7 +46,7 @@
         curl -o $LOCAL_PIP $PIP_GET_PIP_URL || \
             die $LINENO "Download of get-pip.py failed"
     fi
-    sudo -E python $LOCAL_PIP
+    sudo -H -E python $LOCAL_PIP
 }
 
 
@@ -68,6 +68,13 @@
 
 }
 
+# Setuptools 8 implements PEP 440, and 8.0.4 adds a warning triggered any time
+# pkg_resources inspects the list of installed Python packages if there are
+# non-compliant version numbers in the egg-info (for example, from distro
+# system packaged Python libraries). This is off by default after 8.2 but can
+# be enabled by uncommenting the lines below.
+#PYTHONWARNINGS=$PYTHONWARNINGS,always::RuntimeWarning:pkg_resources
+#export PYTHONWARNINGS
 
 # Show starting versions
 get_versions
@@ -83,6 +90,6 @@
     configure_pypi_alternative_url
 fi
 
-pip_install -U "setuptools<8.0"
+pip_install -U setuptools
 
 get_versions
diff --git a/unstack.sh b/unstack.sh
index 3403919..ea45da9 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -66,6 +66,8 @@
     done
 fi
 
+load_plugin_settings
+
 # Determine what system we are running on.  This provides ``os_VENDOR``,
 # ``os_RELEASE``, ``os_UPDATE``, ``os_PACKAGE``, ``os_CODENAME``
 GetOSVersion
@@ -78,11 +80,7 @@
 # ==========
 
 # Phase: unstack
-if [[ -d $TOP_DIR/extras.d ]]; then
-    for i in $TOP_DIR/extras.d/*.sh; do
-        [[ -r $i ]] && source $i unstack
-    done
-fi
+run_phase unstack
 
 if [[ "$Q_USE_DEBUG_COMMAND" == "True" ]]; then
     source $TOP_DIR/openrc