Merge "configuration.rst: Document post-extra meta section phase"
diff --git a/.gitignore b/.gitignore
index 2778a65..8870bb3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@
 shocco
 src
 stack-screenrc
+userrc_early
diff --git a/HACKING.rst b/HACKING.rst
index a40af54..d763c75 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -250,8 +250,7 @@
   database access from the exercise itself.
 
 * If specific configuration needs to be present for the exercise to complete,
-  it should be staged in ``stack.sh``, or called from ``stack.sh`` (see
-  ``files/keystone_data.sh`` for an example of this).
+  it should be staged in ``stack.sh``, or called from ``stack.sh``.
 
 * The ``OS_*`` environment variables should be the only ones used for all
   authentication to OpenStack clients as documented in the CLIAuth_ wiki page.
@@ -329,7 +328,7 @@
 your change
 
 * **Is it passing tests** -- your change will not be reviewed
-  throughly unless the official CI has run successfully against it.
+  thoroughly unless the official CI has run successfully against it.
 
 * **Does this belong in DevStack** -- DevStack reviewers have a
   default position of "no" but are ready to be convinced by your
@@ -355,7 +354,7 @@
 
 * **Should this be upstream** -- DevStack generally does not override
   default choices provided by projects and attempts to not
-  unexpectedly modify behaviour.
+  unexpectedly modify behavior.
 
 * **Context in commit messages** -- DevStack touches many different
   areas and reviewers need context around changes to make good
diff --git a/Makefile b/Makefile
index 082aff2..a94d60a 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,6 @@
 
 # Duplicated from stackrc for now
 DEST=/opt/stack
-WHEELHOUSE=$(DEST)/.wheelhouse
 
 all:
 	echo "This just saved you from a terrible mistake!"
@@ -25,9 +24,6 @@
 unstack:
 	./unstack.sh
 
-wheels:
-	WHEELHOUSE=$(WHEELHOUSE) tools/build-wheels.sh
-
 docs:
 	tox -edocs
 
@@ -57,7 +53,7 @@
 
 # Clean out the cache too
 realclean: clean
-	rm -rf files/cirros*.tar.gz files/Fedora*.qcow2 $(WHEELHOUSE)
+	rm -rf files/cirros*.tar.gz files/Fedora*.qcow2
 
 # Repo stuffs
 
diff --git a/README.md b/README.md
index acc3e5a..dd394c2 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
 * To provide an environment for the OpenStack CI testing on every commit
   to the projects
 
-Read more at http://devstack.org.
+Read more at http://docs.openstack.org/developer/devstack
 
 IMPORTANT: Be sure to carefully read `stack.sh` and any other scripts you
 execute before you run them, as they install software and will alter your
@@ -77,361 +77,21 @@
 of your hypervisor of choice to reduce testing cycle times.  You might even save
 enough time to write one more feature before the next feature freeze...
 
-``stack.sh`` needs to have root access for a lot of tasks, but uses ``sudo``
-for all of those tasks.  However, it needs to be not-root for most of its
-work and for all of the OpenStack services.  ``stack.sh`` specifically
-does not run if started as root.
+``stack.sh`` needs to have root access for a lot of tasks, but uses
+``sudo`` for all of those tasks.  However, it needs to be not-root for
+most of its work and for all of the OpenStack services.  ``stack.sh``
+specifically does not run if started as root.
 
-This is a recent change (Oct 2013) from the previous behaviour of
-automatically creating a ``stack`` user.  Automatically creating
-user accounts is not the right response to running as root, so
-that bit is now an explicit step using ``tools/create-stack-user.sh``.
-Run that (as root!) or just check it out to see what DevStack's
-expectations are for the account it runs under.  Many people simply
-use their usual login (the default 'ubuntu' login on a UEC image
-for example).
+DevStack will not automatically create the user, but provides a helper
+script in ``tools/create-stack-user.sh``.  Run that (as root!) or just
+check it out to see what DevStack's expectations are for the account
+it runs under.  Many people simply use their usual login (the default
+'ubuntu' login on a UEC image for example).
 
 # Customizing
 
-You can override environment variables used in `stack.sh` by creating file
-name `local.conf` with a ``localrc`` section as shown below.  It is likely
-that you will need to do this to tweak your networking configuration should
-you need to access your cloud from a different host.
-
-    [[local|localrc]]
-    VARIABLE=value
-
-See the **Local Configuration** section below for more details.
-
-# Database Backend
-
-Multiple database backends are available. The available databases are defined
-in the lib/databases directory.
-`mysql` is the default database, choose a different one by putting the
-following in the `localrc` section:
-
-    disable_service mysql
-    enable_service postgresql
-
-`mysql` is the default database.
-
-# RPC Backend
-
-Support for a RabbitMQ RPC backend is included. Additional RPC backends may
-be available via external plugins.  Enabling or disabling RabbitMQ is handled
-via the usual service functions and ``ENABLED_SERVICES``.
-
-Example disabling RabbitMQ in ``local.conf``:
-
-    disable_service rabbit
-
-# Apache Frontend
-
-Apache web server can be enabled for wsgi services that support being deployed
-under HTTPD + mod_wsgi. By default, services that recommend running under
-HTTPD + mod_wsgi are deployed under Apache. To use an alternative deployment
-strategy (e.g. eventlet) for services that support an alternative to HTTPD +
-mod_wsgi set ``ENABLE_HTTPD_MOD_WSGI_SERVICES`` to ``False`` in your
-``local.conf``.
-
-Each service that can be run under HTTPD + mod_wsgi also has an override
-toggle available that can be set in your ``local.conf``.
-
-Keystone is run under HTTPD + mod_wsgi by default.
-
-Example (Keystone):
-
-    KEYSTONE_USE_MOD_WSGI="True"
-
-Example (Nova):
-
-    NOVA_USE_MOD_WSGI="True"
-
-Example (Swift):
-
-    SWIFT_USE_MOD_WSGI="True"
-
-# Swift
-
-Swift is disabled by default.  When enabled, it is configured with
-only one replica to avoid being IO/memory intensive on a small
-vm. When running with only one replica the account, container and
-object services will run directly in screen. The others services like
-replicator, updaters or auditor runs in background.
-
-If you would like to enable Swift you can add this to your `localrc` section:
-
-    enable_service s-proxy s-object s-container s-account
-
-If you want a minimal Swift install with only Swift and Keystone you
-can have this instead in your `localrc` section:
-
-    disable_all_services
-    enable_service key mysql s-proxy s-object s-container s-account
-
-If you only want to do some testing of a real normal swift cluster
-with multiple replicas you can do so by customizing the variable
-`SWIFT_REPLICAS` in your `localrc` section (usually to 3).
-
-# Swift S3
-
-If you are enabling `swift3` in `ENABLED_SERVICES` DevStack will
-install the swift3 middleware emulation. Swift will be configured to
-act as a S3 endpoint for Keystone so effectively replacing the
-`nova-objectstore`.
-
-Only Swift proxy server is launched in the screen session all other
-services are started in background and managed by `swift-init` tool.
-
-# Neutron
-
-Basic Setup
-
-In order to enable Neutron in a single node setup, you'll need the
-following settings in your `local.conf`:
-
-    disable_service n-net
-    enable_service q-svc
-    enable_service q-agt
-    enable_service q-dhcp
-    enable_service q-l3
-    enable_service q-meta
-    enable_service q-metering
-
-Then run `stack.sh` as normal.
-
-DevStack supports setting specific Neutron configuration flags to the
-service, ML2 plugin, DHCP and L3 configuration files:
-
-    [[post-config|/$Q_PLUGIN_CONF_FILE]]
-    [ml2]
-    mechanism_drivers=openvswitch,l2population
-
-    [[post-config|$NEUTRON_CONF]]
-    [DEFAULT]
-    quota_port=42
-
-    [[post-config|$Q_L3_CONF_FILE]]
-    [DEFAULT]
-    agent_mode=legacy
-
-    [[post-config|$Q_DHCP_CONF_FILE]]
-    [DEFAULT]
-    dnsmasq_dns_servers = 8.8.8.8,8.8.4.4
-
-The ML2 plugin can run with the OVS, LinuxBridge, or Hyper-V agents on compute
-hosts. This is a simple way to configure the ml2 plugin:
-
-    # VLAN configuration
-    ENABLE_TENANT_VLANS=True
-
-    # GRE tunnel configuration
-    ENABLE_TENANT_TUNNELS=True
-
-    # VXLAN tunnel configuration
-    Q_ML2_TENANT_NETWORK_TYPE=vxlan
-
-The above will default in DevStack to using the OVS on each compute host.
-To change this, set the `Q_AGENT` variable to the agent you want to run
-(e.g. linuxbridge).
-
-    Variable Name                    Notes
-    ----------------------------------------------------------------------------
-    Q_AGENT                          This specifies which agent to run with the
-                                     ML2 Plugin (Typically either `openvswitch`
-                                     or `linuxbridge`).
-                                     Defaults to `openvswitch`.
-    Q_ML2_PLUGIN_MECHANISM_DRIVERS   The ML2 MechanismDrivers to load. The default
-                                     is `openvswitch,linuxbridge`.
-    Q_ML2_PLUGIN_TYPE_DRIVERS        The ML2 TypeDrivers to load. Defaults to
-                                     all available TypeDrivers.
-    Q_ML2_PLUGIN_GRE_TYPE_OPTIONS    GRE TypeDriver options. Defaults to
-                                     `tunnel_id_ranges=1:1000'.
-    Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS  VXLAN TypeDriver options. Defaults to
-                                     `vni_ranges=1001:2000`
-    Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS   VLAN TypeDriver options. Defaults to none.
-
-# Heat
-
-Heat is disabled by default (see `stackrc` file). To enable it explicitly
-you'll need the following settings in your `localrc` section:
-
-    enable_service heat h-api h-api-cfn h-api-cw h-eng
-
-Heat can also run in standalone mode, and be configured to orchestrate
-on an external OpenStack cloud. To launch only Heat in standalone mode
-you'll need the following settings in your `localrc` section:
-
-    disable_all_services
-    enable_service rabbit mysql heat h-api h-api-cfn h-api-cw h-eng
-    HEAT_STANDALONE=True
-    KEYSTONE_SERVICE_HOST=...
-    KEYSTONE_AUTH_HOST=...
-
-# Tempest
-
-If tempest has been successfully configured, a basic set of smoke
-tests can be run as follows:
-
-    $ cd /opt/stack/tempest
-    $ tox -efull  tempest.scenario.test_network_basic_ops
-
-By default tempest is downloaded and the config file is generated, but the
-tempest package is not installed in the system's global site-packages (the
-package install includes installing dependences). So tempest won't run
-outside of tox. If you would like to install it add the following to your
-``localrc`` section:
-
-    INSTALL_TEMPEST=True
-
-# DevStack on Xenserver
-
-If you would like to use Xenserver as the hypervisor, please refer
-to the instructions in `./tools/xen/README.md`.
-
-# Additional Projects
-
-DevStack has a hook mechanism to call out to a dispatch script at specific
-points in the execution of `stack.sh`, `unstack.sh` and `clean.sh`.  This
-allows upper-layer projects, especially those that the lower layer projects
-have no dependency on, to be added to DevStack without modifying the core
-scripts.  Tempest is built this way as an example of how to structure the
-dispatch script, see `extras.d/80-tempest.sh`.  See `extras.d/README.md`
-for more information.
-
-# Multi-Node Setup
-
-A more interesting setup involves running multiple compute nodes, with Neutron
-networks connecting VMs on different compute nodes.
-You should run at least one "controller node", which should have a `stackrc`
-that includes at least:
-
-    disable_service n-net
-    enable_service q-svc
-    enable_service q-agt
-    enable_service q-dhcp
-    enable_service q-l3
-    enable_service q-meta
-    enable_service neutron
-
-You likely want to change your `localrc` section to run a scheduler that
-will balance VMs across hosts:
-
-    SCHEDULER=nova.scheduler.filter_scheduler.FilterScheduler
-
-You can then run many compute nodes, each of which should have a `stackrc`
-which includes the following, with the IP address of the above controller node:
-
-    ENABLED_SERVICES=n-cpu,rabbit,neutron,q-agt
-    SERVICE_HOST=[IP of controller node]
-    MYSQL_HOST=$SERVICE_HOST
-    RABBIT_HOST=$SERVICE_HOST
-    Q_HOST=$SERVICE_HOST
-    MATCHMAKER_REDIS_HOST=$SERVICE_HOST
-
-# Multi-Region Setup
-
-We want to setup two devstack (RegionOne and RegionTwo) with shared keystone
-(same users and services) and horizon.
-Keystone and Horizon will be located in RegionOne.
-Full spec is available at:
-https://wiki.openstack.org/wiki/Heat/Blueprints/Multi_Region_Support_for_Heat.
-
-In RegionOne:
-
-    REGION_NAME=RegionOne
-
-In RegionTwo:
-
-    disable_service horizon
-    KEYSTONE_SERVICE_HOST=<KEYSTONE_IP_ADDRESS_FROM_REGION_ONE>
-    KEYSTONE_AUTH_HOST=<KEYSTONE_IP_ADDRESS_FROM_REGION_ONE>
-    REGION_NAME=RegionTwo
-
-# Cells
-
-Cells is a new scaling option with a full spec at:
-http://wiki.openstack.org/blueprint-nova-compute-cells.
-
-To setup a cells environment add the following to your `localrc` section:
-
-    enable_service n-cell
-
-Be aware that there are some features currently missing in cells, one notable
-one being security groups.  The exercises have been patched to disable
-functionality not supported by cells.
-
-# IPv6
-
-By default, most Openstack services are bound to 0.0.0.0
-and service endpoints are registered as IPv4 addresses.
-A new variable was created to control this behavior, and to
-allow for operation over IPv6 instead of IPv4.
-
-For this, add the following to `local.conf`:
-
-    SERVICE_IP_VERSION=6
-
-When set to "6" devstack services will open listen sockets on ::
-and service endpoints will be registered using HOST_IPV6 as the
-address.  The default value for this setting is `4`.  Dual-mode
-support, for example `4+6` is not currently supported.
-
-
-# Local Configuration
-
-Historically DevStack has used ``localrc`` to contain all local configuration
-and customizations. More and more of the configuration variables available for
-DevStack are passed-through to the individual project configuration files.
-The old mechanism for this required specific code for each file and did not
-scale well.  This is handled now by a master local configuration file.
-
-# local.conf
-
-The new config file ``local.conf`` is an extended-INI format that introduces
-a new meta-section header that provides some additional information such
-as a phase name and destination config filename:
-
-    [[ <phase> | <config-file-name> ]]
-
-where ``<phase>`` is one of a set of phase names defined by ``stack.sh``
-and ``<config-file-name>`` is the configuration filename.  The filename is
-eval'ed in the ``stack.sh`` context so all environment variables are
-available and may be used.  Using the project config file variables in
-the header is strongly suggested (see the ``NOVA_CONF`` example below).
-If the path of the config file does not exist it is skipped.
-
-The defined phases are:
-
-* **local** - extracts ``localrc`` from ``local.conf`` before ``stackrc`` is sourced
-* **post-config** - runs after the layer 2 services are configured
-                    and before they are started
-* **extra** - runs after services are started and before any files
-              in ``extra.d`` are executed
-* **post-extra** - runs after files in ``extra.d`` are executed
-
-The file is processed strictly in sequence; meta-sections may be specified more
-than once but if any settings are duplicated the last to appear in the file
-will be used.
-
-    [[post-config|$NOVA_CONF]]
-    [DEFAULT]
-    use_syslog = True
-
-    [osapi_v3]
-    enabled = False
-
-A specific meta-section ``local|localrc`` is used to provide a default
-``localrc`` file (actually ``.localrc.auto``).  This allows all custom
-settings for DevStack to be contained in a single file.  If ``localrc``
-exists it will be used instead to preserve backward-compatibility.
-
-    [[local|localrc]]
-    FIXED_RANGE=10.254.1.0/24
-    ADMIN_PASSWORD=speciale
-    LOGFILE=$DEST/logs/stack.sh.log
-
-Note that ``Q_PLUGIN_CONF_FILE`` is unique in that it is assumed to *NOT*
-start with a ``/`` (slash) character.  A slash will need to be added:
-
-    [[post-config|/$Q_PLUGIN_CONF_FILE]]
+DevStack can be extensively configured via the configuration file
+`local.conf`.  It is likely that you will need to provide and modify
+this file if you want anything other than the most basic setup.  Start
+by reading the [configuration guide](doc/source/configuration.rst) for
+details of the configuration file and the many available options.
diff --git a/clean.sh b/clean.sh
index 74bcaee..ae28aa9 100755
--- a/clean.sh
+++ b/clean.sh
@@ -41,13 +41,13 @@
 source $TOP_DIR/lib/tls
 
 source $TOP_DIR/lib/oslo
+source $TOP_DIR/lib/lvm
 source $TOP_DIR/lib/horizon
 source $TOP_DIR/lib/keystone
 source $TOP_DIR/lib/glance
 source $TOP_DIR/lib/nova
 source $TOP_DIR/lib/cinder
 source $TOP_DIR/lib/swift
-source $TOP_DIR/lib/ceilometer
 source $TOP_DIR/lib/heat
 source $TOP_DIR/lib/neutron-legacy
 source $TOP_DIR/lib/ironic
@@ -134,7 +134,9 @@
 
 # Clean up files
 
-FILES_TO_CLEAN=".localrc.auto docs/files docs/html shocco/ stack-screenrc test*.conf* test.ini*"
+FILES_TO_CLEAN=".localrc.auto .localrc.password "
+FILES_TO_CLEAN+="docs/files docs/html shocco/ "
+FILES_TO_CLEAN+="stack-screenrc test*.conf* test.ini* "
 FILES_TO_CLEAN+=".stackenv .prereqs"
 
 for file in $FILES_TO_CLEAN; do
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 96f91ec..386fbbb 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -2,40 +2,28 @@
 Configuration
 =============
 
-DevStack has always tried to be mostly-functional with a minimal amount
-of configuration. The number of options has ballooned as projects add
-features, new projects added and more combinations need to be tested.
-Historically DevStack obtained all local configuration and
-customizations from a ``localrc`` file. The number of configuration
-variables that are simply passed-through to the individual project
-configuration files is also increasing. The old mechanism for this
-(``EXTRAS_OPTS`` and friends) required specific code for each file and
-did not scale well.
-
-In Oct 2013 a new configuration method was introduced (in `review
-46768 <https://review.openstack.org/#/c/46768/>`__) to hopefully
-simplify this process and meet the following goals:
-
--  contain all non-default local configuration in a single file
--  be backward-compatible with ``localrc`` to smooth the transition
-   process
--  allow settings in arbitrary configuration files to be changed
+.. contents::
+   :local:
+   :depth: 1
 
 local.conf
 ==========
 
-The new configuration file is ``local.conf`` and resides in the root
-DevStack directory like the old ``localrc`` file. It is a modified INI
-format file that introduces a meta-section header to carry additional
-information regarding the configuration files to be changed.
+DevStack configuration is modified via the file ``local.conf``.  It is
+a modified INI format file that introduces a meta-section header to
+carry additional information regarding the configuration files to be
+changed.
+
+A sample is provided in ``devstack/samples``
 
 The new header is similar to a normal INI section header but with double
 brackets (``[[ ... ]]``) and two internal fields separated by a pipe
-(``|``):
-
+(``|``). Note that there are no spaces between the double brackets and the
+internal fields. Likewise, there are no spaces between the pipe and the
+internal fields:
 ::
 
-    [[ <phase> | <config-file-name> ]]
+    '[[' <phase> '|' <config-file-name> ']]'
 
 where ``<phase>`` is one of a set of phase names defined by ``stack.sh``
 and ``<config-file-name>`` is the configuration filename. The filename
@@ -143,36 +131,198 @@
 Setting it here also makes it available for ``openrc`` to set ``OS_AUTH_URL``.
 ``HOST_IPV6`` is not set by default.
 
-Common Configuration Variables
-==============================
+Historical Notes
+================
+
+Historically DevStack obtained all local configuration and
+customizations from a ``localrc`` file.  In Oct 2013 the
+``local.conf`` configuration method was introduced (in `review 46768
+<https://review.openstack.org/#/c/46768/>`__) to simplify this
+process.
+
+Configuration Notes
+===================
+
+.. contents::
+   :local:
 
 Installation Directory
 ----------------------
 
-    | *Default: ``DEST=/opt/stack``*
-    |  The DevStack install directory is set by the ``DEST`` variable.
-    |  By setting it early in the ``localrc`` section you can reference it
-       in later variables. It can be useful to set it even though it is not
-       changed from the default value.
-    |
+The DevStack install directory is set by the ``DEST`` variable.  By
+default it is ``/opt/stack``.
+
+By setting it early in the ``localrc`` section you can reference it in
+later variables.  It can be useful to set it even though it is not
+changed from the default value.
 
     ::
 
         DEST=/opt/stack
 
+Logging
+-------
+
+Enable Logging
+~~~~~~~~~~~~~~
+
+By default ``stack.sh`` output is only written to the console where it
+runs. It can be sent to a file in addition to the console by setting
+``LOGFILE`` to the fully-qualified name of the destination log file. A
+timestamp will be appended to the given filename for each run of
+``stack.sh``.
+
+    ::
+
+        LOGFILE=$DEST/logs/stack.sh.log
+
+Old log files are cleaned automatically if ``LOGDAYS`` is set to the
+number of days of old log files to keep.
+
+    ::
+
+        LOGDAYS=1
+
+The some of the project logs (Nova, Cinder, etc) will be colorized by
+default (if ``SYSLOG`` is not set below); this can be turned off by
+setting ``LOG_COLOR`` to ``False``.
+
+    ::
+
+        LOG_COLOR=False
+
+Logging the Service Output
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+DevStack will log the ``stdout`` output of the services it starts.
+When using ``screen`` this logs the output in the screen windows to a
+file.  Without ``screen`` this simply redirects stdout of the service
+process to a file in ``LOGDIR``.
+
+    ::
+
+        LOGDIR=$DEST/logs
+
+Note the use of ``DEST`` to locate the main install directory; this
+is why we suggest setting it in ``local.conf``.
+
+Enabling Syslog
+~~~~~~~~~~~~~~~
+
+Logging all services to a single syslog can be convenient. Enable
+syslogging by setting ``SYSLOG`` to ``True``. If the destination log
+host is not localhost ``SYSLOG_HOST`` and ``SYSLOG_PORT`` can be used
+to direct the message stream to the log host.
+
+    ::
+
+        SYSLOG=True
+        SYSLOG_HOST=$HOST_IP
+        SYSLOG_PORT=516
+
+
+Example Logging Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For example, non-interactive installs probably wish to save output to
+a file, keep service logs and disable color in the stored files.
+
+   ::
+
+       [[local|localrc]]
+       DEST=/opt/stack/
+       LOGDIR=$DEST/logs
+       LOGFILE=$LOGDIR/stack.sh.log
+       LOG_COLOR=False
+
+Database Backend
+----------------
+
+Multiple database backends are available. The available databases are defined
+in the lib/databases directory.
+``mysql`` is the default database, choose a different one by putting the
+following in the ``localrc`` section:
+
+   ::
+
+      disable_service mysql
+      enable_service postgresql
+
+``mysql`` is the default database.
+
+RPC Backend
+-----------
+
+Support for a RabbitMQ RPC backend is included. Additional RPC
+backends may be available via external plugins.  Enabling or disabling
+RabbitMQ is handled via the usual service functions and
+``ENABLED_SERVICES``.
+
+Example disabling RabbitMQ in ``local.conf``:
+
+::
+
+    disable_service rabbit
+
+
+Apache Frontend
+---------------
+
+The Apache web server can be enabled for wsgi services that support
+being deployed under HTTPD + mod_wsgi. By default, services that
+recommend running under HTTPD + mod_wsgi are deployed under Apache. To
+use an alternative deployment strategy (e.g. eventlet) for services
+that support an alternative to HTTPD + mod_wsgi set
+``ENABLE_HTTPD_MOD_WSGI_SERVICES`` to ``False`` in your
+``local.conf``.
+
+Each service that can be run under HTTPD + mod_wsgi also has an
+override toggle available that can be set in your ``local.conf``.
+
+Keystone is run under Apache with ``mod_wsgi`` by default.
+
+Example (Keystone)
+
+::
+
+    KEYSTONE_USE_MOD_WSGI="True"
+
+Example (Nova):
+
+::
+
+    NOVA_USE_MOD_WSGI="True"
+
+Example (Swift):
+
+::
+
+    SWIFT_USE_MOD_WSGI="True"
+
+Example (Heat):
+
+::
+
+    HEAT_USE_MOD_WSGI="True"
+
+
+Example (Cinder):
+
+::
+
+    CINDER_USE_MOD_WSGI="True"
+
+
 Libraries from Git
 ------------------
 
-   | *Default: ``LIBS_FROM_GIT=""``*
-
-   | By default devstack installs OpenStack server components from
-     git, however it installs client libraries from released versions
-     on pypi. This is appropriate if you are working on server
-     development, but if you want to see how an unreleased version of
-     the client affects the system you can have devstack install it
-     from upstream, or from local git trees.
-   | Multiple libraries can be specified as a comma separated list.
-   |
+By default devstack installs OpenStack server components from git,
+however it installs client libraries from released versions on pypi.
+This is appropriate if you are working on server development, but if
+you want to see how an unreleased version of the client affects the
+system you can have devstack install it from upstream, or from local
+git trees by specifying it in ``LIBS_FROM_GIT``.  Multiple libraries
+can be specified as a comma separated list.
 
    ::
 
@@ -181,99 +331,37 @@
 Virtual Environments
 --------------------
 
-  | *Default: ``USE_VENV=False``*
-  |   Enable the use of Python virtual environments by setting ``USE_VENV``
-      to ``True``.  This will enable the creation of venvs for each project
-      that is defined in the ``PROJECT_VENV`` array.
+Enable the use of Python virtual environments by setting ``USE_VENV``
+to ``True``.  This will enable the creation of venvs for each project
+that is defined in the ``PROJECT_VENV`` array.
 
-  | *Default: ``PROJECT_VENV['<project>']='<project-dir>.venv'*
-  |   Each entry in the ``PROJECT_VENV`` array contains the directory name
-      of a venv to be used for the project.  The array index is the project
-      name.  Multiple projects can use the same venv if desired.
+Each entry in the ``PROJECT_VENV`` array contains the directory name
+of a venv to be used for the project.  The array index is the project
+name.  Multiple projects can use the same venv if desired.
 
   ::
 
     PROJECT_VENV["glance"]=${GLANCE_DIR}.venv
 
-  | *Default: ``ADDITIONAL_VENV_PACKAGES=""``*
-  |   A comma-separated list of additional packages to be installed into each
-      venv.  Often projects will not have certain packages listed in its
-      ``requirements.txt`` file because they are 'optional' requirements,
-      i.e. only needed for certain configurations.  By default, the enabled
-      databases will have their Python bindings added when they are enabled.
+``ADDITIONAL_VENV_PACKAGES`` is a comma-separated list of additional
+packages to be installed into each venv.  Often projects will not have
+certain packages listed in its ``requirements.txt`` file because they
+are 'optional' requirements, i.e. only needed for certain
+configurations.  By default, the enabled databases will have their
+Python bindings added when they are enabled.
 
-Enable Logging
---------------
+  ::
 
-    | *Defaults: ``LOGFILE="" LOGDAYS=7 LOG_COLOR=True``*
-    |  By default ``stack.sh`` output is only written to the console
-       where it runs. It can be sent to a file in addition to the console
-       by setting ``LOGFILE`` to the fully-qualified name of the
-       destination log file. A timestamp will be appended to the given
-       filename for each run of ``stack.sh``.
-    |
+     ADDITIONAL_VENV_PACKAGES="python-foo, python-bar"
 
-    ::
-
-        LOGFILE=$DEST/logs/stack.sh.log
-
-    Old log files are cleaned automatically if ``LOGDAYS`` is set to the
-    number of days of old log files to keep.
-
-    ::
-
-        LOGDAYS=1
-
-    The some of the project logs (Nova, Cinder, etc) will be colorized
-    by default (if ``SYSLOG`` is not set below); this can be turned off
-    by setting ``LOG_COLOR`` False.
-
-    ::
-
-        LOG_COLOR=False
-
-Logging the Service Output
---------------------------
-
-    | *Default: ``LOGDIR=""``*
-    |  DevStack will log the stdout output of the services it starts.
-       When using ``screen`` this logs the output in the screen windows
-       to a file.  Without ``screen`` this simply redirects stdout of
-       the service process to a file in ``LOGDIR``.
-    |
-
-    ::
-
-        LOGDIR=$DEST/logs
-
-    *Note the use of ``DEST`` to locate the main install directory; this
-    is why we suggest setting it in ``local.conf``.*
-
-Enabling Syslog
----------------
-
-    | *Default: ``SYSLOG=False SYSLOG_HOST=$HOST_IP SYSLOG_PORT=516``*
-    |  Logging all services to a single syslog can be convenient. Enable
-       syslogging by setting ``SYSLOG`` to ``True``. If the destination log
-       host is not localhost ``SYSLOG_HOST`` and ``SYSLOG_PORT`` can be
-       used to direct the message stream to the log host.
-    |
-
-    ::
-
-        SYSLOG=True
-        SYSLOG_HOST=$HOST_IP
-        SYSLOG_PORT=516
 
 A clean install every time
 --------------------------
 
-    | *Default: ``RECLONE=""``*
-    |  By default ``stack.sh`` only clones the project repos if they do
-       not exist in ``$DEST``. ``stack.sh`` will freshen each repo on each
-       run if ``RECLONE`` is set to ``yes``. This avoids having to manually
-       remove repos in order to get the current branch from ``$GIT_BASE``.
-    |
+By default ``stack.sh`` only clones the project repos if they do not
+exist in ``$DEST``. ``stack.sh`` will freshen each repo on each run if
+``RECLONE`` is set to ``yes``. This avoids having to manually remove
+repos in order to get the current branch from ``$GIT_BASE``.
 
     ::
 
@@ -282,139 +370,90 @@
 Upgrade packages installed by pip
 ---------------------------------
 
-    | *Default: ``PIP_UPGRADE=""``*
-    |  By default ``stack.sh`` only installs Python packages if no version
-       is currently installed or the current version does not match a specified
-       requirement. If ``PIP_UPGRADE`` is set to ``True`` then existing required
-       Python packages will be upgraded to the most recent version that
-       matches requirements.
-    |
+By default ``stack.sh`` only installs Python packages if no version is
+currently installed or the current version does not match a specified
+requirement. If ``PIP_UPGRADE`` is set to ``True`` then existing
+required Python packages will be upgraded to the most recent version
+that matches requirements.
 
     ::
 
         PIP_UPGRADE=True
 
-Swift
------
-
-    | Default: SWIFT_HASH=""
-    | SWIFT_REPLICAS=1
-    | SWIFT_DATA_DIR=$DEST/data/swift
-
-    | Swift is now used as the back-end for the S3-like object store.
-      When enabled Nova's objectstore (n-obj in ENABLED_SERVICES) is
-      automatically disabled. Enable Swift by adding it services to
-      ENABLED_SERVICES: enable_service s-proxy s-object s-container
-      s-account
-
-    Setting Swift's hash value is required and you will be prompted for
-    it if Swift is enabled so just set it to something already:
-
-    ::
-
-        SWIFT_HASH=66a3d6b56c1f479c8b4e70ab5c2000f5
-
-    For development purposes the default number of replicas is set to
-    ``1`` to reduce the overhead required. To better simulate a
-    production deployment set this to ``3`` or more.
-
-    ::
-
-        SWIFT_REPLICAS=3
-
-    The data for Swift is stored in the source tree by default (in
-    ``$DEST/swift/data``) and can be moved by setting
-    ``SWIFT_DATA_DIR``. The specified directory will be created if it
-    does not exist.
-
-    ::
-
-        SWIFT_DATA_DIR=$DEST/data/swift
-
-    *Note: Previously just enabling ``swift`` was sufficient to start
-    the Swift services. That does not provide proper service
-    granularity, particularly in multi-host configurations, and is
-    considered deprecated. Some service combination tests now check for
-    specific Swift services and the old blanket acceptance will longer
-    work correctly.*
 
 Service Catalog Backend
 -----------------------
 
-    | *Default: ``KEYSTONE_CATALOG_BACKEND=sql``*
-    |  DevStack uses Keystone's ``sql`` service catalog backend. An
-       alternate ``template`` backend is also available. However, it does
-       not support the ``service-*`` and ``endpoint-*`` commands of the
-       ``keystone`` CLI. To do so requires the ``sql`` backend be enabled:
-    |
+By default DevStack uses Keystone's ``sql`` service catalog backend.
+An alternate ``template`` backend is also available, however, it does
+not support the ``service-*`` and ``endpoint-*`` commands of the
+``keystone`` CLI.  To do so requires the ``sql`` backend be enabled
+with ``KEYSTONE_CATALOG_BACKEND``:
 
     ::
 
         KEYSTONE_CATALOG_BACKEND=template
 
-    DevStack's default configuration in ``sql`` mode is set in
-    ``files/keystone_data.sh``
+DevStack's default configuration in ``sql`` mode is set in
+``lib/keystone``
 
-Cinder
-------
 
-    | Default:
-    | VOLUME_GROUP="stack-volumes" VOLUME_NAME_PREFIX="volume-" VOLUME_BACKING_FILE_SIZE=10250M
-    |  The logical volume group used to hold the Cinder-managed volumes
-       is set by ``VOLUME_GROUP``, the logical volume name prefix is set
-       with ``VOLUME_NAME_PREFIX`` and the size of the volume backing file
-       is set with ``VOLUME_BACKING_FILE_SIZE``.
-    |
+Guest Images
+------------
+
+Images provided in URLS via the comma-separated ``IMAGE_URLS``
+variable will be downloaded and uploaded to glance by DevStack.
+
+Default guest-images are predefined for each type of hypervisor and
+their testing-requirements in ``stack.sh``.  Setting
+``DOWNLOAD_DEFAULT_IMAGES=False`` will prevent DevStack downloading
+these default images; in that case, you will want to populate
+``IMAGE_URLS`` with sufficient images to satisfy testing-requirements.
 
     ::
 
-        VOLUME_GROUP="stack-volumes"
-        VOLUME_NAME_PREFIX="volume-"
-        VOLUME_BACKING_FILE_SIZE=10250M
+        DOWNLOAD_DEFAULT_IMAGES=False
+        IMAGE_URLS="http://foo.bar.com/image.qcow,"
+        IMAGE_URLS+="http://foo.bar.com/image2.qcow"
 
-Multi-host DevStack
--------------------
 
-    | *Default: ``MULTI_HOST=False``*
-    |  Running DevStack with multiple hosts requires a custom
-       ``local.conf`` section for each host. The master is the same as a
-       single host installation with ``MULTI_HOST=True``. The slaves have
-       fewer services enabled and a couple of host variables pointing to
-       the master.
-    |  **Master**
+Instance Type
+-------------
+
+``DEFAULT_INSTANCE_TYPE`` can be used to configure the default instance
+type. When this parameter is not specified, Devstack creates additional
+micro & nano flavors for really small instances to run Tempest tests.
+
+For guests with larger memory requirements, ``DEFAULT_INSTANCE_TYPE``
+should be specified in the configuration file so Tempest selects the
+default flavors instead.
+
+KVM on Power with QEMU 2.4 requires 512 MB to load the firmware -
+`QEMU 2.4 - PowerPC <http://wiki.qemu.org/ChangeLog/2.4>`__ so users
+running instances on ppc64/ppc64le can choose one of the default
+created flavors as follows:
 
     ::
 
-        MULTI_HOST=True
+        DEFAULT_INSTANCE_TYPE=m1.tiny
 
-    **Slave**
-
-    ::
-
-        MYSQL_HOST=w.x.y.z
-        RABBIT_HOST=w.x.y.z
-        GLANCE_HOSTPORT=w.x.y.z:9292
-        ENABLED_SERVICES=n-vol,n-cpu,n-net,n-api
 
 IP Version
 ----------
 
-    | Default: ``IP_VERSION=4+6``
-    | This setting can be used to configure DevStack to create either an IPv4,
-      IPv6, or dual stack tenant data network by setting ``IP_VERSION`` to
-      either ``IP_VERSION=4``, ``IP_VERSION=6``, or ``IP_VERSION=4+6``
-      respectively. This functionality requires that the Neutron networking
-      service is enabled by setting the following options:
-    |
+``IP_VERSION`` can be used to configure DevStack to create either an
+IPv4, IPv6, or dual-stack tenant data-network by with either
+``IP_VERSION=4``, ``IP_VERSION=6``, or ``IP_VERSION=4+6``
+respectively.  This functionality requires that the Neutron networking
+service is enabled by setting the following options:
 
     ::
 
         disable_service n-net
         enable_service q-svc q-agt q-dhcp q-l3
 
-    | The following optional variables can be used to alter the default IPv6
-      behavior:
-    |
+The following optional variables can be used to alter the default IPv6
+behavior:
 
     ::
 
@@ -423,52 +462,190 @@
         FIXED_RANGE_V6=fd$IPV6_GLOBAL_ID::/64
         IPV6_PRIVATE_NETWORK_GATEWAY=fd$IPV6_GLOBAL_ID::1
 
-    | *Note: ``FIXED_RANGE_V6`` and ``IPV6_PRIVATE_NETWORK_GATEWAY``
-      can be configured with any valid IPv6 prefix. The default values make
-      use of an auto-generated ``IPV6_GLOBAL_ID`` to comply with RFC 4193.*
-    |
+*Note*: ``FIXED_RANGE_V6`` and ``IPV6_PRIVATE_NETWORK_GATEWAY`` can be
+configured with any valid IPv6 prefix. The default values make use of
+an auto-generated ``IPV6_GLOBAL_ID`` to comply with RFC4193.
 
-    | Default: ``SERVICE_IP_VERSION=4``
-    | This setting can be used to configure DevStack to enable services to
-      operate over either IPv4 or IPv6, by setting ``SERVICE_IP_VERSION`` to
-      either ``SERVICE_IP_VERSION=4`` or ``SERVICE_IP_VERSION=6`` respectively.
-      When set to ``4`` devstack services will open listen sockets on 0.0.0.0
-      and service endpoints will be registered using ``HOST_IP`` as the address.
-      When set to ``6`` devstack services will open listen sockets on :: and
-      service endpoints will be registered using ``HOST_IPV6`` as the address.
-      The default value for this setting is ``4``.  Dual-mode support, for
-      example ``4+6`` is not currently supported.
-    | The following optional variable can be used to alter the default IPv6
-      address used:
-    |
+Service Version
+~~~~~~~~~~~~~~~
+
+DevStack can enable service operation over either IPv4 or IPv6 by
+setting ``SERVICE_IP_VERSION`` to either ``SERVICE_IP_VERSION=4`` or
+``SERVICE_IP_VERSION=6`` respectively.
+
+When set to ``4`` devstack services will open listen sockets on
+``0.0.0.0`` and service endpoints will be registered using ``HOST_IP``
+as the address.
+
+When set to ``6`` devstack services will open listen sockets on ``::``
+and service endpoints will be registered using ``HOST_IPV6`` as the
+address.
+
+The default value for this setting is ``4``.  Dual-mode support, for
+example ``4+6`` is not currently supported.  ``HOST_IPV6`` can
+optionally be used to alter the default IPv6 address
 
     ::
 
         HOST_IPV6=${some_local_ipv6_address}
 
-Examples
-========
+Multi-node setup
+~~~~~~~~~~~~~~~~
 
--  Eliminate a Cinder pass-through (``CINDER_PERIODIC_INTERVAL``):
+See the :doc:`multi-node lab guide<guides/multinode-lab>`
 
-   ::
+Projects
+--------
 
-       [[post-config|$CINDER_CONF]]
-       [DEFAULT]
-       periodic_interval = 60
+Neutron
+~~~~~~~
 
--  Sample ``local.conf`` with screen logging enabled:
+See the :doc:`neutron configuration guide<guides/neutron>` for
+details on configuration of Neutron
 
-   ::
 
-       [[local|localrc]]
-       FIXED_RANGE=10.254.1.0/24
-       NETWORK_GATEWAY=10.254.1.1
-       LOGDAYS=1
-       LOGDIR=$DEST/logs
-       LOGFILE=$LOGDIR/stack.sh.log
-       ADMIN_PASSWORD=quiet
-       DATABASE_PASSWORD=$ADMIN_PASSWORD
-       RABBIT_PASSWORD=$ADMIN_PASSWORD
-       SERVICE_PASSWORD=$ADMIN_PASSWORD
-       SERVICE_TOKEN=a682f596-76f3-11e3-b3b2-e716f9080d50
+Swift
+~~~~~
+
+Swift is disabled by default.  When enabled, it is configured with
+only one replica to avoid being IO/memory intensive on a small
+VM. When running with only one replica the account, container and
+object services will run directly in screen. The others services like
+replicator, updaters or auditor runs in background.
+
+If you would like to enable Swift you can add this to your ``localrc``
+section:
+
+::
+
+    enable_service s-proxy s-object s-container s-account
+
+If you want a minimal Swift install with only Swift and Keystone you
+can have this instead in your ``localrc`` section:
+
+::
+
+    disable_all_services
+    enable_service key mysql s-proxy s-object s-container s-account
+
+If you only want to do some testing of a real normal swift cluster
+with multiple replicas you can do so by customizing the variable
+``SWIFT_REPLICAS`` in your ``localrc`` section (usually to 3).
+
+Swift S3
+++++++++
+
+If you are enabling ``swift3`` in ``ENABLED_SERVICES`` DevStack will
+install the swift3 middleware emulation. Swift will be configured to
+act as a S3 endpoint for Keystone so effectively replacing the
+``nova-objectstore``.
+
+Only Swift proxy server is launched in the screen session all other
+services are started in background and managed by ``swift-init`` tool.
+
+Heat
+~~~~
+
+Heat is disabled by default (see ``stackrc`` file). To enable it
+explicitly you'll need the following settings in your ``localrc``
+section
+
+::
+
+    enable_service heat h-api h-api-cfn h-api-cw h-eng
+
+Heat can also run in standalone mode, and be configured to orchestrate
+on an external OpenStack cloud. To launch only Heat in standalone mode
+you'll need the following settings in your ``localrc`` section
+
+::
+
+    disable_all_services
+    enable_service rabbit mysql heat h-api h-api-cfn h-api-cw h-eng
+    HEAT_STANDALONE=True
+    KEYSTONE_SERVICE_HOST=...
+    KEYSTONE_AUTH_HOST=...
+
+Tempest
+~~~~~~~
+
+If tempest has been successfully configured, a basic set of smoke
+tests can be run as follows:
+
+::
+
+    $ cd /opt/stack/tempest
+    $ tox -efull  tempest.scenario.test_network_basic_ops
+
+By default tempest is downloaded and the config file is generated, but the
+tempest package is not installed in the system's global site-packages (the
+package install includes installing dependences). So tempest won't run
+outside of tox. If you would like to install it add the following to your
+``localrc`` section:
+
+::
+
+    INSTALL_TEMPEST=True
+
+
+Xenserver
+~~~~~~~~~
+
+If you would like to use Xenserver as the hypervisor, please refer to
+the instructions in ``./tools/xen/README.md``.
+
+Cells
+~~~~~
+
+`Cells <http://wiki.openstack.org/blueprint-nova-compute-cells>`__ is
+an alternative scaling option.  To setup a cells environment add the
+following to your ``localrc`` section:
+
+::
+
+    enable_service n-cell
+
+Be aware that there are some features currently missing in cells, one
+notable one being security groups.  The exercises have been patched to
+disable functionality not supported by cells.
+
+Cinder
+~~~~~~
+
+The logical volume group used to hold the Cinder-managed volumes is
+set by ``VOLUME_GROUP``, the logical volume name prefix is set with
+``VOLUME_NAME_PREFIX`` and the size of the volume backing file is set
+with ``VOLUME_BACKING_FILE_SIZE``.
+
+    ::
+
+        VOLUME_GROUP="stack-volumes"
+        VOLUME_NAME_PREFIX="volume-"
+        VOLUME_BACKING_FILE_SIZE=10250M
+
+
+Keystone
+~~~~~~~~
+
+Multi-Region Setup
+++++++++++++++++++
+
+We want to setup two devstack (RegionOne and RegionTwo) with shared
+keystone (same users and services) and horizon.  Keystone and Horizon
+will be located in RegionOne.  Full spec is available at:
+`<https://wiki.openstack.org/wiki/Heat/Blueprints/Multi_Region_Support_for_Heat>`__.
+
+In RegionOne:
+
+::
+
+    REGION_NAME=RegionOne
+
+In RegionTwo:
+
+::
+   
+    disable_service horizon
+    KEYSTONE_SERVICE_HOST=<KEYSTONE_IP_ADDRESS_FROM_REGION_ONE>
+    KEYSTONE_AUTH_HOST=<KEYSTONE_IP_ADDRESS_FROM_REGION_ONE>
+    REGION_NAME=RegionTwo
diff --git a/doc/source/faq.rst b/doc/source/faq.rst
index 0db8932..7aca8d0 100644
--- a/doc/source/faq.rst
+++ b/doc/source/faq.rst
@@ -54,7 +54,7 @@
 releases other than those documented in ``README.md`` on a best-effort
 basis.
 
-Are there any differences between Ubuntu and Centos/Fedora support?
+Are there any differences between Ubuntu and CentOS/Fedora support?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Both should work well and are tested by DevStack CI.
@@ -124,24 +124,30 @@
 
         enable_service q-svc
 
-How do I run a specific OpenStack milestone?
+How do I run a specific OpenStack release?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-OpenStack milestones have tags set in the git repo. Set the
-appropriate tag in the ``*_BRANCH`` variables in ``local.conf``.
-Swift is on its own release schedule so pick a tag in the Swift repo
-that is just before the milestone release. For example:
+DevStack master tracks the upstream master of all the projects. If you
+would like to run a stable branch of OpenStack, you should use the
+corresponding stable branch of DevStack as well. For instance the
+``stable/kilo`` version of DevStack will already default to all the
+projects running at ``stable/kilo`` levels.
 
-    ::
+Note: it's also possible to manually adjust the ``*_BRANCH`` variables
+further if you would like to test specific milestones, or even custom
+out of tree branches. This is done with entries like the following in
+your ``local.conf``
+
+::
 
         [[local|localrc]]
-        GLANCE_BRANCH=stable/kilo
-        HORIZON_BRANCH=stable/kilo
-        KEYSTONE_BRANCH=stable/kilo
-        NOVA_BRANCH=stable/kilo
-        GLANCE_BRANCH=stable/kilo
-        NEUTRON_BRANCH=stable/kilo
-        SWIFT_BRANCH=2.3.0
+        GLANCE_BRANCH=11.0.0.0rc1
+        NOVA_BRANCH=12.0.0.0.rc1
+
+
+Upstream DevStack is only tested with master and stable
+branches. Setting custom BRANCH definitions is not guaranteed to
+produce working results.
 
 What can I do about RabbitMQ not wanting to start on my fresh new VM?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/source/guides/devstack-with-lbaas-v2.rst b/doc/source/guides/devstack-with-lbaas-v2.rst
index f679783..f3bd2fe 100644
--- a/doc/source/guides/devstack-with-lbaas-v2.rst
+++ b/doc/source/guides/devstack-with-lbaas-v2.rst
@@ -1,13 +1,17 @@
-Configure Load-Balancer in Kilo
+Configure Load-Balancer Version 2
 =================================
 
-The Kilo release of OpenStack will support Version 2 of the neutron load balancer. Until now, using OpenStack `LBaaS V2 <http://docs.openstack.org/api/openstack-network/2.0/content/lbaas_ext.html>`_ has required a good understanding of neutron and LBaaS architecture and several manual steps.
+Starting in the OpenStack Liberty release, the
+`neutron LBaaS v2 API <http://developer.openstack.org/api-ref-networking-v2-ext.html>`_
+is now stable while the LBaaS v1 API has been deprecated.  The LBaaS v2 reference
+driver is based on Octavia.
 
 
 Phase 1: Create DevStack + 2 nova instances
 --------------------------------------------
 
-First, set up a vm of your choice with at least 8 GB RAM and 16 GB disk space, make sure it is updated. Install git and any other developer tools you find useful.
+First, set up a vm of your choice with at least 8 GB RAM and 16 GB disk space,
+make sure it is updated. Install git and any other developer tools you find useful.
 
 Install devstack
 
@@ -17,13 +21,14 @@
     cd devstack
 
 
-Edit your `local.conf` to look like
+Edit your ``local.conf`` to look like
 
   ::
 
     [[local|localrc]]
     # Load the external LBaaS plugin.
     enable_plugin neutron-lbaas https://git.openstack.org/openstack/neutron-lbaas
+    enable_plugin octavia https://git.openstack.org/openstack/octavia
 
     # ===== BEGIN localrc =====
     DATABASE_PASSWORD=password
@@ -42,13 +47,13 @@
     ENABLED_SERVICES+=,horizon
     # Nova
     ENABLED_SERVICES+=,n-api,n-crt,n-obj,n-cpu,n-cond,n-sch
-    IMAGE_URLS+=",https://launchpad.net/cirros/trunk/0.3.0/+download/cirros-0.3.0-x86_64-disk.img"
     # Glance
     ENABLED_SERVICES+=,g-api,g-reg
     # Neutron
     ENABLED_SERVICES+=,q-svc,q-agt,q-dhcp,q-l3,q-meta
-    # Enable LBaaS V2
+    # Enable LBaaS v2
     ENABLED_SERVICES+=,q-lbaasv2
+    ENABLED_SERVICES+=,octavia,o-cw,o-hk,o-hm,o-api
     # Cinder
     ENABLED_SERVICES+=,c-api,c-vol,c-sch
     # Tempest
@@ -69,11 +74,11 @@
   ::
 
     #create nova instances on private network
-    nova boot --image $(nova image-list | awk '/ cirros-0.3.0-x86_64-disk / {print $2}') --flavor 1 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') node1
-    nova boot --image $(nova image-list | awk '/ cirros-0.3.0-x86_64-disk / {print $2}') --flavor 1 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') node2
+    nova boot --image $(nova image-list | awk '/ cirros-.*-x86_64-uec / {print $2}') --flavor 1 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') node1
+    nova boot --image $(nova image-list | awk '/ cirros-.*-x86_64-uec / {print $2}') --flavor 1 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') node2
     nova list # should show the nova instances just created
 
-    #add secgroup rule to allow ssh etc..
+    #add secgroup rules to allow ssh etc..
     neutron security-group-rule-create default --protocol icmp
     neutron security-group-rule-create default --protocol tcp --port-range-min 22 --port-range-max 22
     neutron security-group-rule-create default --protocol tcp --port-range-min 80 --port-range-max 80
@@ -91,9 +96,16 @@
  ::
 
     neutron lbaas-loadbalancer-create --name lb1 private-subnet
+    neutron lbaas-loadbalancer-show lb1  # Wait for the provisioning_status to be ACTIVE.
     neutron lbaas-listener-create --loadbalancer lb1 --protocol HTTP --protocol-port 80 --name listener1
+    sleep 10  # Sleep since LBaaS actions can take a few seconds depending on the environment.
     neutron lbaas-pool-create --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP --name pool1
+    sleep 10
     neutron lbaas-member-create  --subnet private-subnet --address 10.0.0.3 --protocol-port 80 pool1
+    sleep 10
     neutron lbaas-member-create  --subnet private-subnet --address 10.0.0.5 --protocol-port 80 pool1
 
-Please note here that the "10.0.0.3" and "10.0.0.5" in the above commands are the IPs of the nodes (in my test run-thru, they were actually 10.2 and 10.4), and the address of the created LB will be reported as "vip_address" from the lbaas-loadbalancer-create, and a quick test of that LB is "curl that-lb-ip", which should alternate between showing the IPs of the two nodes.
+Please note here that the "10.0.0.3" and "10.0.0.5" in the above commands are the IPs of the nodes
+(in my test run-thru, they were actually 10.2 and 10.4), and the address of the created LB will be
+reported as "vip_address" from the lbaas-loadbalancer-create, and a quick test of that LB is
+"curl that-lb-ip", which should alternate between showing the IPs of the two nodes.
diff --git a/doc/source/guides/devstack-with-nested-kvm.rst b/doc/source/guides/devstack-with-nested-kvm.rst
index c652bac..85a5656 100644
--- a/doc/source/guides/devstack-with-nested-kvm.rst
+++ b/doc/source/guides/devstack-with-nested-kvm.rst
@@ -50,7 +50,7 @@
     parm:           nested:bool
 
 Start your VM, now it should have KVM capabilities -- you can verify
-that by ensuring `/dev/kvm` character device is present.
+that by ensuring ``/dev/kvm`` character device is present.
 
 
 Configure Nested KVM for AMD-based Machines
@@ -97,7 +97,7 @@
 Expose Virtualization Extensions to DevStack VM
 -----------------------------------------------
 
-Edit the VM's libvirt XML configuration via `virsh` utility:
+Edit the VM's libvirt XML configuration via ``virsh`` utility:
 
 ::
 
@@ -115,10 +115,10 @@
 -------------------------------
 
 Before invoking ``stack.sh`` in the VM, ensure that KVM is enabled. This
-can be verified by checking for the presence of the file `/dev/kvm` in
+can be verified by checking for the presence of the file ``/dev/kvm`` in
 your VM. If it is present, DevStack will default to using the config
-attribute `virt_type = kvm` in `/etc/nova.conf`; otherwise, it'll fall
-back to `virt_type=qemu`, i.e. plain QEMU emulation.
+attribute ``virt_type = kvm`` in ``/etc/nova.conf``; otherwise, it'll fall
+back to ``virt_type=qemu``, i.e. plain QEMU emulation.
 
 Optionally, to explicitly set the type of virtualization, to KVM, by the
 libvirt driver in nova, the below config attribute can be used in
@@ -131,7 +131,7 @@
 
 Once DevStack is configured successfully, verify if the Nova instances
 are using KVM by noticing the QEMU CLI invoked by Nova is using the
-parameter `accel=kvm`, e.g.:
+parameter ``accel=kvm``, e.g.:
 
 ::
 
diff --git a/doc/source/guides/multinode-lab.rst b/doc/source/guides/multinode-lab.rst
index 27d71f1..5660bc5 100644
--- a/doc/source/guides/multinode-lab.rst
+++ b/doc/source/guides/multinode-lab.rst
@@ -128,7 +128,7 @@
     MULTI_HOST=1
     LOGFILE=/opt/stack/logs/stack.sh.log
     ADMIN_PASSWORD=labstack
-    MYSQL_PASSWORD=supersecret
+    DATABASE_PASSWORD=supersecret
     RABBIT_PASSWORD=supersecrete
     SERVICE_PASSWORD=supersecrete
     SERVICE_TOKEN=xyzpdqlazydog
@@ -169,21 +169,26 @@
     MULTI_HOST=1
     LOGFILE=/opt/stack/logs/stack.sh.log
     ADMIN_PASSWORD=labstack
-    MYSQL_PASSWORD=supersecret
+    DATABASE_PASSWORD=supersecret
     RABBIT_PASSWORD=supersecrete
     SERVICE_PASSWORD=supersecrete
     SERVICE_TOKEN=xyzpdqlazydog
     DATABASE_TYPE=mysql
     SERVICE_HOST=192.168.42.11
-    MYSQL_HOST=192.168.42.11
-    RABBIT_HOST=192.168.42.11
-    GLANCE_HOSTPORT=192.168.42.11:9292
-    ENABLED_SERVICES=n-cpu,n-net,n-api,c-vol
+    MYSQL_HOST=$SERVICE_HOST
+    RABBIT_HOST=$SERVICE_HOST
+    GLANCE_HOSTPORT=$SERVICE_HOST:9292
+    ENABLED_SERVICES=n-cpu,n-net,n-api-meta,c-vol
     NOVA_VNC_ENABLED=True
-    NOVNCPROXY_URL="http://192.168.42.11:6080/vnc_auto.html"
+    NOVNCPROXY_URL="http://$SERVICE_HOST:6080/vnc_auto.html"
     VNCSERVER_LISTEN=$HOST_IP
     VNCSERVER_PROXYCLIENT_ADDRESS=$VNCSERVER_LISTEN
 
+**Note:** the ``n-api-meta`` service is a version of the api server
+that only serves the metadata service. It's needed because the
+computes created won't have a routing path to the metadata service on
+the controller.
+
 Fire up OpenStack:
 
 ::
@@ -263,7 +268,7 @@
 -----
 
 Swift, OpenStack Object Storage, requires a significant amount of resources
-and is disabled by default in DevStack. The support in DevStack is geared 
+and is disabled by default in DevStack. The support in DevStack is geared
 toward a minimal installation but can be used for testing. To implement a
 true multi-node test of swift, additional steps will be required. Enabling it is as
 simple as enabling the ``swift`` service in ``local.conf``:
diff --git a/doc/source/guides/neutron.rst b/doc/source/guides/neutron.rst
index 40a5632..9dcb654 100644
--- a/doc/source/guides/neutron.rst
+++ b/doc/source/guides/neutron.rst
@@ -35,7 +35,7 @@
                 network hardware_network {
                         address = "172.18.161.0/24"
                         router [ address = "172.18.161.1" ];
-                        devstack_laptop [ address = "172.18.161.6" ];
+                        devstack-1 [ address = "172.18.161.6" ];
                 }
         }
 
@@ -43,23 +43,33 @@
 DevStack Configuration
 ----------------------
 
+The following is a complete `local.conf` for the host named
+`devstack-1`. It will run all the API and services, as well as
+serving as a hypervisor for guest instances.
 
 ::
 
+        [[local|localrc]]
         HOST_IP=172.18.161.6
         SERVICE_HOST=172.18.161.6
         MYSQL_HOST=172.18.161.6
         RABBIT_HOST=172.18.161.6
         GLANCE_HOSTPORT=172.18.161.6:9292
         ADMIN_PASSWORD=secrete
-        MYSQL_PASSWORD=secrete
+        DATABASE_PASSWORD=secrete
         RABBIT_PASSWORD=secrete
         SERVICE_PASSWORD=secrete
         SERVICE_TOKEN=secrete
 
+        # Do not use Nova-Network
+        disable_service n-net
+        # Enable Neutron
+        ENABLED_SERVICES+=,q-svc,q-dhcp,q-meta,q-agt,q-l3
+
+
         ## Neutron options
         Q_USE_SECGROUP=True
-        FLOATING_RANGE="172.18.161.1/24"
+        FLOATING_RANGE="172.18.161.0/24"
         FIXED_RANGE="10.0.0.0/24"
         Q_FLOATING_ALLOCATION_POOL=start=172.18.161.250,end=172.18.161.254
         PUBLIC_NETWORK_GATEWAY="172.18.161.1"
@@ -71,21 +81,218 @@
         OVS_BRIDGE_MAPPINGS=public:br-ex
 
 
+Adding Additional Compute Nodes
+-------------------------------
+
+Let's suppose that after installing DevStack on the first host, you
+also want to do multinode testing and networking.
+
+Physical Network Setup
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. nwdiag::
+
+        nwdiag {
+                inet [ shape = cloud ];
+                router;
+                inet -- router;
+
+                network hardware_network {
+                        address = "172.18.161.0/24"
+                        router [ address = "172.18.161.1" ];
+                        devstack-1 [ address = "172.18.161.6" ];
+                        devstack-2 [ address = "172.18.161.7" ];
+                }
+        }
+
+
+After DevStack installs and configures Neutron, traffic from guest VMs
+flows out of `devstack-2` (the compute node) and is encapsulated in a
+VXLAN tunnel back to `devstack-1` (the control node) where the L3
+agent is running.
+
+::
+
+    stack@devstack-2:~/devstack$ sudo ovs-vsctl show
+    8992d965-0ba0-42fd-90e9-20ecc528bc29
+        Bridge br-int
+            fail_mode: secure
+            Port br-int
+                Interface br-int
+                    type: internal
+            Port patch-tun
+                Interface patch-tun
+                    type: patch
+                    options: {peer=patch-int}
+        Bridge br-tun
+            fail_mode: secure
+            Port "vxlan-c0a801f6"
+                Interface "vxlan-c0a801f6"
+                    type: vxlan
+                    options: {df_default="true", in_key=flow, local_ip="172.18.161.7", out_key=flow, remote_ip="172.18.161.6"}
+            Port patch-int
+                Interface patch-int
+                    type: patch
+                    options: {peer=patch-tun}
+            Port br-tun
+                Interface br-tun
+                    type: internal
+        ovs_version: "2.0.2"
+
+Open vSwitch on the control node, where the L3 agent runs, is
+configured to de-encapsulate traffic from compute nodes, then forward
+it over the `br-ex` bridge, where `eth0` is attached.
+
+::
+
+    stack@devstack-1:~/devstack$ sudo ovs-vsctl show
+    422adeea-48d1-4a1f-98b1-8e7239077964
+        Bridge br-tun
+            fail_mode: secure
+            Port br-tun
+                Interface br-tun
+                    type: internal
+            Port patch-int
+                Interface patch-int
+                    type: patch
+                    options: {peer=patch-tun}
+            Port "vxlan-c0a801d8"
+                Interface "vxlan-c0a801d8"
+                    type: vxlan
+                    options: {df_default="true", in_key=flow, local_ip="172.18.161.6", out_key=flow, remote_ip="172.18.161.7"}
+        Bridge br-ex
+            Port phy-br-ex
+                Interface phy-br-ex
+                    type: patch
+                    options: {peer=int-br-ex}
+            Port "eth0"
+                Interface "eth0"
+            Port br-ex
+                Interface br-ex
+                    type: internal
+        Bridge br-int
+            fail_mode: secure
+            Port "tapce66332d-ea"
+                tag: 1
+                Interface "tapce66332d-ea"
+                    type: internal
+            Port "qg-65e5a4b9-15"
+                tag: 2
+                Interface "qg-65e5a4b9-15"
+                    type: internal
+            Port "qr-33e5e471-88"
+                tag: 1
+                Interface "qr-33e5e471-88"
+                    type: internal
+            Port "qr-acbe9951-70"
+                tag: 1
+                Interface "qr-acbe9951-70"
+                    type: internal
+            Port br-int
+                Interface br-int
+                    type: internal
+            Port patch-tun
+                Interface patch-tun
+                    type: patch
+                    options: {peer=patch-int}
+            Port int-br-ex
+                Interface int-br-ex
+                    type: patch
+                    options: {peer=phy-br-ex}
+        ovs_version: "2.0.2"
+
+`br-int` is a bridge that the Open vSwitch mechanism driver creates,
+which is used as the "integration bridge" where ports are created, and
+plugged into the virtual switching fabric. `br-ex` is an OVS bridge
+that is used to connect physical ports (like `eth0`), so that floating
+IP traffic for tenants can be received from the physical network
+infrastructure (and the internet), and routed to tenant network ports.
+`br-tun` is a tunnel bridge that is used to connect OpenStack nodes
+(like `devstack-2`) together. This bridge is used so that tenant
+network traffic, using the VXLAN tunneling protocol, flows between
+each compute node where tenant instances run.
 
 
 
-Using Neutron with Multiple Interfaces
-======================================
+DevStack Compute Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The first interface, eth0 is used for the OpenStack management (API,
-message bus, etc) as well as for ssh for an administrator to access
-the machine.
+The host `devstack-2` has a very minimal `local.conf`.
+
+::
+
+    [[local|localrc]]
+    HOST_IP=172.18.161.7
+    SERVICE_HOST=172.18.161.6
+    MYSQL_HOST=172.18.161.6
+    RABBIT_HOST=172.18.161.6
+    GLANCE_HOSTPORT=172.18.161.6:9292
+    ADMIN_PASSWORD=secrete
+    MYSQL_PASSWORD=secrete
+    RABBIT_PASSWORD=secrete
+    SERVICE_PASSWORD=secrete
+    SERVICE_TOKEN=secrete
+
+    ## Neutron options
+    PUBLIC_INTERFACE=eth0
+    ENABLED_SERVICES=n-cpu,rabbit,q-agt
+
+Network traffic from `eth0` on the compute nodes is then NAT'd by the
+controller node that runs Neutron's `neutron-l3-agent` and provides L3
+connectivity.
+
+
+Neutron Networking with Open vSwitch and Provider Networks
+==========================================================
+
+In some instances, it is desirable to use neutron's provider
+networking extension, so that networks that are configured on an
+external router can be utilized by neutron, and instances created via
+Nova can attach to the network managed by the external router.
+
+For example, in some lab environments, a hardware router has been
+pre-configured by another party, and an OpenStack developer has been
+given a VLAN tag and IP address range, so that instances created via
+DevStack will use the external router for L3 connectivity, as opposed
+to the neutron L3 service.
+
+Physical Network Setup
+----------------------
+
+.. nwdiag::
+
+        nwdiag {
+                inet [ shape = cloud ];
+                router;
+                inet -- router;
+
+                network provider_net {
+                        address = "203.0.113.0/24"
+                        router [ address = "203.0.113.1" ];
+                        controller;
+                        compute1;
+                        compute2;
+                }
+
+                network control_plane {
+                        router [ address = "10.0.0.1" ]
+                        address = "10.0.0.0/24"
+                        controller [ address = "10.0.0.2" ]
+                        compute1 [ address = "10.0.0.3" ]
+                        compute2 [ address = "10.0.0.4" ]
+                }
+        }
+
+
+On a compute node, the first interface, eth0 is used for the OpenStack
+management (API, message bus, etc) as well as for ssh for an
+administrator to access the machine.
 
 ::
 
         stack@compute:~$ ifconfig eth0
         eth0      Link encap:Ethernet  HWaddr bc:16:65:20:af:fc
-                  inet addr:192.168.1.18
+                  inet addr:10.0.0.3
 
 eth1 is manually configured at boot to not have an IP address.
 Consult your operating system documentation for the appropriate
@@ -101,9 +308,6 @@
 
 The second physical interface, eth1 is added to a bridge (in this case
 named br-ex), which is used to forward network traffic from guest VMs.
-Network traffic from eth1 on the compute nodes is then NAT'd by the
-controller node that runs Neutron's `neutron-l3-agent` and provides L3
-connectivity.
 
 ::
 
@@ -123,100 +327,6 @@
                     Interface "eth1"
 
 
-
-
-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
-====================================
-
-Configuring neutron, OpenStack Networking in DevStack is very similar to
-configuring `nova-network` - many of the same configuration variables
-(like `FIXED_RANGE` and `FLOATING_RANGE`) used by `nova-network` are
-used by neutron, which is intentional.
-
-The only difference is the disabling of `nova-network` in your
-local.conf, and the enabling of the neutron components.
-
-
-Configuration
--------------
-
-::
-
-        FIXED_RANGE=10.0.0.0/24
-        FLOATING_RANGE=192.168.27.0/24
-        PUBLIC_NETWORK_GATEWAY=192.168.27.2
-
-        disable_service n-net
-        enable_service q-svc
-        enable_service q-agt
-        enable_service q-dhcp
-        enable_service q-meta
-        enable_service q-l3
-
-        Q_USE_SECGROUP=True
-        ENABLE_TENANT_VLANS=True
-        TENANT_VLAN_RANGE=1000:1999
-        PHYSICAL_NETWORK=default
-        OVS_PHYSICAL_BRIDGE=br-ex
-
-In this configuration we are defining FLOATING_RANGE to be a
-subnet that exists in the private RFC1918 address space - however in
-in a real setup FLOATING_RANGE would be a public IP address range.
-
-Note that extension drivers for the ML2 plugin is set by
-`Q_ML2_PLUGIN_EXT_DRIVERS`, and it includes 'port_security' by default. If you
-want to remove all the extension drivers (even 'port_security'), set
-`Q_ML2_PLUGIN_EXT_DRIVERS` to blank.
-
-Neutron Networking with Open vSwitch and Provider Networks
-==========================================================
-
-In some instances, it is desirable to use neutron's provider
-networking extension, so that networks that are configured on an
-external router can be utilized by neutron, and instances created via
-Nova can attach to the network managed by the external router.
-
-For example, in some lab environments, a hardware router has been
-pre-configured by another party, and an OpenStack developer has been
-given a VLAN tag and IP address range, so that instances created via
-DevStack will use the external router for L3 connectivity, as opposed
-to the neutron L3 service.
-
-
 Service Configuration
 ---------------------
 
@@ -230,8 +340,8 @@
 **Compute Nodes**
 
 In this example, the nodes that will host guest instances will run
-the `neutron-openvswitch-agent` for network connectivity, as well as
-the compute service `nova-compute`.
+the ``neutron-openvswitch-agent`` for network connectivity, as well as
+the compute service ``nova-compute``.
 
 DevStack Configuration
 ----------------------
@@ -241,8 +351,19 @@
 
 ::
 
+        HOST_IP=10.0.0.2
+        SERVICE_HOST=10.0.0.2
+        MYSQL_HOST=10.0.0.2
+        RABBIT_HOST=10.0.0.2
+        GLANCE_HOSTPORT=10.0.0.2:9292
         PUBLIC_INTERFACE=eth1
 
+        ADMIN_PASSWORD=secrete
+        MYSQL_PASSWORD=secrete
+        RABBIT_PASSWORD=secrete
+        SERVICE_PASSWORD=secrete
+        SERVICE_TOKEN=secrete
+
         ## Neutron options
         Q_USE_SECGROUP=True
         ENABLE_TENANT_VLANS=True
@@ -274,30 +395,85 @@
 allocated to you, so that you could access your instances from the
 public internet.
 
-The following is a snippet of the DevStack configuration on the
-compute node.
+The following is the DevStack configuration on
+compute node 1.
 
 ::
 
+        HOST_IP=10.0.0.3
+        SERVICE_HOST=10.0.0.2
+        MYSQL_HOST=10.0.0.2
+        RABBIT_HOST=10.0.0.2
+        GLANCE_HOSTPORT=10.0.0.2:9292
+        ADMIN_PASSWORD=secrete
+        MYSQL_PASSWORD=secrete
+        RABBIT_PASSWORD=secrete
+        SERVICE_PASSWORD=secrete
+        SERVICE_TOKEN=secrete
+
         # Services that a compute node runs
         ENABLED_SERVICES=n-cpu,rabbit,q-agt
 
         ## Neutron options
-        Q_USE_SECGROUP=True
-        ENABLE_TENANT_VLANS=True
-        TENANT_VLAN_RANGE=3001:4000
         PHYSICAL_NETWORK=default
         OVS_PHYSICAL_BRIDGE=br-ex
         PUBLIC_INTERFACE=eth1
         Q_USE_PROVIDER_NETWORKING=True
         Q_L3_ENABLED=False
 
+Compute node 2's configuration will be exactly the same, except
+``HOST_IP`` will be ``10.0.0.4``
+
 When DevStack is configured to use provider networking (via
-`Q_USE_PROVIDER_NETWORKING` is True and `Q_L3_ENABLED` is False) -
+``Q_USE_PROVIDER_NETWORKING`` is True and ``Q_L3_ENABLED`` is False) -
 DevStack will automatically add the network interface defined in
-`PUBLIC_INTERFACE` to the `OVS_PHYSICAL_BRIDGE`
+``PUBLIC_INTERFACE`` to the ``OVS_PHYSICAL_BRIDGE``
 
 For example, with the above  configuration, a bridge is
-created, named `br-ex` which is managed by Open vSwitch, and the
-second interface on the compute node, `eth1` is attached to the
+created, named ``br-ex`` which is managed by Open vSwitch, and the
+second interface on the compute node, ``eth1`` is attached to the
 bridge, to forward traffic sent by guest VMs.
+
+Miscellaneous Tips
+==================
+
+
+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
+
+Configuring Extension Drivers for the ML2 Plugin
+------------------------------------------------
+
+Extension drivers for the ML2 plugin are set with the variable
+``Q_ML2_PLUGIN_EXT_DRIVERS``, and includes the 'port_security' extension
+by default. If you want to remove all the extension drivers (even
+'port_security'), set ``Q_ML2_PLUGIN_EXT_DRIVERS`` to blank.
+
diff --git a/doc/source/guides/single-machine.rst b/doc/source/guides/single-machine.rst
index 236ece9..a01c368 100644
--- a/doc/source/guides/single-machine.rst
+++ b/doc/source/guides/single-machine.rst
@@ -105,7 +105,7 @@
     FIXED_NETWORK_SIZE=256
     FLAT_INTERFACE=eth0
     ADMIN_PASSWORD=supersecret
-    MYSQL_PASSWORD=iheartdatabases
+    DATABASE_PASSWORD=iheartdatabases
     RABBIT_PASSWORD=flopsymopsy
     SERVICE_PASSWORD=iheartksl
     SERVICE_TOKEN=xyzpdqlazydog
diff --git a/doc/source/guides/single-vm.rst b/doc/source/guides/single-vm.rst
index c2ce1a3..53c3fa9 100644
--- a/doc/source/guides/single-vm.rst
+++ b/doc/source/guides/single-vm.rst
@@ -64,7 +64,7 @@
             cd devstack
             echo '[[local|localrc]]' > local.conf
             echo ADMIN_PASSWORD=password >> local.conf
-            echo MYSQL_PASSWORD=password >> local.conf
+            echo DATABASE_PASSWORD=password >> local.conf
             echo RABBIT_PASSWORD=password >> local.conf
             echo SERVICE_PASSWORD=password >> local.conf
             echo SERVICE_TOKEN=tokentoken >> local.conf
@@ -78,6 +78,11 @@
 As DevStack will refuse to run as root, this configures ``cloud-init``
 to create a non-root user and run the ``start.sh`` script as that user.
 
+If you are using cloud-init and you have not
+`enabled custom logging <../configuration.html#enable-logging>`_ of the stack
+output, then the stack output can be found in
+``/var/log/cloud-init-output.log`` by default.
+
 Launching By Hand
 -----------------
 
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 2dd0241..b65730f 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -154,7 +154,6 @@
 * `functions <functions.html>`__ - DevStack-specific functions
 * `functions-common <functions-common.html>`__ - Functions shared with other projects
 * `lib/apache <lib/apache.html>`__
-* `lib/ceilometer <lib/ceilometer.html>`__
 * `lib/ceph <lib/ceph.html>`__
 * `lib/cinder <lib/cinder.html>`__
 * `lib/database <lib/database.html>`__
@@ -173,7 +172,7 @@
 * `lib/swift <lib/swift.html>`__
 * `lib/tempest <lib/tempest.html>`__
 * `lib/tls <lib/tls.html>`__
-* `lib/zaqar <lib/zaqar.html>`__
+* `lib/trove <lib/trove.html>`__
 * `unstack.sh <unstack.sh.html>`__
 * `clean.sh <clean.sh.html>`__
 * `run\_tests.sh <run_tests.sh.html>`__
@@ -181,7 +180,6 @@
 * `extras.d/50-ironic.sh <extras.d/50-ironic.sh.html>`__
 * `extras.d/60-ceph.sh <extras.d/60-ceph.sh.html>`__
 * `extras.d/70-tuskar.sh <extras.d/70-tuskar.sh.html>`__
-* `extras.d/70-zaqar.sh <extras.d/70-zaqar.sh.html>`__
 * `extras.d/80-tempest.sh <extras.d/80-tempest.sh.html>`__
 
 * `inc/ini-config <inc/ini-config.html>`__
@@ -208,7 +206,6 @@
 
 * `tools/build\_docs.sh <tools/build_docs.sh.html>`__
 * `tools/build\_venv.sh <tools/build_venv.sh.html>`__
-* `tools/build\_wheels.sh <tools/build_wheels.sh.html>`__
 * `tools/create-stack-user.sh <tools/create-stack-user.sh.html>`__
 * `tools/create\_userrc.sh <tools/create_userrc.sh.html>`__
 * `tools/fixup\_stuff.sh <tools/fixup_stuff.sh.html>`__
@@ -239,4 +236,3 @@
 * `exercises/sec\_groups.sh <exercises/sec_groups.sh.html>`__
 * `exercises/swift.sh <exercises/swift.sh.html>`__
 * `exercises/volumes.sh <exercises/volumes.sh.html>`__
-* `exercises/zaqar.sh <exercises/zaqar.sh.html>`__
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
index 99bfb85..49b3a7f 100644
--- a/doc/source/plugin-registry.rst
+++ b/doc/source/plugin-registry.rst
@@ -17,17 +17,31 @@
 
 The following are plugins that exist for official OpenStack projects.
 
-+--------------------+-------------------------------------------+--------------------+
-|Plugin Name         |URL                                        |Comments            |
-+--------------------+-------------------------------------------+--------------------+
-|magnum              |git://git.openstack.org/openstack/magnum   |                    |
-+--------------------+-------------------------------------------+--------------------+
-|sahara              |git://git.openstack.org/openstack/sahara   |                    |
-+--------------------+-------------------------------------------+--------------------+
-|trove               |git://git.openstack.org/openstack/trove    |                    |
-+--------------------+-------------------------------------------+--------------------+
-|zaqar               |git://git.openstack.org/openstack/zaqar    |                    |
-+--------------------+-------------------------------------------+--------------------+
++------------------+---------------------------------------------+--------------------+
+|Plugin Name       |URL                                          |Comments            |
++------------------+---------------------------------------------+--------------------+
+|aodh              |git://git.openstack.org/openstack/aodh       | alarming           |
++------------------+---------------------------------------------+--------------------+
+|barbican          |git://git.openstack.org/openstack/barbican   | key management     |
++------------------+---------------------------------------------+--------------------+
+|ceilometer        |git://git.openstack.org/openstack/ceilometer | metering           |
++------------------+---------------------------------------------+--------------------+
+|gnocchi           |git://git.openstack.org/openstack/gnocchi    | metric             |
++------------------+---------------------------------------------+--------------------+
+|magnum            |git://git.openstack.org/openstack/magnum     |                    |
++------------------+---------------------------------------------+--------------------+
+|manila            |git://git.openstack.org/openstack/manila     | file shares        |
++------------------+---------------------------------------------+--------------------+
+|mistral           |git://git.openstack.org/openstack/mistral    |                    |
++------------------+---------------------------------------------+--------------------+
+|rally             |git://git.openstack.org/openstack/rally      |                    |
++------------------+---------------------------------------------+--------------------+
+|sahara            |git://git.openstack.org/openstack/sahara     |                    |
++------------------+---------------------------------------------+--------------------+
+|trove             |git://git.openstack.org/openstack/trove      |                    |
++------------------+---------------------------------------------+--------------------+
+|zaqar             |git://git.openstack.org/openstack/zaqar      |                    |
++------------------+---------------------------------------------+--------------------+
 
 
 
@@ -54,7 +68,7 @@
 | Plugin Name | URL                                                        | Comments   |
 |             |                                                            |            |
 +-------------+------------------------------------------------------------+------------+
-|glusterfs    |git://git.openstack.org/stackforge/devstack-plugin-glusterfs|            |
+|glusterfs    |git://git.openstack.org/openstack/devstack-plugin-glusterfs |            |
 +-------------+------------------------------------------------------------+------------+
 |             |                                                            |            |
 +-------------+------------------------------------------------------------+------------+
@@ -62,14 +76,16 @@
 Additional Services
 ===================
 
-+-------------+------------------------------------------+------------+
-| Plugin Name | URL                                      | Comments   |
-|             |                                          |            |
-+-------------+------------------------------------------+------------+
-|ec2-api      |git://git.openstack.org/stackforge/ec2api |[as1]_      |
-+-------------+------------------------------------------+------------+
-|             |                                          |            |
-+-------------+------------------------------------------+------------+
++----------------+--------------------------------------------------+------------+
+| Plugin Name    | URL                                              | Comments   |
+|                |                                                  |            |
++----------------+--------------------------------------------------+------------+
+|ec2-api         |git://git.openstack.org/openstack/ec2-api         |[as1]_      |
++----------------+--------------------------------------------------+------------+
+|ironic-inspector|git://git.openstack.org/openstack/ironic-inspector|            |
++----------------+--------------------------------------------------+------------+
+|                |                                                  |            |
++----------------+--------------------------------------------------+------------+
 
 .. [as1] first functional devstack plugin, hence why used in most of
          the examples.
diff --git a/doc/source/plugins.rst b/doc/source/plugins.rst
index 1b6f5e3..b8da7e1 100644
--- a/doc/source/plugins.rst
+++ b/doc/source/plugins.rst
@@ -15,11 +15,20 @@
 Plugin Interface
 ================
 
-DevStack supports a standard mechansim for including plugins from
+DevStack supports a standard mechanism 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.
+directory. Inside this directory there can be 3 files.
+
+- ``override_defaults`` - a file containing global variables that
+  will be sourced before the lib/* files. This allows the plugin
+  to override the defaults that are otherwise set in the lib/*
+  files.
+
+  For example, override_defaults may export CINDER_ENABLED_BACKENDS
+  to include the plugin-specific storage backend and thus be able
+  to override the default lvm only storage backend for Cinder.
 
 - ``settings`` - a file containing global variables that will be
   sourced very early in the process. This is helpful if other plugins
@@ -38,7 +47,7 @@
 
 - ``plugin.sh`` - the actual plugin. It is executed by devstack at
   well defined points during a ``stack.sh`` run. The plugin.sh
-  internal structure is discussed bellow.
+  internal structure is discussed below.
 
 
 Plugins are registered by adding the following to the localrc section
@@ -49,14 +58,14 @@
   [[local|localrc]]
   enable_plugin <NAME> <GITURL> [GITREF]
 
-- ``name`` - an arbitrary name. (ex: glustfs, docker, zaqar, congress)
+- ``name`` - an arbitrary name. (ex: glusterfs, 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 ec2api git://git.openstack.org/stackforge/ec2api
+  enable_plugin ec2-api git://git.openstack.org/openstack/ec2-api
 
 plugin.sh contract
 ==================
@@ -178,7 +187,7 @@
 ===============
 
 Devstack provides a framework for getting packages installed at an early
-phase of its execution. This packages may be defined in a plugin as files
+phase of its execution. These packages may be defined in a plugin as files
 that contain new-line separated lists of packages required by the plugin
 
 Supported packaging systems include apt and yum across multiple distributions.
@@ -202,14 +211,13 @@
 For everyday use, DevStack plugins can exist in any git tree that's
 accessible on the internet. However, when using DevStack plugins in
 the OpenStack gate, they must live in projects in OpenStack's
-gerrit. Both ``openstack`` namespace and ``stackforge`` namespace are
-fine. This allows testing of the plugin as well as provides network
+gerrit. This allows testing of the plugin as well as provides network
 isolation against upstream git repository failures (which we see often
 enough to be an issue).
 
 Ideally a plugin will be included within the ``devstack`` directory of
-the project they are being tested. For example, the stackforge/ec2-api
-project has its pluggin support in its own tree.
+the project they are being tested. For example, the openstack/ec2-api
+project has its plugin support in its own tree.
 
 However, some times a DevStack plugin might be used solely to
 configure a backend service that will be used by the rest of
@@ -218,7 +226,7 @@
 integration of SDN controllers (e.g. ovn, OpenDayLight), or
 integration of alternate RPC systems (e.g. zmq, qpid). In these cases
 the best practice is to build a dedicated
-``stackforge/devstack-plugin-FOO`` project.
+``openstack/devstack-plugin-FOO`` project.
 
 To enable a plugin to be used in a gate job, the following lines will
 be needed in your ``jenkins/jobs/<project>.yaml`` definition in
@@ -228,12 +236,12 @@
   # Because we are testing a non standard project, add the
   # our project repository. This makes zuul do the right
   # reference magic for testing changes.
-  export PROJECTS="stackforge/ec2-api $PROJECTS"
+  export PROJECTS="openstack/ec2-api $PROJECTS"
 
   # note the actual url here is somewhat irrelevant because it
   # caches in nodepool, however make it a valid url for
   # documentation purposes.
-  export DEVSTACK_LOCAL_CONFIG="enable_plugin ec2-api git://git.openstack.org/stackforge/ec2-api"
+  export DEVSTACK_LOCAL_CONFIG="enable_plugin ec2-api git://git.openstack.org/openstack/ec2-api"
 
 See Also
 ========
diff --git a/exercises/aggregates.sh b/exercises/aggregates.sh
index 01d548d..808ef76 100755
--- a/exercises/aggregates.sh
+++ b/exercises/aggregates.sh
@@ -31,18 +31,13 @@
 EXERCISE_DIR=$(cd $(dirname "$0") && pwd)
 TOP_DIR=$(cd $EXERCISE_DIR/..; pwd)
 
-# Import common functions
-source $TOP_DIR/functions
-
-# Import configuration
-source $TOP_DIR/openrc
+# Test as the admin user
+# note this imports stackrc/functions, etc
+. $TOP_DIR/openrc admin admin
 
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
-# Test as the admin user
-. $TOP_DIR/openrc admin admin
-
 # If nova api is not enabled we exit with exitcode 55 so that
 # the exercise is skipped
 is_service_enabled n-api || exit 55
diff --git a/exercises/client-args.sh b/exercises/client-args.sh
index c33ef44..7cfef1c 100755
--- a/exercises/client-args.sh
+++ b/exercises/client-args.sh
@@ -41,7 +41,6 @@
 unset NOVA_REGION_NAME
 unset NOVA_URL
 unset NOVA_USERNAME
-unset NOVA_VERSION
 
 # Save the known variables for later
 export x_TENANT_NAME=$OS_TENANT_NAME
diff --git a/exercises/client-env.sh b/exercises/client-env.sh
index 4a0609a..1d2f4f5 100755
--- a/exercises/client-env.sh
+++ b/exercises/client-env.sh
@@ -41,7 +41,6 @@
 unset NOVA_REGION_NAME
 unset NOVA_URL
 unset NOVA_USERNAME
-unset NOVA_VERSION
 
 for i in OS_TENANT_NAME OS_USERNAME OS_PASSWORD OS_AUTH_URL; do
     is_set $i
@@ -101,9 +100,6 @@
             STATUS_EC2="Failed"
             RETURN=1
         fi
-
-        # Clean up side effects
-        unset NOVA_VERSION
     fi
 fi
 
diff --git a/exercises/neutron-adv-test.sh b/exercises/neutron-adv-test.sh
index a0de4cc..9bcb766 100755
--- a/exercises/neutron-adv-test.sh
+++ b/exercises/neutron-adv-test.sh
@@ -122,41 +122,47 @@
 }
 
 function get_image_id {
-    local IMAGE_ID=$(openstack image list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
+    local IMAGE_ID
+    IMAGE_ID=$(openstack image list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
     die_if_not_set $LINENO IMAGE_ID "Failure retrieving IMAGE_ID"
     echo "$IMAGE_ID"
 }
 
 function get_tenant_id {
     local TENANT_NAME=$1
-    local TENANT_ID=`openstack project list | grep " $TENANT_NAME " | head -n 1 | get_field 1`
+    local TENANT_ID
+    TENANT_ID=`openstack project list | grep " $TENANT_NAME " | head -n 1 | get_field 1`
     die_if_not_set $LINENO TENANT_ID "Failure retrieving TENANT_ID for $TENANT_NAME"
     echo "$TENANT_ID"
 }
 
 function get_user_id {
     local USER_NAME=$1
-    local USER_ID=`openstack user list | grep $USER_NAME | awk '{print $2}'`
+    local USER_ID
+    USER_ID=`openstack user list | grep $USER_NAME | awk '{print $2}'`
     die_if_not_set $LINENO USER_ID "Failure retrieving USER_ID for $USER_NAME"
     echo "$USER_ID"
 }
 
 function get_role_id {
     local ROLE_NAME=$1
-    local ROLE_ID=`openstack role list | grep $ROLE_NAME | awk '{print $2}'`
+    local ROLE_ID
+    ROLE_ID=`openstack role list | grep $ROLE_NAME | awk '{print $2}'`
     die_if_not_set $LINENO ROLE_ID "Failure retrieving ROLE_ID for $ROLE_NAME"
     echo "$ROLE_ID"
 }
 
 function get_network_id {
     local NETWORK_NAME="$1"
-    local NETWORK_ID=`neutron net-list -F id  -- --name=$NETWORK_NAME | awk "NR==4" | awk '{print $2}'`
+    local NETWORK_ID
+    NETWORK_ID=`neutron net-list -F id  -- --name=$NETWORK_NAME | awk "NR==4" | awk '{print $2}'`
     echo $NETWORK_ID
 }
 
 function get_flavor_id {
     local INSTANCE_TYPE=$1
-    local FLAVOR_ID=`nova flavor-list | grep $INSTANCE_TYPE | awk '{print $2}'`
+    local FLAVOR_ID
+    FLAVOR_ID=`nova flavor-list | grep $INSTANCE_TYPE | awk '{print $2}'`
     die_if_not_set $LINENO FLAVOR_ID "Failure retrieving FLAVOR_ID for $INSTANCE_TYPE"
     echo "$FLAVOR_ID"
 }
@@ -185,13 +191,15 @@
 
 function remove_tenant {
     local TENANT=$1
-    local TENANT_ID=$(get_tenant_id $TENANT)
+    local TENANT_ID
+    TENANT_ID=$(get_tenant_id $TENANT)
     openstack project delete $TENANT_ID
 }
 
 function remove_user {
     local USER=$1
-    local USER_ID=$(get_user_id $USER)
+    local USER_ID
+    USER_ID=$(get_user_id $USER)
     openstack user delete $USER_ID
 }
 
@@ -221,11 +229,13 @@
     local NET_NAME="${TENANT}-net$NUM"
     local ROUTER_NAME="${TENANT}-router${NUM}"
     source $TOP_DIR/openrc admin admin
-    local TENANT_ID=$(get_tenant_id $TENANT)
+    local TENANT_ID
+    TENANT_ID=$(get_tenant_id $TENANT)
     source $TOP_DIR/openrc $TENANT $TENANT
-    local NET_ID=$(neutron net-create --tenant-id $TENANT_ID $NET_NAME $EXTRA| grep ' id ' | awk '{print $4}' )
+    local NET_ID
+    NET_ID=$(neutron net-create --tenant-id $TENANT_ID $NET_NAME $EXTRA| grep ' id ' | awk '{print $4}' )
     die_if_not_set $LINENO NET_ID "Failure creating NET_ID for $TENANT_ID $NET_NAME $EXTRA"
-    neutron subnet-create --ip-version 4 --tenant-id $TENANT_ID --gateway $GATEWAY $NET_ID $CIDR
+    neutron subnet-create --ip-version 4 --tenant-id $TENANT_ID --gateway $GATEWAY --subnetpool None $NET_ID $CIDR
     neutron_debug_admin probe-create --device-owner compute $NET_ID
     source $TOP_DIR/openrc demo demo
 }
@@ -251,7 +261,8 @@
     done
     #TODO (nati) Add multi-nic test
     #TODO (nati) Add public-net test
-    local VM_UUID=`nova boot --flavor $(get_flavor_id m1.tiny) \
+    local VM_UUID
+    VM_UUID=`nova boot --flavor $(get_flavor_id m1.tiny) \
         --image $(get_image_id) \
         $NIC \
         $TENANT-server$NUM | grep ' id ' | cut -d"|" -f3 | sed 's/ //g'`
@@ -309,7 +320,8 @@
     local NUM=$2
     local NET_NAME="${TENANT}-net$NUM"
     source $TOP_DIR/openrc admin admin
-    local TENANT_ID=$(get_tenant_id $TENANT)
+    local TENANT_ID
+    TENANT_ID=$(get_tenant_id $TENANT)
     #TODO(nati) comment out until l3-agent merged
     #for res in port subnet net router;do
     for net_id in `neutron net-list -c id -c name | grep $NET_NAME | awk '{print $2}'`;do
diff --git a/exercises/swift.sh b/exercises/swift.sh
index afcede8..4a41e0f 100755
--- a/exercises/swift.sh
+++ b/exercises/swift.sh
@@ -2,7 +2,7 @@
 
 # **swift.sh**
 
-# Test swift via the ``swift`` command line from ``python-swiftclient``
+# Test swift via the ``python-openstackclient`` command line
 
 echo "*********************************************************************"
 echo "Begin DevStack Exercise: $0"
@@ -39,26 +39,29 @@
 
 # Container name
 CONTAINER=ex-swift
+OBJECT=/etc/issue
 
 
 # Testing Swift
 # =============
 
 # Check if we have to swift via keystone
-swift stat || die $LINENO "Failure getting status"
+openstack object store account show || die $LINENO "Failure getting account status"
 
 # We start by creating a test container
 openstack container create $CONTAINER || die $LINENO "Failure creating container $CONTAINER"
 
-# add some files into it.
-openstack object create $CONTAINER /etc/issue || die $LINENO "Failure uploading file to container $CONTAINER"
+# add a file into it.
+openstack object create $CONTAINER $OBJECT || die $LINENO "Failure uploading file to container $CONTAINER"
 
-# list them
+# list the objects
 openstack object list $CONTAINER || die $LINENO "Failure listing contents of container $CONTAINER"
 
-# And we may want to delete them now that we have tested that
-# everything works.
-swift delete $CONTAINER || die $LINENO "Failure deleting container $CONTAINER"
+# delete the object first
+openstack object delete $CONTAINER $OBJECT || die $LINENO "Failure deleting object $OBJECT in container $CONTAINER"
+
+# delete the container
+openstack container delete $CONTAINER || die $LINENO "Failure deleting container $CONTAINER"
 
 set +o xtrace
 echo "*********************************************************************"
diff --git a/exercises/zaqar.sh b/exercises/zaqar.sh
deleted file mode 100755
index c370b12..0000000
--- a/exercises/zaqar.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-
-# **zaqar.sh**
-
-# Sanity check that Zaqar started if enabled
-
-echo "*********************************************************************"
-echo "Begin DevStack Exercise: $0"
-echo "*********************************************************************"
-
-# This script exits on an error so that errors don't compound and you see
-# only the first error that occurred.
-set -o errexit
-
-# Print the commands being run so that we can see the command that triggers
-# an error.  It is also useful for following allowing as the install occurs.
-set -o xtrace
-
-
-# Settings
-# ========
-
-# Keep track of the current directory
-EXERCISE_DIR=$(cd $(dirname "$0") && pwd)
-TOP_DIR=$(cd $EXERCISE_DIR/..; pwd)
-
-# Import common functions
-source $TOP_DIR/functions
-
-# Import configuration
-source $TOP_DIR/openrc
-
-# Import exercise configuration
-source $TOP_DIR/exerciserc
-
-is_service_enabled zaqar-server || exit 55
-
-$CURL_GET http://$SERVICE_HOST:8888/v1/ 2>/dev/null | grep -q 'queue_name' || die $LINENO "Zaqar API not functioning!"
-
-set +o xtrace
-echo "*********************************************************************"
-echo "SUCCESS: End DevStack Exercise: $0"
-echo "*********************************************************************"
diff --git a/extras.d/70-zaqar.sh b/extras.d/70-zaqar.sh
deleted file mode 100644
index 63c4fd5..0000000
--- a/extras.d/70-zaqar.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-# zaqar.sh - Devstack extras script to install Zaqar
-
-if is_service_enabled zaqar-server; then
-    if [[ "$1" == "source" ]]; then
-        # Initial source
-        source $TOP_DIR/lib/zaqar
-    elif [[ "$1" == "stack" && "$2" == "install" ]]; then
-        echo_summary "Installing Zaqar"
-        install_zaqarclient
-        install_zaqar
-    elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
-        echo_summary "Configuring Zaqar"
-        configure_zaqar
-        configure_zaqarclient
-
-        if is_service_enabled key; then
-            create_zaqar_accounts
-        fi
-
-    elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
-        echo_summary "Initializing Zaqar"
-        init_zaqar
-        start_zaqar
-    fi
-
-    if [[ "$1" == "unstack" ]]; then
-        stop_zaqar
-    fi
-fi
diff --git a/extras.d/README.md b/extras.d/README.md
index 7c2e4fe..4cec14b 100644
--- a/extras.d/README.md
+++ b/extras.d/README.md
@@ -14,10 +14,13 @@
 entire `stack.sh` variable space is available.  The scripts are
 sourced with one or more arguments, the first of which defines the hook phase:
 
-    source | stack | unstack | clean
+    override_defaults | source | stack | unstack | clean
 
-    source: always called first in any of the scripts, used to set the
-        initial defaults in a lib/* script or similar
+    override_defaults: always called first in any of the scripts, used to
+        override defaults (if need be) that are otherwise set in lib/* scripts
+
+    source: called by stack.sh. Used to set the initial defaults in a lib/*
+        script or similar
 
     stack: called by stack.sh.  There are four possible values for
         the second arg to distinguish the phase stack.sh is in:
diff --git a/files/apache-ceilometer.template b/files/apache-ceilometer.template
deleted file mode 100644
index 79f14c3..0000000
--- a/files/apache-ceilometer.template
+++ /dev/null
@@ -1,15 +0,0 @@
-Listen %PORT%
-
-<VirtualHost *:%PORT%>
-    WSGIDaemonProcess ceilometer-api processes=2 threads=10 user=%USER% display-name=%{GROUP} %VIRTUALENV%
-    WSGIProcessGroup ceilometer-api
-    WSGIScriptAlias / %WSGIAPP%
-    WSGIApplicationGroup %{GLOBAL}
-    <IfVersion >= 2.4>
-        ErrorLogFormat "%{cu}t %M"
-    </IfVersion>
-    ErrorLog /var/log/%APACHE_NAME%/ceilometer.log
-    CustomLog /var/log/%APACHE_NAME%/ceilometer_access.log combined
-</VirtualHost>
-
-WSGISocketPrefix /var/run/%APACHE_NAME%
diff --git a/files/apache-cinder-api.template b/files/apache-cinder-api.template
new file mode 100644
index 0000000..e1246f1
--- /dev/null
+++ b/files/apache-cinder-api.template
@@ -0,0 +1,26 @@
+Listen %PUBLICPORT%
+
+<VirtualHost *:%PUBLICPORT%>
+    WSGIDaemonProcess osapi_volume processes=%APIWORKERS% threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV%
+    WSGIProcessGroup osapi_volume
+    WSGIScriptAlias / %CINDER_BIN_DIR%/cinder-wsgi
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+    ErrorLog /var/log/%APACHE_NAME%/c-api.log
+    %SSLENGINE%
+    %SSLCERTFILE%
+    %SSLKEYFILE%
+
+    <Directory %CINDER_BIN_DIR%>
+        <IfVersion >= 2.4>
+            Require all granted
+        </IfVersion>
+        <IfVersion < 2.4>
+            Order allow,deny
+            Allow from all
+        </IfVersion>
+    </Directory>
+</VirtualHost>
diff --git a/files/apache-heat-api-cfn.template b/files/apache-heat-api-cfn.template
new file mode 100644
index 0000000..ab33c66
--- /dev/null
+++ b/files/apache-heat-api-cfn.template
@@ -0,0 +1,27 @@
+Listen %PUBLICPORT%
+
+<VirtualHost *:%PUBLICPORT%>
+    WSGIDaemonProcess heat-api-cfn processes=2 threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV%
+    WSGIProcessGroup heat-api-cfn
+    WSGIScriptAlias / %HEAT_BIN_DIR%/heat-wsgi-api-cfn
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    AllowEncodedSlashes On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+    ErrorLog /var/log/%APACHE_NAME%/heat-api-cfn.log
+    %SSLENGINE%
+    %SSLCERTFILE%
+    %SSLKEYFILE%
+
+    <Directory %HEAT_BIN_DIR%>
+        <IfVersion >= 2.4>
+            Require all granted
+        </IfVersion>
+        <IfVersion < 2.4>
+            Order allow,deny
+            Allow from all
+        </IfVersion>
+    </Directory>
+</VirtualHost>
diff --git a/files/apache-heat-api-cloudwatch.template b/files/apache-heat-api-cloudwatch.template
new file mode 100644
index 0000000..06c91bb
--- /dev/null
+++ b/files/apache-heat-api-cloudwatch.template
@@ -0,0 +1,27 @@
+Listen %PUBLICPORT%
+
+<VirtualHost *:%PUBLICPORT%>
+    WSGIDaemonProcess heat-api-cloudwatch processes=2 threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV%
+    WSGIProcessGroup heat-api-cloudwatch
+    WSGIScriptAlias / %HEAT_BIN_DIR%/heat-wsgi-api-cloudwatch
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    AllowEncodedSlashes On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+    ErrorLog /var/log/%APACHE_NAME%/heat-api-cloudwatch.log
+    %SSLENGINE%
+    %SSLCERTFILE%
+    %SSLKEYFILE%
+
+    <Directory %HEAT_BIN_DIR%>
+        <IfVersion >= 2.4>
+            Require all granted
+        </IfVersion>
+        <IfVersion < 2.4>
+            Order allow,deny
+            Allow from all
+        </IfVersion>
+    </Directory>
+</VirtualHost>
diff --git a/files/apache-heat-api.template b/files/apache-heat-api.template
new file mode 100644
index 0000000..4924b39
--- /dev/null
+++ b/files/apache-heat-api.template
@@ -0,0 +1,27 @@
+Listen %PUBLICPORT%
+
+<VirtualHost *:%PUBLICPORT%>
+    WSGIDaemonProcess heat-api processes=3 threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV%
+    WSGIProcessGroup heat-api
+    WSGIScriptAlias / %HEAT_BIN_DIR%/heat-wsgi-api
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    AllowEncodedSlashes On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+    ErrorLog /var/log/%APACHE_NAME%/heat-api.log
+    %SSLENGINE%
+    %SSLCERTFILE%
+    %SSLKEYFILE%
+
+    <Directory %HEAT_BIN_DIR%>
+        <IfVersion >= 2.4>
+            Require all granted
+        </IfVersion>
+        <IfVersion < 2.4>
+            Order allow,deny
+            Allow from all
+        </IfVersion>
+    </Directory>
+</VirtualHost>
diff --git a/files/apache-horizon.template b/files/apache-horizon.template
index 6883898..bfd7567 100644
--- a/files/apache-horizon.template
+++ b/files/apache-horizon.template
@@ -1,5 +1,5 @@
 <VirtualHost *:80>
-    WSGIScriptAlias / %HORIZON_DIR%/openstack_dashboard/wsgi/django.wsgi
+    WSGIScriptAlias %WEBROOT% %HORIZON_DIR%/openstack_dashboard/wsgi/django.wsgi
     WSGIDaemonProcess horizon user=%USER% group=%GROUP% processes=3 threads=10 home=%HORIZON_DIR% display-name=%{GROUP}
     WSGIApplicationGroup %{GLOBAL}
 
@@ -8,7 +8,10 @@
     WSGIProcessGroup horizon
 
     DocumentRoot %HORIZON_DIR%/.blackhole/
-    Alias /media %HORIZON_DIR%/openstack_dashboard/static
+    Alias %WEBROOT%/media %HORIZON_DIR%/openstack_dashboard/static
+    Alias %WEBROOT%/static %HORIZON_DIR%/static
+
+    RedirectMatch "^/$" "%WEBROOT%/"
 
     <Directory />
         Options FollowSymLinks
diff --git a/files/apache-keystone.template b/files/apache-keystone.template
index 6dd1ad9..f9fa265 100644
--- a/files/apache-keystone.template
+++ b/files/apache-keystone.template
@@ -2,10 +2,20 @@
 Listen %ADMINPORT%
 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D(us)" keystone_combined
 
+<Directory %KEYSTONE_BIN%>
+    <IfVersion >= 2.4>
+        Require all granted
+    </IfVersion>
+    <IfVersion < 2.4>
+        Order allow,deny
+        Allow from all
+    </IfVersion>
+</Directory>
+
 <VirtualHost *:%PUBLICPORT%>
     WSGIDaemonProcess keystone-public processes=5 threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV%
     WSGIProcessGroup keystone-public
-    WSGIScriptAlias / %PUBLICWSGI%
+    WSGIScriptAlias / %KEYSTONE_BIN%/keystone-wsgi-public
     WSGIApplicationGroup %{GLOBAL}
     WSGIPassAuthorization On
     <IfVersion >= 2.4>
@@ -21,7 +31,7 @@
 <VirtualHost *:%ADMINPORT%>
     WSGIDaemonProcess keystone-admin processes=5 threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV%
     WSGIProcessGroup keystone-admin
-    WSGIScriptAlias / %ADMINWSGI%
+    WSGIScriptAlias / %KEYSTONE_BIN%/keystone-wsgi-admin
     WSGIApplicationGroup %{GLOBAL}
     WSGIPassAuthorization On
     <IfVersion >= 2.4>
@@ -34,7 +44,7 @@
     %SSLKEYFILE%
 </VirtualHost>
 
-Alias /identity %PUBLICWSGI%
+Alias /identity %KEYSTONE_BIN%/keystone-wsgi-public
 <Location /identity>
     SetHandler wsgi-script
     Options +ExecCGI
@@ -44,7 +54,7 @@
     WSGIPassAuthorization On
 </Location>
 
-Alias /identity_admin %ADMINWSGI%
+Alias /identity_admin %KEYSTONE_BIN%/keystone-wsgi-admin
 <Location /identity_admin>
     SetHandler wsgi-script
     Options +ExecCGI
diff --git a/files/apache-nova-api.template b/files/apache-nova-api.template
index 4908152..bcf406e 100644
--- a/files/apache-nova-api.template
+++ b/files/apache-nova-api.template
@@ -7,7 +7,7 @@
     WSGIApplicationGroup %{GLOBAL}
     WSGIPassAuthorization On
     <IfVersion >= 2.4>
-      ErrorLogFormat "%{cu}t %M"
+      ErrorLogFormat "%M"
     </IfVersion>
     ErrorLog /var/log/%APACHE_NAME%/nova-api.log
     %SSLENGINE%
diff --git a/files/apache-nova-ec2-api.template b/files/apache-nova-ec2-api.template
index 235d958..7b1d68b 100644
--- a/files/apache-nova-ec2-api.template
+++ b/files/apache-nova-ec2-api.template
@@ -7,10 +7,19 @@
     WSGIApplicationGroup %{GLOBAL}
     WSGIPassAuthorization On
     <IfVersion >= 2.4>
-      ErrorLogFormat "%{cu}t %M"
+      ErrorLogFormat "%M"
     </IfVersion>
     ErrorLog /var/log/%APACHE_NAME%/nova-ec2-api.log
     %SSLENGINE%
     %SSLCERTFILE%
     %SSLKEYFILE%
 </VirtualHost>
+
+Alias /ec2 %PUBLICWSGI%
+<Location /ec2>
+    SetHandler wsgi-script
+    Options +ExecCGI
+    WSGIProcessGroup nova-ec2-api
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+</Location>
diff --git a/files/apache-nova-metadata.template b/files/apache-nova-metadata.template
new file mode 100644
index 0000000..6231c1c
--- /dev/null
+++ b/files/apache-nova-metadata.template
@@ -0,0 +1,25 @@
+Listen %PUBLICPORT%
+
+<VirtualHost *:%PUBLICPORT%>
+    WSGIDaemonProcess nova-metadata processes=%APIWORKERS% threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV%
+    WSGIProcessGroup nova-metadata
+    WSGIScriptAlias / %PUBLICWSGI%
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%M"
+    </IfVersion>
+    ErrorLog /var/log/%APACHE_NAME%/nova-metadata.log
+    %SSLENGINE%
+    %SSLCERTFILE%
+    %SSLKEYFILE%
+</VirtualHost>
+
+Alias /metadata %PUBLICWSGI%
+<Location /metadata>
+    SetHandler wsgi-script
+    Options +ExecCGI
+    WSGIProcessGroup nova-metadata
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+</Location>
diff --git a/files/debs/ceilometer-collector b/files/debs/ceilometer-collector
index f1b692a..d1e9eef 100644
--- a/files/debs/ceilometer-collector
+++ b/files/debs/ceilometer-collector
@@ -1,6 +1,3 @@
-python-pymongo #NOPRIME
-mongodb-server #NOPRIME
 libnspr4-dev
-pkg-config
-libxml2-dev
-libxslt-dev
\ No newline at end of file
+mongodb-server #NOPRIME
+python-pymongo #NOPRIME
diff --git a/files/debs/cinder b/files/debs/cinder
index 51908eb..3595e01 100644
--- a/files/debs/cinder
+++ b/files/debs/cinder
@@ -1,6 +1,5 @@
-tgt # NOPRIME
 lvm2
-qemu-utils
-libpq-dev
 open-iscsi
 open-iscsi-utils # Deprecated since quantal dist:precise
+qemu-utils
+tgt # NOPRIME
diff --git a/files/debs/devlibs b/files/debs/devlibs
deleted file mode 100644
index 0446ceb..0000000
--- a/files/debs/devlibs
+++ /dev/null
@@ -1,7 +0,0 @@
-libffi-dev  # pyOpenSSL
-libmysqlclient-dev  # MySQL-python
-libpq-dev  # psycopg2
-libssl-dev  # pyOpenSSL
-libxml2-dev  # lxml
-libxslt1-dev  # lxml
-python-dev  # pyOpenSSL
diff --git a/files/debs/general b/files/debs/general
index 1460526..1215147 100644
--- a/files/debs/general
+++ b/files/debs/general
@@ -1,27 +1,33 @@
+bc
 bridge-utils
-screen
-unzip
-wget
-psmisc
-gcc
+curl
 g++
+gcc
+gettext  # used for compiling message catalogs
 git
 graphviz # needed for docs
+iputils-ping
+libffi-dev # for pyOpenSSL
+libjpeg-dev # Pillow 3.0.0
+libmysqlclient-dev  # MySQL-python
+libpq-dev  # psycopg2
+libssl-dev # for pyOpenSSL
+libxml2-dev  # lxml
+libxslt1-dev  # lxml
+libyaml-dev
 lsof # useful when debugging
+openjdk-7-jre-headless  # NOPRIME
 openssh-server
 openssl
-iputils-ping
-wget
-curl
-tcpdump
-tar
-python-dev
-python2.7
-python-gdbm # needed for testr
-bc
-libyaml-dev
-libffi-dev
-libssl-dev # for pyOpenSSL
-gettext  # used for compiling message catalogs
-openjdk-7-jre-headless  # NOPRIME
 pkg-config
+psmisc
+python2.7
+python-dev
+python-gdbm # needed for testr
+screen
+tar
+tcpdump
+unzip
+wget
+wget
+zlib1g-dev
diff --git a/files/debs/glance b/files/debs/glance
deleted file mode 100644
index 37877a8..0000000
--- a/files/debs/glance
+++ /dev/null
@@ -1,6 +0,0 @@
-libmysqlclient-dev
-libpq-dev
-libssl-dev
-libxml2-dev
-libxslt1-dev
-zlib1g-dev
diff --git a/files/debs/ironic b/files/debs/ironic
index 0a906db..4d5a6aa 100644
--- a/files/debs/ironic
+++ b/files/debs/ironic
@@ -6,8 +6,8 @@
 libvirt-bin
 open-iscsi
 openssh-client
-openvswitch-switch
 openvswitch-datapath-dkms
+openvswitch-switch
 python-libguestfs
 python-libvirt
 qemu
diff --git a/files/debs/keystone b/files/debs/keystone
index 70a5649..0795167 100644
--- a/files/debs/keystone
+++ b/files/debs/keystone
@@ -1,7 +1,6 @@
-python-lxml
-sqlite3
-python-mysqldb
-python-mysql.connector
+libkrb5-dev
 libldap2-dev
 libsasl2-dev
-libkrb5-dev
+python-mysql.connector
+python-mysqldb
+sqlite3
diff --git a/files/debs/ldap b/files/debs/ldap
index 26f7aef..aa3a934 100644
--- a/files/debs/ldap
+++ b/files/debs/ldap
@@ -1,3 +1,3 @@
 ldap-utils
-slapd
 python-ldap
+slapd
diff --git a/files/debs/n-cpu b/files/debs/n-cpu
index 5d5052a..0da57ee 100644
--- a/files/debs/n-cpu
+++ b/files/debs/n-cpu
@@ -1,7 +1,8 @@
-qemu-utils
+cryptsetup
+genisoimage
 lvm2 # NOPRIME
 open-iscsi
-genisoimage
-sysfsutils
-sg3-utils
 python-guestfs # NOPRIME
+qemu-utils
+sg3-utils
+sysfsutils
diff --git a/files/debs/n-novnc b/files/debs/n-novnc
deleted file mode 100644
index c8722b9..0000000
--- a/files/debs/n-novnc
+++ /dev/null
@@ -1 +0,0 @@
-python-numpy
diff --git a/files/debs/neutron b/files/debs/neutron
index b5a457e..85145d3 100644
--- a/files/debs/neutron
+++ b/files/debs/neutron
@@ -1,18 +1,18 @@
 acl
-ebtables
-iptables
-iputils-ping
-iputils-arping
-libmysqlclient-dev
-mysql-server #NOPRIME
-sudo
-postgresql-server-dev-all
-python-mysqldb
-python-mysql.connector
 dnsmasq-base
 dnsmasq-utils # for dhcp_release only available in dist:precise
+ebtables
+iptables
+iputils-arping
+iputils-ping
+libmysqlclient-dev
+mysql-server #NOPRIME
+postgresql-server-dev-all
+python-mysql.connector
+python-mysqldb
 rabbitmq-server # NOPRIME
-sqlite3
-vlan
 radvd # NOPRIME
+sqlite3
+sudo
 uuid-runtime
+vlan
diff --git a/files/debs/nova b/files/debs/nova
index 346b8b3..fe57fc4 100644
--- a/files/debs/nova
+++ b/files/debs/nova
@@ -1,31 +1,26 @@
+conntrack
+curl
 dnsmasq-base
 dnsmasq-utils # for dhcp_release
-conntrack
-kpartx
-parted
-iputils-arping
-libmysqlclient-dev
-mysql-server # NOPRIME
-python-mysqldb
-python-mysql.connector
-python-lxml # needed for glance which is needed for nova --- this shouldn't be here
-gawk
-iptables
 ebtables
-sqlite3
-sudo
-qemu-kvm # NOPRIME
-qemu # dist:wheezy,jessie NOPRIME
+gawk
+genisoimage # required for config_drive
+iptables
+iputils-arping
+kpartx
+libjs-jquery-tablesorter # Needed for coverage html reports
+libmysqlclient-dev
 libvirt-bin # NOPRIME
 libvirt-dev # NOPRIME
+mysql-server # NOPRIME
+parted
 pm-utils
-libjs-jquery-tablesorter # Needed for coverage html reports
-vlan
-curl
-genisoimage # required for config_drive
+python-mysql.connector
+python-mysqldb
+qemu # dist:wheezy,jessie NOPRIME
+qemu-kvm # NOPRIME
 rabbitmq-server # NOPRIME
 socat # used by ajaxterm
-python-libvirt # NOPRIME
-python-libxml2
-python-numpy # used by websockify for spice console
-python-m2crypto
+sqlite3
+sudo
+vlan
diff --git a/files/debs/swift b/files/debs/swift
index 726786e..4b8ac3d 100644
--- a/files/debs/swift
+++ b/files/debs/swift
@@ -1,4 +1,5 @@
 curl
+liberasurecode-dev
 make
 memcached
 sqlite3
diff --git a/files/debs/tempest b/files/debs/tempest
deleted file mode 100644
index bb09529..0000000
--- a/files/debs/tempest
+++ /dev/null
@@ -1,2 +0,0 @@
-libxml2-dev
-libxslt1-dev
diff --git a/files/debs/trove b/files/debs/trove
deleted file mode 100644
index 96f8f29..0000000
--- a/files/debs/trove
+++ /dev/null
@@ -1 +0,0 @@
-libxslt1-dev
diff --git a/files/debs/zaqar-server b/files/debs/zaqar-server
deleted file mode 100644
index 6c2a4d1..0000000
--- a/files/debs/zaqar-server
+++ /dev/null
@@ -1,4 +0,0 @@
-python-pymongo
-mongodb-server
-pkg-config
-redis-server # NOPRIME
\ No newline at end of file
diff --git a/files/debs/zookeeper b/files/debs/zookeeper
new file mode 100644
index 0000000..f41b559
--- /dev/null
+++ b/files/debs/zookeeper
@@ -0,0 +1 @@
+zookeeperd
diff --git a/files/ebtables.workaround b/files/ebtables.workaround
new file mode 100644
index 0000000..c8af51f
--- /dev/null
+++ b/files/ebtables.workaround
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2015 Hewlett-Packard Development Company, L.P.
+#
+# 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 is a terrible, terrible, truly terrible work around for
+# environments that have libvirt < 1.2.11. ebtables requires that you
+# specifically tell it you would like to not race and get punched in
+# the face when 2 run at the same time with a --concurrent flag.
+
+flock -w 300 /var/lock/ebtables.nova /sbin/ebtables.real $@
diff --git a/files/rpms-suse/ceilometer-collector b/files/rpms-suse/ceilometer-collector
index 5e4dfcc..fc75ffa 100644
--- a/files/rpms-suse/ceilometer-collector
+++ b/files/rpms-suse/ceilometer-collector
@@ -1,3 +1,3 @@
-# Not available in openSUSE main repositories, but can be fetched from OBS
 # (devel:languages:python and server:database projects)
 mongodb
+# Not available in openSUSE main repositories, but can be fetched from OBS
diff --git a/files/rpms-suse/ceph b/files/rpms-suse/ceph
index 8d46500..8c4955d 100644
--- a/files/rpms-suse/ceph
+++ b/files/rpms-suse/ceph
@@ -1,3 +1,3 @@
 ceph    # NOPRIME
-xfsprogs
 lsb
+xfsprogs
diff --git a/files/rpms-suse/cinder b/files/rpms-suse/cinder
index 3fd03cc..189a232 100644
--- a/files/rpms-suse/cinder
+++ b/files/rpms-suse/cinder
@@ -1,6 +1,4 @@
 lvm2
-tgt # NOPRIME
-qemu-tools
-python-devel
-postgresql-devel
 open-iscsi
+qemu-tools
+tgt # NOPRIME
diff --git a/files/rpms-suse/devlibs b/files/rpms-suse/devlibs
deleted file mode 100644
index 54d13a3..0000000
--- a/files/rpms-suse/devlibs
+++ /dev/null
@@ -1,6 +0,0 @@
-libffi-devel  # pyOpenSSL
-libopenssl-devel  # pyOpenSSL
-libxslt-devel  # lxml
-postgresql-devel  # psycopg2
-libmysqlclient-devel # MySQL-python
-python-devel  # pyOpenSSL
diff --git a/files/rpms-suse/general b/files/rpms-suse/general
index 42756d8..34a2955 100644
--- a/files/rpms-suse/general
+++ b/files/rpms-suse/general
@@ -8,16 +8,23 @@
 git-core
 graphviz # docs
 iputils
+libffi-devel  # pyOpenSSL
+libjpeg8-devel # Pillow 3.0.0
+libmysqlclient-devel # MySQL-python
 libopenssl-devel # to rebuild pyOpenSSL if needed
+libxslt-devel  # lxml
 lsof # useful when debugging
 make
+net-tools
 openssh
 openssl
+postgresql-devel  # psycopg2
 psmisc
 python-cmd2 # dist:opensuse-12.3
+python-devel  # pyOpenSSL
 screen
 tar
 tcpdump
 unzip
 wget
-net-tools
+zlib-devel
diff --git a/files/rpms-suse/glance b/files/rpms-suse/glance
deleted file mode 100644
index bf512de..0000000
--- a/files/rpms-suse/glance
+++ /dev/null
@@ -1 +0,0 @@
-python-devel
diff --git a/files/rpms-suse/horizon b/files/rpms-suse/horizon
index 77f7c34..753ea76 100644
--- a/files/rpms-suse/horizon
+++ b/files/rpms-suse/horizon
@@ -1,2 +1,2 @@
-apache2  # NOPRIME
 apache2-mod_wsgi  # NOPRIME
+apache2  # NOPRIME
diff --git a/files/rpms-suse/keystone b/files/rpms-suse/keystone
index c838b41..46832c7 100644
--- a/files/rpms-suse/keystone
+++ b/files/rpms-suse/keystone
@@ -1,4 +1,3 @@
 cyrus-sasl-devel
 openldap2-devel
-python-devel
 sqlite3
diff --git a/files/rpms-suse/n-api b/files/rpms-suse/n-api
index 6f59e60..af5ac2f 100644
--- a/files/rpms-suse/n-api
+++ b/files/rpms-suse/n-api
@@ -1,2 +1,2 @@
-python-dateutil
 fping
+python-dateutil
diff --git a/files/rpms-suse/n-cpu b/files/rpms-suse/n-cpu
index 7040b84..29bd31b 100644
--- a/files/rpms-suse/n-cpu
+++ b/files/rpms-suse/n-cpu
@@ -1,6 +1,7 @@
-# Stuff for diablo volumes
+cryptsetup
 genisoimage
 lvm2
 open-iscsi
-sysfsutils
 sg3_utils
+# Stuff for diablo volumes
+sysfsutils
diff --git a/files/rpms-suse/neutron b/files/rpms-suse/neutron
index 1339799..e9abc6e 100644
--- a/files/rpms-suse/neutron
+++ b/files/rpms-suse/neutron
@@ -5,9 +5,8 @@
 iptables
 iputils
 mariadb # NOPRIME
-postgresql-devel
 rabbitmq-server # NOPRIME
+radvd # NOPRIME
 sqlite3
 sudo
 vlan
-radvd # NOPRIME
diff --git a/files/rpms-suse/nova b/files/rpms-suse/nova
index 039456f..ae115d2 100644
--- a/files/rpms-suse/nova
+++ b/files/rpms-suse/nova
@@ -1,7 +1,7 @@
+conntrack-tools
 curl
 dnsmasq
 dnsmasq-utils # dist:opensuse-12.3,opensuse-13.1
-conntrack-tools
 ebtables
 gawk
 genisoimage # required for config_drive
@@ -9,14 +9,13 @@
 iputils
 kpartx
 kvm # NOPRIME
-# qemu as fallback if kvm cannot be used
-qemu # NOPRIME
 libvirt # NOPRIME
 libvirt-python # NOPRIME
 mariadb # NOPRIME
 parted
 polkit
-python-devel
+# qemu as fallback if kvm cannot be used
+qemu # NOPRIME
 rabbitmq-server # NOPRIME
 socat
 sqlite3
diff --git a/files/rpms-suse/openvswitch b/files/rpms-suse/openvswitch
index edfb4d2..53f8bb2 100644
--- a/files/rpms-suse/openvswitch
+++ b/files/rpms-suse/openvswitch
@@ -1,3 +1,3 @@
+
 openvswitch
 openvswitch-switch
-
diff --git a/files/rpms-suse/swift b/files/rpms-suse/swift
index 6a824f9..3663b98 100644
--- a/files/rpms-suse/swift
+++ b/files/rpms-suse/swift
@@ -1,6 +1,6 @@
 curl
+liberasurecode-devel
 memcached
-python-devel
 sqlite3
 xfsprogs
 xinetd
diff --git a/files/rpms/ceilometer-collector b/files/rpms/ceilometer-collector
index b139ed2..a8b8118 100644
--- a/files/rpms/ceilometer-collector
+++ b/files/rpms/ceilometer-collector
@@ -1,3 +1,3 @@
-selinux-policy-targeted
-mongodb-server #NOPRIME
 mongodb # NOPRIME
+mongodb-server #NOPRIME
+selinux-policy-targeted
diff --git a/files/rpms/ceph b/files/rpms/ceph
index 5483735..64befc5 100644
--- a/files/rpms/ceph
+++ b/files/rpms/ceph
@@ -1,3 +1,3 @@
 ceph    # NOPRIME
-xfsprogs
 redhat-lsb-core
+xfsprogs
diff --git a/files/rpms/cinder b/files/rpms/cinder
index a88503b..0274642 100644
--- a/files/rpms/cinder
+++ b/files/rpms/cinder
@@ -1,5 +1,4 @@
-lvm2
-scsi-target-utils # NOPRIME
-qemu-img
-postgresql-devel
 iscsi-initiator-utils
+lvm2
+qemu-img
+scsi-target-utils # NOPRIME
diff --git a/files/rpms/devlibs b/files/rpms/devlibs
deleted file mode 100644
index 385ed3b..0000000
--- a/files/rpms/devlibs
+++ /dev/null
@@ -1,8 +0,0 @@
-libffi-devel  # pyOpenSSL
-libxml2-devel  # lxml
-libxslt-devel  # lxml
-mariadb-devel  # MySQL-python
-openssl-devel  # pyOpenSSL
-postgresql-devel  # psycopg2
-python-devel  # pyOpenSSL
-redhat-rpm-config # MySQL-python rhbz-1195207 f21
diff --git a/files/rpms/dstat b/files/rpms/dstat
index 8a8f8fe..2b643b8 100644
--- a/files/rpms/dstat
+++ b/files/rpms/dstat
@@ -1 +1 @@
-dstat
\ No newline at end of file
+dstat
diff --git a/files/rpms/general b/files/rpms/general
index c3f3de8..2804682 100644
--- a/files/rpms/general
+++ b/files/rpms/general
@@ -1,31 +1,36 @@
+bc
 bridge-utils
 curl
 dbus
 euca2ools # only for testing client
 gcc
 gcc-c++
+gettext  # used for compiling message catalogs
 git-core
 graphviz # needed only for docs
+iptables-services  # NOPRIME f21,f22,f23
+java-1.7.0-openjdk-headless  # NOPRIME rhel7
+java-1.8.0-openjdk-headless  # NOPRIME f21,f22,f23
+libffi-devel
+libjpeg-turbo-devel # Pillow 3.0.0
+libxml2-devel # lxml
+libxslt-devel # lxml
+libyaml-devel
+mariadb-devel  # MySQL-python
+net-tools
 openssh-server
 openssl
 openssl-devel # to rebuild pyOpenSSL if needed
-libffi-devel
-libxml2-devel
-libxslt-devel
 pkgconfig
+postgresql-devel  # psycopg2
 psmisc
+pyOpenSSL # version in pip uses too much memory
 python-devel
+redhat-rpm-config # MySQL-python rhbz-1195207 f21
 screen
 tar
 tcpdump
 unzip
 wget
 which
-bc
-libyaml-devel
-gettext  # used for compiling message catalogs
-net-tools
-java-1.7.0-openjdk-headless  # NOPRIME rhel7
-java-1.8.0-openjdk-headless  # NOPRIME f21,f22
-pyOpenSSL # version in pip uses too much memory
-iptables-services  # NOPRIME f21,f22
+zlib-devel
diff --git a/files/rpms/glance b/files/rpms/glance
deleted file mode 100644
index 479194f..0000000
--- a/files/rpms/glance
+++ /dev/null
@@ -1,6 +0,0 @@
-libxml2-devel
-libxslt-devel
-mysql-devel
-openssl-devel
-postgresql-devel
-zlib-devel
diff --git a/files/rpms/horizon b/files/rpms/horizon
index b2cf0de..aeb2cb5 100644
--- a/files/rpms/horizon
+++ b/files/rpms/horizon
@@ -1,5 +1,5 @@
 Django
 httpd # NOPRIME
 mod_wsgi  # NOPRIME
-pyxattr
 pcre-devel  # pyScss
+pyxattr
diff --git a/files/rpms/keystone b/files/rpms/keystone
index 8074119..c01c261 100644
--- a/files/rpms/keystone
+++ b/files/rpms/keystone
@@ -1,4 +1,3 @@
-MySQL-python
-libxslt-devel
-sqlite
 mod_ssl
+MySQL-python
+sqlite
diff --git a/files/rpms/ldap b/files/rpms/ldap
index d89c4cf..d5b8fa4 100644
--- a/files/rpms/ldap
+++ b/files/rpms/ldap
@@ -1,2 +1,2 @@
-openldap-servers
 openldap-clients
+openldap-servers
diff --git a/files/rpms/n-cpu b/files/rpms/n-cpu
index c1a8e8f..7773b04 100644
--- a/files/rpms/n-cpu
+++ b/files/rpms/n-cpu
@@ -1,7 +1,7 @@
-# Stuff for diablo volumes
+cryptsetup
+genisoimage
 iscsi-initiator-utils
 lvm2
-genisoimage
-sysfsutils
 sg3_utils
-
+# Stuff for diablo volumes
+sysfsutils
diff --git a/files/rpms/neutron b/files/rpms/neutron
index 29851be..9683475 100644
--- a/files/rpms/neutron
+++ b/files/rpms/neutron
@@ -1,4 +1,3 @@
-MySQL-python
 acl
 dnsmasq # for q-dhcp
 dnsmasq-utils # for dhcp_release
@@ -7,10 +6,10 @@
 iputils
 mysql-connector-python
 mysql-devel
+MySQL-python
 mysql-server # NOPRIME
 openvswitch # NOPRIME
-postgresql-devel
 rabbitmq-server # NOPRIME
+radvd # NOPRIME
 sqlite
 sudo
-radvd # NOPRIME
diff --git a/files/rpms/nova b/files/rpms/nova
index 6eeb623..00e7596 100644
--- a/files/rpms/nova
+++ b/files/rpms/nova
@@ -1,27 +1,28 @@
-MySQL-python
+conntrack-tools
 curl
 dnsmasq # for nova-network
 dnsmasq-utils # for dhcp_release
-conntrack-tools
 ebtables
 gawk
 genisoimage # required for config_drive
 iptables
 iputils
+kernel-modules # dist:f21,f22,f23
 kpartx
 kvm # NOPRIME
-qemu-kvm # NOPRIME
 libvirt-bin # NOPRIME
 libvirt-devel # NOPRIME
 libvirt-python # NOPRIME
 libxml2-python
-numpy # needed by websockify for spice console
 m2crypto
 mysql-connector-python
 mysql-devel
+MySQL-python
 mysql-server # NOPRIME
+numpy # needed by websockify for spice console
 parted
 polkit
+qemu-kvm # NOPRIME
 rabbitmq-server # NOPRIME
 sqlite
 sudo
diff --git a/files/rpms/swift b/files/rpms/swift
index 1bf57cc..46dc59d 100644
--- a/files/rpms/swift
+++ b/files/rpms/swift
@@ -1,7 +1,8 @@
 curl
+liberasurecode-devel
 memcached
 pyxattr
+rsync-daemon # dist:f22,f23
 sqlite
 xfsprogs
 xinetd
-rsync-daemon # dist:f22,f23
diff --git a/files/rpms/tempest b/files/rpms/tempest
deleted file mode 100644
index e7bbd43..0000000
--- a/files/rpms/tempest
+++ /dev/null
@@ -1 +0,0 @@
-libxslt-devel
diff --git a/files/rpms/trove b/files/rpms/trove
deleted file mode 100644
index e7bbd43..0000000
--- a/files/rpms/trove
+++ /dev/null
@@ -1 +0,0 @@
-libxslt-devel
diff --git a/files/rpms/zaqar-server b/files/rpms/zaqar-server
deleted file mode 100644
index 78806fb..0000000
--- a/files/rpms/zaqar-server
+++ /dev/null
@@ -1,5 +0,0 @@
-selinux-policy-targeted
-mongodb
-mongodb-server
-pymongo
-redis # NOPRIME
diff --git a/files/rpms/zookeeper b/files/rpms/zookeeper
new file mode 100644
index 0000000..1bfac53
--- /dev/null
+++ b/files/rpms/zookeeper
@@ -0,0 +1 @@
+zookeeper
diff --git a/files/venv-requirements.txt b/files/venv-requirements.txt
deleted file mode 100644
index b9a55b4..0000000
--- a/files/venv-requirements.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Once we can prebuild wheels before a devstack run, uncomment the skipped libraries
-cryptography
-# lxml # still install from from packages
-# netifaces # still install from packages
-#numpy    # slowest wheel by far, stop building until we are actually using the output
-posix-ipc
-# psycopg # still install from packages
-pycrypto
-pyOpenSSL
-PyYAML
-xattr
diff --git a/files/zookeeper/environment b/files/zookeeper/environment
new file mode 100644
index 0000000..afa2d2f
--- /dev/null
+++ b/files/zookeeper/environment
@@ -0,0 +1,36 @@
+#
+# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
+#
+# 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.
+#
+
+# Modified from http://packages.ubuntu.com/saucy/zookeeperd
+NAME=zookeeper
+ZOOCFGDIR=/etc/zookeeper/conf
+
+# seems, that log4j requires the log4j.properties file to be in the classpath
+CLASSPATH="$ZOOCFGDIR:/usr/share/java/jline.jar:/usr/share/java/log4j-1.2.jar:/usr/share/java/xercesImpl.jar:/usr/share/java/xmlParserAPIs.jar:/usr/share/java/netty.jar:/usr/share/java/slf4j-api.jar:/usr/share/java/slf4j-log4j12.jar:/usr/share/java/zookeeper.jar"
+
+ZOOCFG="$ZOOCFGDIR/zoo.cfg"
+ZOO_LOG_DIR=/var/log/zookeeper
+USER=$NAME
+GROUP=$NAME
+PIDDIR=/var/run/$NAME
+PIDFILE=$PIDDIR/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+JAVA=/usr/bin/java
+ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
+ZOO_LOG4J_PROP="INFO,ROLLINGFILE"
+JMXLOCALONLY=false
+JAVA_OPTS=""
diff --git a/files/zookeeper/log4j.properties b/files/zookeeper/log4j.properties
new file mode 100644
index 0000000..6c45a4a
--- /dev/null
+++ b/files/zookeeper/log4j.properties
@@ -0,0 +1,69 @@
+#
+# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
+#
+# 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.
+#
+
+# From http://packages.ubuntu.com/saucy/zookeeperd
+
+# ZooKeeper Logging Configuration
+#
+
+# Format is "<default threshold> (, <appender>)+
+
+log4j.rootLogger=${zookeeper.root.logger}
+
+# Example: console appender only
+# log4j.rootLogger=INFO, CONSOLE
+
+# Example with rolling log file
+#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
+
+# Example with rolling log file and tracing
+#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
+
+#
+# Log INFO level and above messages to the console
+#
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=INFO
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
+
+#
+# Add ROLLINGFILE to rootLogger to get log file output
+#    Log DEBUG level and above messages to a log file
+log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
+log4j.appender.ROLLINGFILE.Threshold=WARN
+log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/zookeeper.log
+
+# Max log file size of 10MB
+log4j.appender.ROLLINGFILE.MaxFileSize=10MB
+# uncomment the next line to limit number of backup files
+#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
+
+log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
+
+
+#
+# Add TRACEFILE to rootLogger to get log file output
+#    Log DEBUG level and above messages to a log file
+log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
+log4j.appender.TRACEFILE.Threshold=TRACE
+log4j.appender.TRACEFILE.File=${zookeeper.log.dir}/zookeeper_trace.log
+
+log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
+### Notice we are including log4j's NDC here (%x)
+log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n
diff --git a/files/zookeeper/myid b/files/zookeeper/myid
new file mode 100644
index 0000000..c227083
--- /dev/null
+++ b/files/zookeeper/myid
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/files/zookeeper/zoo.cfg b/files/zookeeper/zoo.cfg
new file mode 100644
index 0000000..b8f5582
--- /dev/null
+++ b/files/zookeeper/zoo.cfg
@@ -0,0 +1,74 @@
+#
+# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
+#
+# 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.
+#
+# http://hadoop.apache.org/zookeeper/docs/current/zookeeperAdmin.html
+
+# The number of milliseconds of each tick
+tickTime=2000
+# The number of ticks that the initial
+# synchronization phase can take
+initLimit=10
+# The number of ticks that can pass between
+# sending a request and getting an acknowledgement
+syncLimit=5
+# the directory where the snapshot is stored.
+dataDir=/var/lib/zookeeper
+# Place the dataLogDir to a separate physical disc for better performance
+# dataLogDir=/disk2/zookeeper
+
+# the port at which the clients will connect
+clientPort=2181
+
+# Maximum number of clients that can connect from one client
+maxClientCnxns=60
+
+# specify all zookeeper servers
+# The fist port is used by followers to connect to the leader
+# The second one is used for leader election
+
+server.0=127.0.0.1:2888:3888
+
+# To avoid seeks ZooKeeper allocates space in the transaction log file in
+# blocks of preAllocSize kilobytes. The default block size is 64M. One reason
+# for changing the size of the blocks is to reduce the block size if snapshots
+# are taken more often. (Also, see snapCount).
+#preAllocSize=65536
+
+# Clients can submit requests faster than ZooKeeper can process them,
+# especially if there are a lot of clients. To prevent ZooKeeper from running
+# out of memory due to queued requests, ZooKeeper will throttle clients so that
+# there is no more than globalOutstandingLimit outstanding requests in the
+# system. The default limit is 1,000.ZooKeeper logs transactions to a
+# transaction log. After snapCount transactions are written to a log file a
+# snapshot is started and a new transaction log file is started. The default
+# snapCount is 10,000.
+#snapCount=1000
+
+# If this option is defined, requests will be will logged to a trace file named
+# traceFile.year.month.day.
+#traceFile=
+
+# Leader accepts client connections. Default value is "yes". The leader machine
+# coordinates updates. For higher update throughput at thes slight expense of
+# read throughput the leader can be configured to not accept clients and focus
+# on coordination.
+#leaderServes=yes
+
+# Autopurge every hour to avoid using lots of disk in bursts
+# Order of the next 2 properties matters.
+# autopurge.snapRetainCount must be before autopurge.purgeInterval.
+autopurge.snapRetainCount=3
+autopurge.purgeInterval=1
\ No newline at end of file
diff --git a/functions b/functions
index 1668e16..34da1ba 100644
--- a/functions
+++ b/functions
@@ -10,6 +10,10 @@
 # - ``GLANCE_HOSTPORT``
 #
 
+# ensure we don't re-source this in the same environment
+[[ -z "$_DEVSTACK_FUNCTIONS" ]] || return 0
+declare -r _DEVSTACK_FUNCTIONS=1
+
 # Include the common functions
 FUNC_DIR=$(cd $(dirname "${BASH_SOURCE:-$0}") && pwd)
 source ${FUNC_DIR}/functions-common
@@ -32,10 +36,9 @@
 # - ``FILES`` must be set to the cache dir
 # - ``GLANCE_HOSTPORT``
 #
-# upload_image image-url glance-token
+# upload_image image-url
 function upload_image {
     local image_url=$1
-    local token=$2
 
     local image image_fname image_name
 
@@ -67,7 +70,7 @@
     # OpenVZ-format images are provided as .tar.gz, but not decompressed prior to loading
     if [[ "$image_url" =~ 'openvz' ]]; then
         image_name="${image_fname%.tar.gz}"
-        openstack --os-token $token --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image create "$image_name" --public --container-format ami --disk-format ami < "${image}"
+        openstack --os-cloud=devstack-admin image create "$image_name" --public --container-format ami --disk-format ami < "${image}"
         return
     fi
 
@@ -178,7 +181,7 @@
         vmdk_adapter_type="${props[1]:-$vmdk_adapter_type}"
         vmdk_net_adapter="${props[2]:-$vmdk_net_adapter}"
 
-        openstack --os-token $token --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image create "$image_name" --public --container-format bare --disk-format vmdk --property vmware_disktype="$vmdk_disktype" --property vmware_adaptertype="$vmdk_adapter_type" --property hw_vif_model="$vmdk_net_adapter" < "${image}"
+        openstack --os-cloud=devstack-admin image create "$image_name" --public --container-format bare --disk-format vmdk --property vmware_disktype="$vmdk_disktype" --property vmware_adaptertype="$vmdk_adapter_type" --property hw_vif_model="$vmdk_net_adapter" < "${image}"
         return
     fi
 
@@ -195,8 +198,7 @@
             force_vm_mode="--property vm_mode=xen"
         fi
         openstack \
-            --os-token $token \
-            --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT \
+            --os-cloud=devstack-admin \
             image create \
             "$image_name" --public \
             --container-format=ovf --disk-format=vhd \
@@ -210,8 +212,7 @@
     if [[ "$image_url" =~ '.xen-raw.tgz' ]]; then
         image_name="${image_fname%.xen-raw.tgz}"
         openstack \
-            --os-token $token \
-            --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT \
+            --os-cloud=devstack-admin \
             image create \
             "$image_name" --public \
             --container-format=tgz --disk-format=raw \
@@ -219,6 +220,22 @@
         return
     fi
 
+    if [[ "$image_url" =~ '.hds' ]]; then
+        image_name="${image_fname%.hds}"
+        vm_mode=${image_name##*-}
+        if [[ $vm_mode != 'exe' && $vm_mode != 'hvm' ]]; then
+            die $LINENO "Unknown vm_mode=${vm_mode} for Virtuozzo image"
+        fi
+
+        openstack \
+            --os-cloud=devstack-admin \
+            image create \
+            "$image_name" --public \
+            --container-format=bare --disk-format=ploop \
+            --property vm_mode=$vm_mode < "${image}"
+        return
+    fi
+
     local kernel=""
     local ramdisk=""
     local disk_format=""
@@ -247,7 +264,8 @@
             ;;
         *.img)
             image_name=$(basename "$image" ".img")
-            local format=$(qemu-img info ${image} | awk '/^file format/ { print $3; exit }')
+            local format
+            format=$(qemu-img info ${image} | awk '/^file format/ { print $3; exit }')
             if [[ ",qcow2,raw,vdi,vmdk,vpc," =~ ",$format," ]]; then
                 disk_format=$format
             else
@@ -293,9 +311,9 @@
 
     if [ "$container_format" = "bare" ]; then
         if [ "$unpack" = "zcat" ]; then
-            openstack --os-token $token --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < <(zcat --force "${image}")
+            openstack --os-cloud=devstack-admin image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < <(zcat --force "${image}")
         else
-            openstack --os-token $token --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < "${image}"
+            openstack --os-cloud=devstack-admin image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < "${image}"
         fi
     else
         # Use glance client to add the kernel the root filesystem.
@@ -303,12 +321,12 @@
         # kernel for use when uploading the root filesystem.
         local kernel_id="" ramdisk_id="";
         if [ -n "$kernel" ]; then
-            kernel_id=$(openstack --os-token $token --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image create "$image_name-kernel" $img_property --public --container-format aki --disk-format aki < "$kernel" | grep ' id ' | get_field 2)
+            kernel_id=$(openstack --os-cloud=devstack-admin image create "$image_name-kernel" $img_property --public --container-format aki --disk-format aki < "$kernel" | grep ' id ' | get_field 2)
         fi
         if [ -n "$ramdisk" ]; then
-            ramdisk_id=$(openstack --os-token $token --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image create "$image_name-ramdisk" $img_property --public --container-format ari --disk-format ari < "$ramdisk" | grep ' id ' | get_field 2)
+            ramdisk_id=$(openstack --os-cloud=devstack-admin image create "$image_name-ramdisk" $img_property --public --container-format ari --disk-format ari < "$ramdisk" | grep ' id ' | get_field 2)
         fi
-        openstack --os-token $token --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image create "${image_name%.img}" $img_property --public --container-format ami --disk-format ami ${kernel_id:+--property kernel_id=$kernel_id} ${ramdisk_id:+--property ramdisk_id=$ramdisk_id} < "${image}"
+        openstack --os-cloud=devstack-admin image create "${image_name%.img}" $img_property --public --container-format ami --disk-format ami ${kernel_id:+--property kernel_id=$kernel_id} ${ramdisk_id:+--property ramdisk_id=$ramdisk_id} < "${image}"
     fi
 }
 
@@ -324,7 +342,7 @@
         # No backends registered means this is likely called from ``localrc``
         # This is now deprecated usage
         DATABASE_TYPE=$1
-        DEPRECATED_TEXT="$DEPRECATED_TEXT\nThe database backend needs to be properly set in ENABLED_SERVICES; use_database is deprecated localrc\n"
+        deprecated "The database backend needs to be properly set in ENABLED_SERVICES; use_database is deprecated localrc"
     else
         # This should no longer get called...here for posterity
         use_exclusive_service DATABASE_BACKENDS DATABASE_TYPE $1
@@ -388,10 +406,11 @@
     local vm_id=$1
     local network_name=$2
     local nova_result="$(nova show $vm_id)"
-    local ip=$(echo "$nova_result" | grep "$network_name" | get_field 2)
+    local ip
+    ip=$(echo "$nova_result" | grep "$network_name" | get_field 2)
     if [[ $ip = "" ]];then
         echo "$nova_result"
-        die $LINENO "[Fail] Coudn't get ipaddress of VM"
+        die $LINENO "[Fail] Couldn't get ipaddress of VM"
     fi
     echo $ip
 }
@@ -438,7 +457,8 @@
     # homedir permissions on RHEL and common practice of making DEST in
     # the stack user's homedir.
 
-    local real_path=$(readlink -f $1)
+    local real_path
+    real_path=$(readlink -f $1)
     local rebuilt_path=""
     for i in $(echo ${real_path} | tr "/" " "); do
         rebuilt_path=$rebuilt_path"/"$i
diff --git a/functions-common b/functions-common
index 60cf04c..6a065ba 100644
--- a/functions-common
+++ b/functions-common
@@ -28,7 +28,6 @@
 # - ``REQUIREMENTS_DIR``
 # - ``STACK_USER``
 # - ``TRACK_DEPENDS``
-# - ``UNDO_REQUIREMENTS``
 # - ``http_proxy``, ``https_proxy``, ``no_proxy``
 #
 
@@ -36,6 +35,10 @@
 XTRACE=$(set +o | grep xtrace)
 set +o xtrace
 
+# ensure we don't re-source this in the same environment
+[[ -z "$_DEVSTACK_FUNCTIONS_COMMON" ]] || return 0
+declare -r _DEVSTACK_FUNCTIONS_COMMON=1
+
 # Global Config Variables
 declare -A GITREPO
 declare -A GITBRANCH
@@ -64,15 +67,68 @@
     done
 }
 
-# Normalize config values to True or False
-# Accepts as False: 0 no No NO false False FALSE
-# Accepts as True: 1 yes Yes YES true True TRUE
-# VAR=$(trueorfalse default-value test-value)
+# Update/create user clouds.yaml file.
+# clouds.yaml will have
+# - A `devstack` entry for the `demo` user for the `demo` project.
+# - A `devstack-admin` entry for the `admin` user for the `admin` project.
+# write_clouds_yaml
+function write_clouds_yaml {
+    # The location is a variable to allow for easier refactoring later to make it
+    # overridable. There is currently no usecase where doing so makes sense, so
+    # it's not currently configurable.
+
+    CLOUDS_YAML=/etc/openstack/clouds.yaml
+
+    sudo mkdir -p $(dirname $CLOUDS_YAML)
+    sudo chown -R $STACK_USER /etc/openstack
+
+    CA_CERT_ARG=''
+    if [ -f "$SSL_BUNDLE_FILE" ]; then
+        CA_CERT_ARG="--os-cacert $SSL_BUNDLE_FILE"
+    fi
+    $TOP_DIR/tools/update_clouds_yaml.py \
+        --file $CLOUDS_YAML \
+        --os-cloud devstack \
+        --os-region-name $REGION_NAME \
+        --os-identity-api-version 3 \
+        $CA_CERT_ARG \
+        --os-auth-url $KEYSTONE_AUTH_URI \
+        --os-username demo \
+        --os-password $ADMIN_PASSWORD \
+        --os-project-name demo
+    $TOP_DIR/tools/update_clouds_yaml.py \
+        --file $CLOUDS_YAML \
+        --os-cloud devstack-admin \
+        --os-region-name $REGION_NAME \
+        --os-identity-api-version 3 \
+        $CA_CERT_ARG \
+        --os-auth-url $KEYSTONE_AUTH_URI \
+        --os-username admin \
+        --os-password $ADMIN_PASSWORD \
+        --os-project-name admin
+}
+
+# trueorfalse <True|False> <VAR>
+#
+# Normalize config-value provided in variable VAR to either "True" or
+# "False".  If VAR is unset (i.e. $VAR evaluates as empty), the value
+# of the second argument will be used as the default value.
+#
+#  Accepts as False: 0 no  No  NO  false False FALSE
+#  Accepts as True:  1 yes Yes YES true  True  TRUE
+#
+# usage:
+#  VAL=$(trueorfalse False VAL)
 function trueorfalse {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
 
     local default=$1
+
+    if [ -z $2 ]; then
+        die $LINENO "variable to normalize required"
+    fi
     local testval=${!2:-}
 
     case "$testval" in
@@ -97,7 +153,8 @@
 # backtrace level
 function backtrace {
     local level=$1
-    local deep=$((${#BASH_SOURCE[@]} - 1))
+    local deep
+    deep=$((${#BASH_SOURCE[@]} - 1))
     echo "[Call Trace]"
     while [ $level -le $deep ]; do
         echo "${BASH_SOURCE[$deep]}:${BASH_LINENO[$deep-1]}:${FUNCNAME[$deep-1]}"
@@ -127,7 +184,8 @@
 # die_if_not_set $LINENO env-var "message"
 function die_if_not_set {
     local exitcode=$?
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local line=$1; shift
     local evar=$1; shift
@@ -137,11 +195,18 @@
     $xtrace
 }
 
+function deprecated {
+    local text=$1
+    DEPRECATED_TEXT+="\n$text"
+    echo "WARNING: $text"
+}
+
 # Prints line number and "message" in error format
 # err $LINENO "message"
 function err {
     local exitcode=$?
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local msg="[ERROR] ${BASH_SOURCE[2]}:$1 $2"
     echo $msg 1>&2;
@@ -158,7 +223,8 @@
 # err_if_not_set $LINENO env-var "message"
 function err_if_not_set {
     local exitcode=$?
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local line=$1; shift
     local evar=$1; shift
@@ -194,7 +260,8 @@
 # warn $LINENO "message"
 function warn {
     local exitcode=$?
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local msg="[WARNING] ${BASH_SOURCE[2]}:$1 $2"
     echo $msg
@@ -430,7 +497,8 @@
     local git_remote=$1
     local git_dest=$2
     local git_ref=$3
-    local orig_dir=$(pwd)
+    local orig_dir
+    orig_dir=$(pwd)
     local git_clone_flags=""
 
     RECLONE=$(trueorfalse False RECLONE)
@@ -450,8 +518,11 @@
     if echo $git_ref | egrep -q "^refs"; then
         # If our branch name is a gerrit style refs/changes/...
         if [[ ! -d $git_dest ]]; then
-            [[ "$ERROR_ON_CLONE" = "True" ]] && \
+            if [[ "$ERROR_ON_CLONE" = "True" ]]; then
+                echo "The $git_dest project was not found; if this is a gate job, add"
+                echo "the project to the \$PROJECTS variable in the job definition."
                 die $LINENO "Cloning not allowed in this configuration"
+            fi
             git_timed clone $git_clone_flags $git_remote $git_dest
         fi
         cd $git_dest
@@ -459,8 +530,11 @@
     else
         # do a full clone only if the directory doesn't exist
         if [[ ! -d $git_dest ]]; then
-            [[ "$ERROR_ON_CLONE" = "True" ]] && \
+            if [[ "$ERROR_ON_CLONE" = "True" ]]; then
+                echo "The $git_dest project was not found; if this is a gate job, add"
+                echo "the project to the \$PROJECTS variable in the job definition."
                 die $LINENO "Cloning not allowed in this configuration"
+            fi
             git_timed clone $git_clone_flags $git_remote $git_dest
             cd $git_dest
             # This checkout syntax works for both branches and tags
@@ -588,7 +662,8 @@
         host_ip=""
         # Find the interface used for the default route
         host_ip_iface=${host_ip_iface:-$(ip -f $af route | awk '/default/ {print $5}' | head -1)}
-        local host_ips=$(LC_ALL=C ip -f $af addr show ${host_ip_iface} | awk /$af'/ {split($2,parts,"/");  print parts[1]}')
+        local host_ips
+        host_ips=$(LC_ALL=C ip -f $af addr show ${host_ip_iface} | sed /temporary/d |awk /$af'/ {split($2,parts,"/");  print parts[1]}')
         local ip
         for ip in $host_ips; do
             # Attempt to filter out IP addresses that are part of the fixed and
@@ -637,7 +712,8 @@
 # copy over a default policy.json and policy.d for projects
 function install_default_policy {
     local project=$1
-    local project_uc=$(echo $1|tr a-z A-Z)
+    local project_uc
+    project_uc=$(echo $1|tr a-z A-Z)
     local conf_dir="${project_uc}_CONF_DIR"
     # eval conf dir to get the variable
     conf_dir="${!conf_dir}"
@@ -670,7 +746,8 @@
 
     # Add a terminating comma to policy lines without one
     # Remove the closing '}' and all lines following to the end-of-file
-    local tmpfile=$(mktemp)
+    local tmpfile
+    tmpfile=$(mktemp)
     uniq ${policy_file} | sed -e '
         s/]$/],/
         /^[}]/,$d
@@ -687,16 +764,13 @@
 # Usage: get_or_create_domain <name> <description>
 function get_or_create_domain {
     local domain_id
-    local os_url="$KEYSTONE_SERVICE_URI_V3"
     # Gets domain id
     domain_id=$(
         # Gets domain id
-        openstack --os-token=$OS_TOKEN --os-url=$os_url \
-            --os-identity-api-version=3 domain show $1 \
+        openstack domain show $1 \
             -f value -c id 2>/dev/null ||
         # Creates new domain
-        openstack --os-token=$OS_TOKEN --os-url=$os_url \
-            --os-identity-api-version=3 domain create $1 \
+        openstack domain create $1 \
             --description "$2" \
             -f value -c id
     )
@@ -707,13 +781,11 @@
 # Usage: get_or_create_group <groupname> <domain> [<description>]
 function get_or_create_group {
     local desc="${3:-}"
-    local os_url="$KEYSTONE_SERVICE_URI_V3"
     local group_id
     # Gets group id
     group_id=$(
         # Creates new group with --or-show
-        openstack --os-token=$OS_TOKEN --os-url=$os_url \
-            --os-identity-api-version=3 group create $1 \
+        openstack group create $1 \
             --domain $2 --description "$desc" --or-show \
             -f value -c id
     )
@@ -735,8 +807,6 @@
         openstack user create \
             $1 \
             --password "$2" \
-            --os-url=$KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
             --domain=$3 \
             $email \
             --or-show \
@@ -751,9 +821,7 @@
     local project_id
     project_id=$(
         # Creates new project with --or-show
-        openstack --os-url=$KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
-            project create $1 \
+        openstack project create $1 \
             --domain=$2 \
             --or-show -f value -c id
     )
@@ -767,8 +835,6 @@
     role_id=$(
         # Creates role with --or-show
         openstack role create $1 \
-            --os-url=$KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
             --or-show -f value -c id
     )
     echo $role_id
@@ -781,21 +847,21 @@
     # Gets user role id
     user_role_id=$(openstack role list \
         --user $2 \
-        --os-url=$KEYSTONE_SERVICE_URI_V3 \
-        --os-identity-api-version=3 \
         --column "ID" \
         --project $3 \
         --column "Name" \
         | grep " $1 " | get_field 1)
     if [[ -z "$user_role_id" ]]; then
-        # Adds role to user
-        user_role_id=$(openstack role add \
-            $1 \
+        # Adds role to user and get it
+        openstack role add $1 \
             --user $2 \
+            --project $3
+        user_role_id=$(openstack role list \
+            --user $2 \
+            --column "ID" \
             --project $3 \
-            --os-url=$KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
-            | grep " id " | get_field 2)
+            --column "Name" \
+            | grep " $1 " | get_field 1)
     fi
     echo $user_role_id
 }
@@ -806,21 +872,15 @@
     local group_role_id
     # Gets group role id
     group_role_id=$(openstack role list \
-        --os-url=$KEYSTONE_SERVICE_URI_V3 \
-        --os-identity-api-version=3 \
         --group $2 \
         --project $3 \
         -c "ID" -f value)
     if [[ -z "$group_role_id" ]]; then
         # Adds role to group and get it
         openstack role add $1 \
-            --os-url=$KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
             --group $2 \
             --project $3
         group_role_id=$(openstack role list \
-            --os-url=$KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
             --group $2 \
             --project $3 \
             -c "ID" -f value)
@@ -838,8 +898,6 @@
         openstack service show $2 -f value -c id 2>/dev/null ||
         # Creates new service if not exists
         openstack service create \
-            --os-url $KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
             $2 \
             --name $1 \
             --description="$3" \
@@ -853,8 +911,6 @@
 function _get_or_create_endpoint_with_interface {
     local endpoint_id
     endpoint_id=$(openstack endpoint list \
-        --os-url $KEYSTONE_SERVICE_URI_V3 \
-        --os-identity-api-version=3 \
         --service $1 \
         --interface $2 \
         --region $4 \
@@ -862,8 +918,6 @@
     if [[ -z "$endpoint_id" ]]; then
         # Creates new endpoint
         endpoint_id=$(openstack endpoint create \
-            --os-url $KEYSTONE_SERVICE_URI_V3 \
-            --os-identity-api-version=3 \
             $1 $2 $3 --region $4 -f value -c id)
     fi
 
@@ -881,7 +935,8 @@
     # scenarios currently that use the returned id. Ideally this behaviour
     # should be pushed out to the service setups and let them create the
     # endpoints they need.
-    local public_id=$(_get_or_create_endpoint_with_interface $1 public $3 $2)
+    local public_id
+    public_id=$(_get_or_create_endpoint_with_interface $1 public $3 $2)
     _get_or_create_endpoint_with_interface $1 admin $4 $2
     _get_or_create_endpoint_with_interface $1 internal $5 $2
 
@@ -927,19 +982,26 @@
 # Uses globals ``OFFLINE``, ``*_proxy``
 # apt_get operation package [package ...]
 function apt_get {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
 
     [[ "$OFFLINE" = "True" || -z "$@" ]] && return
     local sudo="sudo"
     [[ "$(id -u)" = "0" ]] && sudo="env"
 
+    # time all the apt operations
+    time_start "apt-get"
+
     $xtrace
 
     $sudo DEBIAN_FRONTEND=noninteractive \
         http_proxy=${http_proxy:-} https_proxy=${https_proxy:-} \
         no_proxy=${no_proxy:-} \
         apt-get --option "Dpkg::Options::=--force-confold" --assume-yes "$@"
+
+    # stop the clock
+    time_stop "apt-get"
 }
 
 function _parse_package_files {
@@ -969,7 +1031,7 @@
                 # We are using BASH regexp matching feature.
                 package=${BASH_REMATCH[1]}
                 distros=${BASH_REMATCH[2]}
-                # In bash ${VAR,,} will lowecase VAR
+                # In bash ${VAR,,} will lowercase VAR
                 # Look for a match in the distro list
                 if [[ ! ${distros,,} =~ ${DISTRO,,} ]]; then
                     # If no match then skip this package
@@ -996,13 +1058,19 @@
 # - ``# dist:DISTRO`` or ``dist:DISTRO1,DISTRO2`` limits the selection
 #   of the package to the distros listed.  The distro names are case insensitive.
 function get_packages {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local services=$@
-    local package_dir=$(_get_package_dir)
+    local package_dir
+    package_dir=$(_get_package_dir)
     local file_to_parse=""
     local service=""
 
+    if [ $# -ne 1 ]; then
+        die $LINENO "get_packages takes a single, comma-separated argument"
+    fi
+
     if [[ -z "$package_dir" ]]; then
         echo "No package directory supplied"
         return 1
@@ -1025,10 +1093,6 @@
             if [[ ! $file_to_parse =~ $package_dir/cinder ]]; then
                 file_to_parse="${file_to_parse} ${package_dir}/cinder"
             fi
-        elif [[ $service == ceilometer-* ]]; then
-            if [[ ! $file_to_parse =~ $package_dir/ceilometer ]]; then
-                file_to_parse="${file_to_parse} ${package_dir}/ceilometer"
-            fi
         elif [[ $service == s-* ]]; then
             if [[ ! $file_to_parse =~ $package_dir/swift ]]; then
                 file_to_parse="${file_to_parse} ${package_dir}/swift"
@@ -1068,7 +1132,8 @@
 # The same metadata used in the main DevStack prerequisite files may be used
 # in these prerequisite files, see get_packages() for more info.
 function get_plugin_packages {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local files_to_parse=""
     local package_dir=""
@@ -1093,7 +1158,8 @@
     fi
 
     if is_ubuntu; then
-        local xtrace=$(set +o | grep xtrace)
+        local xtrace
+        xtrace=$(set +o | grep xtrace)
         set +o xtrace
         if [[ "$REPOS_UPDATED" != "True" || "$RETRY_UPDATE" = "True" ]]; then
             # if there are transient errors pulling the updates, that's fine.
@@ -1281,6 +1347,7 @@
 # If the command includes shell metachatacters (;<>*) it must be run using a shell
 # If an optional group is provided sg will be used to run the
 # command as that group.
+# Uses globals ``USE_SCREEN``
 # run_process service "command-line" [group]
 function run_process {
     local service=$1
@@ -1299,7 +1366,7 @@
 
 # Helper to launch a process in a named screen
 # Uses globals ``CURRENT_LOG_TIME``, ```LOGDIR``, ``SCREEN_LOGDIR``, `SCREEN_NAME``,
-# ``SERVICE_DIR``, ``USE_SCREEN``
+# ``SERVICE_DIR``, ``SCREEN_IS_LOGGING``
 # screen_process name "command-line" [group]
 # Run a command in a shell in a screen window, if an optional group
 # is provided, use sg to set the group of the command.
@@ -1310,7 +1377,6 @@
 
     SCREEN_NAME=${SCREEN_NAME:-stack}
     SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
-    USE_SCREEN=$(trueorfalse True USE_SCREEN)
 
     screen -S $SCREEN_NAME -X screen -t $name
 
@@ -1319,8 +1385,12 @@
     echo "SCREEN_LOGDIR: $SCREEN_LOGDIR"
     echo "log: $real_logfile"
     if [[ -n ${LOGDIR} ]]; then
-        screen -S $SCREEN_NAME -p $name -X logfile "$real_logfile"
-        screen -S $SCREEN_NAME -p $name -X log on
+        if [[ "$SCREEN_IS_LOGGING" == "True" ]]; then
+            screen -S $SCREEN_NAME -p $name -X logfile "$real_logfile"
+            screen -S $SCREEN_NAME -p $name -X log on
+        fi
+        # If logging isn't active then avoid a broken symlink
+        touch "$real_logfile"
         ln -sf "$real_logfile" ${LOGDIR}/${name}.log
         if [[ -n ${SCREEN_LOGDIR} ]]; then
             # Drop the backward-compat symlink
@@ -1359,7 +1429,7 @@
 }
 
 # Screen rc file builder
-# Uses globals ``SCREEN_NAME``, ``SCREENRC``
+# Uses globals ``SCREEN_NAME``, ``SCREENRC``, ``SCREEN_IS_LOGGING``
 # screen_rc service "command-line"
 function screen_rc {
     SCREEN_NAME=${SCREEN_NAME:-stack}
@@ -1379,7 +1449,7 @@
         echo "screen -t $1 bash" >> $SCREENRC
         echo "stuff \"$2$NL\"" >> $SCREENRC
 
-        if [[ -n ${LOGDIR} ]]; then
+        if [[ -n ${LOGDIR} ]] && [[ "$SCREEN_IS_LOGGING" == "True" ]]; then
             echo "logfile ${LOGDIR}/${1}.log.${CURRENT_LOG_TIME}" >>$SCREENRC
             echo "log on" >>$SCREENRC
         fi
@@ -1390,14 +1460,13 @@
 # If a PID is available use it, kill the whole process group via TERM
 # If screen is being used kill the screen window; this will catch processes
 # that did not leave a PID behind
-# Uses globals ``SCREEN_NAME``, ``SERVICE_DIR``, ``USE_SCREEN``
+# Uses globals ``SCREEN_NAME``, ``SERVICE_DIR``
 # screen_stop_service service
 function screen_stop_service {
     local service=$1
 
     SCREEN_NAME=${SCREEN_NAME:-stack}
     SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
-    USE_SCREEN=$(trueorfalse True USE_SCREEN)
 
     if is_service_enabled $service; then
         # Clean up the screen window
@@ -1415,12 +1484,27 @@
     local service=$1
 
     SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
-    USE_SCREEN=$(trueorfalse True USE_SCREEN)
 
     if is_service_enabled $service; then
         # Kill via pid if we have one available
         if [[ -r $SERVICE_DIR/$SCREEN_NAME/$service.pid ]]; then
             pkill -g $(cat $SERVICE_DIR/$SCREEN_NAME/$service.pid)
+            # oslo.service tends to stop actually shutting down
+            # reliably in between releases because someone believes it
+            # is dying too early due to some inflight work they
+            # have. This is a tension. It happens often enough we're
+            # going to just account for it in devstack and assume it
+            # doesn't work.
+            #
+            # Set OSLO_SERVICE_WORKS=True to skip this block
+            if [[ -z "$OSLO_SERVICE_WORKS" ]]; then
+                # TODO(danms): Remove this double-kill when we have
+                # this fixed in all services:
+                # https://bugs.launchpad.net/oslo-incubator/+bug/1446583
+                sleep 1
+                # /bin/true because pkill on a non existent process returns an error
+                pkill -g $(cat $SERVICE_DIR/$SCREEN_NAME/$service.pid) || /bin/true
+            fi
             rm $SERVICE_DIR/$SCREEN_NAME/$service.pid
         fi
         if [[ "$USE_SCREEN" = "True" ]]; then
@@ -1445,7 +1529,7 @@
         return
     fi
 
-    # Check if there is any falure flag file under $SERVICE_DIR/$SCREEN_NAME
+    # Check if there is any failure flag file under $SERVICE_DIR/$SCREEN_NAME
     # make this -o errexit safe
     failures=`ls "$SERVICE_DIR/$SCREEN_NAME"/*.failure 2>/dev/null || /bin/true`
 
@@ -1461,11 +1545,11 @@
 }
 
 # Tail a log file in a screen if USE_SCREEN is true.
+# Uses globals ``USE_SCREEN``
 function tail_log {
     local name=$1
     local logfile=$2
 
-    USE_SCREEN=$(trueorfalse True USE_SCREEN)
     if [[ "$USE_SCREEN" = "True" ]]; then
         screen_process "$name" "sudo tail -f $logfile"
     fi
@@ -1626,7 +1710,7 @@
         if [[ -f $dir/devstack/override-defaults ]]; then
             # be really verbose that an override is happening, as it
             # may not be obvious if things fail later.
-            echo "$plugin has overriden the following defaults"
+            echo "$plugin has overridden the following defaults"
             cat $dir/devstack/override-defaults
             source $dir/devstack/override-defaults
         fi
@@ -1655,13 +1739,26 @@
     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
+        local extra_plugin_file_name
+        for extra_plugin_file_name in $TOP_DIR/extras.d/*.sh; do
+            [[ -r $extra_plugin_file_name ]] && source $extra_plugin_file_name $mode $phase
+            # NOTE(sdague): generate a big warning about using
+            # extras.d in an unsupported way which will let us track
+            # unsupported usage in the gate.
+            local exceptions="50-ironic.sh 60-ceph.sh 80-tempest.sh"
+            local extra
+            extra=$(basename $extra_plugin_file_name)
+            if [[ ! ( $exceptions =~ "$extra" ) ]]; then
+                deprecated "extras.d support is being removed in Mitaka-1"
+                deprecated "jobs for project $extra will break after that point"
+                deprecated "please move project to a supported devstack plugin model"
+            fi
         done
     fi
     # the source phase corresponds to settings loading in plugins
     if [[ "$mode" == "source" ]]; then
         load_plugin_settings
+        verify_disabled_services
     elif [[ "$mode" == "override_defaults" ]]; then
         plugin_override_defaults
     else
@@ -1676,11 +1773,17 @@
 # remove extra commas from the input string (i.e. ``ENABLED_SERVICES``)
 # _cleanup_service_list service-list
 function _cleanup_service_list {
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
+    set +o xtrace
+
     echo "$1" | sed -e '
         s/,,/,/g;
         s/^,//;
         s/,$//
     '
+
+    $xtrace
 }
 
 # disable_all_services() removes all current services
@@ -1698,6 +1801,10 @@
 # Uses global ``ENABLED_SERVICES``
 # disable_negated_services
 function disable_negated_services {
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
+    set +o xtrace
+
     local to_remove=""
     local remaining=""
     local service
@@ -1715,27 +1822,36 @@
     # go through the service list.  if this service appears in the "to
     # be removed" list, drop it
     ENABLED_SERVICES=$(remove_disabled_services "$remaining" "$to_remove")
+
+    $xtrace
 }
 
-# disable_service() removes the services passed as argument to the
-# ``ENABLED_SERVICES`` list, if they are present.
+# disable_service() prepares the services passed as argument to be
+# removed from the ``ENABLED_SERVICES`` list, if they are present.
 #
 # For example:
 #   disable_service rabbit
 #
-# This function does not know about the special cases
-# for nova, glance, and neutron built into is_service_enabled().
-# Uses global ``ENABLED_SERVICES``
+# Uses global ``DISABLED_SERVICES``
 # disable_service service [service ...]
 function disable_service {
-    local tmpsvcs=",${ENABLED_SERVICES},"
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
+    set +o xtrace
+
+    local disabled_svcs="${DISABLED_SERVICES}"
+    local enabled_svcs=",${ENABLED_SERVICES},"
     local service
     for service in $@; do
+        disabled_svcs+=",$service"
         if is_service_enabled $service; then
-            tmpsvcs=${tmpsvcs//,$service,/,}
+            enabled_svcs=${enabled_svcs//,$service,/,}
         fi
     done
-    ENABLED_SERVICES=$(_cleanup_service_list "$tmpsvcs")
+    DISABLED_SERVICES=$(_cleanup_service_list "$disabled_svcs")
+    ENABLED_SERVICES=$(_cleanup_service_list "$enabled_svcs")
+
+    $xtrace
 }
 
 # enable_service() adds the services passed as argument to the
@@ -1749,15 +1865,25 @@
 # Uses global ``ENABLED_SERVICES``
 # enable_service service [service ...]
 function enable_service {
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
+    set +o xtrace
+
     local tmpsvcs="${ENABLED_SERVICES}"
     local service
     for service in $@; do
+        if [[ ,${DISABLED_SERVICES}, =~ ,${service}, ]]; then
+            warn $LINENO "Attempt to enable_service ${service} when it has been disabled"
+            continue
+        fi
         if ! is_service_enabled $service; then
             tmpsvcs+=",$service"
         fi
     done
     ENABLED_SERVICES=$(_cleanup_service_list "$tmpsvcs")
     disable_negated_services
+
+    $xtrace
 }
 
 # is_service_enabled() checks if the service(s) specified as arguments are
@@ -1769,7 +1895,6 @@
 # There are special cases for some 'catch-all' services::
 #   **nova** returns true if any service enabled start with **n-**
 #   **cinder** returns true if any service enabled start with **c-**
-#   **ceilometer** returns true if any service enabled start with **ceilometer**
 #   **glance** returns true if any service enabled start with **g-**
 #   **neutron** returns true if any service enabled start with **q-**
 #   **swift** returns true if any service enabled start with **s-**
@@ -1784,8 +1909,10 @@
 # Uses global ``ENABLED_SERVICES``
 # is_service_enabled service [service ...]
 function is_service_enabled {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+
     local enabled=1
     local services=$@
     local service
@@ -1795,8 +1922,7 @@
         # Look for top-level 'enabled' function for this service
         if type is_${service}_enabled >/dev/null 2>&1; then
             # A function exists for this service, use it
-            is_${service}_enabled
-            enabled=$?
+            is_${service}_enabled && enabled=0
         fi
 
         # TODO(dtroyer): Remove these legacy special-cases after the is_XXX_enabled()
@@ -1805,7 +1931,6 @@
         [[ ${service} == n-cell-* && ${ENABLED_SERVICES} =~ "n-cell" ]] && enabled=0
         [[ ${service} == n-cpu-* && ${ENABLED_SERVICES} =~ "n-cpu" ]] && enabled=0
         [[ ${service} == "nova" && ${ENABLED_SERVICES} =~ "n-" ]] && enabled=0
-        [[ ${service} == "ceilometer" && ${ENABLED_SERVICES} =~ "ceilometer-" ]] && enabled=0
         [[ ${service} == "glance" && ${ENABLED_SERVICES} =~ "g-" ]] && enabled=0
         [[ ${service} == "ironic" && ${ENABLED_SERVICES} =~ "ir-" ]] && enabled=0
         [[ ${service} == "neutron" && ${ENABLED_SERVICES} =~ "q-" ]] && enabled=0
@@ -1813,6 +1938,7 @@
         [[ ${service} == "swift" && ${ENABLED_SERVICES} =~ "s-" ]] && enabled=0
         [[ ${service} == s-* && ${ENABLED_SERVICES} =~ "swift" ]] && enabled=0
     done
+
     $xtrace
     return $enabled
 }
@@ -1820,6 +1946,10 @@
 # remove specified list from the input string
 # remove_disabled_services service-list remove-list
 function remove_disabled_services {
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
+    set +o xtrace
+
     local service_list=$1
     local remove_list=$2
     local service
@@ -1838,6 +1968,9 @@
             enabled="${enabled},$service"
         fi
     done
+
+    $xtrace
+
     _cleanup_service_list "$enabled"
 }
 
@@ -1858,6 +1991,18 @@
     return 0
 }
 
+# Make sure that nothing has manipulated ENABLED_SERVICES in a way
+# that conflicts with prior calls to disable_service.
+# Uses global ``ENABLED_SERVICES``
+function verify_disabled_services {
+    local service
+    for service in ${ENABLED_SERVICES//,/ }; do
+        if [[ ,${DISABLED_SERVICES}, =~ ,${service}, ]]; then
+            die $LINENO "ENABLED_SERVICES directly modified to overcome 'disable_service ${service}'"
+        fi
+    done
+}
+
 
 # System Functions
 # ================
@@ -1865,7 +2010,8 @@
 # Only run the command if the target file (the last arg) is not on an
 # NFS filesystem.
 function _safe_permission_operation {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local args=( $@ )
     local last
@@ -1901,8 +2047,10 @@
     local ip=$1
     local range=$2
     local masklen=${range#*/}
-    local network=$(maskip ${range%/*} $(cidr2netmask $masklen))
-    local subnet=$(maskip $ip $(cidr2netmask $masklen))
+    local network
+    network=$(maskip ${range%/*} $(cidr2netmask $masklen))
+    local subnet
+    subnet=$(maskip $ip $(cidr2netmask $masklen))
     [[ $network == $subnet ]]
 }
 
@@ -1954,7 +2102,8 @@
 
 # Returns true if the directory is on a filesystem mounted via NFS.
 function is_nfs_directory {
-    local mount_type=`stat -f -L -c %T $1`
+    local mount_type
+    mount_type=`stat -f -L -c %T $1`
     test "$mount_type" == "nfs"
 }
 
@@ -1965,13 +2114,15 @@
     local ip=$1
     local mask=$2
     local l="${ip%.*}"; local r="${ip#*.}"; local n="${mask%.*}"; local m="${mask#*.}"
-    local subnet=$((${ip%%.*}&${mask%%.*})).$((${r%%.*}&${m%%.*})).$((${l##*.}&${n##*.})).$((${ip##*.}&${mask##*.}))
+    local subnet
+    subnet=$((${ip%%.*}&${mask%%.*})).$((${r%%.*}&${m%%.*})).$((${l##*.}&${n##*.})).$((${ip##*.}&${mask##*.}))
     echo $subnet
 }
 
 # Return the current python as "python<major>.<minor>"
 function python_version {
-    local python_version=$(python -c 'import sys; print("%s.%s" % sys.version_info[0:2])')
+    local python_version
+    python_version=$(python -c 'import sys; print("%s.%s" % sys.version_info[0:2])')
     echo "python${python_version}"
 }
 
@@ -2030,6 +2181,77 @@
     fi
 }
 
+# Timing infrastructure - figure out where large blocks of time are
+# used in DevStack
+#
+# The timing infrastructure for DevStack is about collecting buckets
+# of time that are spend in some subtask. For instance, that might be
+# 'apt', 'pip', 'osc', even database migrations. We do this by a pair
+# of functions: time_start / time_stop.
+#
+# These take a single parameter: $name - which specifies the name of
+# the bucket to be accounted against. time_totals function spits out
+# the results.
+#
+# Resolution is only in whole seconds, so should be used for long
+# running activities.
+
+declare -A TOTAL_TIME
+declare -A START_TIME
+
+# time_start $name
+#
+# starts the clock for a timer by name. Errors if that clock is
+# already started.
+function time_start {
+    local name=$1
+    local start_time=${START_TIME[$name]}
+    if [[ -n "$start_time" ]]; then
+        die $LINENO "Trying to start the clock on $name, but it's already been started"
+    fi
+    START_TIME[$name]=$(date +%s)
+}
+
+# time_stop $name
+#
+# stops the clock for a timer by name, and accumulate that time in the
+# global counter for that name. Errors if that clock had not
+# previously been started.
+function time_stop {
+    local name
+    local end_time
+    local elpased_time
+    local total
+    local start_time
+
+    name=$1
+    start_time=${START_TIME[$name]}
+
+    if [[ -z "$start_time" ]]; then
+        die $LINENO "Trying to stop the clock on $name, but it was never started"
+    fi
+    end_time=$(date +%s)
+    elapsed_time=$(($end_time - $start_time))
+    total=${TOTAL_TIME[$name]:-0}
+    # reset the clock so we can start it in the future
+    START_TIME[$name]=""
+    TOTAL_TIME[$name]=$(($total + $elapsed_time))
+}
+
+# time_totals
+#
+# prints out total time
+function time_totals {
+    echo
+    echo "========================"
+    echo "DevStack Components Timed"
+    echo "========================"
+    echo
+    for t in ${!TOTAL_TIME[*]}; do
+        local v=${TOTAL_TIME[$t]}
+        echo "$t - $v secs"
+    done
+}
 
 # Restore xtrace
 $XTRACE
diff --git a/inc/ini-config b/inc/ini-config
index 26401f3..42a66c6 100644
--- a/inc/ini-config
+++ b/inc/ini-config
@@ -15,37 +15,50 @@
 # ================
 
 # Append a new option in an ini file without replacing the old value
-# iniadd config-file section option value1 value2 value3 ...
+# iniadd [-sudo] config-file section option value1 value2 value3 ...
 function iniadd {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+    local sudo=""
+    if [ $1 == "-sudo" ]; then
+        sudo="-sudo "
+        shift
+    fi
     local file=$1
     local section=$2
     local option=$3
     shift 3
 
     local values="$(iniget_multiline $file $section $option) $@"
-    iniset_multiline $file $section $option $values
+    iniset_multiline $sudo $file $section $option $values
     $xtrace
 }
 
 # Comment an option in an INI file
-# inicomment config-file section option
+# inicomment [-sudo] config-file section option
 function inicomment {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+    local sudo=""
+    if [ $1 == "-sudo" ]; then
+        sudo="sudo "
+        shift
+    fi
     local file=$1
     local section=$2
     local option=$3
 
-    sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=.*$\)|#\1|" "$file"
+    $sudo sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=.*$\)|#\1|" "$file"
     $xtrace
 }
 
 # Get an option from an INI file
 # iniget config-file section option
 function iniget {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local file=$1
     local section=$2
@@ -60,7 +73,8 @@
 # Get a multiple line option from an INI file
 # iniget_multiline config-file section option
 function iniget_multiline {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local file=$1
     local section=$2
@@ -75,7 +89,8 @@
 # Determinate is the given option present in the INI file
 # ini_has_option config-file section option
 function ini_has_option {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local file=$1
     local section=$2
@@ -95,19 +110,28 @@
 # in the argument list. Doing that will cause incorrect configuration
 # if spaces are used in the config values.
 #
-# iniadd_literal config-file section option value
+# iniadd_literal [-sudo] config-file section option value
 function iniadd_literal {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+    local sudo=""
+    if [ $1 == "-sudo" ]; then
+        sudo="sudo "
+        shift
+    fi
     local file=$1
     local section=$2
     local option=$3
     local value=$4
 
-    [[ -z $section || -z $option ]] && return
+    if [[ -z $section || -z $option ]]; then
+        $xtrace
+        return
+    fi
 
     # Add it
-    sed -i -e "/^\[$section\]/ a\\
+    $sudo sed -i -e "/^\[$section\]/ a\\
 $option = $value
 " "$file"
 
@@ -115,56 +139,82 @@
 }
 
 # Remove an option from an INI file
-# inidelete config-file section option
+# inidelete [-sudo] config-file section option
 function inidelete {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+    local sudo=""
+    if [ $1 == "-sudo" ]; then
+        sudo="sudo "
+        shift
+    fi
     local file=$1
     local section=$2
     local option=$3
 
-    [[ -z $section || -z $option ]] && return
+    if [[ -z $section || -z $option ]]; then
+        $xtrace
+        return
+    fi
 
     # Remove old values
-    sed -i -e "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ d; }" "$file"
+    $sudo sed -i -e "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ d; }" "$file"
 
     $xtrace
 }
 
 # Set an option in an INI file
-# iniset config-file section option value
+# iniset [-sudo] config-file section option value
+#  - if the file does not exist, it is created
 function iniset {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+    local sudo=""
+    if [ $1 == "-sudo" ]; then
+        sudo="sudo "
+        shift
+    fi
     local file=$1
     local section=$2
     local option=$3
     local value=$4
 
-    [[ -z $section || -z $option ]] && return
+    if [[ -z $section || -z $option ]]; then
+        $xtrace
+        return
+    fi
 
     if ! grep -q "^\[$section\]" "$file" 2>/dev/null; then
         # Add section at the end
-        echo -e "\n[$section]" >>"$file"
+        echo -e "\n[$section]" | $sudo tee --append "$file" > /dev/null
     fi
     if ! ini_has_option "$file" "$section" "$option"; then
         # Add it
-        sed -i -e "/^\[$section\]/ a\\
+        $sudo sed -i -e "/^\[$section\]/ a\\
 $option = $value
 " "$file"
     else
-        local sep=$(echo -ne "\x01")
+        local sep
+        sep=$(echo -ne "\x01")
         # Replace it
-        sed -i -e '/^\['${section}'\]/,/^\[.*\]/ s'${sep}'^\('${option}'[ \t]*=[ \t]*\).*$'${sep}'\1'"${value}"${sep} "$file"
+        $sudo sed -i -e '/^\['${section}'\]/,/^\[.*\]/ s'${sep}'^\('${option}'[ \t]*=[ \t]*\).*$'${sep}'\1'"${value}"${sep} "$file"
     fi
     $xtrace
 }
 
 # Set a multiple line option in an INI file
-# iniset_multiline config-file section option value1 value2 valu3 ...
+# iniset_multiline [-sudo] config-file section option value1 value2 valu3 ...
 function iniset_multiline {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+    local sudo=""
+    if [ $1 == "-sudo" ]; then
+        sudo="sudo "
+        shift
+    fi
     local file=$1
     local section=$2
     local option=$3
@@ -179,14 +229,14 @@
     done
     if ! grep -q "^\[$section\]" "$file"; then
         # Add section at the end
-        echo -e "\n[$section]" >>"$file"
+        echo -e "\n[$section]" | $sudo tee --append "$file" > /dev/null
     else
         # Remove old values
-        sed -i -e "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ d; }" "$file"
+        $sudo sed -i -e "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ d; }" "$file"
     fi
     # Add new ones
     for v in $values; do
-        sed -i -e "/^\[$section\]/ a\\
+        $sudo sed -i -e "/^\[$section\]/ a\\
 $option = $v
 " "$file"
     done
@@ -196,12 +246,18 @@
 # Uncomment an option in an INI file
 # iniuncomment config-file section option
 function iniuncomment {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
+    local sudo=""
+    if [ $1 == "-sudo" ]; then
+        sudo="sudo "
+        shift
+    fi
     local file=$1
     local section=$2
     local option=$3
-    sed -i -e "/^\[$section\]/,/^\[.*\]/ s|[^ \t]*#[ \t]*\($option[ \t]*=.*$\)|\1|" "$file"
+    $sudo sed -i -e "/^\[$section\]/,/^\[.*\]/ s|[^ \t]*#[ \t]*\($option[ \t]*=.*$\)|\1|" "$file"
     $xtrace
 }
 
diff --git a/inc/meta-config b/inc/meta-config
index e5f902d..b9ab6b2 100644
--- a/inc/meta-config
+++ b/inc/meta-config
@@ -89,9 +89,10 @@
     # note, configfile might be a variable (note the iniset, etc
     # created in the mega-awk below is "eval"ed too, so we just leave
     # it alone.
-    local real_configfile=$(eval echo $configfile)
+    local real_configfile
+    real_configfile=$(eval echo $configfile)
     if [ ! -f $real_configfile ]; then
-        touch $real_configfile
+        touch $real_configfile || die $LINENO "could not create config file $real_configfile ($configfile)"
     fi
 
     get_meta_section $file $matchgroup $configfile | \
@@ -177,8 +178,18 @@
     local configfile group
     for group in $matchgroups; do
         for configfile in $(get_meta_section_files $localfile $group); do
-            if [[ -d $(dirname $(eval "echo $configfile")) ]]; then
+            local realconfigfile
+            local dir
+
+            realconfigfile=$(eval "echo $configfile")
+            if [[ -z $realconfigfile ]]; then
+                die $LINENO "bogus config file specification: $configfile is undefined"
+            fi
+            dir=$(dirname $realconfigfile)
+            if [[ -d $dir ]]; then
                 merge_config_file $localfile $group $configfile
+            else
+                die $LINENO "bogus config file specification $configfile ($configfile=$realconfigfile, $dir is not a directory)"
             fi
         done
     done
diff --git a/inc/python b/inc/python
index 54e19a7..91ceb44 100644
--- a/inc/python
+++ b/inc/python
@@ -38,7 +38,8 @@
 # Get the path to the direcotry where python executables are installed.
 # get_python_exec_prefix
 function get_python_exec_prefix {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     if [[ -z "$os_PACKAGE" ]]; then
         GetOSVersion
@@ -60,17 +61,18 @@
 # pip_install_gr packagename
 function pip_install_gr {
     local name=$1
-    local clean_name=$(get_from_global_requirements $name)
+    local clean_name
+    clean_name=$(get_from_global_requirements $name)
     pip_install $clean_name
 }
 
 # Wrapper for ``pip install`` to set cache and proxy environment variables
 # Uses globals ``OFFLINE``, ``PIP_VIRTUAL_ENV``,
 # ``PIP_UPGRADE``, ``TRACK_DEPENDS``, ``*_proxy``,
-# ``USE_CONSTRAINTS``
 # pip_install package [package ...]
 function pip_install {
-    local xtrace=$(set +o | grep xtrace)
+    local xtrace
+    xtrace=$(set +o | grep xtrace)
     set +o xtrace
     local upgrade=""
     local offline=${OFFLINE:-False}
@@ -79,6 +81,8 @@
         return
     fi
 
+    time_start "pip_install"
+
     PIP_UPGRADE=$(trueorfalse False PIP_UPGRADE)
     if [[ "$PIP_UPGRADE" = "True" ]] ; then
         upgrade="--upgrade"
@@ -99,19 +103,18 @@
             local cmd_pip=$PIP_VIRTUAL_ENV/bin/pip
             local sudo_pip="env"
         else
-            local cmd_pip=$(get_pip_command)
+            local cmd_pip
+            cmd_pip=$(get_pip_command)
             local sudo_pip="sudo -H"
         fi
     fi
 
     cmd_pip="$cmd_pip install"
+    # Always apply constraints
+    cmd_pip="$cmd_pip -c $REQUIREMENTS_DIR/upper-constraints.txt"
 
-    # Handle a constraints file, if needed.
-    if [[ "$USE_CONSTRAINTS" == "True" ]]; then
-        cmd_pip="$cmd_pip -c $REQUIREMENTS_DIR/upper-constraints.txt"
-    fi
-
-    local pip_version=$(python -c "import pip; \
+    local pip_version
+    pip_version=$(python -c "import pip; \
                         print(pip.__version__.strip('.')[0])")
     if (( pip_version<6 )); then
         die $LINENO "Currently installed pip version ${pip_version} does not" \
@@ -128,7 +131,7 @@
         $@
 
     # Also install test requirements
-    local test_req="$@/test-requirements.txt"
+    local test_req="${!#}/test-requirements.txt"
     if [[ -e "$test_req" ]]; then
         echo "Installing test-requirements for $test_req"
         $sudo_pip \
@@ -139,13 +142,16 @@
             $cmd_pip $upgrade \
             -r $test_req
     fi
+
+    time_stop "pip_install"
 }
 
 # 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 -i -h ^${package} $REQUIREMENTS_DIR/global-requirements.txt | cut -d\# -f1)
+    local required_pkg
+    required_pkg=$(grep -i -h ^${package} $REQUIREMENTS_DIR/global-requirements.txt | cut -d\# -f1)
     if [[ $required_pkg == ""  ]]; then
         die $LINENO "Can't find package $package in requirements"
     fi
@@ -161,6 +167,28 @@
     return $enabled
 }
 
+# determine if a package was installed from git
+function lib_installed_from_git {
+    local name=$1
+    pip freeze 2>/dev/null | grep -- "$name" | grep -q -- '-e git'
+}
+
+# check that everything that's in LIBS_FROM_GIT was actually installed
+# correctly, this helps double check issues with library fat fingering.
+function check_libs_from_git {
+    local lib=""
+    local not_installed=""
+    for lib in $(echo ${LIBS_FROM_GIT} | tr "," " "); do
+        if ! lib_installed_from_git "$lib"; then
+            not_installed+=" $lib"
+        fi
+    done
+    # if anything is not installed, say what it is.
+    if [[ -n "$not_installed" ]]; then
+        die $LINENO "The following LIBS_FROM_GIT were not installed correct: $not_installed"
+    fi
+}
+
 # setup a library by name. If we are trying to use the library from
 # git, we'll do a git based install, otherwise we'll punt and the
 # library should be installed by a requirements pull from another
@@ -187,13 +215,13 @@
 # use this, especially *oslo* ones
 function setup_install {
     local project_dir=$1
-    setup_package_with_req_sync $project_dir
+    setup_package_with_constraints_edit $project_dir
 }
 
 # this should be used for projects which run services, like all services
 function setup_develop {
     local project_dir=$1
-    setup_package_with_req_sync $project_dir -e
+    setup_package_with_constraints_edit $project_dir -e
 }
 
 # determine if a project as specified by directory is in
@@ -202,42 +230,28 @@
 # practical ways.
 function is_in_projects_txt {
     local project_dir=$1
-    local project_name=$(basename $project_dir)
+    local project_name
+    project_name=$(basename $project_dir)
     grep -q "/$project_name\$" $REQUIREMENTS_DIR/projects.txt
 }
 
 # ``pip install -e`` the package, which processes the dependencies
 # using pip before running `setup.py develop`
 #
-# Updates the dependencies in project_dir from the
-# openstack/requirements global list before installing anything.
+# Updates the constraints from REQUIREMENTS_DIR to reflect the
+# future installed state of this package. This ensures when we
+# install this package we get the from source version.
 #
-# Uses globals ``TRACK_DEPENDS``, ``REQUIREMENTS_DIR``, ``UNDO_REQUIREMENTS``
+# Uses globals ``REQUIREMENTS_DIR``
 # setup_develop directory
-function setup_package_with_req_sync {
+function setup_package_with_constraints_edit {
     local project_dir=$1
     local flags=$2
 
-    # Don't update repo if local changes exist
-    # Don't use buggy "git diff --quiet"
-    # ``errexit`` requires us to trap the exit code when the repo is changed
-    local update_requirements=$(cd $project_dir && git diff --exit-code >/dev/null || echo "changed")
-
-    if [[ $update_requirements != "changed" && "$USE_CONSTRAINTS" == "False" ]]; then
-        if is_in_projects_txt $project_dir; then
-            (cd $REQUIREMENTS_DIR; \
-                ./.venv/bin/python update.py $project_dir)
-        else
-            # soft update projects not found in requirements project.txt
-            echo "$project_dir not a constrained repository, soft enforcing requirements"
-            (cd $REQUIREMENTS_DIR; \
-                ./.venv/bin/python update.py -s $project_dir)
-        fi
-    fi
-
     if [ -n "$REQUIREMENTS_DIR" ]; then
         # Constrain this package to this project directory from here on out.
-        local name=$(awk '/^name.*=/ {print $3}' $project_dir/setup.cfg)
+        local name
+        name=$(awk '/^name.*=/ {print $3}' $project_dir/setup.cfg)
         $REQUIREMENTS_DIR/.venv/bin/edit-constraints \
             $REQUIREMENTS_DIR/upper-constraints.txt -- $name \
             "$flags file://$project_dir#egg=$name"
@@ -245,19 +259,6 @@
 
     setup_package $project_dir $flags
 
-    # We've just gone and possibly modified the user's source tree in an
-    # automated way, which is considered bad form if it's a development
-    # tree because we've screwed up their next git checkin. So undo it.
-    #
-    # However... there are some circumstances, like running in the gate
-    # where we really really want the overridden version to stick. So provide
-    # a variable that tells us whether or not we should UNDO the requirements
-    # changes (this will be set to False in the OpenStack ci gate)
-    if [ $UNDO_REQUIREMENTS = "True" ]; then
-        if [[ $update_requirements != "changed" ]]; then
-            (cd $project_dir && git reset --hard)
-        fi
-    fi
 }
 
 # ``pip install -e`` the package, which processes the dependencies
diff --git a/inc/rootwrap b/inc/rootwrap
index f91e557..63ab59a 100644
--- a/inc/rootwrap
+++ b/inc/rootwrap
@@ -41,7 +41,8 @@
 # configure_rootwrap project
 function configure_rootwrap {
     local project=$1
-    local project_uc=$(echo $1|tr a-z A-Z)
+    local project_uc
+    project_uc=$(echo $1|tr a-z A-Z)
     local bin_dir="${project_uc}_BIN_DIR"
     bin_dir="${!bin_dir}"
     local project_dir="${project_uc}_DIR"
@@ -60,7 +61,8 @@
     sudo sed -e "s:^filters_path=.*$:filters_path=/etc/${project}/rootwrap.d:" -i /etc/${project}/rootwrap.conf
 
     # Set up the rootwrap sudoers
-    local tempfile=$(mktemp)
+    local tempfile
+    tempfile=$(mktemp)
     # Specify rootwrap.conf as first parameter to rootwrap
     rootwrap_sudo_cmd="${rootwrap_bin} /etc/${project}/rootwrap.conf *"
     echo "$STACK_USER ALL=(root) NOPASSWD: $rootwrap_sudo_cmd" >$tempfile
diff --git a/lib/apache b/lib/apache
index c7d69f2..17526c7 100644
--- a/lib/apache
+++ b/lib/apache
@@ -11,7 +11,6 @@
 # lib/apache exports the following functions:
 #
 # - install_apache_wsgi
-# - config_apache_wsgi
 # - apache_site_config_for
 # - enable_apache_site
 # - disable_apache_site
@@ -73,11 +72,14 @@
 # various differences between Apache 2.2 and 2.4 that warrant special handling.
 function get_apache_version {
     if is_ubuntu; then
-        local version_str=$(sudo /usr/sbin/apache2ctl -v | awk '/Server version/ {print $3}' | cut -f2 -d/)
+        local version_str
+        version_str=$(sudo /usr/sbin/apache2ctl -v | awk '/Server version/ {print $3}' | cut -f2 -d/)
     elif is_fedora; then
-        local version_str=$(rpm -qa --queryformat '%{VERSION}' httpd)
+        local version_str
+        version_str=$(rpm -qa --queryformat '%{VERSION}' httpd)
     elif is_suse; then
-        local version_str=$(rpm -qa --queryformat '%{VERSION}' apache2)
+        local version_str
+        version_str=$(rpm -qa --queryformat '%{VERSION}' apache2)
     else
         exit_distro_not_supported "cannot determine apache version"
     fi
@@ -116,7 +118,8 @@
 function apache_site_config_for {
     local site=$@
     if is_ubuntu; then
-        local apache_version=$(get_apache_version)
+        local apache_version
+        apache_version=$(get_apache_version)
         if [[ "$apache_version" == "2.2" ]]; then
             # Ubuntu 12.04 - Apache 2.2
             echo $APACHE_CONF_DIR/${site}
diff --git a/lib/ceilometer b/lib/ceilometer
deleted file mode 100644
index 9226d85..0000000
--- a/lib/ceilometer
+++ /dev/null
@@ -1,420 +0,0 @@
-#!/bin/bash
-#
-# lib/ceilometer
-# Install and start **Ceilometer** service
-
-# To enable a minimal set of Ceilometer services, add the following to the
-# ``localrc`` section of ``local.conf``:
-#
-#   enable_service ceilometer-acompute ceilometer-acentral ceilometer-anotification ceilometer-collector ceilometer-api
-#
-# To ensure Ceilometer alarming services are enabled also, further add to the
-# localrc section of local.conf:
-#
-#   enable_service ceilometer-alarm-notifier ceilometer-alarm-evaluator
-#
-# To enable Ceilometer to collect the IPMI based meters, further add to the
-# localrc section of local.conf:
-#
-#   enable_service ceilometer-aipmi
-#
-# NOTE: Currently, there are two ways to get the IPMI based meters in
-# OpenStack. One way is to configure Ironic conductor to report those meters
-# for the nodes managed by Ironic and to have Ceilometer notification
-# agent to collect them. Ironic by default does NOT enable that reporting
-# functionality. So in order to do so, users need to set the option of
-# conductor.send_sensor_data to true in the ironic.conf configuration file
-# for the Ironic conductor service, and also enable the
-# ceilometer-anotification service.
-#
-# The other way is to use Ceilometer ipmi agent only to get the IPMI based
-# meters. To avoid duplicated meters, users need to make sure to set the
-# option of conductor.send_sensor_data to false in the ironic.conf
-# configuration file if the node on which Ceilometer ipmi agent is running
-# is also managed by Ironic.
-#
-# Several variables set in the localrc section adjust common behaviors
-# of Ceilometer (see within for additional settings):
-#
-#   CEILOMETER_USE_MOD_WSGI:       When True, run the api under mod_wsgi.
-#   CEILOMETER_PIPELINE_INTERVAL:  Seconds between pipeline processing runs. Default 600.
-#   CEILOMETER_BACKEND:            Database backend (e.g. 'mysql', 'mongodb', 'es')
-#   CEILOMETER_COORDINATION_URL:   URL for group membership service provided by tooz.
-#   CEILOMETER_EVENTS:             Enable event collection
-
-# Dependencies:
-#
-# - functions
-# - OS_AUTH_URL for auth in api
-# - DEST set to the destination directory
-# - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api
-# - STACK_USER service user
-
-# stack.sh
-# ---------
-# - install_ceilometer
-# - configure_ceilometer
-# - init_ceilometer
-# - start_ceilometer
-# - stop_ceilometer
-# - cleanup_ceilometer
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Defaults
-# --------
-
-# Set up default directories
-GITDIR["python-ceilometerclient"]=$DEST/python-ceilometerclient
-GITDIR["ceilometermiddleware"]=$DEST/ceilometermiddleware
-
-CEILOMETER_DIR=$DEST/ceilometer
-CEILOMETER_CONF_DIR=/etc/ceilometer
-CEILOMETER_CONF=$CEILOMETER_CONF_DIR/ceilometer.conf
-CEILOMETER_API_LOG_DIR=/var/log/ceilometer-api
-CEILOMETER_AUTH_CACHE_DIR=${CEILOMETER_AUTH_CACHE_DIR:-/var/cache/ceilometer}
-CEILOMETER_WSGI_DIR=${CEILOMETER_WSGI_DIR:-/var/www/ceilometer}
-
-# Support potential entry-points console scripts in VENV or not
-if [[ ${USE_VENV} = True ]]; then
-    PROJECT_VENV["ceilometer"]=${CEILOMETER_DIR}.venv
-    CEILOMETER_BIN_DIR=${PROJECT_VENV["ceilometer"]}/bin
-else
-    CEILOMETER_BIN_DIR=$(get_python_exec_prefix)
-fi
-
-# Set up database backend
-CEILOMETER_BACKEND=${CEILOMETER_BACKEND:-mysql}
-
-# Ceilometer connection info.
-CEILOMETER_SERVICE_PROTOCOL=http
-CEILOMETER_SERVICE_HOST=$SERVICE_HOST
-CEILOMETER_SERVICE_PORT=${CEILOMETER_SERVICE_PORT:-8777}
-CEILOMETER_USE_MOD_WSGI=$(trueorfalse False CEILOMETER_USE_MOD_WSGI)
-
-# To enable OSprofiler change value of this variable to "notifications,profiler"
-CEILOMETER_NOTIFICATION_TOPICS=${CEILOMETER_NOTIFICATION_TOPICS:-notifications}
-CEILOMETER_EVENTS=${CEILOMETER_EVENTS:-True}
-
-CEILOMETER_COORDINATION_URL=${CEILOMETER_COORDINATION_URL:-}
-CEILOMETER_PIPELINE_INTERVAL=${CEILOMETER_PIPELINE_INTERVAL:-}
-
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,ceilometer
-
-
-# Functions
-# ---------
-
-# Test if any Ceilometer services are enabled
-# is_ceilometer_enabled
-function is_ceilometer_enabled {
-    [[ ,${ENABLED_SERVICES} =~ ,"ceilometer-" ]] && return 0
-    return 1
-}
-
-# create_ceilometer_accounts() - Set up common required Ceilometer accounts
-#
-# Project              User         Roles
-# ------------------------------------------------------------------
-# SERVICE_TENANT_NAME  ceilometer   admin
-# SERVICE_TENANT_NAME  ceilometer   ResellerAdmin (if Swift is enabled)
-function create_ceilometer_accounts {
-
-    # Ceilometer
-    if [[ "$ENABLED_SERVICES" =~ "ceilometer-api" ]]; then
-
-        create_service_user "ceilometer" "admin"
-
-        if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
-            get_or_create_service "ceilometer" "metering" "OpenStack Telemetry Service"
-            get_or_create_endpoint "metering" \
-                "$REGION_NAME" \
-                "$CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/" \
-                "$CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/" \
-                "$CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/"
-        fi
-        if is_service_enabled swift; then
-            # Ceilometer needs ResellerAdmin role to access Swift account stats.
-            get_or_add_user_project_role "ResellerAdmin" "ceilometer" $SERVICE_TENANT_NAME
-        fi
-    fi
-}
-
-
-# _cleanup_keystone_apache_wsgi() - Remove WSGI files, disable and remove Apache vhost file
-function _cleanup_ceilometer_apache_wsgi {
-    sudo rm -f $CEILOMETER_WSGI_DIR/*
-    sudo rm -f $(apache_site_config_for ceilometer)
-}
-
-# cleanup_ceilometer() - Remove residual data files, anything left over from previous
-# runs that a clean run would need to clean up
-function cleanup_ceilometer {
-    if [ "$CEILOMETER_BACKEND" = 'mongodb' ] ; then
-        mongo ceilometer --eval "db.dropDatabase();"
-    elif [ "$CEILOMETER_BACKEND" = 'es' ] ; then
-        curl -XDELETE "localhost:9200/events_*"
-    fi
-    if [ "$CEILOMETER_USE_MOD_WSGI" == "True" ]; then
-        _cleanup_ceilometer_apache_wsgi
-    fi
-}
-
-function _config_ceilometer_apache_wsgi {
-    sudo mkdir -p $CEILOMETER_WSGI_DIR
-
-    local ceilometer_apache_conf=$(apache_site_config_for ceilometer)
-    local apache_version=$(get_apache_version)
-    local venv_path=""
-
-    # Copy proxy vhost and wsgi file
-    sudo cp $CEILOMETER_DIR/ceilometer/api/app.wsgi $CEILOMETER_WSGI_DIR/app
-
-    if [[ ${USE_VENV} = True ]]; then
-        venv_path="python-path=${PROJECT_VENV["ceilometer"]}/lib/$(python_version)/site-packages"
-    fi
-
-    sudo cp $FILES/apache-ceilometer.template $ceilometer_apache_conf
-    sudo sed -e "
-        s|%PORT%|$CEILOMETER_SERVICE_PORT|g;
-        s|%APACHE_NAME%|$APACHE_NAME|g;
-        s|%WSGIAPP%|$CEILOMETER_WSGI_DIR/app|g;
-        s|%USER%|$STACK_USER|g;
-        s|%VIRTUALENV%|$venv_path|g
-    " -i $ceilometer_apache_conf
-}
-
-# configure_ceilometer() - Set config files, create data dirs, etc
-function configure_ceilometer {
-    sudo install -d -o $STACK_USER -m 755 $CEILOMETER_CONF_DIR $CEILOMETER_API_LOG_DIR
-
-    iniset_rpc_backend ceilometer $CEILOMETER_CONF
-
-    iniset $CEILOMETER_CONF DEFAULT notification_topics "$CEILOMETER_NOTIFICATION_TOPICS"
-    iniset $CEILOMETER_CONF DEFAULT verbose True
-    iniset $CEILOMETER_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
-
-    if [[ -n "$CEILOMETER_COORDINATION_URL" ]]; then
-        iniset $CEILOMETER_CONF coordination backend_url $CEILOMETER_COORDINATION_URL
-        iniset $CEILOMETER_CONF compute workload_partitioning True
-    fi
-
-    # Install the policy file for the API server
-    cp $CEILOMETER_DIR/etc/ceilometer/policy.json $CEILOMETER_CONF_DIR
-    iniset $CEILOMETER_CONF oslo_policy policy_file $CEILOMETER_CONF_DIR/policy.json
-
-    cp $CEILOMETER_DIR/etc/ceilometer/pipeline.yaml $CEILOMETER_CONF_DIR
-    cp $CEILOMETER_DIR/etc/ceilometer/event_pipeline.yaml $CEILOMETER_CONF_DIR
-    cp $CEILOMETER_DIR/etc/ceilometer/api_paste.ini $CEILOMETER_CONF_DIR
-    cp $CEILOMETER_DIR/etc/ceilometer/event_definitions.yaml $CEILOMETER_CONF_DIR
-    cp $CEILOMETER_DIR/etc/ceilometer/meters.yaml $CEILOMETER_CONF_DIR
-
-    if [ "$CEILOMETER_PIPELINE_INTERVAL" ]; then
-        sed -i "s/interval:.*/interval: ${CEILOMETER_PIPELINE_INTERVAL}/" $CEILOMETER_CONF_DIR/pipeline.yaml
-    fi
-
-    # The compute and central agents need these credentials in order to
-    # call out to other services' public APIs.
-    # The alarm evaluator needs these options to call ceilometer APIs
-    iniset $CEILOMETER_CONF service_credentials os_username ceilometer
-    iniset $CEILOMETER_CONF service_credentials os_password $SERVICE_PASSWORD
-    iniset $CEILOMETER_CONF service_credentials os_tenant_name $SERVICE_TENANT_NAME
-    iniset $CEILOMETER_CONF service_credentials os_region_name $REGION_NAME
-    iniset $CEILOMETER_CONF service_credentials os_auth_url $KEYSTONE_SERVICE_URI/v2.0
-
-    configure_auth_token_middleware $CEILOMETER_CONF ceilometer $CEILOMETER_AUTH_CACHE_DIR
-
-    iniset $CEILOMETER_CONF notification store_events $CEILOMETER_EVENTS
-
-    if [ "$CEILOMETER_BACKEND" = 'mysql' ] || [ "$CEILOMETER_BACKEND" = 'postgresql' ] ; then
-        iniset $CEILOMETER_CONF database alarm_connection $(database_connection_url ceilometer)
-        iniset $CEILOMETER_CONF database event_connection $(database_connection_url ceilometer)
-        iniset $CEILOMETER_CONF database metering_connection $(database_connection_url ceilometer)
-        iniset $CEILOMETER_CONF DEFAULT collector_workers $API_WORKERS
-    elif [ "$CEILOMETER_BACKEND" = 'es' ] ; then
-        # es is only supported for events. we will use sql for alarming/metering.
-        iniset $CEILOMETER_CONF database alarm_connection $(database_connection_url ceilometer)
-        iniset $CEILOMETER_CONF database event_connection es://localhost:9200
-        iniset $CEILOMETER_CONF database metering_connection $(database_connection_url ceilometer)
-        iniset $CEILOMETER_CONF DEFAULT collector_workers $API_WORKERS
-        ${TOP_DIR}/pkg/elasticsearch.sh start
-        cleanup_ceilometer
-    elif [ "$CEILOMETER_BACKEND" = 'mongodb' ] ; then
-        iniset $CEILOMETER_CONF database alarm_connection mongodb://localhost:27017/ceilometer
-        iniset $CEILOMETER_CONF database event_connection mongodb://localhost:27017/ceilometer
-        iniset $CEILOMETER_CONF database metering_connection mongodb://localhost:27017/ceilometer
-        configure_mongodb
-        cleanup_ceilometer
-    else
-        die $LINENO "Unable to configure unknown CEILOMETER_BACKEND $CEILOMETER_BACKEND"
-    fi
-
-    if [[ "$VIRT_DRIVER" = 'vsphere' ]]; then
-        iniset $CEILOMETER_CONF DEFAULT hypervisor_inspector vsphere
-        iniset $CEILOMETER_CONF vmware host_ip "$VMWAREAPI_IP"
-        iniset $CEILOMETER_CONF vmware host_username "$VMWAREAPI_USER"
-        iniset $CEILOMETER_CONF vmware host_password "$VMWAREAPI_PASSWORD"
-    fi
-
-    if [ "$CEILOMETER_USE_MOD_WSGI" == "True" ]; then
-        iniset $CEILOMETER_CONF api pecan_debug "False"
-        _config_ceilometer_apache_wsgi
-    fi
-
-    if is_service_enabled ceilometer-aipmi; then
-        # Configure rootwrap for the ipmi agent
-        configure_rootwrap ceilometer
-    fi
-}
-
-function configure_mongodb {
-    # Server package is the same on all
-    local packages=mongodb-server
-
-    if is_fedora; then
-        # mongodb client
-        packages="${packages} mongodb"
-    fi
-
-    install_package ${packages}
-
-    if is_fedora; then
-        # Ensure smallfiles is selected to minimize freespace requirements
-        sudo sed -i '/--smallfiles/!s/OPTIONS=\"/OPTIONS=\"--smallfiles /' /etc/sysconfig/mongod
-
-        restart_service mongod
-    fi
-
-    # Give mongodb time to start-up
-    sleep 5
-}
-
-# init_ceilometer() - Initialize etc.
-function init_ceilometer {
-    # Create cache dir
-    sudo install -d -o $STACK_USER $CEILOMETER_AUTH_CACHE_DIR
-    rm -f $CEILOMETER_AUTH_CACHE_DIR/*
-
-    if is_service_enabled mysql postgresql; then
-        if [ "$CEILOMETER_BACKEND" = 'mysql' ] || [ "$CEILOMETER_BACKEND" = 'postgresql' ] || [ "$CEILOMETER_BACKEND" = 'es' ] ; then
-            recreate_database ceilometer
-            $CEILOMETER_BIN_DIR/ceilometer-dbsync
-        fi
-    fi
-}
-
-# install_redis() - Install the redis server.
-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
-}
-
-# install_ceilometer() - Collect source and prepare
-function install_ceilometer {
-    git_clone $CEILOMETER_REPO $CEILOMETER_DIR $CEILOMETER_BRANCH
-    setup_develop $CEILOMETER_DIR
-
-    if echo $CEILOMETER_COORDINATION_URL | grep -q '^memcached:'; then
-        install_package memcached
-    elif echo $CEILOMETER_COORDINATION_URL | grep -q '^redis:'; then
-        install_redis
-    fi
-
-    if [ "$CEILOMETER_BACKEND" = 'mongodb' ] ; then
-        pip_install_gr pymongo
-    fi
-
-    # Only install virt drivers if we're running nova compute
-    if is_service_enabled n-cpu ; then
-        if [[ "$VIRT_DRIVER" = 'libvirt' ]]; then
-            pip_install_gr libvirt-python
-        fi
-
-        if [[ "$VIRT_DRIVER" = 'vsphere' ]]; then
-            pip_install_gr oslo.vmware
-        fi
-    fi
-
-    if [ "$CEILOMETER_BACKEND" = 'es' ] ; then
-        ${TOP_DIR}/pkg/elasticsearch.sh download
-        ${TOP_DIR}/pkg/elasticsearch.sh install
-    fi
-}
-
-# install_ceilometerclient() - Collect source and prepare
-function install_ceilometerclient {
-    if use_library_from_git "python-ceilometerclient"; then
-        git_clone_by_name "python-ceilometerclient"
-        setup_dev_lib "python-ceilometerclient"
-        sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-ceilometerclient"]}/tools/,/etc/bash_completion.d/}ceilometer.bash_completion
-    fi
-}
-
-# start_ceilometer() - Start running processes, including screen
-function start_ceilometer {
-    run_process ceilometer-acentral "$CEILOMETER_BIN_DIR/ceilometer-agent-central --config-file $CEILOMETER_CONF"
-    run_process ceilometer-anotification "$CEILOMETER_BIN_DIR/ceilometer-agent-notification --config-file $CEILOMETER_CONF"
-    run_process ceilometer-collector "$CEILOMETER_BIN_DIR/ceilometer-collector --config-file $CEILOMETER_CONF"
-    run_process ceilometer-aipmi "$CEILOMETER_BIN_DIR/ceilometer-agent-ipmi --config-file $CEILOMETER_CONF"
-
-    if [[ "$CEILOMETER_USE_MOD_WSGI" == "False" ]]; then
-        run_process ceilometer-api "$CEILOMETER_BIN_DIR/ceilometer-api -d -v --log-dir=$CEILOMETER_API_LOG_DIR --config-file $CEILOMETER_CONF"
-    else
-        enable_apache_site ceilometer
-        restart_apache_server
-        tail_log ceilometer /var/log/$APACHE_NAME/ceilometer.log
-        tail_log ceilometer-api /var/log/$APACHE_NAME/ceilometer_access.log
-    fi
-
-
-    # Start the compute agent last to allow time for the collector to
-    # fully wake up and connect to the message bus. See bug #1355809
-    if [[ "$VIRT_DRIVER" = 'libvirt' ]]; then
-        run_process ceilometer-acompute "$CEILOMETER_BIN_DIR/ceilometer-agent-compute --config-file $CEILOMETER_CONF" $LIBVIRT_GROUP
-    fi
-    if [[ "$VIRT_DRIVER" = 'vsphere' ]]; then
-        run_process ceilometer-acompute "$CEILOMETER_BIN_DIR/ceilometer-agent-compute --config-file $CEILOMETER_CONF"
-    fi
-
-    # Only die on API if it was actually intended to be turned on
-    if is_service_enabled ceilometer-api; then
-        echo "Waiting for ceilometer-api to start..."
-        if ! wait_for_service $SERVICE_TIMEOUT $CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/v2/; then
-            die $LINENO "ceilometer-api did not start"
-        fi
-    fi
-
-    run_process ceilometer-alarm-notifier "$CEILOMETER_BIN_DIR/ceilometer-alarm-notifier --config-file $CEILOMETER_CONF"
-    run_process ceilometer-alarm-evaluator "$CEILOMETER_BIN_DIR/ceilometer-alarm-evaluator --config-file $CEILOMETER_CONF"
-}
-
-# stop_ceilometer() - Stop running processes
-function stop_ceilometer {
-    if [ "$CEILOMETER_USE_MOD_WSGI" == "True" ]; then
-        disable_apache_site ceilometer
-        restart_apache_server
-    fi
-    # Kill the ceilometer screen windows
-    for serv in ceilometer-acompute ceilometer-acentral ceilometer-aipmi ceilometer-anotification ceilometer-collector ceilometer-api ceilometer-alarm-notifier ceilometer-alarm-evaluator; do
-        stop_process $serv
-    done
-}
-
-
-# Restore xtrace
-$XTRACE
-
-# Tell emacs to use shell-script-mode
-## Local variables:
-## mode: shell-script
-## End:
diff --git a/lib/ceph b/lib/ceph
index 6cf481e..f573136 100644
--- a/lib/ceph
+++ b/lib/ceph
@@ -83,7 +83,8 @@
 # ------------
 
 function get_ceph_version {
-    local ceph_version_str=$(sudo ceph daemon mon.$(hostname) version | cut -d '"' -f 4 | cut -f 1,2 -d '.')
+    local ceph_version_str
+    ceph_version_str=$(sudo ceph daemon mon.$(hostname) version | cut -d '"' -f 4 | cut -f 1,2 -d '.')
     echo $ceph_version_str
 }
 
@@ -106,7 +107,8 @@
 # undefine_virsh_secret() - Undefine Cinder key secret from libvirt
 function undefine_virsh_secret {
     if is_service_enabled cinder || is_service_enabled nova; then
-        local virsh_uuid=$(sudo virsh secret-list | awk '/^ ?[0-9a-z]/ { print $1 }')
+        local virsh_uuid
+        virsh_uuid=$(sudo virsh secret-list | awk '/^ ?[0-9a-z]/ { print $1 }')
         sudo virsh secret-undefine ${virsh_uuid} >/dev/null 2>&1
     fi
 }
@@ -114,7 +116,7 @@
 
 # check_os_support_ceph() - Check if the operating system provides a decent version of Ceph
 function check_os_support_ceph {
-    if [[ ! ${DISTRO} =~ (trusty|f21|f22) ]]; then
+    if [[ ! ${DISTRO} =~ (trusty|f21|f22|f23) ]]; then
         echo "WARNING: your distro $DISTRO does not provide (at least) the Firefly release. Please use Ubuntu Trusty or Fedora 20 (and higher)"
         if [[ "$FORCE_CEPH_INSTALL" != "yes" ]]; then
             die $LINENO "If you wish to install Ceph on this distribution anyway run with FORCE_CEPH_INSTALL=yes"
@@ -176,7 +178,9 @@
     sudo mkdir -p ${CEPH_DATA_DIR}/{bootstrap-mds,bootstrap-osd,mds,mon,osd,tmp}
 
     # create ceph monitor initial key and directory
-    sudo ceph-authtool /var/lib/ceph/tmp/keyring.mon.$(hostname) --create-keyring --name=mon. --add-key=$(ceph-authtool --gen-print-key) --cap mon 'allow *'
+    sudo ceph-authtool /var/lib/ceph/tmp/keyring.mon.$(hostname) \
+        --create-keyring --name=mon. --add-key=$(ceph-authtool --gen-print-key) \
+        --cap mon 'allow *'
     sudo mkdir /var/lib/ceph/mon/ceph-$(hostname)
 
     # create a default ceph configuration file
@@ -194,12 +198,14 @@
 EOF
 
     # bootstrap the ceph monitor
-    sudo ceph-mon -c ${CEPH_CONF_FILE} --mkfs -i $(hostname) --keyring /var/lib/ceph/tmp/keyring.mon.$(hostname)
+    sudo ceph-mon -c ${CEPH_CONF_FILE} --mkfs -i $(hostname) \
+        --keyring /var/lib/ceph/tmp/keyring.mon.$(hostname)
+
     if is_ubuntu; then
-    sudo touch /var/lib/ceph/mon/ceph-$(hostname)/upstart
+        sudo touch /var/lib/ceph/mon/ceph-$(hostname)/upstart
         sudo initctl emit ceph-mon id=$(hostname)
     else
-    sudo touch /var/lib/ceph/mon/ceph-$(hostname)/sysvinit
+        sudo touch /var/lib/ceph/mon/ceph-$(hostname)/sysvinit
         sudo service ceph start mon.$(hostname)
     fi
 
@@ -215,7 +221,8 @@
     done
 
     # pools data and metadata were removed in the Giant release so depending on the version we apply different commands
-    local ceph_version=$(get_ceph_version)
+    local ceph_version
+    ceph_version=$(get_ceph_version)
     # change pool replica size according to the CEPH_REPLICAS set by the user
     if [[ ${ceph_version%%.*} -eq 0 ]] && [[ ${ceph_version##*.} -lt 87 ]]; then
         sudo ceph -c ${CEPH_CONF_FILE} osd pool set rbd size ${CEPH_REPLICAS}
@@ -240,7 +247,9 @@
         OSD_ID=$(sudo ceph -c ${CEPH_CONF_FILE} osd create)
         sudo mkdir -p ${CEPH_DATA_DIR}/osd/ceph-${OSD_ID}
         sudo ceph-osd -c ${CEPH_CONF_FILE} -i ${OSD_ID} --mkfs
-        sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create osd.${OSD_ID} mon 'allow profile osd ' osd 'allow *' | sudo tee ${CEPH_DATA_DIR}/osd/ceph-${OSD_ID}/keyring
+        sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create osd.${OSD_ID} \
+            mon 'allow profile osd ' osd 'allow *' | \
+            sudo tee ${CEPH_DATA_DIR}/osd/ceph-${OSD_ID}/keyring
 
         # ceph's init script is parsing ${CEPH_DATA_DIR}/osd/ceph-${OSD_ID}/ and looking for a file
         # 'upstart' or 'sysinitv', thanks to these 'touches' we are able to control OSDs daemons
@@ -264,9 +273,13 @@
 # configure_ceph_glance() - Glance config needs to come after Glance is set up
 function configure_ceph_glance {
     sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${GLANCE_CEPH_POOL} ${GLANCE_CEPH_POOL_PG} ${GLANCE_CEPH_POOL_PGP}
-    sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${GLANCE_CEPH_USER} mon "allow r" osd "allow class-read object_prefix rbd_children, allow rwx pool=${GLANCE_CEPH_POOL}" | sudo tee ${CEPH_CONF_DIR}/ceph.client.${GLANCE_CEPH_USER}.keyring
+    sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${GLANCE_CEPH_USER} \
+        mon "allow r" \
+        osd "allow class-read object_prefix rbd_children, allow rwx pool=${GLANCE_CEPH_POOL}" | \
+        sudo tee ${CEPH_CONF_DIR}/ceph.client.${GLANCE_CEPH_USER}.keyring
     sudo chown ${STACK_USER}:$(id -g -n $whoami) ${CEPH_CONF_DIR}/ceph.client.${GLANCE_CEPH_USER}.keyring
 
+    iniset $GLANCE_API_CONF DEFAULT show_image_direct_url True
     iniset $GLANCE_API_CONF glance_store default_store rbd
     iniset $GLANCE_API_CONF glance_store stores "file, http, rbd"
     iniset $GLANCE_API_CONF glance_store rbd_store_ceph_conf $CEPH_CONF_FILE
@@ -295,7 +308,10 @@
     iniset $NOVA_CONF libvirt images_rbd_ceph_conf ${CEPH_CONF_FILE}
 
     if ! is_service_enabled cinder; then
-        sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${CINDER_CEPH_USER} mon "allow r" osd "allow class-read object_prefix rbd_children, allow rwx pool=${CINDER_CEPH_POOL}, allow rwx pool=${NOVA_CEPH_POOL},allow rx pool=${GLANCE_CEPH_POOL}" | sudo tee ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring > /dev/null
+        sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${CINDER_CEPH_USER} \
+            mon "allow r" \
+            osd "allow class-read object_prefix rbd_children, allow rwx pool=${CINDER_CEPH_POOL}, allow rwx pool=${NOVA_CEPH_POOL},allow rwx pool=${GLANCE_CEPH_POOL}" | \
+            sudo tee ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring > /dev/null
         sudo chown ${STACK_USER}:$(id -g -n $whoami) ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring
     fi
 }
@@ -311,7 +327,10 @@
 # configure_ceph_cinder() - Cinder config needs to come after Cinder is set up
 function configure_ceph_cinder {
     sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${CINDER_CEPH_POOL} ${CINDER_CEPH_POOL_PG} ${CINDER_CEPH_POOL_PGP}
-    sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${CINDER_CEPH_USER} mon "allow r" osd "allow class-read object_prefix rbd_children, allow rwx pool=${CINDER_CEPH_POOL}, allow rwx pool=${NOVA_CEPH_POOL},allow rx pool=${GLANCE_CEPH_POOL}" | sudo tee ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring
+    sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${CINDER_CEPH_USER} \
+        mon "allow r" \
+        osd "allow class-read object_prefix rbd_children, allow rwx pool=${CINDER_CEPH_POOL}, allow rwx pool=${NOVA_CEPH_POOL},allow rwx pool=${GLANCE_CEPH_POOL}" | \
+        sudo tee ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring
     sudo chown ${STACK_USER}:$(id -g -n $whoami) ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring
 }
 
diff --git a/lib/cinder b/lib/cinder
index e5ed2db..1307c11 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -93,7 +93,7 @@
     if [[ $CINDER_SECURE_DELETE == "False" ]]; then
         CINDER_VOLUME_CLEAR_DEFAULT="none"
     fi
-    DEPRECATED_TEXT="$DEPRECATED_TEXT\nConfigure secure Cinder volume deletion using CINDER_VOLUME_CLEAR instead of CINDER_SECURE_DELETE.\n"
+    deprecated "Configure secure Cinder volume deletion using CINDER_VOLUME_CLEAR instead of CINDER_SECURE_DELETE."
 fi
 CINDER_VOLUME_CLEAR=${CINDER_VOLUME_CLEAR:-${CINDER_VOLUME_CLEAR_DEFAULT:-zero}}
 CINDER_VOLUME_CLEAR=$(echo ${CINDER_VOLUME_CLEAR} | tr '[:upper:]' '[:lower:]')
@@ -108,9 +108,8 @@
 
 CINDER_ISCSI_HELPER=${CINDER_ISCSI_HELPER:-tgtadm}
 
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,cinder
-
+# Toggle for deploying Cinder under HTTPD + mod_wsgi
+CINDER_USE_MOD_WSGI=${CINDER_USE_MOD_WSGI:-False}
 
 # Source the enabled backends
 if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
@@ -140,13 +139,19 @@
     return 1
 }
 
+# _cinder_cleanup_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
+function _cinder_cleanup_apache_wsgi {
+    sudo rm -f $(apache_site_config_for osapi-volume)
+}
+
 # cleanup_cinder() - Remove residual data files, anything left over from previous
 # runs that a clean run would need to clean up
 function cleanup_cinder {
     # ensure the volume group is cleared up because fails might
     # leave dead volumes in the group
     if [ "$CINDER_ISCSI_HELPER" = "tgtadm" ]; then
-        local targets=$(sudo tgtadm --op show --mode target)
+        local targets
+        targets=$(sudo tgtadm --op show --mode target)
         if [ $? -ne 0 ]; then
             # If tgt driver isn't running this won't work obviously
             # So check the response and restart if need be
@@ -186,6 +191,44 @@
             fi
         done
     fi
+
+    if [ "$CINDER_USE_MOD_WSGI" == "True" ]; then
+        _cinder_cleanup_apache_wsgi
+    fi
+}
+
+# _cinder_config_apache_wsgi() - Set WSGI config files
+function _cinder_config_apache_wsgi {
+    local cinder_apache_conf
+    cinder_apache_conf=$(apache_site_config_for osapi-volume)
+    local cinder_ssl=""
+    local cinder_certfile=""
+    local cinder_keyfile=""
+    local cinder_api_port=$CINDER_SERVICE_PORT
+    local venv_path=""
+
+    if is_ssl_enabled_service c-api; then
+        cinder_ssl="SSLEngine On"
+        cinder_certfile="SSLCertificateFile $CINDER_SSL_CERT"
+        cinder_keyfile="SSLCertificateKeyFile $CINDER_SSL_KEY"
+    fi
+    if [[ ${USE_VENV} = True ]]; then
+        venv_path="python-path=${PROJECT_VENV["cinder"]}/lib/python2.7/site-packages"
+    fi
+
+    # copy proxy vhost file
+    sudo cp $FILES/apache-cinder-api.template $cinder_apache_conf
+    sudo sed -e "
+        s|%PUBLICPORT%|$cinder_api_port|g;
+        s|%APACHE_NAME%|$APACHE_NAME|g;
+        s|%APIWORKERS%|$API_WORKERS|g
+        s|%CINDER_BIN_DIR%|$CINDER_BIN_DIR|g;
+        s|%SSLENGINE%|$cinder_ssl|g;
+        s|%SSLCERTFILE%|$cinder_certfile|g;
+        s|%SSLKEYFILE%|$cinder_keyfile|g;
+        s|%USER%|$STACK_USER|g;
+        s|%VIRTUALENV%|$venv_path|g
+    " -i $cinder_apache_conf
 }
 
 # configure_cinder() - Set config files, create data dirs, etc
@@ -256,7 +299,7 @@
     fi
 
     if is_service_enabled swift; then
-        iniset $CINDER_CONF DEFAULT backup_swift_url "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:8080/v1/AUTH_"
+        iniset $CINDER_CONF DEFAULT backup_swift_url "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:$SWIFT_DEFAULT_BIND_PORT/v1/AUTH_"
     fi
 
     if is_service_enabled ceilometer; then
@@ -274,18 +317,20 @@
 
     iniset_rpc_backend cinder $CINDER_CONF
 
-    if [[ "$CINDER_VOLUME_CLEAR" == "none" ]] || [[ "$CINDER_VOLUME_CLEAR" == "zero" ]] || [[ "$CINDER_VOLUME_CLEAR" == "shred" ]]; then
-        iniset $CINDER_CONF DEFAULT volume_clear $CINDER_VOLUME_CLEAR
-    fi
+    iniset $CINDER_CONF DEFAULT volume_clear $CINDER_VOLUME_CLEAR
 
     # Format logging
-    if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
+    if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ] && [ "$CINDER_USE_MOD_WSGI" == "False" ]; then
         setup_colorized_logging $CINDER_CONF DEFAULT "project_id" "user_id"
     else
         # Set req-id, project-name and resource in log format
         iniset $CINDER_CONF DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(project_name)s] %(resource)s%(message)s"
     fi
 
+    if [ "$CINDER_USE_MOD_WSGI" == "True" ]; then
+        _cinder_config_apache_wsgi
+    fi
+
     if [[ -r $CINDER_PLUGINS/$CINDER_DRIVER ]]; then
         configure_cinder_driver
     fi
@@ -402,6 +447,13 @@
             install_package tgt
         fi
     fi
+
+    if [ "$CINDER_USE_MOD_WSGI" == "True" ]; then
+        install_apache_wsgi
+        if is_ssl_enabled_service "c-api"; then
+            enable_mod_ssl
+        fi
+    fi
 }
 
 # install_cinderclient() - Collect source and prepare
@@ -449,10 +501,16 @@
         fi
     fi
 
-    run_process c-api "$CINDER_BIN_DIR/cinder-api --config-file $CINDER_CONF"
-    echo "Waiting for Cinder API to start..."
-    if ! wait_for_service $SERVICE_TIMEOUT $service_protocol://$CINDER_SERVICE_HOST:$service_port; then
-        die $LINENO "c-api did not start"
+    if [ "$CINDER_USE_MOD_WSGI" == "True" ]; then
+        enable_apache_site osapi-volume
+        restart_apache_server
+        tail_log c-api /var/log/$APACHE_NAME/c-api.log
+    else
+        run_process c-api "$CINDER_BIN_DIR/cinder-api --config-file $CINDER_CONF"
+        echo "Waiting for Cinder API to start..."
+        if ! wait_for_service $SERVICE_TIMEOUT $service_protocol://$CINDER_SERVICE_HOST:$service_port; then
+            die $LINENO "c-api did not start"
+        fi
     fi
 
     run_process c-sch "$CINDER_BIN_DIR/cinder-scheduler --config-file $CINDER_CONF"
@@ -471,9 +529,16 @@
 
 # stop_cinder() - Stop running processes
 function stop_cinder {
+    if [ "$CINDER_USE_MOD_WSGI" == "True" ]; then
+        disable_apache_site osapi-volume
+        restart_apache_server
+    else
+        stop_process c-api
+    fi
+
     # Kill the cinder screen windows
     local serv
-    for serv in c-api c-bak c-sch c-vol; do
+    for serv in c-bak c-sch c-vol; do
         stop_process $serv
     done
 }
diff --git a/lib/database b/lib/database
index 5bbbe31..13740b9 100644
--- a/lib/database
+++ b/lib/database
@@ -101,7 +101,7 @@
     # a multi-node DevStack installation.
 
     # NOTE: Don't specify ``/db`` in this string so we can use it for multiple services
-    BASE_SQL_CONN=${BASE_SQL_CONN:-$(get_database_type)://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST}
+    BASE_SQL_CONN=${BASE_SQL_CONN:-$(get_database_type_$DATABASE_TYPE)://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST}
 
     return 0
 }
@@ -135,14 +135,6 @@
     database_connection_url_$DATABASE_TYPE $db
 }
 
-function get_database_type {
-    if [[ -n "${SQLALCHEMY_DATABASE_DRIVER}" ]]; then
-        echo "${DATABASE_TYPE}+${SQLALCHEMY_DATABASE_DRIVER}"
-    else
-        echo "${DATABASE_TYPE}"
-    fi
-}
-
 
 # Restore xtrace
 $XTRACE
diff --git a/lib/databases/mysql b/lib/databases/mysql
index fb55b60..cc74b33 100644
--- a/lib/databases/mysql
+++ b/lib/databases/mysql
@@ -12,12 +12,6 @@
 set +o xtrace
 
 MYSQL_DRIVER=${MYSQL_DRIVER:-PyMySQL}
-# Force over to pymysql driver by default if we are using it.
-if is_service_enabled mysql; then
-    if [[ "$MYSQL_DRIVER" == "PyMySQL" ]]; then
-        SQLALCHEMY_DATABASE_DRIVER=${SQLALCHEMY_DATABASE_DRIVER:-"pymysql"}
-    fi
-fi
 
 register_database mysql
 
@@ -30,6 +24,14 @@
 # Functions
 # ---------
 
+function get_database_type_mysql {
+    if [[ "$MYSQL_DRIVER" == "PyMySQL" ]]; then
+        echo mysql+pymysql
+    else
+        echo mysql
+    fi
+}
+
 # Get rid of everything enough to cleanly change database backends
 function cleanup_database_mysql {
     stop_service $MYSQL
@@ -85,21 +87,19 @@
         sudo mysqladmin -u root password $DATABASE_PASSWORD || true
     fi
 
-    # Update the DB to give user ‘$DATABASE_USER’@’%’ full control of the all databases:
+    # Update the DB to give user '$DATABASE_USER'@'%' full control of the all databases:
     sudo mysql -uroot -p$DATABASE_PASSWORD -h127.0.0.1 -e "GRANT ALL PRIVILEGES ON *.* TO '$DATABASE_USER'@'%' identified by '$DATABASE_PASSWORD';"
 
     # Now update ``my.cnf`` for some local needs and restart the mysql service
 
-    # Change ‘bind-address’ from localhost (127.0.0.1) to any (::) and
+    # Change bind-address from localhost (127.0.0.1) to any (::) and
     # set default db type to InnoDB
-    sudo bash -c "source $TOP_DIR/functions && \
-        iniset $my_conf mysqld bind-address "$SERVICE_LISTEN_ADDRESS" && \
-        iniset $my_conf mysqld sql_mode STRICT_ALL_TABLES && \
-        iniset $my_conf mysqld default-storage-engine InnoDB && \
-        iniset $my_conf mysqld max_connections 1024 && \
-        iniset $my_conf mysqld query_cache_type OFF && \
-        iniset $my_conf mysqld query_cache_size 0"
-
+    iniset -sudo $my_conf mysqld bind-address "$SERVICE_LISTEN_ADDRESS"
+    iniset -sudo $my_conf mysqld sql_mode STRICT_ALL_TABLES
+    iniset -sudo $my_conf mysqld default-storage-engine InnoDB
+    iniset -sudo $my_conf mysqld max_connections 1024
+    iniset -sudo $my_conf mysqld query_cache_type OFF
+    iniset -sudo $my_conf mysqld query_cache_size 0
 
     if [[ "$DATABASE_QUERY_LOGGING" == "True" ]]; then
         echo_summary "Enabling MySQL query logging"
@@ -115,12 +115,10 @@
 
         # Turn on slow query log, log all queries (any query taking longer than
         # 0 seconds) and log all non-indexed queries
-        sudo bash -c "source $TOP_DIR/functions && \
-            iniset $my_conf mysqld slow-query-log 1 && \
-            iniset $my_conf mysqld slow-query-log-file $slow_log && \
-            iniset $my_conf mysqld long-query-time 0 && \
-            iniset $my_conf mysqld log-queries-not-using-indexes 1"
-
+        iniset -sudo $my_conf mysqld slow-query-log 1
+        iniset -sudo $my_conf mysqld slow-query-log-file $slow_log
+        iniset -sudo $my_conf mysqld long-query-time 0
+        iniset -sudo $my_conf mysqld log-queries-not-using-indexes 1
     fi
 
     restart_service $mysql
@@ -145,7 +143,7 @@
 [client]
 user=$DATABASE_USER
 password=$DATABASE_PASSWORD
-host=$DATABASE_HOST
+host=$MYSQL_HOST
 EOF
         chmod 0600 $HOME/.my.cnf
     fi
diff --git a/lib/databases/postgresql b/lib/databases/postgresql
index e087a1e..78c7bed 100644
--- a/lib/databases/postgresql
+++ b/lib/databases/postgresql
@@ -21,6 +21,10 @@
 # Functions
 # ---------
 
+function get_database_type_postgresql {
+    echo postgresql
+}
+
 # Get rid of everything enough to cleanly change database backends
 function cleanup_database_postgresql {
     stop_service postgresql
diff --git a/lib/dlm b/lib/dlm
new file mode 100644
index 0000000..95e9b0a
--- /dev/null
+++ b/lib/dlm
@@ -0,0 +1,108 @@
+#!/bin/bash
+#
+# lib/dlm
+#
+# Functions to control the installation and configuration of software
+# that provides a dlm (and possibly other functions). The default is
+# **zookeeper**, and is going to be the only backend supported in the
+# devstack tree.
+
+# Dependencies:
+#
+# - ``functions`` file
+
+# ``stack.sh`` calls the entry points in this order:
+#
+# - is_dlm_enabled
+# - install_dlm
+# - configure_dlm
+# - cleanup_dlm
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Defaults
+# --------
+
+# <define global variables here that belong to this project>
+
+# Set up default directories
+ZOOKEEPER_DATA_DIR=$DEST/data/zookeeper
+ZOOKEEPER_CONF_DIR=/etc/zookeeper
+
+
+# Entry Points
+# ------------
+#
+# NOTE(sdague): it is expected that when someone wants to implement
+# another one of these out of tree, they'll implement the following
+# functions:
+#
+# - dlm_backend
+# - install_dlm
+# - configure_dlm
+# - cleanup_dlm
+
+# This should be declared in the settings file of any plugin or
+# service that needs to have a dlm in their environment.
+function use_dlm {
+    enable_service $(dlm_backend)
+}
+
+# A function to return the name of the backend in question, some users
+# are going to need to know this.
+function dlm_backend {
+    echo "zookeeper"
+}
+
+# Test if a dlm is enabled (defaults to a zookeeper specific check)
+function is_dlm_enabled {
+    [[ ,${ENABLED_SERVICES}, =~ ,"$(dlm_backend)", ]] && return 0
+    return 1
+}
+
+# cleanup_dlm() - Remove residual data files, anything left over from previous
+# runs that a clean run would need to clean up
+function cleanup_dlm {
+    # NOTE(sdague): we don't check for is_enabled here because we
+    # should just delete this regardless. Some times users updated
+    # their service list before they run cleanup.
+    sudo rm -rf $ZOOKEEPER_DATA_DIR
+}
+
+# configure_dlm() - Set config files, create data dirs, etc
+function configure_dlm {
+    if is_dlm_enabled; then
+        sudo cp $FILES/zookeeper/* $ZOOKEEPER_CONF_DIR
+        sudo sed -i -e 's|.*dataDir.*|dataDir='$ZOOKEEPER_DATA_DIR'|' $ZOOKEEPER_CONF_DIR/zoo.cfg
+        # clean up from previous (possibly aborted) runs
+        # create required data files
+        sudo rm -rf $ZOOKEEPER_DATA_DIR
+        sudo mkdir -p $ZOOKEEPER_DATA_DIR
+        # restart after configuration, there is no reason to make this
+        # another step, because having data files that don't match the
+        # zookeeper running is just going to cause tears.
+        restart_service zookeeper
+    fi
+}
+
+# install_dlm() - Collect source and prepare
+function install_dlm {
+    if is_dlm_enabled; then
+        if is_ubuntu; then
+            install_package zookeeperd
+        else
+            die $LINENO "Don't know how to install zookeeper on this platform"
+        fi
+    fi
+}
+
+# Restore xtrace
+$XTRACE
+
+# Tell emacs to use shell-script-mode
+## Local variables:
+## mode: shell-script
+## End:
diff --git a/lib/dstat b/lib/dstat
index f11bfa5..fe4790b 100644
--- a/lib/dstat
+++ b/lib/dstat
@@ -19,8 +19,7 @@
 # start_dstat() - Start running processes, including screen
 function start_dstat {
     # A better kind of sysstat, with the top process per time slice
-    DSTAT_OPTS="-tcmndrylpg --top-cpu-adv --top-io-adv"
-    run_process dstat "dstat $DSTAT_OPTS"
+    run_process dstat "$TOP_DIR/tools/dstat.sh $LOGDIR"
 
     # To enable peakmem_tracker add:
     #    enable_service peakmem_tracker
diff --git a/lib/glance b/lib/glance
index f200dca..2eb93a4 100644
--- a/lib/glance
+++ b/lib/glance
@@ -75,9 +75,6 @@
 GLANCE_SEARCH_PORT_INT=${GLANCE_SEARCH_PORT_INT:-19393}
 GLANCE_SEARCH_HOSTPORT=${GLANCE_SEARCH_HOSTPORT:-$GLANCE_SERVICE_HOST:$GLANCE_SEARCH_PORT}
 
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,glance
-
 # Functions
 # ---------
 
@@ -109,7 +106,8 @@
     iniset $GLANCE_REGISTRY_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
     iniset $GLANCE_REGISTRY_CONF DEFAULT bind_host $GLANCE_SERVICE_LISTEN_ADDRESS
     inicomment $GLANCE_REGISTRY_CONF DEFAULT log_file
-    local dburl=`database_connection_url glance`
+    local dburl
+    dburl=`database_connection_url glance`
     iniset $GLANCE_REGISTRY_CONF database connection $dburl
     iniset $GLANCE_REGISTRY_CONF DEFAULT use_syslog $SYSLOG
     iniset $GLANCE_REGISTRY_CONF DEFAULT workers "$API_WORKERS"
@@ -154,7 +152,10 @@
 
         iniset $GLANCE_SWIFT_STORE_CONF ref1 user $SERVICE_TENANT_NAME:glance-swift
         iniset $GLANCE_SWIFT_STORE_CONF ref1 key $SERVICE_PASSWORD
-        iniset $GLANCE_SWIFT_STORE_CONF ref1 auth_address $KEYSTONE_SERVICE_URI/v2.0/
+        iniset $GLANCE_SWIFT_STORE_CONF ref1 auth_address $KEYSTONE_SERVICE_URI/v3
+        iniset $GLANCE_SWIFT_STORE_CONF ref1 user_domain_id default
+        iniset $GLANCE_SWIFT_STORE_CONF ref1 project_domain_id default
+        iniset $GLANCE_SWIFT_STORE_CONF ref1 auth_version 3
 
         # commenting is not strictly necessary but it's confusing to have bad values in conf
         inicomment $GLANCE_API_CONF glance_store swift_store_user
@@ -265,7 +266,8 @@
         # required for swift access
         if is_service_enabled s-proxy; then
 
-            local glance_swift_user=$(get_or_create_user "glance-swift" \
+            local glance_swift_user
+            glance_swift_user=$(get_or_create_user "glance-swift" \
                 "$SERVICE_PASSWORD" "default" "glance-swift@example.com")
             get_or_add_user_project_role "ResellerAdmin" $glance_swift_user $SERVICE_TENANT_NAME
         fi
diff --git a/lib/heat b/lib/heat
index cedddd2..e42bdf0 100644
--- a/lib/heat
+++ b/lib/heat
@@ -16,6 +16,7 @@
 # - install_heat
 # - configure_heatclient
 # - configure_heat
+# - _config_heat_apache_wsgi
 # - init_heat
 # - start_heat
 # - stop_heat
@@ -32,6 +33,9 @@
 # set up default directories
 GITDIR["python-heatclient"]=$DEST/python-heatclient
 
+# Toggle for deploying Heat-API under HTTPD + mod_wsgi
+HEAT_USE_MOD_WSGI=${HEAT_USE_MOD_WSGI:-False}
+
 HEAT_DIR=$DEST/heat
 HEAT_CFNTOOLS_DIR=$DEST/heat-cfntools
 HEAT_TEMPLATES_REPO_DIR=$DEST/heat-templates
@@ -53,20 +57,20 @@
 HEAT_API_HOST=${HEAT_API_HOST:-$HOST_IP}
 HEAT_API_PORT=${HEAT_API_PORT:-8004}
 
+# Support entry points installation of console scripts
+HEAT_BIN_DIR=$(get_python_exec_prefix)
 
 # other default options
 if [[ "$HEAT_STANDALONE" = "True" ]]; then
     # for standalone, use defaults which require no service user
-    HEAT_STACK_DOMAIN=`trueorfalse False $HEAT_STACK_DOMAIN`
+    HEAT_STACK_DOMAIN=$(trueorfalse False HEAT_STACK_DOMAIN)
     HEAT_DEFERRED_AUTH=${HEAT_DEFERRED_AUTH:-password}
 else
-    HEAT_STACK_DOMAIN=`trueorfalse True $HEAT_STACK_DOMAIN`
+    HEAT_STACK_DOMAIN=$(trueorfalse True HEAT_STACK_DOMAIN)
     HEAT_DEFERRED_AUTH=${HEAT_DEFERRED_AUTH:-trusts}
 fi
-
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,heat
-
+HEAT_PLUGIN_DIR=${HEAT_PLUGIN_DIR:-$DATA_DIR/heat/plugins}
+ENABLE_HEAT_PLUGINS=${ENABLE_HEAT_PLUGINS:-}
 
 # Functions
 # ---------
@@ -119,13 +123,17 @@
     # logging
     iniset $HEAT_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
     iniset $HEAT_CONF DEFAULT use_syslog $SYSLOG
-    if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
+    if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ] && [ "$HEAT_USE_MOD_WSGI" == "False" ]  ; then
         # Add color to logging output
         setup_colorized_logging $HEAT_CONF DEFAULT tenant user
     fi
 
     iniset $HEAT_CONF DEFAULT deferred_auth_method $HEAT_DEFERRED_AUTH
 
+    if [ "$HEAT_USE_MOD_WSGI" == "True" ]; then
+        _config_heat_apache_wsgi
+    fi
+
     # NOTE(jamielennox): heat re-uses specific values from the
     # keystone_authtoken middleware group and so currently fails when using the
     # auth plugin setup. This should be fixed in heat.  Heat is also the only
@@ -182,6 +190,35 @@
     # copy the default templates
     cp $HEAT_DIR/etc/heat/templates/* $HEAT_TEMPLATES_DIR/
 
+    # Enable heat plugins.
+    # NOTE(nic): The symlink nonsense is necessary because when
+    # plugins are installed in "developer mode", the final component
+    # of their target directory is always "resources", which confuses
+    # Heat's plugin loader into believing that all plugins are named
+    # "resources", and therefore are all the same plugin; so it
+    # will only load one of them.  Linking them all to a common
+    # location with unique names avoids that type of collision,
+    # while still allowing the plugins to be edited in-tree.
+    local err_count=0
+
+    if [ -n "$ENABLE_HEAT_PLUGINS" ]; then
+        mkdir -p $HEAT_PLUGIN_DIR
+        # Clean up cruft from any previous runs
+        rm -f $HEAT_PLUGIN_DIR/*
+        iniset $HEAT_CONF DEFAULT plugin_dirs $HEAT_PLUGIN_DIR
+    fi
+
+    for heat_plugin in $ENABLE_HEAT_PLUGINS; do
+        if [ -d $HEAT_DIR/contrib/$heat_plugin ]; then
+            setup_package $HEAT_DIR/contrib/$heat_plugin -e
+            ln -s $HEAT_DIR/contrib/$heat_plugin/$heat_plugin/resources $HEAT_PLUGIN_DIR/$heat_plugin
+        else
+            : # clear retval on the test so that we can roll up errors
+            err $LINENO "Requested Heat plugin(${heat_plugin}) not found."
+            err_count=$(($err_count + 1))
+        fi
+    done
+    [ $err_count -eq 0 ] || die $LINENO "$err_count of the requested Heat plugins could not be installed."
 }
 
 # init_heat() - Initialize database
@@ -190,7 +227,7 @@
     # (re)create heat database
     recreate_database heat
 
-    $HEAT_DIR/bin/heat-manage db_sync
+    $HEAT_BIN_DIR/heat-manage db_sync
     create_heat_cache_dir
 }
 
@@ -213,6 +250,9 @@
 function install_heat {
     git_clone $HEAT_REPO $HEAT_DIR $HEAT_BRANCH
     setup_develop $HEAT_DIR
+    if [ "$HEAT_USE_MOD_WSGI" == "True" ]; then
+        install_apache_wsgi
+    fi
 }
 
 # install_heat_other() - Collect source and prepare
@@ -227,21 +267,107 @@
 
 # start_heat() - Start running processes, including screen
 function start_heat {
-    run_process h-eng "$HEAT_DIR/bin/heat-engine --config-file=$HEAT_CONF"
-    run_process h-api "$HEAT_DIR/bin/heat-api --config-file=$HEAT_CONF"
-    run_process h-api-cfn "$HEAT_DIR/bin/heat-api-cfn --config-file=$HEAT_CONF"
-    run_process h-api-cw "$HEAT_DIR/bin/heat-api-cloudwatch --config-file=$HEAT_CONF"
+    run_process h-eng "$HEAT_BIN_DIR/heat-engine --config-file=$HEAT_CONF"
+
+    # If the site is not enabled then we are in a grenade scenario
+    local enabled_site_file
+    enabled_site_file=$(apache_site_config_for heat-api)
+    if [ -f ${enabled_site_file} ] && [ "$HEAT_USE_MOD_WSGI" == "True" ]; then
+        enable_apache_site heat-api
+        enable_apache_site heat-api-cfn
+        enable_apache_site heat-api-cloudwatch
+        restart_apache_server
+        tail_log heat-api /var/log/$APACHE_NAME/heat-api.log
+        tail_log heat-api-cfn /var/log/$APACHE_NAME/heat-api-cfn.log
+        tail_log heat-api-cloudwatch /var/log/$APACHE_NAME/heat-api-cloudwatch.log
+    else
+        run_process h-api "$HEAT_BIN_DIR/heat-api --config-file=$HEAT_CONF"
+        run_process h-api-cfn "$HEAT_BIN_DIR/heat-api-cfn --config-file=$HEAT_CONF"
+        run_process h-api-cw "$HEAT_BIN_DIR/heat-api-cloudwatch --config-file=$HEAT_CONF"
+    fi
 }
 
 # stop_heat() - Stop running processes
 function stop_heat {
     # Kill the screen windows
-    local serv
-    for serv in h-eng h-api h-api-cfn h-api-cw; do
-        stop_process $serv
-    done
+    stop_process h-eng
+
+    if [ "$HEAT_USE_MOD_WSGI" == "True" ]; then
+        disable_apache_site heat-api
+        disable_apache_site heat-api-cfn
+        disable_apache_site heat-api-cloudwatch
+        restart_apache_server
+    else
+        local serv
+        for serv in h-api h-api-cfn h-api-cw; do
+            stop_process $serv
+        done
+    fi
+
 }
 
+# _cleanup_heat_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
+function _cleanup_heat_apache_wsgi {
+    sudo rm -f $(apache_site_config_for heat-api)
+    sudo rm -f $(apache_site_config_for heat-api-cfn)
+    sudo rm -f $(apache_site_config_for heat-api-cloudwatch)
+}
+
+# _config_heat_apache_wsgi() - Set WSGI config files of Heat
+function _config_heat_apache_wsgi {
+
+    local heat_apache_conf
+    heat_apache_conf=$(apache_site_config_for heat-api)
+    local heat_cfn_apache_conf
+    heat_cfn_apache_conf=$(apache_site_config_for heat-api-cfn)
+    local heat_cloudwatch_apache_conf
+    heat_cloudwatch_apache_conf=$(apache_site_config_for heat-api-cloudwatch)
+    local heat_ssl=""
+    local heat_certfile=""
+    local heat_keyfile=""
+    local heat_api_port=$HEAT_API_PORT
+    local heat_cfn_api_port=$HEAT_API_CFN_PORT
+    local heat_cw_api_port=$HEAT_API_CW_PORT
+    local venv_path=""
+
+    sudo cp $FILES/apache-heat-api.template $heat_apache_conf
+    sudo sed -e "
+        s|%PUBLICPORT%|$heat_api_port|g;
+        s|%APACHE_NAME%|$APACHE_NAME|g;
+        s|%HEAT_BIN_DIR%|$HEAT_BIN_DIR|g;
+        s|%SSLENGINE%|$heat_ssl|g;
+        s|%SSLCERTFILE%|$heat_certfile|g;
+        s|%SSLKEYFILE%|$heat_keyfile|g;
+        s|%USER%|$STACK_USER|g;
+        s|%VIRTUALENV%|$venv_path|g
+    " -i $heat_apache_conf
+
+    sudo cp $FILES/apache-heat-api-cfn.template $heat_cfn_apache_conf
+    sudo sed -e "
+        s|%PUBLICPORT%|$heat_cfn_api_port|g;
+        s|%APACHE_NAME%|$APACHE_NAME|g;
+        s|%HEAT_BIN_DIR%|$HEAT_BIN_DIR|g;
+        s|%SSLENGINE%|$heat_ssl|g;
+        s|%SSLCERTFILE%|$heat_certfile|g;
+        s|%SSLKEYFILE%|$heat_keyfile|g;
+        s|%USER%|$STACK_USER|g;
+        s|%VIRTUALENV%|$venv_path|g
+    " -i $heat_cfn_apache_conf
+
+    sudo cp $FILES/apache-heat-api-cloudwatch.template $heat_cloudwatch_apache_conf
+    sudo sed -e "
+        s|%PUBLICPORT%|$heat_cw_api_port|g;
+        s|%APACHE_NAME%|$APACHE_NAME|g;
+        s|%HEAT_BIN_DIR%|$HEAT_BIN_DIR|g;
+        s|%SSLENGINE%|$heat_ssl|g;
+        s|%SSLCERTFILE%|$heat_certfile|g;
+        s|%SSLKEYFILE%|$heat_keyfile|g;
+        s|%USER%|$STACK_USER|g;
+        s|%VIRTUALENV%|$venv_path|g
+    " -i $heat_cloudwatch_apache_conf
+}
+
+
 # create_heat_accounts() - Set up common required heat accounts
 function create_heat_accounts {
     if [[ "$HEAT_STANDALONE" != "True" ]]; then
@@ -323,7 +449,8 @@
 
     echo "</body></html>" >> $HEAT_PIP_REPO/index.html
 
-    local heat_pip_repo_apache_conf=$(apache_site_config_for heat_pip_repo)
+    local heat_pip_repo_apache_conf
+    heat_pip_repo_apache_conf=$(apache_site_config_for heat_pip_repo)
 
     sudo cp $FILES/apache-heat-pip-repo.template $heat_pip_repo_apache_conf
     sudo sed -e "
diff --git a/lib/horizon b/lib/horizon
index b0f306b..ff63b06 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -35,10 +35,6 @@
 # The example file in Horizon repo is used by default.
 HORIZON_SETTINGS=${HORIZON_SETTINGS:-$HORIZON_DIR/openstack_dashboard/local/local_settings.py.example}
 
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,horizon
-
-
 # Functions
 # ---------
 
@@ -53,7 +49,8 @@
         sed -e "/^$option/d" -i $local_settings
         echo -e "\n$option=$value" >> $file
     elif grep -q "^$section" $file; then
-        local line=$(sed -ne "/^$section/,/^}/ { /^ *'$option':/ p; }" $file)
+        local line
+        line=$(sed -ne "/^$section/,/^}/ { /^ *'$option':/ p; }" $file)
         if [ -n "$line" ]; then
             sed -i -e "/^$section/,/^}/ s/^\( *'$option'\) *:.*$/\1: $value,/" $file
         else
@@ -72,7 +69,8 @@
 # cleanup_horizon() - Remove residual data files, anything left over from previous
 # runs that a clean run would need to clean up
 function cleanup_horizon {
-    local horizon_conf=$(apache_site_config_for horizon)
+    local horizon_conf
+    horizon_conf=$(apache_site_config_for horizon)
     sudo rm -f $horizon_conf
 }
 
@@ -93,18 +91,16 @@
     local local_settings=$HORIZON_DIR/openstack_dashboard/local/local_settings.py
     cp $HORIZON_SETTINGS $local_settings
 
+    _horizon_config_set $local_settings "" WEBROOT \"$HORIZON_APACHE_ROOT/\"
+    _horizon_config_set $local_settings "" CUSTOM_THEME_PATH \"themes/webroot\"
+
     _horizon_config_set $local_settings "" COMPRESS_OFFLINE True
     _horizon_config_set $local_settings "" OPENSTACK_KEYSTONE_DEFAULT_ROLE \"Member\"
 
     _horizon_config_set $local_settings "" OPENSTACK_HOST \"${KEYSTONE_SERVICE_HOST}\"
 
-    if [ "$ENABLE_IDENTITY_V2" == "False" ]; then
-        # Only Identity v3 API is available; then use it with v3 auth tokens
-        _horizon_config_set $local_settings "" OPENSTACK_API_VERSIONS {\"identity\":3}
-        _horizon_config_set $local_settings "" OPENSTACK_KEYSTONE_URL "\"${KEYSTONE_SERVICE_PROTOCOL}://${KEYSTONE_SERVICE_HOST}:${KEYSTONE_SERVICE_PORT}/v3\""
-    else
-        _horizon_config_set $local_settings "" OPENSTACK_KEYSTONE_URL "\"${KEYSTONE_SERVICE_PROTOCOL}://${KEYSTONE_SERVICE_HOST}:${KEYSTONE_SERVICE_PORT}/v2.0\""
-    fi
+    _horizon_config_set $local_settings "" OPENSTACK_API_VERSIONS {\"identity\":3}
+    _horizon_config_set $local_settings "" OPENSTACK_KEYSTONE_URL "\"${KEYSTONE_SERVICE_PROTOCOL}://${KEYSTONE_SERVICE_HOST}:${KEYSTONE_SERVICE_PORT}/v3\""
 
     if [ -f $SSL_BUNDLE_FILE ]; then
         _horizon_config_set $local_settings "" OPENSTACK_SSL_CACERT \"${SSL_BUNDLE_FILE}\"
@@ -113,7 +109,8 @@
     # Create an empty directory that apache uses as docroot
     sudo mkdir -p $HORIZON_DIR/.blackhole
 
-    local horizon_conf=$(apache_site_config_for horizon)
+    local horizon_conf
+    horizon_conf=$(apache_site_config_for horizon)
 
     # Configure apache to run horizon
     sudo sh -c "sed -e \"
@@ -122,6 +119,7 @@
         s,%HORIZON_DIR%,$HORIZON_DIR,g;
         s,%APACHE_NAME%,$APACHE_NAME,g;
         s,%DEST%,$DEST,g;
+        s,%WEBROOT%,$HORIZON_APACHE_ROOT,g;
     \" $FILES/apache-horizon.template >$horizon_conf"
 
     if is_ubuntu; then
diff --git a/lib/infra b/lib/infra
index 3d68e45..ab32efe 100644
--- a/lib/infra
+++ b/lib/infra
@@ -22,7 +22,6 @@
 # Defaults
 # --------
 GITDIR["pbr"]=$DEST/pbr
-REQUIREMENTS_DIR=$DEST/requirements
 
 # Entry Points
 # ------------
@@ -30,8 +29,6 @@
 # install_infra() - Collect source and prepare
 function install_infra {
     local PIP_VIRTUAL_ENV="$REQUIREMENTS_DIR/.venv"
-    # bring down global requirements
-    git_clone $REQUIREMENTS_REPO $REQUIREMENTS_DIR $REQUIREMENTS_BRANCH
     [ ! -d $PIP_VIRTUAL_ENV ] && virtualenv $PIP_VIRTUAL_ENV
     # We don't care about testing git pbr in the requirements venv.
     PIP_VIRTUAL_ENV=$PIP_VIRTUAL_ENV pip_install -U pbr
@@ -44,7 +41,7 @@
     # Install pbr
     if use_library_from_git "pbr"; then
         git_clone_by_name "pbr"
-        setup_lib "pbr"
+        setup_dev_lib "pbr"
     else
         # Always upgrade pbr to latest version as we may have pulled it
         # in via system packages.
diff --git a/lib/ironic b/lib/ironic
index 1323446..6a32983 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -31,6 +31,7 @@
 
 # Set up default directories
 GITDIR["python-ironicclient"]=$DEST/python-ironicclient
+GITDIR["ironic-lib"]=$DEST/ironic-lib
 
 IRONIC_DIR=$DEST/ironic
 IRONIC_PYTHON_AGENT_DIR=$DEST/ironic-python-agent
@@ -91,7 +92,7 @@
 # Use DIB to create deploy ramdisk and kernel.
 IRONIC_BUILD_DEPLOY_RAMDISK=$(trueorfalse True IRONIC_BUILD_DEPLOY_RAMDISK)
 # If not use DIB, these files are used as deploy ramdisk/kernel.
-# (The value must be a absolute path)
+# (The value must be an absolute path)
 IRONIC_DEPLOY_RAMDISK=${IRONIC_DEPLOY_RAMDISK:-}
 IRONIC_DEPLOY_KERNEL=${IRONIC_DEPLOY_KERNEL:-}
 IRONIC_DEPLOY_ELEMENT=${IRONIC_DEPLOY_ELEMENT:-deploy-ironic}
@@ -114,9 +115,6 @@
 IRONIC_SERVICE_PORT=${IRONIC_SERVICE_PORT:-6385}
 IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST:$IRONIC_SERVICE_PORT}
 
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,ironic
-
 # Enable iPXE
 IRONIC_IPXE_ENABLED=$(trueorfalse False IRONIC_IPXE_ENABLED)
 IRONIC_HTTP_DIR=${IRONIC_HTTP_DIR:-$IRONIC_DATA_DIR/httpboot}
@@ -191,6 +189,12 @@
             die $LINENO "$srv should be enabled for Ironic."
         fi
     done
+
+    if use_library_from_git "ironic-lib"; then
+        git_clone_by_name "ironic-lib"
+        setup_dev_lib "ironic-lib"
+    fi
+
     git_clone $IRONIC_REPO $IRONIC_DIR $IRONIC_BRANCH
     setup_develop $IRONIC_DIR
 
@@ -221,7 +225,8 @@
 
 # _config_ironic_apache_wsgi() - Set WSGI config files of Ironic
 function _config_ironic_apache_wsgi {
-    local ironic_apache_conf=$(apache_site_config_for ironic)
+    local ironic_apache_conf
+    ironic_apache_conf=$(apache_site_config_for ironic)
     sudo cp $FILES/apache-ironic.template $ironic_apache_conf
     sudo sed -e "
         s|%PUBLICPORT%|$IRONIC_HTTP_PORT|g;
@@ -321,11 +326,13 @@
 function configure_ironic_conductor {
     cp $IRONIC_DIR/etc/ironic/rootwrap.conf $IRONIC_ROOTWRAP_CONF
     cp -r $IRONIC_DIR/etc/ironic/rootwrap.d $IRONIC_CONF_DIR
-    local ironic_rootwrap=$(get_rootwrap_location ironic)
+    local ironic_rootwrap
+    ironic_rootwrap=$(get_rootwrap_location ironic)
     local rootwrap_isudoer_cmd="$ironic_rootwrap $IRONIC_CONF_DIR/rootwrap.conf *"
 
     # Set up the rootwrap sudoers for ironic
-    local tempfile=`mktemp`
+    local tempfile
+    tempfile=`mktemp`
     echo "$STACK_USER ALL=(root) NOPASSWD: $rootwrap_isudoer_cmd" >$tempfile
     chmod 0440 $tempfile
     sudo chown root:root $tempfile
@@ -366,7 +373,8 @@
         fi
         iniset $IRONIC_CONF_FILE glance swift_endpoint_url http://${HOST_IP}:${SWIFT_DEFAULT_BIND_PORT:-8080}
         iniset $IRONIC_CONF_FILE glance swift_api_version v1
-        local tenant_id=$(get_or_create_project $SERVICE_TENANT_NAME default)
+        local tenant_id
+        tenant_id=$(get_or_create_project $SERVICE_TENANT_NAME default)
         iniset $IRONIC_CONF_FILE glance swift_account AUTH_${tenant_id}
         iniset $IRONIC_CONF_FILE glance swift_container glance
         iniset $IRONIC_CONF_FILE glance swift_temp_url_duration 3600
@@ -375,7 +383,8 @@
     fi
 
     if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
-        local pxebin=`basename $IRONIC_PXE_BOOT_IMAGE`
+        local pxebin
+        pxebin=`basename $IRONIC_PXE_BOOT_IMAGE`
         iniset $IRONIC_CONF_FILE pxe ipxe_enabled True
         iniset $IRONIC_CONF_FILE pxe pxe_config_template '\$pybasedir/drivers/modules/ipxe_config.template'
         iniset $IRONIC_CONF_FILE pxe pxe_bootfile_name $pxebin
@@ -441,7 +450,8 @@
 # _ironic_bm_vm_names() - Generates list of names for baremetal VMs.
 function _ironic_bm_vm_names {
     local idx
-    local num_vms=$(($IRONIC_VM_COUNT - 1))
+    local num_vms
+    num_vms=$(($IRONIC_VM_COUNT - 1))
     for idx in $(seq 0 $num_vms); do
         echo "baremetal${IRONIC_VM_NETWORK_BRIDGE}_${idx}"
     done
@@ -494,22 +504,22 @@
 }
 
 function create_ovs_taps {
-    local ironic_net_id=$(neutron net-list | grep private | get_field 1)
+    local ironic_net_id
+    ironic_net_id=$(neutron net-list | grep private | get_field 1)
 
     # Work around: No netns exists on host until a Neutron port is created.  We
     # need to create one in Neutron to know what netns to tap into prior to the
     # first node booting.
-    local port_id=$(neutron port-create private | grep " id " | get_field 2)
+    local port_id
+    port_id=$(neutron port-create private | grep " id " | get_field 2)
 
     # intentional sleep to make sure the tag has been set to port
     sleep 10
 
-    if  [[ "$Q_USE_NAMESPACE" = "True" ]]; then
-        local tapdev=$(sudo ip netns exec qdhcp-${ironic_net_id} ip link list | grep " tap" | cut -d':' -f2 | cut -b2-)
-    else
-        local tapdev=$(sudo ip link list | grep " tap" | cut -d':' -f2 | cut -b2-)
-    fi
-    local tag_id=$(sudo ovs-vsctl show |grep ${tapdev} -A1 -m1 | grep tag | cut -d':' -f2 | cut -b2-)
+    local tapdev
+    tapdev=$(sudo ip netns exec qdhcp-${ironic_net_id} ip link list | grep " tap" | cut -d':' -f2 | cut -d'@' -f1 | cut -b2-)
+    local tag_id
+    tag_id=$(sudo ovs-vsctl show |grep ${tapdev} -A1 -m1 | grep tag | cut -d':' -f2 | cut -b2-)
 
     # make sure veth pair is not existing, otherwise delete its links
     sudo ip link show ovs-tap1 && sudo ip link delete ovs-tap1
@@ -555,6 +565,7 @@
     # timing out.
     local resource=$1
     local expected_count=$2
+    local i
     echo_summary "Waiting 2 minutes for Nova resource tracker to pick up $resource >= $expected_count"
     for i in $(seq 1 120); do
         if [ $(nova hypervisor-stats | grep " $resource " | get_field 2) -ge $expected_count ]; then
@@ -566,7 +577,8 @@
 }
 
 function enroll_nodes {
-    local chassis_id=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
+    local chassis_id
+    chassis_id=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
 
     if ! is_ironic_hardware; then
         local ironic_node_cpu=$IRONIC_VM_SPECS_CPU
@@ -598,10 +610,14 @@
         if ! is_ironic_hardware; then
             local mac_address=$hardware_info
         elif [[ -z "${IRONIC_DEPLOY_DRIVER##*_ipmitool}" ]]; then
-            local ipmi_address=$(echo $hardware_info |awk  '{print $1}')
-            local mac_address=$(echo $hardware_info |awk '{print $2}')
-            local ironic_ipmi_username=$(echo $hardware_info |awk '{print $3}')
-            local ironic_ipmi_passwd=$(echo $hardware_info |awk '{print $4}')
+            local ipmi_address
+            ipmi_address=$(echo $hardware_info |awk  '{print $1}')
+            local mac_address
+            mac_address=$(echo $hardware_info |awk '{print $2}')
+            local ironic_ipmi_username
+            ironic_ipmi_username=$(echo $hardware_info |awk '{print $3}')
+            local ironic_ipmi_passwd
+            ironic_ipmi_passwd=$(echo $hardware_info |awk '{print $4}')
             # Currently we require all hardware platform have same CPU/RAM/DISK info
             # in future, this can be enhanced to support different type, and then
             # we create the bare metal flavor with minimum value
@@ -613,11 +629,16 @@
 
         # First node created will be used for testing in ironic w/o glance
         # scenario, so we need to know its UUID.
-        local standalone_node_uuid=$([ $total_nodes -eq 0 ] && echo "--uuid $IRONIC_NODE_UUID")
+        local standalone_node_uuid=""
+        if [ $total_nodes -eq 0 ]; then
+            standalone_node_uuid="--uuid $IRONIC_NODE_UUID"
+        fi
 
-        local node_id=$(ironic node-create $standalone_node_uuid\
+        local node_id
+        node_id=$(ironic node-create $standalone_node_uuid\
             --chassis_uuid $chassis_id \
             --driver $IRONIC_DEPLOY_DRIVER \
+            --name node-$total_nodes \
             -p cpus=$ironic_node_cpu\
             -p memory_mb=$ironic_node_ram\
             -p local_gb=$ironic_node_disk\
@@ -635,7 +656,8 @@
     # NOTE(adam_g): Attempting to use an autogenerated UUID for flavor id here uncovered
     # bug (LP: #1333852) in Trove.  This can be changed to use an auto flavor id when the
     # bug is fixed in Juno.
-    local adjusted_disk=$(($ironic_node_disk - $ironic_ephemeral_disk))
+    local adjusted_disk
+    adjusted_disk=$(($ironic_node_disk - $ironic_ephemeral_disk))
     nova flavor-create --ephemeral $ironic_ephemeral_disk baremetal 551 $ironic_node_ram $adjusted_disk $ironic_node_cpu
 
     nova flavor-key baremetal set "cpu_arch"="x86_64"
@@ -650,6 +672,8 @@
     # enable tftp natting for allowing connections to HOST_IP's tftp server
     sudo modprobe nf_conntrack_tftp
     sudo modprobe nf_nat_tftp
+    # explicitly allow DHCP - packets are occasionally being dropped here
+    sudo iptables -I INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT || true
     # nodes boot from TFTP and callback to the API server listening on $HOST_IP
     sudo iptables -I INPUT -d $HOST_IP -p udp --dport 69 -j ACCEPT || true
     sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true
@@ -766,13 +790,14 @@
         fi
     fi
 
-    local token=$(openstack token issue -c id -f value)
+    local token
+    token=$(openstack token issue -c id -f value)
     die_if_not_set $LINENO token "Keystone fail to get token"
 
     # load them into glance
     IRONIC_DEPLOY_KERNEL_ID=$(openstack \
         --os-token $token \
-        --os-url http://$GLANCE_HOSTPORT \
+        --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT \
         image create \
         $(basename $IRONIC_DEPLOY_KERNEL_PATH) \
         --public --disk-format=aki \
@@ -780,7 +805,7 @@
         < $IRONIC_DEPLOY_KERNEL_PATH  | grep ' id ' | get_field 2)
     IRONIC_DEPLOY_RAMDISK_ID=$(openstack \
         --os-token $token \
-        --os-url http://$GLANCE_HOSTPORT \
+        --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT \
         image create \
         $(basename $IRONIC_DEPLOY_RAMDISK_PATH) \
         --public --disk-format=ari \
@@ -804,7 +829,8 @@
 function cleanup_baremetal_basic_ops {
     rm -f $IRONIC_VM_MACS_CSV_FILE
     if [ -f $IRONIC_KEY_FILE ]; then
-        local key=$(cat $IRONIC_KEY_FILE.pub)
+        local key
+        key=$(cat $IRONIC_KEY_FILE.pub)
         # remove public key from authorized_keys
         grep -v "$key" $IRONIC_AUTHORIZED_KEYS_FILE > temp && mv temp $IRONIC_AUTHORIZED_KEYS_FILE
         chmod 0600 $IRONIC_AUTHORIZED_KEYS_FILE
diff --git a/lib/keystone b/lib/keystone
index e2448c9..5a2afbf 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -51,11 +51,6 @@
 KEYSTONE_CONF_DIR=${KEYSTONE_CONF_DIR:-/etc/keystone}
 KEYSTONE_CONF=$KEYSTONE_CONF_DIR/keystone.conf
 KEYSTONE_PASTE_INI=${KEYSTONE_PASTE_INI:-$KEYSTONE_CONF_DIR/keystone-paste.ini}
-if is_suse; then
-    KEYSTONE_WSGI_DIR=${KEYSTONE_WSGI_DIR:-/srv/www/htdocs/keystone}
-else
-    KEYSTONE_WSGI_DIR=${KEYSTONE_WSGI_DIR:-/var/www/keystone}
-fi
 
 # Set up additional extensions, such as oauth1, federation
 # Example of KEYSTONE_EXTENSIONS=oauth1,federation
@@ -132,15 +127,13 @@
 
 # _cleanup_keystone_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
 function _cleanup_keystone_apache_wsgi {
-    sudo rm -f $KEYSTONE_WSGI_DIR/*
     sudo rm -f $(apache_site_config_for keystone)
 }
 
 # _config_keystone_apache_wsgi() - Set WSGI config files of Keystone
 function _config_keystone_apache_wsgi {
-    sudo mkdir -p $KEYSTONE_WSGI_DIR
-
-    local keystone_apache_conf=$(apache_site_config_for keystone)
+    local keystone_apache_conf
+    keystone_apache_conf=$(apache_site_config_for keystone)
     local keystone_ssl=""
     local keystone_certfile=""
     local keystone_keyfile=""
@@ -161,22 +154,17 @@
         venv_path="python-path=${PROJECT_VENV["keystone"]}/lib/$(python_version)/site-packages"
     fi
 
-    # copy proxy vhost and wsgi file
-    sudo cp $KEYSTONE_DIR/httpd/keystone.py $KEYSTONE_WSGI_DIR/main
-    sudo cp $KEYSTONE_DIR/httpd/keystone.py $KEYSTONE_WSGI_DIR/admin
-
     sudo cp $FILES/apache-keystone.template $keystone_apache_conf
     sudo sed -e "
         s|%PUBLICPORT%|$keystone_service_port|g;
         s|%ADMINPORT%|$keystone_auth_port|g;
         s|%APACHE_NAME%|$APACHE_NAME|g;
-        s|%PUBLICWSGI%|$KEYSTONE_WSGI_DIR/main|g;
-        s|%ADMINWSGI%|$KEYSTONE_WSGI_DIR/admin|g;
         s|%SSLENGINE%|$keystone_ssl|g;
         s|%SSLCERTFILE%|$keystone_certfile|g;
         s|%SSLKEYFILE%|$keystone_keyfile|g;
         s|%USER%|$STACK_USER|g;
         s|%VIRTUALENV%|$venv_path|g
+        s|%KEYSTONE_BIN%|$KEYSTONE_BIN_DIR|g
     " -i $keystone_apache_conf
 }
 
@@ -266,9 +254,9 @@
 
         # Add swift endpoints to service catalog if swift is enabled
         if is_service_enabled s-proxy; then
-            echo "catalog.RegionOne.object_store.publicURL = http://%SERVICE_HOST%:8080/v1/AUTH_\$(tenant_id)s" >> $KEYSTONE_CATALOG
-            echo "catalog.RegionOne.object_store.adminURL = http://%SERVICE_HOST%:8080/" >> $KEYSTONE_CATALOG
-            echo "catalog.RegionOne.object_store.internalURL = http://%SERVICE_HOST%:8080/v1/AUTH_\$(tenant_id)s" >> $KEYSTONE_CATALOG
+            echo "catalog.RegionOne.object_store.publicURL = http://%SERVICE_HOST%:$SWIFT_DEFAULT_BIND_PORT/v1/AUTH_\$(tenant_id)s" >> $KEYSTONE_CATALOG
+            echo "catalog.RegionOne.object_store.adminURL = http://%SERVICE_HOST%:$SWIFT_DEFAULT_BIND_PORT/" >> $KEYSTONE_CATALOG
+            echo "catalog.RegionOne.object_store.internalURL = http://%SERVICE_HOST%:$SWIFT_DEFAULT_BIND_PORT/v1/AUTH_\$(tenant_id)s" >> $KEYSTONE_CATALOG
             echo "catalog.RegionOne.object_store.name = Swift Service" >> $KEYSTONE_CATALOG
         fi
 
@@ -353,16 +341,19 @@
 # Group                Users      Roles                 Tenant
 # ------------------------------------------------------------------
 # admins               admin      admin                 admin
-# nonadmin             demo       Member, anotherrole   demo
+# nonadmins            demo       Member, anotherrole   demo
 
 
 # Migrated from keystone_data.sh
 function create_keystone_accounts {
 
     # admin
-    local admin_tenant=$(get_or_create_project "admin" default)
-    local admin_user=$(get_or_create_user "admin" "$ADMIN_PASSWORD" default)
-    local admin_role=$(get_or_create_role "admin")
+    local admin_tenant
+    admin_tenant=$(get_or_create_project "admin" default)
+    local admin_user
+    admin_user=$(get_or_create_user "admin" "$ADMIN_PASSWORD" default)
+    local admin_role
+    admin_role=$(get_or_create_role "admin")
     get_or_add_user_project_role $admin_role $admin_user $admin_tenant
 
     # Create service project/role
@@ -378,18 +369,23 @@
     get_or_create_role ResellerAdmin
 
     # The Member role is used by Horizon and Swift so we need to keep it:
-    local member_role=$(get_or_create_role "Member")
+    local member_role
+    member_role=$(get_or_create_role "Member")
 
     # another_role demonstrates that an arbitrary role may be created and used
     # TODO(sleepsonthefloor): show how this can be used for rbac in the future!
-    local another_role=$(get_or_create_role "anotherrole")
+    local another_role
+    another_role=$(get_or_create_role "anotherrole")
 
     # invisible tenant - admin can't see this one
-    local invis_tenant=$(get_or_create_project "invisible_to_admin" default)
+    local invis_tenant
+    invis_tenant=$(get_or_create_project "invisible_to_admin" default)
 
     # demo
-    local demo_tenant=$(get_or_create_project "demo" default)
-    local demo_user=$(get_or_create_user "demo" \
+    local demo_tenant
+    demo_tenant=$(get_or_create_project "demo" default)
+    local demo_user
+    demo_user=$(get_or_create_user "demo" \
         "$ADMIN_PASSWORD" "default" "demo@example.com")
 
     get_or_add_user_project_role $member_role $demo_user $demo_tenant
@@ -397,9 +393,11 @@
     get_or_add_user_project_role $another_role $demo_user $demo_tenant
     get_or_add_user_project_role $member_role $demo_user $invis_tenant
 
-    local admin_group=$(get_or_create_group "admins" \
+    local admin_group
+    admin_group=$(get_or_create_group "admins" \
         "default" "openstack admin group")
-    local non_admin_group=$(get_or_create_group "nonadmins" \
+    local non_admin_group
+    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
@@ -428,7 +426,8 @@
 function create_service_user {
     local role=${2:-service}
 
-    local user=$(get_or_create_user "$1" "$SERVICE_PASSWORD" default)
+    local user
+    user=$(get_or_create_user "$1" "$SERVICE_PASSWORD" default)
     get_or_add_user_project_role "$role" "$user" "$SERVICE_TENANT_NAME"
 }
 
diff --git a/lib/ldap b/lib/ldap
index d2dbc3b..0414fea 100644
--- a/lib/ldap
+++ b/lib/ldap
@@ -82,7 +82,8 @@
 function init_ldap {
     local keystone_ldif
 
-    local tmp_ldap_dir=$(mktemp -d -t ldap.$$.XXXXXXXXXX)
+    local tmp_ldap_dir
+    tmp_ldap_dir=$(mktemp -d -t ldap.$$.XXXXXXXXXX)
 
     # Remove data but not schemas
     clear_ldap_state
@@ -113,7 +114,8 @@
     echo "Installing LDAP inside function"
     echo "os_VENDOR is $os_VENDOR"
 
-    local tmp_ldap_dir=$(mktemp -d -t ldap.$$.XXXXXXXXXX)
+    local tmp_ldap_dir
+    tmp_ldap_dir=$(mktemp -d -t ldap.$$.XXXXXXXXXX)
 
     printf "installing OpenLDAP"
     if is_ubuntu; then
@@ -129,7 +131,8 @@
     fi
 
     echo "LDAP_PASSWORD is $LDAP_PASSWORD"
-    local slappass=$(slappasswd -s $LDAP_PASSWORD)
+    local slappass
+    slappass=$(slappasswd -s $LDAP_PASSWORD)
     printf "LDAP secret is $slappass\n"
 
     # Create manager.ldif and add to olcdb
diff --git a/lib/lvm b/lib/lvm
index 8afd543..468a99a 100644
--- a/lib/lvm
+++ b/lib/lvm
@@ -56,7 +56,8 @@
 
     # If the backing physical device is a loop device, it was probably setup by DevStack
     if [[ -n "$backing_file" ]] && [[ -e "$backing_file" ]]; then
-        local vg_dev=$(sudo losetup -j $backing_file | awk -F':' '/'$BACKING_FILE_SUFFIX'/ { print $1}')
+        local vg_dev
+        vg_dev=$(sudo losetup -j $backing_file | awk -F':' '/'$BACKING_FILE_SUFFIX'/ { print $1}')
         sudo losetup -d $vg_dev
         rm -f $backing_file
     fi
@@ -89,7 +90,8 @@
     if ! sudo vgs $vg; then
         # Only create if the file doesn't already exists
         [[ -f $backing_file ]] || truncate -s $size $backing_file
-        local vg_dev=`sudo losetup -f --show $backing_file`
+        local vg_dev
+        vg_dev=`sudo losetup -f --show $backing_file`
 
         # Only create volume group if it doesn't already exist
         if ! sudo vgs $vg; then
diff --git a/lib/neutron-legacy b/lib/neutron-legacy
index 2c9dd1a..978943d 100644
--- a/lib/neutron-legacy
+++ b/lib/neutron-legacy
@@ -144,8 +144,6 @@
 Q_ADMIN_USERNAME=${Q_ADMIN_USERNAME:-neutron}
 # Default auth strategy
 Q_AUTH_STRATEGY=${Q_AUTH_STRATEGY:-keystone}
-# Use namespace or not
-Q_USE_NAMESPACE=${Q_USE_NAMESPACE:-True}
 # RHEL's support for namespaces requires using veths with ovs
 Q_OVS_USE_VETH=${Q_OVS_USE_VETH:-False}
 Q_USE_ROOTWRAP=${Q_USE_ROOTWRAP:-True}
@@ -158,8 +156,6 @@
 Q_USE_DEBUG_COMMAND=${Q_USE_DEBUG_COMMAND:-False}
 # The name of the default q-l3 router
 Q_ROUTER_NAME=${Q_ROUTER_NAME:-router1}
-# nova vif driver that all plugins should use
-NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtGenericVIFDriver"}
 Q_NOTIFY_NOVA_PORT_STATUS_CHANGES=${Q_NOTIFY_NOVA_PORT_STATUS_CHANGES:-True}
 Q_NOTIFY_NOVA_PORT_DATA_CHANGES=${Q_NOTIFY_NOVA_PORT_DATA_CHANGES:-True}
 VIF_PLUGGING_IS_FATAL=${VIF_PLUGGING_IS_FATAL:-True}
@@ -210,7 +206,7 @@
 # The plugin supports L3.
 Q_L3_ENABLED=${Q_L3_ENABLED:-False}
 # L3 routers exist per tenant
-Q_L3_ROUTER_PER_TENANT=${Q_L3_ROUTER_PER_TENANT:-False}
+Q_L3_ROUTER_PER_TENANT=${Q_L3_ROUTER_PER_TENANT:-True}
 
 # List of config file names in addition to the main plugin config file
 # See _configure_neutron_common() for details about setting it up
@@ -260,7 +256,7 @@
 
 # If using GRE tunnels for tenant networks, specify the range of
 # tunnel IDs from which tenant networks are allocated. Can be
-# overriden in ``localrc`` in necesssary.
+# overridden in ``localrc`` in necessary.
 TENANT_TUNNEL_RANGES=${TENANT_TUNNEL_RANGES:-1:1000}
 
 # To use VLANs for tenant networks, set to True in localrc. VLANs
@@ -328,7 +324,9 @@
 # ---------------------------------
 
 # Please refer to ``lib/neutron_plugins/README.md`` for details.
-source $TOP_DIR/lib/neutron_plugins/$Q_PLUGIN
+if [ -f $TOP_DIR/lib/neutron_plugins/$Q_PLUGIN ]; then
+    source $TOP_DIR/lib/neutron_plugins/$Q_PLUGIN
+fi
 
 # Agent loadbalancer service plugin functions
 # -------------------------------------------
@@ -358,10 +356,6 @@
     Q_USE_SECGROUP=False
 fi
 
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,neutron
-
-
 # Save trace setting
 XTRACE=$(set +o | grep xtrace)
 set +o xtrace
@@ -471,11 +465,15 @@
 
 function create_nova_conf_neutron {
     iniset $NOVA_CONF DEFAULT network_api_class "nova.network.neutronv2.api.API"
-    iniset $NOVA_CONF neutron admin_username "$Q_ADMIN_USERNAME"
-    iniset $NOVA_CONF neutron admin_password "$SERVICE_PASSWORD"
-    iniset $NOVA_CONF neutron admin_auth_url "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_AUTH_PORT/v2.0"
+
+    iniset $NOVA_CONF neutron auth_plugin "v3password"
+    iniset $NOVA_CONF neutron auth_url "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_AUTH_PORT/v3"
+    iniset $NOVA_CONF neutron username "$Q_ADMIN_USERNAME"
+    iniset $NOVA_CONF neutron password "$SERVICE_PASSWORD"
+    iniset $NOVA_CONF neutron user_domain_name "Default"
+    iniset $NOVA_CONF neutron project_name "$SERVICE_TENANT_NAME"
+    iniset $NOVA_CONF neutron project_domain_name "Default"
     iniset $NOVA_CONF neutron auth_strategy "$Q_AUTH_STRATEGY"
-    iniset $NOVA_CONF neutron admin_tenant_name "$SERVICE_TENANT_NAME"
     iniset $NOVA_CONF neutron region_name "$REGION_NAME"
     iniset $NOVA_CONF neutron url "${Q_PROTOCOL}://$Q_HOST:$Q_PORT"
 
@@ -485,11 +483,9 @@
         iniset $NOVA_CONF DEFAULT security_group_api neutron
     fi
 
-    # set NOVA_VIF_DRIVER and optionally set options in nova_conf
+    # optionally set options in nova_conf
     neutron_plugin_create_nova_conf
 
-    iniset $NOVA_CONF libvirt vif_driver "$NOVA_VIF_DRIVER"
-    iniset $NOVA_CONF DEFAULT linuxnet_interface_driver "$LINUXNET_VIF_DRIVER"
     if is_service_enabled q-meta; then
         iniset $NOVA_CONF neutron service_metadata_proxy "True"
     fi
@@ -540,17 +536,17 @@
 
     if is_provider_network; then
         die_if_not_set $LINENO PHYSICAL_NETWORK "You must specify the PHYSICAL_NETWORK"
-        die_if_not_set $LINENO PROVIDER_NETWORK_TYPE "You must specifiy the PROVIDER_NETWORK_TYPE"
+        die_if_not_set $LINENO PROVIDER_NETWORK_TYPE "You must specify the PROVIDER_NETWORK_TYPE"
         NET_ID=$(neutron net-create $PHYSICAL_NETWORK --tenant_id $TENANT_ID --provider:network_type $PROVIDER_NETWORK_TYPE --provider:physical_network "$PHYSICAL_NETWORK" ${SEGMENTATION_ID:+--provider:segmentation_id $SEGMENTATION_ID} --shared | grep ' id ' | get_field 2)
         die_if_not_set $LINENO NET_ID "Failure creating NET_ID for $PHYSICAL_NETWORK $TENANT_ID"
 
         if [[ "$IP_VERSION" =~ 4.* ]]; then
-            SUBNET_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 4 ${ALLOCATION_POOL:+--allocation-pool $ALLOCATION_POOL} --name $PROVIDER_SUBNET_NAME --gateway $NETWORK_GATEWAY $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
+            SUBNET_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 4 ${ALLOCATION_POOL:+--allocation-pool $ALLOCATION_POOL} --name $PROVIDER_SUBNET_NAME --gateway $NETWORK_GATEWAY --subnetpool None $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
             die_if_not_set $LINENO SUBNET_ID "Failure creating SUBNET_ID for $PROVIDER_SUBNET_NAME $TENANT_ID"
         fi
 
         if [[ "$IP_VERSION" =~ .*6 ]]; then
-            SUBNET_V6_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 6 --ipv6-address-mode slaac --gateway $V6_NETWORK_GATEWAY --name $PROVIDER_SUBNET_NAME_V6 $NET_ID $FIXED_RANGE_V6 | grep 'id' | get_field 2)
+            SUBNET_V6_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 6 --ipv6-address-mode $IPV6_ADDRESS_MODE --gateway $V6_NETWORK_GATEWAY --name $PROVIDER_SUBNET_NAME_V6 --subnetpool_id None $NET_ID $FIXED_RANGE_V6 | grep 'id' | get_field 2)
             die_if_not_set $LINENO SUBNET_V6_ID "Failure creating SUBNET_V6_ID for $PROVIDER_SUBNET_NAME_V6 $TENANT_ID"
         fi
 
@@ -609,16 +605,6 @@
     recreate_database $Q_DB_NAME
     # 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
@@ -652,7 +638,7 @@
         plugin_dir=$($ssh_dom0 "$xen_functions; set -eux; xapi_plugin_location")
 
         # install neutron plugins to dom0
-        tar -czf - -C $NEUTRON_DIR/neutron/plugins/openvswitch/agent/xenapi/etc/xapi.d/plugins/ ./ |
+        tar -czf - -C $NEUTRON_DIR/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/ ./ |
             $ssh_dom0 "tar -xzf - -C $plugin_dir && chmod a+x $plugin_dir/*"
     fi
 }
@@ -692,7 +678,7 @@
         service_protocol="http"
     fi
     # Start the Neutron service
-    run_process q-svc "python $NEUTRON_BIN_DIR/neutron-server $cfg_file_options"
+    run_process q-svc "$NEUTRON_BIN_DIR/neutron-server $cfg_file_options"
     echo "Waiting for Neutron to start..."
     if is_ssl_enabled_service "neutron"; then
         ssl_ca="--ca-certificate=${SSL_BUNDLE_FILE}"
@@ -710,7 +696,7 @@
 # Control of the l2 agent is separated out to make it easier to test partial
 # upgrades (everything upgraded except the L2 agent)
 function start_neutron_l2_agent {
-    run_process q-agt "python $AGENT_BINARY --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE"
+    run_process q-agt "$AGENT_BINARY --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE"
 
     if is_provider_network; then
         sudo ovs-vsctl --no-wait -- --may-exist add-port $OVS_PHYSICAL_BRIDGE $PUBLIC_INTERFACE
@@ -728,21 +714,23 @@
 }
 
 function start_neutron_other_agents {
-    run_process q-dhcp "python $AGENT_DHCP_BINARY --config-file $NEUTRON_CONF --config-file=$Q_DHCP_CONF_FILE"
+    run_process q-dhcp "$AGENT_DHCP_BINARY --config-file $NEUTRON_CONF --config-file=$Q_DHCP_CONF_FILE"
 
-    if is_service_enabled q-vpn; then
+    if is_service_enabled neutron-vpnaas; then
+        :  # Started by plugin
+    elif is_service_enabled q-vpn; then
         run_process q-vpn "$AGENT_VPN_BINARY $(determine_config_files neutron-vpn-agent)"
     else
-        run_process q-l3 "python $AGENT_L3_BINARY $(determine_config_files neutron-l3-agent)"
+        run_process q-l3 "$AGENT_L3_BINARY $(determine_config_files neutron-l3-agent)"
     fi
 
-    run_process q-meta "python $AGENT_META_BINARY --config-file $NEUTRON_CONF --config-file=$Q_META_CONF_FILE"
-    run_process q-lbaas "python $AGENT_LBAAS_BINARY --config-file $NEUTRON_CONF --config-file=$LBAAS_AGENT_CONF_FILENAME"
-    run_process q-metering "python $AGENT_METERING_BINARY --config-file $NEUTRON_CONF --config-file $METERING_AGENT_CONF_FILENAME"
+    run_process q-meta "$AGENT_META_BINARY --config-file $NEUTRON_CONF --config-file=$Q_META_CONF_FILE"
+    run_process q-lbaas "$AGENT_LBAAS_BINARY --config-file $NEUTRON_CONF --config-file=$LBAAS_AGENT_CONF_FILENAME"
+    run_process q-metering "$AGENT_METERING_BINARY --config-file $NEUTRON_CONF --config-file $METERING_AGENT_CONF_FILENAME"
 
     if [ "$VIRT_DRIVER" = 'xenserver' ]; then
         # For XenServer, start an agent for the domU openvswitch
-        run_process q-domua "python $AGENT_BINARY --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE.domU"
+        run_process q-domua "$AGENT_BINARY --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE.domU"
     fi
 }
 
@@ -793,7 +781,8 @@
 }
 
 # _move_neutron_addresses_route() - Move the primary IP to the OVS bridge
-# on startup, or back to the public interface on cleanup
+# on startup, or back to the public interface on cleanup. If no IP is
+# configured on the interface, just add it as a port to the OVS bridge.
 function _move_neutron_addresses_route {
     local from_intf=$1
     local to_intf=$2
@@ -806,27 +795,28 @@
         # on configure we will also add $from_intf as a port on $to_intf,
         # assuming it is an OVS bridge.
 
-        local IP_BRD=$(ip -f $af a s dev $from_intf | awk '/inet/ { print $2, $3, $4; exit }')
-        local DEFAULT_ROUTE_GW=$(ip r | awk "/default.+$from_intf/ { print \$3; exit }")
+        local IP_ADD=""
+        local IP_DEL=""
+        local DEFAULT_ROUTE_GW
+        DEFAULT_ROUTE_GW=$(ip -f $af r | awk "/default.+$from_intf/ { print \$3; exit }")
         local ADD_OVS_PORT=""
 
-        if [[ $af == "inet" ]]; then
-            IP_BRD=$(ip -f $af a s dev $from_intf | grep $HOST_IP | awk '{ print $2, $3, $4; exit }')
-        fi
-
-        if [[ $af == "inet6" ]]; then
-            IP_BRD=$(ip -f $af a s dev $from_intf | grep $HOST_IPV6 | awk '{ print $2, $3, $4; exit }')
-        fi
+        IP_BRD=$(ip -f $af a s dev $from_intf scope global primary | grep inet | awk '{ print $2, $3, $4; exit }')
 
         if [ "$DEFAULT_ROUTE_GW" != "" ]; then
-            ADD_DEFAULT_ROUTE="sudo ip r replace default via $DEFAULT_ROUTE_GW dev $to_intf"
+            ADD_DEFAULT_ROUTE="sudo ip -f $af r replace default via $DEFAULT_ROUTE_GW dev $to_intf"
         fi
 
         if [[ "$add_ovs_port" == "True" ]]; then
             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
+        if [[ "$IP_BRD" != "" ]]; then
+            IP_DEL="sudo ip addr del $IP_BRD dev $from_intf"
+            IP_ADD="sudo ip addr add $IP_BRD dev $to_intf"
+        fi
+
+        $IP_DEL; $IP_ADD; $ADD_OVS_PORT; $ADD_DEFAULT_ROUTE
     fi
 }
 
@@ -834,20 +824,24 @@
 # runs that a clean run would need to clean up
 function cleanup_neutron {
 
-    if [[ $(ip -f inet a s dev "$OVS_PHYSICAL_BRIDGE" | grep -c 'global') != 0 ]]; then
+    if [[ -n "$OVS_PHYSICAL_BRIDGE" ]]; then
         _move_neutron_addresses_route "$OVS_PHYSICAL_BRIDGE" "$PUBLIC_INTERFACE" False "inet"
-    fi
 
-    if [[ $(ip -f inet6 a s dev "$OVS_PHYSICAL_BRIDGE" | grep -c 'global') != 0 ]]; then
-        _move_neutron_addresses_route "$OVS_PHYSICAL_BRIDGE" "$PUBLIC_INTERFACE" False "inet6"
-    fi
+        if [[ $(ip -f inet6 a s dev "$OVS_PHYSICAL_BRIDGE" | grep -c 'global') != 0 ]]; then
+            # ip(8) wants the prefix length when deleting
+            local v6_gateway
+            v6_gateway=$(ip -6 a s dev $OVS_PHYSICAL_BRIDGE | grep $IPV6_PUBLIC_NETWORK_GATEWAY | awk '{ print $2 }')
+            sudo ip -6 addr del $v6_gateway dev $OVS_PHYSICAL_BRIDGE
+            _move_neutron_addresses_route "$OVS_PHYSICAL_BRIDGE" "$PUBLIC_INTERFACE" False "inet6"
+        fi
 
-    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
-        done
-        sudo route del -net $FIXED_RANGE gw $NETWORK_GATEWAY dev $OVS_PHYSICAL_BRIDGE
+        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
+            done
+            sudo route del -net $FIXED_RANGE gw $NETWORK_GATEWAY dev $OVS_PHYSICAL_BRIDGE
+        fi
     fi
 
     if is_neutron_ovs_base_plugin; then
@@ -878,6 +872,12 @@
 
     cp $NEUTRON_DIR/etc/neutron.conf $NEUTRON_CONF
 
+    Q_POLICY_FILE=$NEUTRON_CONF_DIR/policy.json
+    cp $NEUTRON_DIR/etc/policy.json $Q_POLICY_FILE
+
+    # allow neutron user to administer neutron to match neutron account
+    sed -i 's/"context_is_admin":  "role:admin"/"context_is_admin":  "role:admin or user_name:neutron"/g' $Q_POLICY_FILE
+
     # Set plugin-specific variables ``Q_DB_NAME``, ``Q_PLUGIN_CLASS``.
     # For main plugin config file, set ``Q_PLUGIN_CONF_PATH``, ``Q_PLUGIN_CONF_FILENAME``.
     # For addition plugin config files, set ``Q_PLUGIN_EXTRA_CONF_PATH``,
@@ -893,7 +893,11 @@
     # If needed, move config file from ``$NEUTRON_DIR/etc/neutron`` to ``NEUTRON_CONF_DIR``
     mkdir -p /$Q_PLUGIN_CONF_PATH
     Q_PLUGIN_CONF_FILE=$Q_PLUGIN_CONF_PATH/$Q_PLUGIN_CONF_FILENAME
-    cp $NEUTRON_DIR/$Q_PLUGIN_CONF_FILE /$Q_PLUGIN_CONF_FILE
+    # NOTE(hichihara): Some neutron vendor plugins were already decomposed and
+    # there is no config file in Neutron tree. They should prepare the file in each plugin.
+    if [ -f $NEUTRON_DIR/$Q_PLUGIN_CONF_FILE ]; then
+        cp $NEUTRON_DIR/$Q_PLUGIN_CONF_FILE /$Q_PLUGIN_CONF_FILE
+    fi
 
     iniset $NEUTRON_CONF database connection `database_connection_url $Q_DB_NAME`
     iniset $NEUTRON_CONF DEFAULT state_path $DATA_DIR/neutron
@@ -959,10 +963,9 @@
 
     iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT verbose False
     iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT debug False
-    iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT use_namespaces $Q_USE_NAMESPACE
-    iniset $NEUTRON_TEST_CONFIG_FILE agent root_helper "$Q_RR_COMMAND"
+    iniset $NEUTRON_TEST_CONFIG_FILE AGENT root_helper "$Q_RR_COMMAND"
     if [[ "$Q_USE_ROOTWRAP_DAEMON" == "True" ]]; then
-        iniset $NEUTRON_TEST_CONFIG_FILE agent root_helper_daemon "$Q_RR_DAEMON_COMMAND"
+        iniset $NEUTRON_TEST_CONFIG_FILE AGENT root_helper_daemon "$Q_RR_DAEMON_COMMAND"
     fi
 
     _neutron_setup_interface_driver $NEUTRON_TEST_CONFIG_FILE
@@ -976,10 +979,9 @@
 
     iniset $Q_DHCP_CONF_FILE DEFAULT verbose True
     iniset $Q_DHCP_CONF_FILE DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
-    iniset $Q_DHCP_CONF_FILE DEFAULT use_namespaces $Q_USE_NAMESPACE
-    iniset $Q_DHCP_CONF_FILE DEFAULT root_helper "$Q_RR_COMMAND"
+    iniset $Q_DHCP_CONF_FILE AGENT root_helper "$Q_RR_COMMAND"
     if [[ "$Q_USE_ROOTWRAP_DAEMON" == "True" ]]; then
-        iniset $NEUTRON_TEST_CONFIG_FILE agent root_helper_daemon "$Q_RR_DAEMON_COMMAND"
+        iniset $Q_DHCP_CONF_FILE AGENT root_helper_daemon "$Q_RR_DAEMON_COMMAND"
     fi
 
     if ! is_service_enabled q-l3; then
@@ -999,10 +1001,7 @@
 }
 
 function _configure_neutron_l3_agent {
-    local cfg_file
     Q_L3_ENABLED=True
-    # for l3-agent, only use per tenant router if we have namespaces
-    Q_L3_ROUTER_PER_TENANT=$Q_USE_NAMESPACE
 
     if is_service_enabled q-vpn; then
         neutron_vpn_configure_agent
@@ -1012,19 +1011,16 @@
 
     iniset $Q_L3_CONF_FILE DEFAULT verbose True
     iniset $Q_L3_CONF_FILE DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
-    iniset $Q_L3_CONF_FILE DEFAULT use_namespaces $Q_USE_NAMESPACE
-    iniset $Q_L3_CONF_FILE DEFAULT root_helper "$Q_RR_COMMAND"
+    iniset $Q_L3_CONF_FILE AGENT root_helper "$Q_RR_COMMAND"
     if [[ "$Q_USE_ROOTWRAP_DAEMON" == "True" ]]; then
-        iniset $Q_L3_CONF_FILE agent root_helper_daemon "$Q_RR_DAEMON_COMMAND"
+        iniset $Q_L3_CONF_FILE AGENT root_helper_daemon "$Q_RR_DAEMON_COMMAND"
     fi
 
     _neutron_setup_interface_driver $Q_L3_CONF_FILE
 
     neutron_plugin_configure_l3_agent
 
-    if [[ $(ip -f inet a s dev "$PUBLIC_INTERFACE" | grep -c 'global') != 0 ]]; then
-        _move_neutron_addresses_route "$PUBLIC_INTERFACE" "$OVS_PHYSICAL_BRIDGE" True "inet"
-    fi
+    _move_neutron_addresses_route "$PUBLIC_INTERFACE" "$OVS_PHYSICAL_BRIDGE" True "inet"
 
     if [[ $(ip -f inet6 a s dev "$PUBLIC_INTERFACE" | grep -c 'global') != 0 ]]; then
         _move_neutron_addresses_route "$PUBLIC_INTERFACE" "$OVS_PHYSICAL_BRIDGE" False "inet6"
@@ -1037,9 +1033,9 @@
     iniset $Q_META_CONF_FILE DEFAULT verbose True
     iniset $Q_META_CONF_FILE DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
     iniset $Q_META_CONF_FILE DEFAULT nova_metadata_ip $Q_META_DATA_IP
-    iniset $Q_META_CONF_FILE DEFAULT root_helper "$Q_RR_COMMAND"
+    iniset $Q_META_CONF_FILE AGENT root_helper "$Q_RR_COMMAND"
     if [[ "$Q_USE_ROOTWRAP_DAEMON" == "True" ]]; then
-        iniset $Q_META_CONF_FILE agent root_helper_daemon "$Q_RR_DAEMON_COMMAND"
+        iniset $Q_META_CONF_FILE AGENT root_helper_daemon "$Q_RR_DAEMON_COMMAND"
     fi
 
     # Configures keystone for metadata_agent
@@ -1107,13 +1103,7 @@
 # It is called when q-svc is enabled.
 function _configure_neutron_service {
     Q_API_PASTE_FILE=$NEUTRON_CONF_DIR/api-paste.ini
-    Q_POLICY_FILE=$NEUTRON_CONF_DIR/policy.json
-
     cp $NEUTRON_DIR/etc/api-paste.ini $Q_API_PASTE_FILE
-    cp $NEUTRON_DIR/etc/policy.json $Q_POLICY_FILE
-
-    # allow neutron user to administer neutron to match neutron account
-    sed -i 's/"context_is_admin":  "role:admin"/"context_is_admin":  "role:admin or user_name:neutron"/g' $Q_POLICY_FILE
 
     # Update either configuration file with plugin
     iniset $NEUTRON_CONF DEFAULT core_plugin $Q_PLUGIN_CLASS
@@ -1130,7 +1120,7 @@
     iniset $NEUTRON_CONF DEFAULT auth_strategy $Q_AUTH_STRATEGY
     _neutron_setup_keystone $NEUTRON_CONF keystone_authtoken
 
-    # Configuration for neutron notifations to nova.
+    # Configuration for neutron notifications to nova.
     iniset $NEUTRON_CONF DEFAULT notify_nova_on_port_status_changes $Q_NOTIFY_NOVA_PORT_STATUS_CHANGES
     iniset $NEUTRON_CONF DEFAULT notify_nova_on_port_data_changes $Q_NOTIFY_NOVA_PORT_DATA_CHANGES
 
@@ -1162,6 +1152,9 @@
 
 # _neutron_deploy_rootwrap_filters() - deploy rootwrap filters to $Q_CONF_ROOTWRAP_D (owned by root).
 function _neutron_deploy_rootwrap_filters {
+    if [[ "$Q_USE_ROOTWRAP" == "False" ]]; then
+        return
+    fi
     local srcdir=$1
     sudo install -d -o root -m 755 $Q_CONF_ROOTWRAP_D
     sudo install -o root -m 644 $srcdir/etc/neutron/rootwrap.d/* $Q_CONF_ROOTWRAP_D/
@@ -1240,8 +1233,10 @@
     subnet_params+="--ip_version 4 "
     subnet_params+="--gateway $NETWORK_GATEWAY "
     subnet_params+="--name $PRIVATE_SUBNET_NAME "
+    subnet_params+="--subnetpool None "
     subnet_params+="$NET_ID $FIXED_RANGE"
-    local subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
+    local subnet_id
+    subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
     die_if_not_set $LINENO subnet_id "Failure creating private IPv4 subnet for $TENANT_ID"
     echo $subnet_id
 }
@@ -1255,8 +1250,10 @@
     subnet_params+="--ip_version 6 "
     subnet_params+="--gateway $IPV6_PRIVATE_NETWORK_GATEWAY "
     subnet_params+="--name $IPV6_PRIVATE_SUBNET_NAME "
+    subnet_params+="--subnetpool None "
     subnet_params+="$NET_ID $FIXED_RANGE_V6 $ipv6_modes"
-    local ipv6_subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
+    local ipv6_subnet_id
+    ipv6_subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
     die_if_not_set $LINENO ipv6_subnet_id "Failure creating private IPv6 subnet for $TENANT_ID"
     echo $ipv6_subnet_id
 }
@@ -1267,9 +1264,11 @@
     subnet_params+="${Q_FLOATING_ALLOCATION_POOL:+--allocation-pool $Q_FLOATING_ALLOCATION_POOL} "
     subnet_params+="--gateway $PUBLIC_NETWORK_GATEWAY "
     subnet_params+="--name $PUBLIC_SUBNET_NAME "
+    subnet_params+="--subnetpool None "
     subnet_params+="$EXT_NET_ID $FLOATING_RANGE "
     subnet_params+="-- --enable_dhcp=False"
-    local id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
+    local id_and_ext_gw_ip
+    id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
     die_if_not_set $LINENO id_and_ext_gw_ip "Failure creating public IPv4 subnet"
     echo $id_and_ext_gw_ip
 }
@@ -1279,9 +1278,11 @@
     local subnet_params="--ip_version 6 "
     subnet_params+="--gateway $IPV6_PUBLIC_NETWORK_GATEWAY "
     subnet_params+="--name $IPV6_PUBLIC_SUBNET_NAME "
+    subnet_params+="--subnetpool None "
     subnet_params+="$EXT_NET_ID $IPV6_PUBLIC_RANGE "
     subnet_params+="-- --enable_dhcp=False"
-    local ipv6_id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
+    local ipv6_id_and_ext_gw_ip
+    ipv6_id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
     die_if_not_set $LINENO ipv6_id_and_ext_gw_ip "Failure creating an IPv6 public subnet"
     echo $ipv6_id_and_ext_gw_ip
 }
@@ -1290,8 +1291,10 @@
 function _neutron_configure_router_v4 {
     neutron router-interface-add $ROUTER_ID $SUBNET_ID
     # Create a public subnet on the external network
-    local id_and_ext_gw_ip=$(_neutron_create_public_subnet_v4 $EXT_NET_ID)
-    local ext_gw_ip=$(echo $id_and_ext_gw_ip  | get_field 2)
+    local id_and_ext_gw_ip
+    id_and_ext_gw_ip=$(_neutron_create_public_subnet_v4 $EXT_NET_ID)
+    local ext_gw_ip
+    ext_gw_ip=$(echo $id_and_ext_gw_ip  | get_field 2)
     PUB_SUBNET_ID=$(echo $id_and_ext_gw_ip | get_field 5)
     # Configure the external network as the default router gateway
     neutron router-gateway-set $ROUTER_ID $EXT_NET_ID
@@ -1300,7 +1303,7 @@
     if is_service_enabled q-l3; then
         # Configure and enable public bridge
         local ext_gw_interface="none"
-        if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
+        if is_neutron_ovs_base_plugin; then
             ext_gw_interface=$(_neutron_get_ext_gw_interface)
         elif [[ "$Q_AGENT" = "linuxbridge" ]]; then
             # Search for the brq device the neutron router and network for $FIXED_RANGE
@@ -1328,9 +1331,12 @@
 function _neutron_configure_router_v6 {
     neutron router-interface-add $ROUTER_ID $IPV6_SUBNET_ID
     # Create a public subnet on the external network
-    local ipv6_id_and_ext_gw_ip=$(_neutron_create_public_subnet_v6 $EXT_NET_ID)
-    local ipv6_ext_gw_ip=$(echo $ipv6_id_and_ext_gw_ip | get_field 2)
-    local ipv6_pub_subnet_id=$(echo $ipv6_id_and_ext_gw_ip | get_field 5)
+    local ipv6_id_and_ext_gw_ip
+    ipv6_id_and_ext_gw_ip=$(_neutron_create_public_subnet_v6 $EXT_NET_ID)
+    local ipv6_ext_gw_ip
+    ipv6_ext_gw_ip=$(echo $ipv6_id_and_ext_gw_ip | get_field 2)
+    local ipv6_pub_subnet_id
+    ipv6_pub_subnet_id=$(echo $ipv6_id_and_ext_gw_ip | get_field 5)
 
     # If the external network has not already been set as the default router
     # gateway when configuring an IPv4 public subnet, do so now
@@ -1347,8 +1353,9 @@
         IPV6_ROUTER_GW_IP=`neutron port-list -c fixed_ips | grep $ipv6_pub_subnet_id | awk -F '"' -v subnet_id=$ipv6_pub_subnet_id '$4 == subnet_id { print $8; }'`
         die_if_not_set $LINENO IPV6_ROUTER_GW_IP "Failure retrieving IPV6_ROUTER_GW_IP"
 
-        if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
-            local ext_gw_interface=$(_neutron_get_ext_gw_interface)
+        if is_neutron_ovs_base_plugin; then
+            local ext_gw_interface
+            ext_gw_interface=$(_neutron_get_ext_gw_interface)
             local ipv6_cidr_len=${IPV6_PUBLIC_RANGE#*/}
 
             # Configure interface for public bridge
@@ -1361,7 +1368,7 @@
 
 # Explicitly set router id in l3 agent configuration
 function _neutron_set_router_id {
-    if [[ "$Q_USE_NAMESPACE" == "False" ]]; then
+    if [[ "$Q_L3_ROUTER_PER_TENANT" == "False" ]]; then
         iniset $Q_L3_CONF_FILE DEFAULT router_id $ROUTER_ID
     fi
 }
diff --git a/lib/neutron_plugins/README.md b/lib/neutron_plugins/README.md
index 4b220d3..f03000e 100644
--- a/lib/neutron_plugins/README.md
+++ b/lib/neutron_plugins/README.md
@@ -16,9 +16,7 @@
 ``lib/neutron-legacy`` calls the following functions when the ``$Q_PLUGIN`` is enabled
 
 * ``neutron_plugin_create_nova_conf`` :
-  set ``NOVA_VIF_DRIVER`` and optionally set options in nova_conf
-  e.g.
-  NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtGenericVIFDriver"}
+  optionally set options in nova_conf
 * ``neutron_plugin_install_agent_packages`` :
   install packages that is specific to plugin agent
   e.g.
diff --git a/lib/neutron_plugins/bigswitch_floodlight b/lib/neutron_plugins/bigswitch_floodlight
index 4166131..f52105e 100644
--- a/lib/neutron_plugins/bigswitch_floodlight
+++ b/lib/neutron_plugins/bigswitch_floodlight
@@ -58,9 +58,9 @@
 function neutron_plugin_setup_interface_driver {
     local conf_file=$1
     if [ "$BS_FL_VIF_DRIVER" = "ivs" ]; then
-        iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.IVSInterfaceDriver
+        iniset $conf_file DEFAULT interface_driver ivs
     else
-        iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.OVSInterfaceDriver
+        iniset $conf_file DEFAULT interface_driver openvswitch
     fi
 }
 
diff --git a/lib/neutron_plugins/brocade b/lib/neutron_plugins/brocade
index b8166d9..953360e 100644
--- a/lib/neutron_plugins/brocade
+++ b/lib/neutron_plugins/brocade
@@ -12,7 +12,7 @@
 }
 
 function neutron_plugin_create_nova_conf {
-    NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtGenericVIFDriver"}
+    :
 }
 
 function neutron_plugin_install_agent_packages {
@@ -68,7 +68,7 @@
 
 function neutron_plugin_setup_interface_driver {
     local conf_file=$1
-    iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.BridgeInterfaceDriver
+    iniset $conf_file DEFAULT interface_driver linuxbridge
 }
 
 function has_neutron_plugin_security_group {
diff --git a/lib/neutron_plugins/cisco b/lib/neutron_plugins/cisco
index 90dcd57..7d0cf1a 100644
--- a/lib/neutron_plugins/cisco
+++ b/lib/neutron_plugins/cisco
@@ -150,7 +150,7 @@
 
 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 interface_driver openvswitch
 }
 
 # Restore xtrace
diff --git a/lib/neutron_plugins/embrane b/lib/neutron_plugins/embrane
index 6b4819e..2028496 100644
--- a/lib/neutron_plugins/embrane
+++ b/lib/neutron_plugins/embrane
@@ -10,7 +10,8 @@
 source $TOP_DIR/lib/neutron_plugins/openvswitch
 
 function save_function {
-    local ORIG_FUNC=$(declare -f $1)
+    local ORIG_FUNC
+    ORIG_FUNC=$(declare -f $1)
     local NEW_FUNC="$2${ORIG_FUNC#$1}"
     eval "$NEW_FUNC"
 }
diff --git a/lib/neutron_plugins/ibm b/lib/neutron_plugins/ibm
deleted file mode 100644
index 3660a9f..0000000
--- a/lib/neutron_plugins/ibm
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/bin/bash
-#
-# Neutron IBM SDN-VE plugin
-# ---------------------------
-
-# Save trace setting
-IBM_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-source $TOP_DIR/lib/neutron_plugins/ovs_base
-
-function neutron_plugin_install_agent_packages {
-    _neutron_ovs_base_install_agent_packages
-}
-
-function _neutron_interface_setup {
-    # Setup one interface on the integration bridge if needed
-    # The plugin agent to be used if more than one interface is used
-    local bridge=$1
-    local interface=$2
-    sudo ovs-vsctl --no-wait -- --may-exist add-port $bridge $interface
-}
-
-function neutron_setup_integration_bridge {
-    # Setup integration bridge if needed
-    if [[ "$SDNVE_INTEGRATION_BRIDGE" != "" ]]; then
-        neutron_ovs_base_cleanup
-        _neutron_ovs_base_setup_bridge $SDNVE_INTEGRATION_BRIDGE
-        if [[ "$SDNVE_INTERFACE_MAPPINGS" != "" ]]; then
-            interfaces=(${SDNVE_INTERFACE_MAPPINGS//[,:]/ })
-            _neutron_interface_setup $SDNVE_INTEGRATION_BRIDGE ${interfaces[1]}
-        fi
-    fi
-
-    # Set controller to SDNVE controller (1st of list) if exists
-    if [[ "$SDNVE_CONTROLLER_IPS" != "" ]]; then
-        # Get the first controller
-        controllers=(${SDNVE_CONTROLLER_IPS//[\[,\]]/ })
-        SDNVE_IP=${controllers[0]}
-        sudo ovs-vsctl set-controller $SDNVE_INTEGRATION_BRIDGE tcp:$SDNVE_IP
-    fi
-}
-
-function neutron_plugin_create_nova_conf {
-    NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtGenericVIFDriver"}
-    # if n-cpu is enabled, then setup integration bridge
-    if is_service_enabled n-cpu; then
-        neutron_setup_integration_bridge
-    fi
-}
-
-function is_neutron_ovs_base_plugin {
-    if [[ "$SDNVE_INTEGRATION_BRIDGE" != "" ]]; then
-        # Yes, we use OVS.
-        return 0
-    else
-        # No, we do not use OVS.
-        return 1
-    fi
-}
-
-function neutron_plugin_configure_common {
-    Q_PLUGIN_CONF_PATH=etc/neutron/plugins/ibm
-    Q_PLUGIN_CONF_FILENAME=sdnve_neutron_plugin.ini
-    Q_PLUGIN_CLASS="neutron.plugins.ibm.sdnve_neutron_plugin.SdnvePluginV2"
-}
-
-function neutron_plugin_configure_service {
-    # Define extra "SDNVE" configuration options when q-svc is configured
-
-    iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
-
-    if [[ "$SDNVE_CONTROLLER_IPS" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE sdnve controller_ips $SDNVE_CONTROLLER_IPS
-    fi
-
-    if [[ "$SDNVE_INTEGRATION_BRIDGE" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE sdnve integration_bridge $SDNVE_INTEGRATION_BRIDGE
-    fi
-
-    if [[ "$SDNVE_RESET_BRIDGE" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE sdnve reset_bridge $SDNVE_RESET_BRIDGE
-    fi
-
-    if [[ "$SDNVE_OUT_OF_BAND" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE sdnve out_of_band $SDNVE_OUT_OF_BAND
-    fi
-
-    if [[ "$SDNVE_INTERFACE_MAPPINGS" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE sdnve interface_mappings $SDNVE_INTERFACE_MAPPINGS
-    fi
-
-    if [[ "$SDNVE_FAKE_CONTROLLER" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE sdnve use_fake_controller $SDNVE_FAKE_CONTROLLER
-    fi
-
-
-    iniset $NEUTRON_CONF DEFAULT notification_driver neutron.openstack.common.notifier.no_op_notifier
-
-}
-
-function neutron_plugin_configure_plugin_agent {
-    AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-ibm-agent"
-}
-
-function neutron_plugin_configure_debug_command {
-    :
-}
-
-function neutron_plugin_setup_interface_driver {
-    return 0
-}
-
-function has_neutron_plugin_security_group {
-    # Does not support Security Groups
-    return 1
-}
-
-function neutron_ovs_base_cleanup {
-    if [[ "$SDNVE_RESET_BRIDGE" != False ]]; then
-        # remove all OVS ports that look like Neutron created ports
-        for port in $(sudo ovs-vsctl list port | grep -o -e tap[0-9a-f\-]* -e q[rg]-[0-9a-f\-]*); do
-            sudo ovs-vsctl del-port ${port}
-        done
-
-        # remove integration bridge created by Neutron
-        for bridge in $(sudo ovs-vsctl list-br | grep -o -e ${SDNVE_INTEGRATION_BRIDGE}); do
-            sudo ovs-vsctl del-br ${bridge}
-        done
-    fi
-}
-
-# Restore xtrace
-$IBM_XTRACE
diff --git a/lib/neutron_plugins/linuxbridge_agent b/lib/neutron_plugins/linuxbridge_agent
old mode 100755
new mode 100644
index fefc1c3..f28bcfe
--- a/lib/neutron_plugins/linuxbridge_agent
+++ b/lib/neutron_plugins/linuxbridge_agent
@@ -69,11 +69,23 @@
     fi
     AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-linuxbridge-agent"
     iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
+
+    # Configure vxlan tunneling
+    if [[ "$ENABLE_TENANT_TUNNELS" == "True" ]]; then
+        if [[ "$Q_ML2_TENANT_NETWORK_TYPE" == "vxlan" ]]; then
+            iniset /$Q_PLUGIN_CONF_FILE vxlan enable_vxlan "True"
+            iniset /$Q_PLUGIN_CONF_FILE vxlan local_ip $TUNNEL_ENDPOINT_IP
+        else
+            iniset /$Q_PLUGIN_CONF_FILE vxlan enable_vxlan "False"
+        fi
+    else
+        iniset /$Q_PLUGIN_CONF_FILE vxlan enable_vxlan "False"
+    fi
 }
 
 function neutron_plugin_setup_interface_driver {
     local conf_file=$1
-    iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.BridgeInterfaceDriver
+    iniset $conf_file DEFAULT interface_driver linuxbridge
 }
 
 function neutron_plugin_check_adv_test_requirements {
diff --git a/lib/neutron_plugins/ml2 b/lib/neutron_plugins/ml2
old mode 100755
new mode 100644
index 13ffee9..ace5335
--- a/lib/neutron_plugins/ml2
+++ b/lib/neutron_plugins/ml2
@@ -19,7 +19,9 @@
 
 # Default openvswitch L2 agent
 Q_AGENT=${Q_AGENT:-openvswitch}
-source $TOP_DIR/lib/neutron_plugins/${Q_AGENT}_agent
+if [ -f $TOP_DIR/lib/neutron_plugins/${Q_AGENT}_agent ]; then
+    source $TOP_DIR/lib/neutron_plugins/${Q_AGENT}_agent
+fi
 
 # List of MechanismDrivers to load
 Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_ML2_PLUGIN_MECHANISM_DRIVERS:-openvswitch,linuxbridge}
diff --git a/lib/neutron_plugins/nuage b/lib/neutron_plugins/nuage
index 7bce233..9e5307b 100644
--- a/lib/neutron_plugins/nuage
+++ b/lib/neutron_plugins/nuage
@@ -10,7 +10,6 @@
 function neutron_plugin_create_nova_conf {
     NOVA_OVS_BRIDGE=${NOVA_OVS_BRIDGE:-"br-int"}
     iniset $NOVA_CONF neutron ovs_bridge $NOVA_OVS_BRIDGE
-    NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtGenericVIFDriver"}
     LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver
     iniset $NOVA_CONF DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
 }
diff --git a/lib/neutron_plugins/ofagent_agent b/lib/neutron_plugins/ofagent_agent
deleted file mode 100644
index 0bc9bff..0000000
--- a/lib/neutron_plugins/ofagent_agent
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-# REVISIT(yamamoto): This file is intentionally left empty
-# in order to keep Q_AGENT=ofagent_agent work.
diff --git a/lib/neutron_plugins/oneconvergence b/lib/neutron_plugins/oneconvergence
deleted file mode 100644
index 48a368a..0000000
--- a/lib/neutron_plugins/oneconvergence
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/bash
-#
-# Neutron One Convergence plugin
-# ------------------------------
-
-# Save trace setting
-OC_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-source $TOP_DIR/lib/neutron_plugins/ovs_base
-
-Q_L3_ENABLED=true
-Q_L3_ROUTER_PER_TENANT=true
-Q_USE_NAMESPACE=true
-
-function neutron_plugin_install_agent_packages {
-    _neutron_ovs_base_install_agent_packages
-}
-# Configure common parameters
-function neutron_plugin_configure_common {
-
-    Q_PLUGIN_CONF_PATH=etc/neutron/plugins/oneconvergence
-    Q_PLUGIN_CONF_FILENAME=nvsdplugin.ini
-    Q_PLUGIN_CLASS="neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2"
-}
-
-# Configure plugin specific information
-function neutron_plugin_configure_service {
-    iniset /$Q_PLUGIN_CONF_FILE nvsd nvsd_ip $NVSD_IP
-    iniset /$Q_PLUGIN_CONF_FILE nvsd nvsd_port $NVSD_PORT
-    iniset /$Q_PLUGIN_CONF_FILE nvsd nvsd_user $NVSD_USER
-    iniset /$Q_PLUGIN_CONF_FILE nvsd nvsd_passwd $NVSD_PASSWD
-}
-
-function neutron_plugin_configure_debug_command {
-    _neutron_ovs_base_configure_debug_command
-}
-
-function neutron_plugin_setup_interface_driver {
-    local conf_file=$1
-    iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.OVSInterfaceDriver
-}
-
-function has_neutron_plugin_security_group {
-    # 1 means False here
-    return 0
-}
-
-function setup_integration_bridge {
-    _neutron_ovs_base_setup_bridge $OVS_BRIDGE
-}
-
-function neutron_plugin_configure_dhcp_agent {
-    setup_integration_bridge
-    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_plugin_configure_plugin_agent {
-
-    AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-nvsd-agent"
-
-    _neutron_ovs_base_configure_firewall_driver
-}
-
-function neutron_plugin_create_nova_conf {
-    NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtGenericVIFDriver"}
-    if ( is_service_enabled n-cpu && ! ( is_service_enabled q-dhcp )) ; then
-        setup_integration_bridge
-    fi
-}
-
-# Restore xtrace
-$OC_XTRACE
diff --git a/lib/neutron_plugins/openvswitch_agent b/lib/neutron_plugins/openvswitch_agent
old mode 100755
new mode 100644
index 1ff3a40..6a33393
--- a/lib/neutron_plugins/openvswitch_agent
+++ b/lib/neutron_plugins/openvswitch_agent
@@ -71,6 +71,9 @@
         # Make a copy of our config for domU
         sudo cp /$Q_PLUGIN_CONF_FILE "/$Q_PLUGIN_CONF_FILE.domU"
 
+        # change domU's config file to STACK_USER
+        sudo chown $STACK_USER:$STACK_USER /$Q_PLUGIN_CONF_FILE.domU
+
         # Deal with Dom0's L2 Agent:
         Q_RR_DOM0_COMMAND="$NEUTRON_BIN_DIR/neutron-rootwrap-xen-dom0 $Q_RR_CONF_FILE"
 
@@ -82,7 +85,14 @@
         # Under XS/XCP, the ovs agent needs to target the dom0
         # integration bridge.  This is enabled by using a root wrapper
         # that executes commands on dom0 via a XenAPI plugin.
+        # XenAPI does not support daemon rootwrap now, so set root_helper_daemon empty
         iniset /$Q_PLUGIN_CONF_FILE agent root_helper "$Q_RR_DOM0_COMMAND"
+        iniset /$Q_PLUGIN_CONF_FILE agent root_helper_daemon ""
+
+        # Disable minimize polling, so that it can always detect OVS and Port changes
+        # This is a problem of xenserver + neutron, bug has been reported
+        # https://bugs.launchpad.net/neutron/+bug/1495423
+        iniset /$Q_PLUGIN_CONF_FILE agent minimize_polling False
 
         # Set "physical" mapping
         iniset /$Q_PLUGIN_CONF_FILE ovs bridge_mappings "physnet1:$FLAT_NETWORK_BRIDGE"
@@ -95,21 +105,26 @@
         # Create a bridge "br-$GUEST_INTERFACE_DEFAULT"
         _neutron_ovs_base_add_bridge "br-$GUEST_INTERFACE_DEFAULT"
         # Add $GUEST_INTERFACE_DEFAULT to that bridge
-        sudo ovs-vsctl add-port "br-$GUEST_INTERFACE_DEFAULT" $GUEST_INTERFACE_DEFAULT
+        sudo ovs-vsctl -- --may-exist add-port "br-$GUEST_INTERFACE_DEFAULT" $GUEST_INTERFACE_DEFAULT
+
+        # Create external bridge and add port
+        _neutron_ovs_base_add_bridge $PUBLIC_BRIDGE
+        sudo ovs-vsctl -- --may-exist add-port $PUBLIC_BRIDGE $PUBLIC_INTERFACE_DEFAULT
 
         # Set bridge mappings to "physnet1:br-$GUEST_INTERFACE_DEFAULT"
-        iniset "/$Q_PLUGIN_CONF_FILE.domU" ovs bridge_mappings "physnet1:br-$GUEST_INTERFACE_DEFAULT"
+        iniset "/$Q_PLUGIN_CONF_FILE.domU" ovs bridge_mappings "physnet1:br-$GUEST_INTERFACE_DEFAULT,physnet-ex:$PUBLIC_BRIDGE"
         # Set integration bridge to domU's
         iniset "/$Q_PLUGIN_CONF_FILE.domU" ovs integration_bridge $OVS_BRIDGE
         # Set root wrap
         iniset "/$Q_PLUGIN_CONF_FILE.domU" agent root_helper "$Q_RR_COMMAND"
     fi
     iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
+    iniset /$Q_PLUGIN_CONF_FILE ovs datapath_type $OVS_DATAPATH_TYPE
 }
 
 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 interface_driver openvswitch
 }
 
 function neutron_plugin_check_adv_test_requirements {
diff --git a/lib/neutron_plugins/ovs_base b/lib/neutron_plugins/ovs_base
old mode 100755
new mode 100644
index f1f7f85..d3fd198
--- a/lib/neutron_plugins/ovs_base
+++ b/lib/neutron_plugins/ovs_base
@@ -8,7 +8,8 @@
 set +o xtrace
 
 OVS_BRIDGE=${OVS_BRIDGE:-br-int}
-OVS_DATAPATH_TYPE=${OVS_DATAPATH_TYPE:-""}
+# OVS recognize default 'system' datapath or 'netdev' for userspace datapath
+OVS_DATAPATH_TYPE=${OVS_DATAPATH_TYPE:-system}
 OVS_TUNNEL_BRIDGE=${OVS_TUNNEL_BRIDGE:-br-tun}
 
 function is_neutron_ovs_base_plugin {
@@ -20,7 +21,7 @@
     local bridge=$1
     local addbr_cmd="sudo ovs-vsctl --no-wait -- --may-exist add-br $bridge"
 
-    if [ "$OVS_DATAPATH_TYPE" != "" ] ; then
+    if [ "$OVS_DATAPATH_TYPE" != "system" ] ; then
         addbr_cmd="$addbr_cmd -- set Bridge $bridge datapath_type=${OVS_DATAPATH_TYPE}"
     fi
 
@@ -48,8 +49,10 @@
 
 function _neutron_ovs_base_install_ubuntu_dkms {
     # install Dynamic Kernel Module Support packages if needed
-    local kernel_version=$(uname -r)
-    local kernel_major_minor=`echo $kernel_version | cut -d. -f1-2`
+    local kernel_version
+    kernel_version=$(uname -r)
+    local kernel_major_minor
+    kernel_major_minor=`echo $kernel_version | cut -d. -f1-2`
     # From kernel 3.13 on, openvswitch-datapath-dkms is not needed
     if [ `vercmp_numbers "$kernel_major_minor" "3.13"` -lt "0" ]; then
         install_package "dkms openvswitch-datapath-dkms linux-headers-$kernel_version"
diff --git a/lib/neutron_plugins/plumgrid b/lib/neutron_plugins/plumgrid
deleted file mode 100644
index 0d711fe..0000000
--- a/lib/neutron_plugins/plumgrid
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/bash
-#
-# PLUMgrid Neutron Plugin
-# Edgar Magana emagana@plumgrid.com
-# ------------------------------------
-
-# Save trace settings
-PG_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-function neutron_plugin_create_nova_conf {
-    :
-}
-
-function neutron_plugin_setup_interface_driver {
-    :
-}
-
-function neutron_plugin_configure_common {
-    Q_PLUGIN_CONF_PATH=etc/neutron/plugins/plumgrid
-    Q_PLUGIN_CONF_FILENAME=plumgrid.ini
-    Q_PLUGIN_CLASS="neutron.plugins.plumgrid.plumgrid_plugin.plumgrid_plugin.NeutronPluginPLUMgridV2"
-    PLUMGRID_DIRECTOR_IP=${PLUMGRID_DIRECTOR_IP:-localhost}
-    PLUMGRID_DIRECTOR_PORT=${PLUMGRID_DIRECTOR_PORT:-7766}
-    PLUMGRID_ADMIN=${PLUMGRID_ADMIN:-username}
-    PLUMGRID_PASSWORD=${PLUMGRID_PASSWORD:-password}
-    PLUMGRID_TIMEOUT=${PLUMGRID_TIMEOUT:-70}
-    PLUMGRID_DRIVER=${PLUMGRID_DRIVER:-neutron.plugins.plumgrid.drivers.fake_plumlib.Plumlib}
-}
-
-function neutron_plugin_configure_service {
-    iniset /$Q_PLUGIN_CONF_FILE plumgriddirector director_server $PLUMGRID_DIRECTOR_IP
-    iniset /$Q_PLUGIN_CONF_FILE plumgriddirector director_server_port $PLUMGRID_DIRECTOR_PORT
-    iniset /$Q_PLUGIN_CONF_FILE plumgriddirector username $PLUMGRID_ADMIN
-    iniset /$Q_PLUGIN_CONF_FILE plumgriddirector password $PLUMGRID_PASSWORD
-    iniset /$Q_PLUGIN_CONF_FILE plumgriddirector servertimeout $PLUMGRID_TIMEOUT
-    iniset /$Q_PLUGIN_CONF_FILE plumgriddirector driver $PLUMGRID_DRIVER
-}
-
-function neutron_plugin_configure_debug_command {
-    :
-}
-
-function is_neutron_ovs_base_plugin {
-    # False
-    return 1
-}
-
-function has_neutron_plugin_security_group {
-    # return 0 means enabled
-    return 0
-}
-
-function neutron_plugin_check_adv_test_requirements {
-    is_service_enabled q-agt && is_service_enabled q-dhcp && return 0
-}
-# Restore xtrace
-$PG_XTRACE
diff --git a/lib/neutron_plugins/services/firewall b/lib/neutron_plugins/services/firewall
index 61a148e..3496da8 100644
--- a/lib/neutron_plugins/services/firewall
+++ b/lib/neutron_plugins/services/firewall
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 # Neutron firewall plugin
 # ---------------------------
 
diff --git a/lib/neutron_plugins/services/loadbalancer b/lib/neutron_plugins/services/loadbalancer
index 34190f9..7865f6f 100644
--- a/lib/neutron_plugins/services/loadbalancer
+++ b/lib/neutron_plugins/services/loadbalancer
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 # Neutron loadbalancer plugin
 # ---------------------------
 
diff --git a/lib/neutron_plugins/services/metering b/lib/neutron_plugins/services/metering
index 37ba019..c75ab19 100644
--- a/lib/neutron_plugins/services/metering
+++ b/lib/neutron_plugins/services/metering
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 # Neutron metering plugin
 # ---------------------------
 
diff --git a/lib/neutron_plugins/services/vpn b/lib/neutron_plugins/services/vpn
index 4d6a2bf..c0e7457 100644
--- a/lib/neutron_plugins/services/vpn
+++ b/lib/neutron_plugins/services/vpn
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 # Neutron VPN plugin
 # ---------------------------
 
diff --git a/lib/neutron_thirdparty/vmware_nsx b/lib/neutron_thirdparty/vmware_nsx
index 03853a9..e182fca 100644
--- a/lib/neutron_thirdparty/vmware_nsx
+++ b/lib/neutron_thirdparty/vmware_nsx
@@ -1,2 +1,4 @@
+#!/bin/bash
+
 # REVISIT(roeyc): this file left empty so that 'enable_service vmware_nsx'
 # continues to work.
diff --git a/lib/nova b/lib/nova
index 6441a89..ba05f53 100644
--- a/lib/nova
+++ b/lib/nova
@@ -7,6 +7,7 @@
 #
 # - ``functions`` file
 # - ``DEST``, ``DATA_DIR``, ``STACK_USER`` must be defined
+# - ``FILES``
 # - ``SERVICE_{TENANT_NAME|PASSWORD}`` must be defined
 # - ``LIBVIRT_TYPE`` must be defined
 # - ``INSTANCE_NAME_PREFIX``, ``VOLUME_NAME_PREFIX`` must be defined
@@ -56,13 +57,11 @@
 NOVA_API_DB=${NOVA_API_DB:-nova_api}
 
 NOVA_API_PASTE_INI=${NOVA_API_PASTE_INI:-$NOVA_CONF_DIR/api-paste.ini}
-# NOVA_API_VERSION valid options
-# - default - setup API end points as nova does out of the box
-# - v21default - make v21 the default on /v2
-#
-# NOTE(sdague): this is for transitional testing of the Nova v21 API.
-# Expect to remove in L or M.
-NOVA_API_VERSION=${NOVA_API_VERSION-default}
+
+# NOVA_V2_LEGACY defines whether we force the Nova v2.0 enpoint onto
+# the Nova v2.0 legacy code base. Remove this option once the Nova
+# v2.0 legacy codebase is removed.
+NOVA_V2_LEGACY=$(trueorfalse False NOVA_V2_LEGACY)
 
 if is_suse; then
     NOVA_WSGI_DIR=${NOVA_WSGI_DIR:-/srv/www/htdocs/nova}
@@ -89,6 +88,7 @@
 NOVA_SERVICE_LISTEN_ADDRESS=${NOVA_SERVICE_LISTEN_ADDRESS:-$SERVICE_LISTEN_ADDRESS}
 EC2_SERVICE_PORT=${EC2_SERVICE_PORT:-8773}
 EC2_SERVICE_PORT_INT=${EC2_SERVICE_PORT_INT:-18773}
+METADATA_SERVICE_PORT=${METADATA_SERVICE_PORT:-8775}
 
 # Option to enable/disable config drive
 # NOTE: Set ``FORCE_CONFIG_DRIVE="False"`` to turn OFF config drive
@@ -167,10 +167,6 @@
 TEST_FLOATING_POOL=${TEST_FLOATING_POOL:-test}
 TEST_FLOATING_RANGE=${TEST_FLOATING_RANGE:-192.168.253.0/29}
 
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,nova
-
-
 # Functions
 # ---------
 
@@ -208,14 +204,16 @@
         clean_iptables
 
         # Destroy old instances
-        local instances=`sudo virsh list --all | grep $INSTANCE_NAME_PREFIX | sed "s/.*\($INSTANCE_NAME_PREFIX[0-9a-fA-F]*\).*/\1/g"`
+        local instances
+        instances=`sudo virsh list --all | grep $INSTANCE_NAME_PREFIX | sed "s/.*\($INSTANCE_NAME_PREFIX[0-9a-fA-F]*\).*/\1/g"`
         if [ ! "$instances" = "" ]; then
             echo $instances | xargs -n1 sudo virsh destroy || true
             echo $instances | xargs -n1 sudo virsh undefine --managed-save || true
         fi
 
         # Logout and delete iscsi sessions
-        local tgts=$(sudo iscsiadm --mode node | grep $VOLUME_NAME_PREFIX | cut -d ' ' -f2)
+        local tgts
+        tgts=$(sudo iscsiadm --mode node | grep $VOLUME_NAME_PREFIX | cut -d ' ' -f2)
         local target
         for target in $tgts; do
             sudo iscsiadm --mode node -T $target --logout || true
@@ -245,19 +243,25 @@
     sudo rm -f $NOVA_WSGI_DIR/*
     sudo rm -f $(apache_site_config_for nova-api)
     sudo rm -f $(apache_site_config_for nova-ec2-api)
+    sudo rm -f $(apache_site_config_for nova-metadata)
 }
 
 # _config_nova_apache_wsgi() - Set WSGI config files of Keystone
 function _config_nova_apache_wsgi {
     sudo mkdir -p $NOVA_WSGI_DIR
 
-    local nova_apache_conf=$(apache_site_config_for nova-api)
-    local nova_ec2_apache_conf=$(apache_site_config_for nova-ec2-api)
+    local nova_apache_conf
+    nova_apache_conf=$(apache_site_config_for nova-api)
+    local nova_ec2_apache_conf
+    nova_ec2_apache_conf=$(apache_site_config_for nova-ec2-api)
+    local nova_metadata_apache_conf
+    nova_metadata_apache_conf=$(apache_site_config_for nova-metadata)
     local nova_ssl=""
     local nova_certfile=""
     local nova_keyfile=""
     local nova_api_port=$NOVA_SERVICE_PORT
     local nova_ec2_api_port=$EC2_SERVICE_PORT
+    local nova_metadata_port=$METADATA_SERVICE_PORT
     local venv_path=""
 
     if is_ssl_enabled_service nova-api; then
@@ -272,6 +276,7 @@
     # copy proxy vhost and wsgi helper files
     sudo cp $NOVA_DIR/nova/wsgi/nova-api.py $NOVA_WSGI_DIR/nova-api
     sudo cp $NOVA_DIR/nova/wsgi/nova-ec2-api.py $NOVA_WSGI_DIR/nova-ec2-api
+    sudo cp $NOVA_DIR/nova/wsgi/nova-metadata.py $NOVA_WSGI_DIR/nova-metadata
 
     sudo cp $FILES/apache-nova-api.template $nova_apache_conf
     sudo sed -e "
@@ -298,6 +303,19 @@
         s|%VIRTUALENV%|$venv_path|g
         s|%APIWORKERS%|$API_WORKERS|g
     " -i $nova_ec2_apache_conf
+
+    sudo cp $FILES/apache-nova-metadata.template $nova_metadata_apache_conf
+    sudo sed -e "
+        s|%PUBLICPORT%|$nova_metadata_port|g;
+        s|%APACHE_NAME%|$APACHE_NAME|g;
+        s|%PUBLICWSGI%|$NOVA_WSGI_DIR/nova-metadata|g;
+        s|%SSLENGINE%|$nova_ssl|g;
+        s|%SSLCERTFILE%|$nova_certfile|g;
+        s|%SSLKEYFILE%|$nova_keyfile|g;
+        s|%USER%|$STACK_USER|g;
+        s|%VIRTUALENV%|$venv_path|g
+        s|%APIWORKERS%|$API_WORKERS|g
+    " -i $nova_metadata_apache_conf
 }
 
 # configure_nova() - Set config files, create data dirs, etc
@@ -313,9 +331,11 @@
         # Get the sample configuration file in place
         cp $NOVA_DIR/etc/nova/api-paste.ini $NOVA_CONF_DIR
 
-        # For testing v21 is equivalent to v2
-        if [[ "$NOVA_API_VERSION" == "v21default" ]]; then
-            sed -i s/": openstack_compute_api_v2$"/": openstack_compute_api_v21"/ "$NOVA_API_PASTE_INI"
+        # For setting up an environment where v2.0 is running on the
+        # v2.0 legacy code base.
+        if [[ "$NOVA_V2_LEGACY" == "True" ]]; then
+            sed -i s@"^/v2: openstack_compute_api_v21_legacy_v2_compatible$"@"/v2: openstack_compute_api_legacy_v2"@ \
+                "$NOVA_API_PASTE_INI"
         fi
     fi
 
@@ -332,7 +352,7 @@
                 if [ ! -e /dev/kvm ]; then
                     echo "WARNING: Switching to QEMU"
                     LIBVIRT_TYPE=qemu
-                    if which selinuxenabled 2>&1 > /dev/null && selinuxenabled; then
+                    if which selinuxenabled >/dev/null 2>&1 && selinuxenabled; then
                         # https://bugzilla.redhat.com/show_bug.cgi?id=753589
                         sudo setsebool virt_use_execmem on
                     fi
@@ -354,6 +374,12 @@
                             sudo mount /cgroup
                         fi
                     fi
+
+                    # enable nbd for lxc unless you're using an lvm backend
+                    # otherwise you can't boot instances
+                    if [[ "$NOVA_BACKEND" != "LVM" ]]; then
+                        sudo modprobe nbd
+                    fi
                 fi
             fi
         fi
@@ -411,15 +437,16 @@
                 nova_api_url="$NOVA_SERVICE_PROTOCOL://$NOVA_SERVICE_HOST/compute"
             fi
 
-            get_or_create_service "nova" "compute" "Nova Compute Service"
-            get_or_create_endpoint "compute" \
+            get_or_create_service "nova_legacy" "compute_legacy" \
+                "Nova Compute Service (Legacy 2.0)"
+            get_or_create_endpoint "compute_legacy" \
                 "$REGION_NAME" \
                 "$nova_api_url/v2/\$(tenant_id)s" \
                 "$nova_api_url/v2/\$(tenant_id)s" \
                 "$nova_api_url/v2/\$(tenant_id)s"
 
-            get_or_create_service "novav21" "computev21" "Nova Compute Service V2.1"
-            get_or_create_endpoint "computev21" \
+            get_or_create_service "nova" "compute" "Nova Compute Service"
+            get_or_create_endpoint "compute" \
                 "$REGION_NAME" \
                 "$nova_api_url/v2.1/\$(tenant_id)s" \
                 "$nova_api_url/v2.1/\$(tenant_id)s" \
@@ -437,13 +464,18 @@
 
         # EC2
         if [[ "$KEYSTONE_CATALOG_BACKEND" = "sql" ]]; then
-
+            local nova_ec2_api_url
+            if [[ "$NOVA_USE_MOD_WSGI" == "False" ]]; then
+                nova_ec2_api_url="$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:$EC2_SERVICE_PORT/"
+            else
+                nova_ec2_api_url="$EC2_SERVICE_PROTOCOL://$SERVICE_HOST/ec2"
+            fi
             get_or_create_service "ec2" "ec2" "EC2 Compatibility Layer"
             get_or_create_endpoint "ec2" \
                 "$REGION_NAME" \
-                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/" \
-                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/" \
-                "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/"
+                "$nova_ec2_api_url" \
+                "$nova_ec2_api_url" \
+                "$nova_ec2_api_url"
         fi
     fi
 
@@ -468,7 +500,6 @@
 
     # (Re)create ``nova.conf``
     rm -f $NOVA_CONF
-    iniset $NOVA_CONF DEFAULT verbose "True"
     iniset $NOVA_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
     if [ "$NOVA_ALLOW_MOVE_TO_SAME_HOST" == "True" ]; then
         iniset $NOVA_CONF DEFAULT allow_resize_to_same_host "True"
@@ -605,6 +636,8 @@
     iniset $NOVA_CONF DEFAULT osapi_compute_workers "$API_WORKERS"
     iniset $NOVA_CONF DEFAULT ec2_workers "$API_WORKERS"
     iniset $NOVA_CONF DEFAULT metadata_workers "$API_WORKERS"
+    # don't let the conductor get out of control now that we're using a pure python db driver
+    iniset $NOVA_CONF conductor workers "$API_WORKERS"
 
     iniset $NOVA_CONF cinder os_region_name "$REGION_NAME"
 
@@ -779,13 +812,16 @@
     export PATH=$NOVA_BIN_DIR:$PATH
 
     # If the site is not enabled then we are in a grenade scenario
-    local enabled_site_file=$(apache_site_config_for nova-api)
+    local enabled_site_file
+    enabled_site_file=$(apache_site_config_for nova-api)
     if [ -f ${enabled_site_file} ] && [ "$NOVA_USE_MOD_WSGI" == "True" ]; then
         enable_apache_site nova-api
         enable_apache_site nova-ec2-api
+        enable_apache_site nova-metadata
         restart_apache_server
         tail_log nova-api /var/log/$APACHE_NAME/nova-api.log
         tail_log nova-ec2-api /var/log/$APACHE_NAME/nova-ec2-api.log
+        tail_log nova-metadata /var/log/$APACHE_NAME/nova-metadata.log
     else
         run_process n-api "$NOVA_BIN_DIR/nova-api"
     fi
@@ -901,6 +937,7 @@
     if [ "$NOVA_USE_MOD_WSGI" == "True" ]; then
         disable_apache_site nova-api
         disable_apache_site nova-ec2-api
+        disable_apache_site nova-metadata
         restart_apache_server
     else
         stop_process n-api
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
old mode 100755
new mode 100644
index 5525cfd..045fc8b
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -23,7 +23,7 @@
 # Installs required distro-specific libvirt packages.
 function install_libvirt {
     if is_ubuntu; then
-        if is_arch "aarch64" && [[ ${DISTRO} =~ (trusty|utopic) ]]; then
+        if is_arch "aarch64" && [[ ${DISTRO} == "trusty" ]]; then
             install_package qemu-system
         else
             install_package qemu-kvm
@@ -31,6 +31,11 @@
         fi
         install_package libvirt-bin libvirt-dev
         pip_install_gr libvirt-python
+        if [[ "$EBTABLES_RACE_FIX" == "True" ]]; then
+            # Work around for bug #1501558. We can remove this once we
+            # get to a version of Ubuntu that has new enough libvirt.
+            TOP_DIR=$TOP_DIR $TOP_DIR/tools/install_ebtables_workaround.sh
+        fi
         #pip_install_gr <there-si-no-guestfs-in-pypi>
     elif is_fedora || is_suse; then
         install_package kvm
diff --git a/lib/nova_plugins/hypervisor-vsphere b/lib/nova_plugins/hypervisor-vsphere
index c406e09..698f836 100644
--- a/lib/nova_plugins/hypervisor-vsphere
+++ b/lib/nova_plugins/hypervisor-vsphere
@@ -42,9 +42,6 @@
     iniset $NOVA_CONF vmware host_username "$VMWAREAPI_USER"
     iniset $NOVA_CONF vmware host_password "$VMWAREAPI_PASSWORD"
     iniset_multiline $NOVA_CONF vmware cluster_name "$VMWAREAPI_CLUSTER"
-    if is_service_enabled neutron; then
-        iniset $NOVA_CONF vmware integration_bridge $OVS_BRIDGE
-    fi
 }
 
 # install_nova_hypervisor() - Install external components
diff --git a/lib/nova_plugins/hypervisor-xenserver b/lib/nova_plugins/hypervisor-xenserver
index efce383..e097990 100644
--- a/lib/nova_plugins/hypervisor-xenserver
+++ b/lib/nova_plugins/hypervisor-xenserver
@@ -79,7 +79,7 @@
 
     # Create a cron job that will rotate guest logs
     $ssh_dom0 crontab - << CRONTAB
-* * * * * /root/rotate_xen_guest_logs.sh
+* * * * * /root/rotate_xen_guest_logs.sh >/dev/null 2>&1
 CRONTAB
 
     # Create directories for kernels and images
diff --git a/lib/oslo b/lib/oslo
index 123572c..56615fa 100644
--- a/lib/oslo
+++ b/lib/oslo
@@ -36,6 +36,7 @@
 GITDIR["oslo.messaging"]=$DEST/oslo.messaging
 GITDIR["oslo.middleware"]=$DEST/oslo.middleware
 GITDIR["oslo.policy"]=$DEST/oslo.policy
+GITDIR["oslo.privsep"]=$DEST/oslo.privsep
 GITDIR["oslo.reports"]=$DEST/oslo.reports
 GITDIR["oslo.rootwrap"]=$DEST/oslo.rootwrap
 GITDIR["oslo.serialization"]=$DEST/oslo.serialization
@@ -59,7 +60,7 @@
     local name=$1
     if use_library_from_git "$name"; then
         git_clone_by_name "$name"
-        setup_lib "$name"
+        setup_dev_lib "$name"
     fi
 }
 
@@ -79,6 +80,7 @@
     _do_install_oslo_lib "oslo.messaging"
     _do_install_oslo_lib "oslo.middleware"
     _do_install_oslo_lib "oslo.policy"
+    _do_install_oslo_lib "oslo.privsep"
     _do_install_oslo_lib "oslo.reports"
     _do_install_oslo_lib "oslo.rootwrap"
     _do_install_oslo_lib "oslo.serialization"
diff --git a/lib/rpc_backend b/lib/rpc_backend
index 03eacd8..298dcb6 100644
--- a/lib/rpc_backend
+++ b/lib/rpc_backend
@@ -58,7 +58,7 @@
         # NOTE(bnemec): Retry initial rabbitmq configuration to deal with
         # the fact that sometimes it fails to start properly.
         # Reference: https://bugzilla.redhat.com/show_bug.cgi?id=1144100
-        # NOTE(tonyb): Extend the orginal retry logic to only restart rabbitmq
+        # NOTE(tonyb): Extend the original retry logic to only restart rabbitmq
         # every second time around the loop.
         # See: https://bugs.launchpad.net/devstack/+bug/1449056 for details on
         # why this is needed.  This can bee seen on vivid and Debian unstable
@@ -106,7 +106,7 @@
     fi
 }
 
-# iniset cofiguration
+# iniset configuration
 function iniset_rpc_backend {
     local package=$1
     local file=$2
diff --git a/lib/stack b/lib/stack
index 47e8ce2..7d98604 100644
--- a/lib/stack
+++ b/lib/stack
@@ -14,7 +14,7 @@
 # Functions
 # ---------
 
-# Generic service install handles venv creation if confgured for service
+# Generic service install handles venv creation if configured for service
 # stack_install_service service
 function stack_install_service {
     local service=$1
diff --git a/lib/swift b/lib/swift
index 96d730e..d7ccc24 100644
--- a/lib/swift
+++ b/lib/swift
@@ -44,6 +44,7 @@
 SWIFT3_DIR=$DEST/swift3
 
 SWIFT_SERVICE_PROTOCOL=${SWIFT_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
+SWIFT_DEFAULT_BIND_PORT=${SWIFT_DEFAULT_BIND_PORT:-8080}
 SWIFT_DEFAULT_BIND_PORT_INT=${SWIFT_DEFAULT_BIND_PORT_INT:-8081}
 SWIFT_SERVICE_LOCAL_HOST=${SWIFT_SERVICE_LOCAL_HOST:-$SERVICE_LOCAL_HOST}
 SWIFT_SERVICE_LISTEN_ADDRESS=${SWIFT_SERVICE_LISTEN_ADDRESS:-$SERVICE_LISTEN_ADDRESS}
@@ -62,7 +63,7 @@
 if is_service_enabled s-proxy && is_service_enabled swift3; then
     # If we are using ``swift3``, we can default the S3 port to swift instead
     # of nova-objectstore
-    S3_SERVICE_PORT=${S3_SERVICE_PORT:-8080}
+    S3_SERVICE_PORT=${S3_SERVICE_PORT:-$SWIFT_DEFAULT_BIND_PORT}
 fi
 
 if is_service_enabled g-api; then
@@ -97,7 +98,7 @@
 # the beginning of the pipeline, before authentication middlewares.
 SWIFT_EXTRAS_MIDDLEWARE_NO_AUTH=${SWIFT_EXTRAS_MIDDLEWARE_NO_AUTH:-crossdomain}
 
-# The ring uses a configurable number of bits from a path’s MD5 hash as
+# The ring uses a configurable number of bits from a path's MD5 hash as
 # a partition index that designates a device. The number of bits kept
 # from the hash is known as the partition power, and 2 to the partition
 # power indicates the partition count. Partitioning the full MD5 hash
@@ -122,17 +123,17 @@
 # trace through the logs when looking for its use.
 SWIFT_LOG_TOKEN_LENGTH=${SWIFT_LOG_TOKEN_LENGTH:-12}
 
-# Set ``SWIFT_MAX_HEADER_SIZE`` to configure the maximun length of headers in
+# Set ``SWIFT_MAX_HEADER_SIZE`` to configure the maximum length of headers in
 # Swift API
 SWIFT_MAX_HEADER_SIZE=${SWIFT_MAX_HEADER_SIZE:-16384}
 
 # Set ``OBJECT_PORT_BASE``, ``CONTAINER_PORT_BASE``, ``ACCOUNT_PORT_BASE``
-# Port bases used in port number calclution for the service "nodes"
-# The specified port number will be used, the additinal ports calculated by
+# Port bases used in port number calculation for the service "nodes"
+# The specified port number will be used, the additional ports calculated by
 # base_port + node_num * 10
-OBJECT_PORT_BASE=${OBJECT_PORT_BASE:-6013}
-CONTAINER_PORT_BASE=${CONTAINER_PORT_BASE:-6011}
-ACCOUNT_PORT_BASE=${ACCOUNT_PORT_BASE:-6012}
+OBJECT_PORT_BASE=${OBJECT_PORT_BASE:-6613}
+CONTAINER_PORT_BASE=${CONTAINER_PORT_BASE:-6611}
+ACCOUNT_PORT_BASE=${ACCOUNT_PORT_BASE:-6612}
 
 # Enable tempurl feature
 SWIFT_ENABLE_TEMPURLS=${SWIFT_ENABLE_TEMPURLS:-False}
@@ -141,10 +142,6 @@
 # Toggle for deploying Swift under HTTPD + mod_wsgi
 SWIFT_USE_MOD_WSGI=${SWIFT_USE_MOD_WSGI:-False}
 
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,swift
-
-
 # Functions
 # ---------
 
@@ -187,7 +184,7 @@
 # _config_swift_apache_wsgi() - Set WSGI config files of Swift
 function _config_swift_apache_wsgi {
     sudo mkdir -p ${SWIFT_APACHE_WSGI_DIR}
-    local proxy_port=${SWIFT_DEFAULT_BIND_PORT:-8080}
+    local proxy_port=${SWIFT_DEFAULT_BIND_PORT}
 
     # copy proxy vhost and wsgi file
     sudo cp ${SWIFT_DIR}/examples/apache2/proxy-server.template $(apache_site_config_for proxy-server)
@@ -209,9 +206,12 @@
     # copy apache vhost file and set name and port
     local node_number
     for node_number in ${SWIFT_REPLICAS_SEQ}; do
-        local object_port=$(( OBJECT_PORT_BASE + 10 * (node_number - 1) ))
-        local container_port=$(( CONTAINER_PORT_BASE + 10 * (node_number - 1) ))
-        local account_port=$(( ACCOUNT_PORT_BASE + 10 * (node_number - 1) ))
+        local object_port
+        object_port=$(( OBJECT_PORT_BASE + 10 * (node_number - 1) ))
+        local container_port
+        container_port=$(( CONTAINER_PORT_BASE + 10 * (node_number - 1) ))
+        local account_port
+        account_port=$(( ACCOUNT_PORT_BASE + 10 * (node_number - 1) ))
 
         sudo cp ${SWIFT_DIR}/examples/apache2/object-server.template $(apache_site_config_for object-server-${node_number})
         sudo sed -e "
@@ -348,7 +348,7 @@
     local csyncfile=${SWIFT_CONF_DIR}/container-sync-realms.conf
     cp ${SWIFT_DIR}/etc/container-sync-realms.conf-sample ${csyncfile}
     iniset ${csyncfile} realm1 key realm1key
-    iniset ${csyncfile} realm1 cluster_name1 "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:8080/v1/"
+    iniset ${csyncfile} realm1 cluster_name1 "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:$SWIFT_DEFAULT_BIND_PORT/v1/"
 
     iniuncomment ${SWIFT_CONFIG_PROXY_SERVER} DEFAULT user
     iniset ${SWIFT_CONFIG_PROXY_SERVER} DEFAULT user ${STACK_USER}
@@ -369,7 +369,7 @@
     if is_service_enabled tls-proxy; then
         iniset ${SWIFT_CONFIG_PROXY_SERVER} DEFAULT bind_port ${SWIFT_DEFAULT_BIND_PORT_INT}
     else
-        iniset ${SWIFT_CONFIG_PROXY_SERVER} DEFAULT bind_port ${SWIFT_DEFAULT_BIND_PORT:-8080}
+        iniset ${SWIFT_CONFIG_PROXY_SERVER} DEFAULT bind_port ${SWIFT_DEFAULT_BIND_PORT}
     fi
 
     if is_ssl_enabled_service s-proxy; then
@@ -455,6 +455,7 @@
 
 [filter:swift3]
 use = egg:swift3#swift3
+location = ${REGION_NAME}
 EOF
     fi
 
@@ -507,7 +508,8 @@
 
     if is_service_enabled keystone; then
         iniuncomment ${testfile} func_test auth_version
-        local auth_vers=$(iniget ${testfile} func_test auth_version)
+        local auth_vers
+        auth_vers=$(iniget ${testfile} func_test auth_version)
         iniset ${testfile} func_test auth_host ${KEYSTONE_SERVICE_HOST}
         iniset ${testfile} func_test auth_port ${KEYSTONE_AUTH_PORT}
         if [[ $auth_vers == "3" ]]; then
@@ -517,7 +519,8 @@
         fi
     fi
 
-    local user_group=$(id -g ${STACK_USER})
+    local user_group
+    user_group=$(id -g ${STACK_USER})
     sudo install -d -o ${STACK_USER} -g ${user_group} ${SWIFT_DATA_DIR}
 
     local swift_log_dir=${SWIFT_DATA_DIR}/logs
@@ -543,7 +546,8 @@
     # First do a bit of setup by creating the directories and
     # changing the permissions so we can run it as our user.
 
-    local user_group=$(id -g ${STACK_USER})
+    local user_group
+    user_group=$(id -g ${STACK_USER})
     sudo install -d -o ${STACK_USER} -g ${user_group} ${SWIFT_DATA_DIR}/{drives,cache,run,logs}
 
     # Create a loopback disk and format it to XFS.
@@ -610,7 +614,8 @@
 
     KEYSTONE_CATALOG_BACKEND=${KEYSTONE_CATALOG_BACKEND:-sql}
 
-    local another_role=$(openstack role list | awk "/ anotherrole / { print \$2 }")
+    local another_role
+    another_role=$(get_or_create_role "anotherrole")
 
     # NOTE(jroll): Swift doesn't need the admin role here, however Ironic uses
     # temp urls, which break when uploaded by a non-admin role
@@ -621,38 +626,45 @@
         get_or_create_service "swift" "object-store" "Swift Service"
         get_or_create_endpoint "object-store" \
             "$REGION_NAME" \
-            "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:8080/v1/AUTH_\$(tenant_id)s" \
-            "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:8080" \
-            "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:8080/v1/AUTH_\$(tenant_id)s"
+            "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:$SWIFT_DEFAULT_BIND_PORT/v1/AUTH_\$(tenant_id)s" \
+            "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:$SWIFT_DEFAULT_BIND_PORT" \
+            "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:$SWIFT_DEFAULT_BIND_PORT/v1/AUTH_\$(tenant_id)s"
     fi
 
-    local swift_tenant_test1=$(get_or_create_project swifttenanttest1 default)
+    local swift_tenant_test1
+    swift_tenant_test1=$(get_or_create_project swifttenanttest1 default)
     die_if_not_set $LINENO swift_tenant_test1 "Failure creating swift_tenant_test1"
     SWIFT_USER_TEST1=$(get_or_create_user swiftusertest1 $swiftusertest1_password \
                         "default" "test@example.com")
     die_if_not_set $LINENO SWIFT_USER_TEST1 "Failure creating SWIFT_USER_TEST1"
     get_or_add_user_project_role admin $SWIFT_USER_TEST1 $swift_tenant_test1
 
-    local swift_user_test3=$(get_or_create_user swiftusertest3 $swiftusertest3_password \
+    local swift_user_test3
+    swift_user_test3=$(get_or_create_user swiftusertest3 $swiftusertest3_password \
                                 "default" "test3@example.com")
     die_if_not_set $LINENO swift_user_test3 "Failure creating swift_user_test3"
     get_or_add_user_project_role $another_role $swift_user_test3 $swift_tenant_test1
 
-    local swift_tenant_test2=$(get_or_create_project swifttenanttest2 default)
+    local swift_tenant_test2
+    swift_tenant_test2=$(get_or_create_project swifttenanttest2 default)
     die_if_not_set $LINENO swift_tenant_test2 "Failure creating swift_tenant_test2"
 
-    local swift_user_test2=$(get_or_create_user swiftusertest2 $swiftusertest2_password \
+    local swift_user_test2
+    swift_user_test2=$(get_or_create_user swiftusertest2 $swiftusertest2_password \
                                 "default" "test2@example.com")
     die_if_not_set $LINENO swift_user_test2 "Failure creating swift_user_test2"
     get_or_add_user_project_role admin $swift_user_test2 $swift_tenant_test2
 
-    local swift_domain=$(get_or_create_domain swift_test 'Used for swift functional testing')
+    local swift_domain
+    swift_domain=$(get_or_create_domain swift_test 'Used for swift functional testing')
     die_if_not_set $LINENO swift_domain "Failure creating swift_test domain"
 
-    local swift_tenant_test4=$(get_or_create_project swifttenanttest4 $swift_domain)
+    local swift_tenant_test4
+    swift_tenant_test4=$(get_or_create_project swifttenanttest4 $swift_domain)
     die_if_not_set $LINENO swift_tenant_test4 "Failure creating swift_tenant_test4"
 
-    local swift_user_test4=$(get_or_create_user swiftusertest4 $swiftusertest4_password \
+    local swift_user_test4
+    swift_user_test4=$(get_or_create_user swiftusertest4 $swiftusertest4_password \
                                 $swift_domain "test4@example.com")
     die_if_not_set $LINENO swift_user_test4 "Failure creating swift_user_test4"
     get_or_add_user_project_role admin $swift_user_test4 $swift_tenant_test4
@@ -764,7 +776,7 @@
         swift-init --run-dir=${SWIFT_DATA_DIR}/run ${type} stop || true
     done
     if is_service_enabled tls-proxy; then
-        local proxy_port=${SWIFT_DEFAULT_BIND_PORT:-8080}
+        local proxy_port=${SWIFT_DEFAULT_BIND_PORT}
         start_tls_proxy '*' $proxy_port $SERVICE_HOST $SWIFT_DEFAULT_BIND_PORT_INT &
     fi
     run_process s-proxy "$SWIFT_DIR/bin/swift-proxy-server ${SWIFT_CONF_DIR}/proxy-server.conf -v"
@@ -801,10 +813,13 @@
 }
 
 function swift_configure_tempurls {
+    # note we are using swift credentials!
     OS_USERNAME=swift \
-        OS_TENANT_NAME=$SERVICE_TENANT_NAME \
-        OS_PASSWORD=$SERVICE_PASSWORD \
-        swift post -m "Temp-URL-Key: $SWIFT_TEMPURL_KEY"
+    OS_PASSWORD=$SERVICE_PASSWORD \
+    OS_PROJECT_NAME=$SERVICE_TENANT_NAME \
+    OS_AUTH_URL=$SERVICE_ENDPOINT \
+    openstack object store account \
+        set --property "Temp-URL-Key=$SWIFT_TEMPURL_KEY"
 }
 
 # Restore xtrace
diff --git a/lib/tempest b/lib/tempest
index 68ddd44..76fd6ca 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -15,7 +15,6 @@
 #   - ``SERVICE_HOST``
 #   - ``BASE_SQL_CONN`` ``lib/database`` declares
 #   - ``PUBLIC_NETWORK_NAME``
-#   - ``Q_USE_NAMESPACE``
 #   - ``Q_ROUTER_NAME``
 #   - ``Q_L3_ENABLED``
 #   - ``VIRT_DRIVER``
@@ -24,7 +23,7 @@
 #
 # Optional Dependencies:
 #
-# - ``ALT_*`` (similar vars exists in keystone_data.sh)
+# - ``ALT_*``
 # - ``LIVE_MIGRATION_AVAILABLE``
 # - ``USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION``
 # - ``DEFAULT_INSTANCE_TYPE``
@@ -82,6 +81,21 @@
 IPV6_ENABLED=$(trueorfalse True IPV6_ENABLED)
 IPV6_SUBNET_ATTRIBUTES_ENABLED=$(trueorfalse True IPV6_SUBNET_ATTRIBUTES_ENABLED)
 
+# Do we want to make a configuration where Tempest has admin on
+# the cloud. We don't always want to so that we can ensure Tempest
+# would work on a public cloud.
+TEMPEST_HAS_ADMIN=$(trueorfalse True TEMPEST_HAS_ADMIN)
+
+# Credential provider configuration option variables
+TEMPEST_ALLOW_TENANT_ISOLATION=${TEMPEST_ALLOW_TENANT_ISOLATION:-$TEMPEST_HAS_ADMIN}
+TEMPEST_USE_TEST_ACCOUNTS=$(trueorfalse False TEMPEST_USE_TEST_ACCOUNTS)
+
+# The number of workers tempest is expected to be run with. This is used for
+# generating a accounts.yaml for running with test-accounts. This is also the
+# same variable that devstack-gate uses to specify the number of workers that
+# it will run tempest with
+TEMPEST_CONCURRENCY=${TEMPEST_CONCURRENCY:-$(nproc)}
+
 
 # Functions
 # ---------
@@ -104,10 +118,6 @@
         pip_install_gr testrepository
     fi
 
-    # Used during configuration so make sure we have the correct
-    # version installed
-    pip_install_gr python-openstackclient
-
     local image_lines
     local images
     local num_images
@@ -121,7 +131,6 @@
     local flavor_lines
     local public_network_id
     local public_router_id
-    local tenant_networks_reachable
     local boto_instance_type="m1.tiny"
     local ssh_connect_method="fixed"
 
@@ -167,18 +176,13 @@
         esac
     fi
 
-    # Create ``tempest.conf`` from ``tempest.conf.sample``
-    # Copy every time because the image UUIDS are going to change
+    # (Re)create ``tempest.conf``
+    # Create every time because the image UUIDS are going to change
     sudo install -d -o $STACK_USER $TEMPEST_CONFIG_DIR
-    install -m 644 $TEMPEST_DIR/etc/tempest.conf.sample $TEMPEST_CONFIG
+    rm -f $TEMPEST_CONFIG
 
     password=${ADMIN_PASSWORD:-secrete}
 
-    # Do we want to make a configuration where Tempest has admin on
-    # the cloud. We don't always want to so that we can ensure Tempest
-    # would work on a public cloud.
-    TEMPEST_HAS_ADMIN=$(trueorfalse True TEMPEST_HAS_ADMIN)
-
     # See ``lib/keystone`` where these users and tenants are set up
     ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
     ADMIN_TENANT_NAME=${ADMIN_TENANT_NAME:-admin}
@@ -195,21 +199,12 @@
         if  [[ -z "$DEFAULT_INSTANCE_TYPE" ]]; then
             available_flavors=$(nova flavor-list)
             if [[ ! ( $available_flavors =~ 'm1.nano' ) ]]; then
-                if is_arch "ppc64"; then
-                    # Qemu needs at least 128MB of memory to boot on ppc64
-                    nova flavor-create m1.nano 42 128 0 1
-                else
-                    nova flavor-create m1.nano 42 64 0 1
-                fi
+                nova flavor-create m1.nano 42 64 0 1
             fi
             flavor_ref=42
             boto_instance_type=m1.nano
             if [[ ! ( $available_flavors =~ 'm1.micro' ) ]]; then
-                if is_arch "ppc64"; then
-                    nova flavor-create m1.micro 84 256 0 1
-                else
-                    nova flavor-create m1.micro 84 128 0 1
-                fi
+                nova flavor-create m1.micro 84 128 0 1
             fi
             flavor_ref_alt=84
         else
@@ -249,13 +244,8 @@
         fi
     fi
 
-    if [ "$Q_USE_NAMESPACE" != "False" ]; then
-        tenant_networks_reachable=false
-        if ! is_service_enabled n-net; then
-            ssh_connect_method="floating"
-        fi
-    else
-        tenant_networks_reachable=true
+    if ! is_service_enabled n-net; then
+        ssh_connect_method="floating"
     fi
 
     ssh_connect_method=${TEMPEST_SSH_CONNECT_METHOD:-$ssh_connect_method}
@@ -263,12 +253,6 @@
     if [ "$Q_L3_ENABLED" = "True" ]; then
         public_network_id=$(neutron net-list | grep $PUBLIC_NETWORK_NAME | \
             awk '{print $2}')
-        if [ "$Q_USE_NAMESPACE" == "False" ]; then
-            # If namespaces are disabled, DevStack will create a single
-            # public router that tempest should be configured to use.
-            public_router_id=$(neutron router-list | awk "/ $Q_ROUTER_NAME / \
-                { print \$2 }")
-        fi
     fi
 
     EC2_URL=$(get_endpoint_url ec2 public || true)
@@ -313,7 +297,7 @@
     fi
     if [ "$ENABLE_IDENTITY_V2" == "False" ]; then
         # Only Identity v3 is available; then skip Identity API v2 tests
-        iniset $TEMPEST_CONFIG identity-feature-enabled v2_api False
+        iniset $TEMPEST_CONFIG identity-feature-enabled api_v2 False
         # In addition, use v3 auth tokens for running all Tempest tests
         iniset $TEMPEST_CONFIG identity auth_version v3
     else
@@ -330,16 +314,10 @@
     if [[ ! -z "$TEMPEST_HTTP_IMAGE" ]]; then
         iniset $TEMPEST_CONFIG image http_image $TEMPEST_HTTP_IMAGE
     fi
-    iniset $TEMPEST_CONFIG image-feature-enabled deactivate_image true
 
     # Image Features
     iniset $TEMPEST_CONFIG image-feature-enabled deactivate_image True
 
-    # Auth
-    TEMPEST_ALLOW_TENANT_ISOLATION=${TEMPEST_ALLOW_TENANT_ISOLATION:-$TEMPEST_HAS_ADMIN}
-    iniset $TEMPEST_CONFIG auth allow_tenant_isolation ${TEMPEST_ALLOW_TENANT_ISOLATION:-True}
-    iniset $TEMPEST_CONFIG auth tempest_roles "Member"
-
     # Compute
     iniset $TEMPEST_CONFIG compute ssh_user ${DEFAULT_INSTANCE_USER:-cirros} # DEPRECATED
     iniset $TEMPEST_CONFIG compute network_for_ssh $PRIVATE_NETWORK_NAME
@@ -356,10 +334,19 @@
         iniset $TEMPEST_CONFIG compute fixed_network_name $PRIVATE_NETWORK_NAME
     fi
 
+    # Set the service catalog entry for Tempest to run on. Typically
+    # used to try different compute API version targets. The tempest
+    # default if 'compute', which is typically valid, so only set this
+    # if you want to change it.
+    if [[ -n "$TEMPEST_COMPUTE_TYPE" ]]; then
+        iniset $TEMPEST_CONFIG compute catalog_type $TEMPEST_COMPUTE_TYPE
+    fi
+
     # Compute Features
     # Run ``verify_tempest_config -ur`` to retrieve enabled extensions on API endpoints
     # NOTE(mtreinish): This must be done after auth settings are added to the tempest config
-    local tmp_cfg_file=$(mktemp)
+    local tmp_cfg_file
+    tmp_cfg_file=$(mktemp)
     cd $TEMPEST_DIR
     tox -revenv -- verify-tempest-config -uro $tmp_cfg_file
 
@@ -385,10 +372,25 @@
     # neutron.allow_duplicate_networks option was removed from nova in Liberty
     # and is now the default behavior.
     iniset $TEMPEST_CONFIG compute-feature-enabled allow_duplicate_networks ${NOVA_ALLOW_DUPLICATE_NETWORKS:-True}
+    if is_service_enabled n-cell; then
+        # Cells doesn't support shelving/unshelving
+        iniset $TEMPEST_CONFIG compute-feature-enabled shelve False
+        # Cells doesn't support hot-plugging virtual interfaces.
+        iniset $TEMPEST_CONFIG compute-feature-enabled interface_attach False
 
-    # Network
+        if  [[ -z "$DEFAULT_INSTANCE_TYPE" ]]; then
+            # Cells supports resize but does not currently work with devstack
+            # because of the custom flavors created for Tempest runs which are
+            # not in the cells database.
+            # TODO(mriedem): work on adding a nova-manage command to sync
+            # flavors into the cells database.
+            iniset $TEMPEST_CONFIG compute-feature-enabled resize False
+        fi
+    fi
+
+    # Network
     iniset $TEMPEST_CONFIG network api_version 2.0
-    iniset $TEMPEST_CONFIG network tenant_networks_reachable "$tenant_networks_reachable"
+    iniset $TEMPEST_CONFIG network tenant_networks_reachable false
     iniset $TEMPEST_CONFIG network public_network_id "$public_network_id"
     iniset $TEMPEST_CONFIG network public_router_id "$public_router_id"
     iniset $TEMPEST_CONFIG network default_network "$FIXED_RANGE"
@@ -413,7 +415,6 @@
     iniset $TEMPEST_CONFIG boto aki_manifest cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-vmlinuz.manifest.xml
     iniset $TEMPEST_CONFIG boto instance_type "$boto_instance_type"
     iniset $TEMPEST_CONFIG boto http_socket_timeout 30
-    iniset $TEMPEST_CONFIG boto ssh_user ${DEFAULT_INSTANCE_USER:-cirros}
 
     # Orchestration Tests
     if is_service_enabled heat; then
@@ -442,9 +443,6 @@
     iniset $TEMPEST_CONFIG scenario large_ops_number ${TEMPEST_LARGE_OPS_NUMBER:-0}
 
     # Telemetry
-    # Ceilometer API optimization happened in Juno that allows to run more tests in tempest.
-    # Once Tempest retires support for icehouse this flag can be removed.
-    iniset $TEMPEST_CONFIG telemetry too_slow_to_test "False"
     iniset $TEMPEST_CONFIG telemetry-feature-enabled events "True"
 
     # Object Store
@@ -457,15 +455,20 @@
     fi
     iniset $TEMPEST_CONFIG object-storage-feature-enabled discoverable_apis $object_storage_api_extensions
 
+    # Validation
+    iniset $TEMPEST_CONFIG validation run_validation ${TEMPEST_RUN_VALIDATION:-False}
+
     # Volume
     # TODO(dkranz): Remove the bootable flag when Juno is end of life.
     iniset $TEMPEST_CONFIG volume-feature-enabled bootable True
+    # TODO(jordanP): Remove the extend_with_snapshot flag when Juno is end of life.
+    iniset $TEMPEST_CONFIG volume-feature-enabled extend_with_snapshot True
 
     local volume_api_extensions=${VOLUME_API_EXTENSIONS:-"all"}
     if [[ ! -z "$DISABLE_VOLUME_API_EXTENSIONS" ]]; then
         # Enabled extensions are either the ones explicitly specified or those available on the API endpoint
         volume_api_extensions=${VOLUME_API_EXTENSIONS:-$(iniget $tmp_cfg_file volume-feature-enabled api_extensions | tr -d " ")}
-        # Remove disabled extensions
+        # Remove disabled extensions
         volume_api_extensions=$(remove_disabled_extensions $volume_api_extensions $DISABLE_VOLUME_API_EXTENSIONS)
     fi
     iniset $TEMPEST_CONFIG volume-feature-enabled api_extensions $volume_api_extensions
@@ -524,10 +527,17 @@
     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 shelve False
+        iniset $TEMPEST_CONFIG compute-feature-enabled snapshot False
         iniset $TEMPEST_CONFIG compute-feature-enabled suspend False
     fi
 
     # ``service_available``
+    #
+    # this tempest service list needs to be all the services that
+    # tempest supports, otherwise we can have an erroneous set of
+    # defaults (something defaulting true in Tempest, but not listed here).
+    TEMPEST_SERVICES="key,glance,nova,neutron,cinder,swift,heat,ceilometer,horizon,sahara,ironic,trove"
     for service in ${TEMPEST_SERVICES//,/ }; do
         if is_service_enabled $service ; then
             iniset $TEMPEST_CONFIG service_available $service "True"
@@ -536,12 +546,35 @@
         fi
     done
 
+    if [ "$VIRT_DRIVER" = "libvirt" ] && [ "$LIBVIRT_TYPE" = "lxc" ]; then
+        # libvirt-lxc does not support boot from volume or attaching volumes
+        # so basically anything with cinder is out of the question.
+        iniset $TEMPEST_CONFIG service_available cinder "False"
+    fi
+
     if is_ssl_enabled_service "key" || is_service_enabled tls-proxy; then
         # Use the ``BOTO_CONFIG`` environment variable to point to this file
-        iniset $BOTO_CONF Boto ca_certificates_file $SSL_BUNDLE_FILE
+        iniset -sudo $BOTO_CONF Boto ca_certificates_file $SSL_BUNDLE_FILE
         sudo chown $STACK_USER $BOTO_CONF
     fi
 
+    # Auth
+    iniset $TEMPEST_CONFIG auth tempest_roles "Member"
+    if [[ $TEMPEST_USE_TEST_ACCOUNTS == "True" ]]; then
+        if [[ $TEMPEST_HAS_ADMIN == "True" ]]; then
+            tempest-account-generator -c $TEMPEST_CONFIG --os-username $ADMIN_USERNAME --os-password $ADMIN_PASSWORD --os-tenant-name $ADMIN_TENANT_NAME -r $TEMPEST_CONCURRENCY --with-admin etc/accounts.yaml
+        else
+            tempest-account-generator -c $TEMPEST_CONFIG --os-username $ADMIN_USERNAME --os-password $ADMIN_PASSWORD --os-tenant-name $ADMIN_TENANT_NAME -r $TEMPEST_CONCURRENCY etc/accounts.yaml
+        fi
+        iniset $TEMPEST_CONFIG auth allow_tenant_isolation False
+        iniset $TEMPEST_CONFIG auth test_accounts_file "etc/accounts.yaml"
+    elif [[ $TEMPEST_HAS_ADMIN == "False" ]]; then
+        iniset $TEMPEST_CONFIG auth allow_tenant_isolation ${TEMPEST_ALLOW_TENANT_ISOLATION:-False}
+
+    else
+        iniset $TEMPEST_CONFIG auth allow_tenant_isolation ${TEMPEST_ALLOW_TENANT_ISOLATION:-True}
+    fi
+
     # Restore IFS
     IFS=$ifs
 }
diff --git a/lib/tls b/lib/tls
index 8ff2027..f4740b8 100644
--- a/lib/tls
+++ b/lib/tls
@@ -346,7 +346,8 @@
 # we need to change it.
 function fix_system_ca_bundle_path {
     if is_service_enabled tls-proxy || [ "$USE_SSL" == "True" ]; then
-        local capath=$(python -c $'try:\n from requests import certs\n print certs.where()\nexcept ImportError: pass')
+        local capath
+        capath=$(python -c $'try:\n from requests import certs\n print certs.where()\nexcept ImportError: pass')
 
         if [[ ! $capath == "" && ! $capath =~ ^/etc/.* && ! -L $capath ]]; then
             if is_fedora; then
diff --git a/lib/zaqar b/lib/zaqar
deleted file mode 100644
index fdab3a2..0000000
--- a/lib/zaqar
+++ /dev/null
@@ -1,229 +0,0 @@
-#!/bin/bash
-#
-# lib/zaqar
-# Install and start **Zaqar** service
-
-# To enable a minimal set of Zaqar services, add the following to localrc:
-#
-#     enable_service zaqar-server
-#
-# Dependencies:
-# - functions
-# - OS_AUTH_URL for auth in api
-# - DEST set to the destination directory
-# - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api
-# - STACK_USER service user
-
-# stack.sh
-# ---------
-# install_zaqar
-# configure_zaqar
-# init_zaqar
-# start_zaqar
-# stop_zaqar
-# cleanup_zaqar
-# cleanup_zaqar_mongodb
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Defaults
-# --------
-
-# Set up default directories
-ZAQAR_DIR=$DEST/zaqar
-ZAQARCLIENT_DIR=$DEST/python-zaqarclient
-ZAQAR_CONF_DIR=/etc/zaqar
-ZAQAR_CONF=$ZAQAR_CONF_DIR/zaqar.conf
-ZAQAR_AUTH_CACHE_DIR=${ZAQAR_AUTH_CACHE_DIR:-/var/cache/zaqar}
-
-# Support potential entry-points console scripts
-ZAQAR_BIN_DIR=$(get_python_exec_prefix)
-
-# Set up database backend
-ZAQAR_BACKEND=${ZAQAR_BACKEND:-mongodb}
-
-
-# Set Zaqar repository
-ZAQAR_REPO=${ZAQAR_REPO:-${GIT_BASE}/openstack/zaqar.git}
-ZAQAR_BRANCH=${ZAQAR_BRANCH:-master}
-
-# Set client library repository
-ZAQARCLIENT_REPO=${ZAQARCLIENT_REPO:-${GIT_BASE}/openstack/python-zaqarclient.git}
-ZAQARCLIENT_BRANCH=${ZAQARCLIENT_BRANCH:-master}
-
-# Set Zaqar Connection Info
-ZAQAR_SERVICE_HOST=${ZAQAR_SERVICE_HOST:-$SERVICE_HOST}
-ZAQAR_SERVICE_PORT=${ZAQAR_SERVICE_PORT:-8888}
-ZAQAR_SERVICE_PROTOCOL=${ZAQAR_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
-
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,zaqar
-
-
-# Functions
-# ---------
-
-# Test if any Zaqar services are enabled
-# is_zaqar_enabled
-function is_zaqar_enabled {
-    [[ ,${ENABLED_SERVICES} =~ ,"zaqar-" ]] && return 0
-    return 1
-}
-
-# cleanup_zaqar() - Cleans up general things from previous
-# runs and storage specific left overs.
-function cleanup_zaqar {
-    if [ "$ZAQAR_BACKEND" = 'mongodb' ] ; then
-        cleanup_zaqar_mongodb
-    fi
-}
-
-# cleanup_zaqar_mongodb() - Remove residual data files, anything left over from previous
-# runs that a clean run would need to clean up
-function cleanup_zaqar_mongodb {
-    if ! timeout $SERVICE_TIMEOUT sh -c "while ! mongo zaqar --eval 'db.dropDatabase();'; do sleep 1; done"; then
-        die $LINENO "Mongo DB did not start"
-    else
-        full_version=$(mongo zaqar --eval 'db.dropDatabase();')
-        mongo_version=`echo $full_version | cut -d' ' -f4`
-        required_mongo_version='2.2'
-        if [[ $mongo_version < $required_mongo_version ]]; then
-            die $LINENO "Zaqar needs Mongo DB version >= 2.2 to run."
-        fi
-    fi
-}
-
-# configure_zaqarclient() - Set config files, create data dirs, etc
-function configure_zaqarclient {
-    setup_develop $ZAQARCLIENT_DIR
-}
-
-# configure_zaqar() - Set config files, create data dirs, etc
-function configure_zaqar {
-    setup_develop $ZAQAR_DIR
-
-    sudo install -d -o $STACK_USER -m 755 $ZAQAR_CONF_DIR
-
-    iniset $ZAQAR_CONF DEFAULT debug True
-    iniset $ZAQAR_CONF DEFAULT verbose True
-    iniset $ZAQAR_CONF DEFAULT admin_mode True
-    iniset $ZAQAR_CONF DEFAULT use_syslog $SYSLOG
-    iniset $ZAQAR_CONF 'drivers:transport:wsgi' bind $ZAQAR_SERVICE_HOST
-
-    configure_auth_token_middleware $ZAQAR_CONF zaqar $ZAQAR_AUTH_CACHE_DIR
-
-    if [ "$ZAQAR_BACKEND" = 'mysql' ] || [ "$ZAQAR_BACKEND" = 'postgresql' ] ; then
-        iniset $ZAQAR_CONF drivers storage sqlalchemy
-        iniset $ZAQAR_CONF 'drivers:storage:sqlalchemy' uri `database_connection_url zaqar`
-    elif [ "$ZAQAR_BACKEND" = 'mongodb' ] ; then
-        iniset $ZAQAR_CONF  drivers storage mongodb
-        iniset $ZAQAR_CONF 'drivers:storage:mongodb' uri mongodb://localhost:27017/zaqar
-        configure_mongodb
-    elif [ "$ZAQAR_BACKEND" = 'redis' ] ; then
-        iniset $ZAQAR_CONF  drivers storage redis
-        iniset $ZAQAR_CONF 'drivers:storage:redis' uri redis://localhost:6379
-        configure_redis
-    fi
-
-    iniset $ZAQAR_CONF DEFAULT notification_driver messaging
-    iniset $ZAQAR_CONF DEFAULT control_exchange zaqar
-
-    iniset_rpc_backend zaqar $ZAQAR_CONF
-
-    cleanup_zaqar
-}
-
-function configure_redis {
-    if is_ubuntu; then
-        install_package redis-server
-        pip_install_gr redis
-    elif is_fedora; then
-        install_package redis
-        pip_install_gr redis
-    else
-        exit_distro_not_supported "redis installation"
-    fi
-}
-
-function configure_mongodb {
-    # Set nssize to 2GB. This increases the number of namespaces supported
-    # # per database.
-    if is_ubuntu; then
-        sudo sed -i -e "
-            s|[^ \t]*#[ \t]*\(nssize[ \t]*=.*\$\)|\1|
-            s|^\(nssize[ \t]*=[ \t]*\).*\$|\1 2047|
-        " /etc/mongodb.conf
-        restart_service mongodb
-    elif is_fedora; then
-        sudo sed -i '/--nssize/!s/OPTIONS=\"/OPTIONS=\"--nssize 2047 /' /etc/sysconfig/mongod
-        restart_service mongod
-    fi
-}
-
-# init_zaqar() - Initialize etc.
-function init_zaqar {
-    # Create cache dir
-    sudo install -d -o $STACK_USER $ZAQAR_AUTH_CACHE_DIR
-    rm -f $ZAQAR_AUTH_CACHE_DIR/*
-}
-
-# install_zaqar() - Collect source and prepare
-function install_zaqar {
-    git_clone $ZAQAR_REPO $ZAQAR_DIR $ZAQAR_BRANCH
-    setup_develop $ZAQAR_DIR
-}
-
-# install_zaqarclient() - Collect source and prepare
-function install_zaqarclient {
-    git_clone $ZAQARCLIENT_REPO $ZAQARCLIENT_DIR $ZAQARCLIENT_BRANCH
-    setup_develop $ZAQARCLIENT_DIR
-}
-
-# start_zaqar() - Start running processes, including screen
-function start_zaqar {
-    if [[ "$USE_SCREEN" = "False" ]]; then
-        run_process zaqar-server "zaqar-server --config-file $ZAQAR_CONF --daemon"
-    else
-        run_process zaqar-server "zaqar-server --config-file $ZAQAR_CONF"
-    fi
-
-    echo "Waiting for Zaqar to start..."
-    if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT/v1/health; do sleep 1; done"; then
-        die $LINENO "Zaqar did not start"
-    fi
-}
-
-# stop_zaqar() - Stop running processes
-function stop_zaqar {
-    local serv
-    # Kill the zaqar screen windows
-    for serv in zaqar-server; do
-        screen -S $SCREEN_NAME -p $serv -X kill
-    done
-}
-
-function create_zaqar_accounts {
-    create_service_user "zaqar"
-
-    if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
-
-        get_or_create_service "zaqar" "messaging" "Zaqar Service"
-        get_or_create_endpoint "messaging" \
-            "$REGION_NAME" \
-            "$ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT" \
-            "$ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT" \
-            "$ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT"
-    fi
-
-}
-
-
-# Restore xtrace
-$XTRACE
-
-# Local variables:
-# mode: shell-script
-# End:
diff --git a/openrc b/openrc
index 71ba5a6..9bc0fd7 100644
--- a/openrc
+++ b/openrc
@@ -95,12 +95,6 @@
     fi
 fi
 
-# Currently novaclient needs you to specify the *compute api* version.  This
-# needs to match the config of your catalog returned by Keystone.
-export NOVA_VERSION=${NOVA_VERSION:-1.1}
-# In the future this will change names:
-export COMPUTE_API_VERSION=${COMPUTE_API_VERSION:-$NOVA_VERSION}
-
 # Currently cinderclient needs you to specify the *volume api* version. This
 # needs to match the config of your catalog returned by Keystone.
 export CINDER_VERSION=${CINDER_VERSION:-2}
diff --git a/pkg/elasticsearch.sh b/pkg/elasticsearch.sh
index 79f67a0..14d13cf 100755
--- a/pkg/elasticsearch.sh
+++ b/pkg/elasticsearch.sh
@@ -6,9 +6,7 @@
 # step can probably be factored out to something nicer
 TOP_DIR=$(cd $(dirname "$0")/.. && pwd)
 FILES=$TOP_DIR/files
-source $TOP_DIR/functions
-DEST=${DEST:-/opt/stack}
-source $TOP_DIR/lib/infra
+source $TOP_DIR/stackrc
 
 # Package source and version, all pkg files are expected to have
 # something like this, as well as a way to override them.
diff --git a/samples/local.conf b/samples/local.conf
index ce70073..b92097d 100644
--- a/samples/local.conf
+++ b/samples/local.conf
@@ -28,7 +28,7 @@
 # and they will be added to ``local.conf``.
 SERVICE_TOKEN=azertytoken
 ADMIN_PASSWORD=nomoresecrete
-MYSQL_PASSWORD=stackdb
+DATABASE_PASSWORD=stackdb
 RABBIT_PASSWORD=stackqueue
 SERVICE_PASSWORD=$ADMIN_PASSWORD
 
@@ -93,9 +93,3 @@
 # moved by setting ``SWIFT_DATA_DIR``. The directory will be created
 # if it does not exist.
 SWIFT_DATA_DIR=$DEST/data
-
-# Tempest
-# -------
-
-# Install the tempest test suite
-enable_service tempest
diff --git a/setup.cfg b/setup.cfg
index 5887134..e4b2888 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
     README.md
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
-home-page = http://devstack.org
+home-page = http://docs.openstack.org/developer/devstack
 classifier =
     Intended Audience :: Developers
     License :: OSI Approved :: Apache Software License
diff --git a/stack.sh b/stack.sh
index cc8bc8c..8625b5a 100755
--- a/stack.sh
+++ b/stack.sh
@@ -1,9 +1,8 @@
 #!/usr/bin/env bash
 
 # ``stack.sh`` is an opinionated OpenStack developer installation.  It
-# installs and configures various combinations of **Ceilometer**, **Cinder**,
-# **Glance**, **Heat**, **Horizon**, **Keystone**, **Nova**, **Neutron**,
-# and **Swift**
+# installs and configures various combinations of **Cinder**, **Glance**,
+# **Heat**, **Horizon**, **Keystone**, **Nova**, **Neutron**, and **Swift**
 
 # This script's options can be changed by setting appropriate environment
 # variables.  You can configure things like which git repositories to use,
@@ -21,16 +20,13 @@
 
 # Learn more and get the most recent version at http://devstack.org
 
+# Print the commands being run so that we can see the command that triggers
+# an error.  It is also useful for following along as the install occurs.
+set -o xtrace
+
 # Make sure custom grep options don't get in the way
 unset GREP_OPTIONS
 
-# Sanitize language settings to avoid commands bailing out
-# with "unsupported locale setting" errors.
-unset LANG
-unset LANGUAGE
-LC_ALL=C
-export LC_ALL
-
 # Make sure umask is sane
 umask 022
 
@@ -97,6 +93,29 @@
     exit 1
 fi
 
+# OpenStack is designed to run at a system level, with system level
+# installation of python packages. It does not support running under a
+# virtual env, and will fail in really odd ways if you do this. Make
+# this explicit as it has come up on the mailing list.
+if [[ -n "$VIRTUAL_ENV" ]]; then
+    echo "You appear to be running under a python virtualenv."
+    echo "DevStack does not support this, as we may break the"
+    echo "virtualenv you are currently in by modifying "
+    echo "external system-level components the virtualenv relies on."
+    echo "We recommend you use a separate virtual-machine if "
+    echo "you are worried about DevStack taking over your system."
+    exit 1
+fi
+
+# Provide a safety switch for devstack. If you do a lot of devstack,
+# on a lot of different environments, you sometimes run it on the
+# wrong box. This makes there be a way to prevent that.
+if [[ -e $HOME/.no-devstack ]]; then
+    echo "You've marked this host as a no-devstack host, to save yourself from"
+    echo "running devstack accidentally. If this is in error, please remove the"
+    echo "~/.no-devstack file"
+    exit 1
+fi
 
 # Prepare the environment
 # -----------------------
@@ -173,7 +192,7 @@
 
 # Warn users who aren't on an explicitly supported distro, but allow them to
 # override check and attempt installation with ``FORCE=yes ./stack``
-if [[ ! ${DISTRO} =~ (precise|trusty|utopic|vivid|7.0|wheezy|sid|testing|jessie|f21|f22|rhel7) ]]; then
+if [[ ! ${DISTRO} =~ (trusty|vivid|wily|7.0|wheezy|sid|testing|jessie|f21|f22|f23|rhel7) ]]; then
     echo "WARNING: this script has not been tested on $DISTRO"
     if [[ "$FORCE" != "yes" ]]; then
         die $LINENO "If you wish to run this script anyway run with FORCE=yes"
@@ -282,14 +301,7 @@
     # ... and also optional to be enabled
     sudo yum-config-manager --enable rhel-7-server-optional-rpms
 
-    RHEL_RDO_REPO_RPM=${RHEL7_RDO_REPO_RPM:-"https://repos.fedorapeople.org/repos/openstack/openstack-kilo/rdo-release-kilo-1.noarch.rpm"}
-    RHEL_RDO_REPO_ID=${RHEL7_RDO_REPO_ID:-"openstack-kilo"}
-
-    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
+    sudo yum install -y https://rdoproject.org/repos/rdo-release.rpm
 
     if is_oraclelinux; then
         sudo yum-config-manager --enable ol7_optional_latest ol7_addons ol7_MySQL56
@@ -310,9 +322,6 @@
 safe_chown -R $STACK_USER $DEST
 safe_chmod 0755 $DEST
 
-# Basic test for ``$DEST`` path permissions (fatal on error unless skipped)
-check_path_perm_sanity ${DEST}
-
 # Destination path for service data
 DATA_DIR=${DATA_DIR:-${DEST}/data}
 sudo mkdir -p $DATA_DIR
@@ -326,6 +335,10 @@
     sudo sed -i "s/\(^127.0.0.1.*\)/\1 $LOCAL_HOSTNAME/" /etc/hosts
 fi
 
+# Ensure python is installed
+# --------------------------
+is_package_installed python || install_package python
+
 
 # Configure Logging
 # -----------------
@@ -431,7 +444,7 @@
 
 # Set up logging of screen windows
 # Set ``SCREEN_LOGDIR`` to turn on logging of screen windows to the
-# directory specified in ``SCREEN_LOGDIR``, we will log to the the file
+# directory specified in ``SCREEN_LOGDIR``, we will log to the file
 # ``screen-$SERVICE_NAME-$TIMESTAMP.log`` in that dir and have a link
 # ``screen-$SERVICE_NAME.log`` to the latest log file.
 # Logs are kept for as long specified in ``LOGDAYS``.
@@ -447,6 +460,8 @@
     fi
 fi
 
+# Basic test for ``$DEST`` path permissions (fatal on error unless skipped)
+check_path_perm_sanity ${DEST}
 
 # Configure Error Traps
 # ---------------------
@@ -494,10 +509,6 @@
 # Begin trapping error exit codes
 set -o errexit
 
-# Print the commands being run so that we can see the command that triggers
-# an error.  It is also useful for following along as the install occurs.
-set -o xtrace
-
 # Print the kernel version
 uname -a
 
@@ -522,7 +533,7 @@
 # Clone all external plugins
 fetch_plugins
 
-# Plugin Phase 0: override_defaults - allow pluggins to override
+# Plugin Phase 0: override_defaults - allow plugins to override
 # defaults before other services are run
 run_phase override_defaults
 
@@ -542,11 +553,11 @@
 source $TOP_DIR/lib/nova
 source $TOP_DIR/lib/cinder
 source $TOP_DIR/lib/swift
-source $TOP_DIR/lib/ceilometer
 source $TOP_DIR/lib/heat
 source $TOP_DIR/lib/neutron-legacy
 source $TOP_DIR/lib/ldap
 source $TOP_DIR/lib/dstat
+source $TOP_DIR/lib/dlm
 
 # Extras Source
 # --------------
@@ -554,6 +565,7 @@
 # Phase: source
 run_phase source
 
+
 # Interactive Configuration
 # -------------------------
 
@@ -569,7 +581,7 @@
     if [[ -f $RC_DIR/localrc ]]; then
         localrc=$TOP_DIR/localrc
     else
-        localrc=$TOP_DIR/.localrc.auto
+        localrc=$TOP_DIR/.localrc.password
     fi
 
     # If the password is not defined yet, proceed to prompt user for a password.
@@ -579,13 +591,15 @@
             touch $localrc
         fi
 
-        # Presumably if we got this far it can only be that our localrc is missing
-        # the required password.  Prompt user for a password and write to localrc.
+        # Presumably if we got this far it can only be that our
+        # localrc is missing the required password.  Prompt user for a
+        # password and write to localrc.
+
         echo ''
         echo '################################################################################'
         echo $msg
         echo '################################################################################'
-        echo "This value will be written to your localrc file so you don't have to enter it "
+        echo "This value will be written to ${localrc} file so you don't have to enter it "
         echo "again.  Use only alphanumeric characters."
         echo "If you leave this blank, a random default value will be used."
         pw=" "
@@ -683,14 +697,16 @@
 
 # OpenStack uses a fair number of other projects.
 
+# Bring down global requirements before any use of pip_install. This is
+# necessary to ensure that the constraints file is in place before we
+# attempt to apply any constraints to pip installs.
+git_clone $REQUIREMENTS_REPO $REQUIREMENTS_DIR $REQUIREMENTS_BRANCH
+
 # Install package requirements
 # Source it so the entire environment is available
 echo_summary "Installing package prerequisites"
 source $TOP_DIR/tools/install_prereqs.sh
 
-# Normalise USE_CONSTRAINTS
-USE_CONSTRAINTS=$(trueorfalse False USE_CONSTRAINTS)
-
 # Configure an appropriate Python environment
 if [[ "$OFFLINE" != "True" ]]; then
     PYPI_ALTERNATIVE_URL=${PYPI_ALTERNATIVE_URL:-""} $TOP_DIR/tools/install_pip.sh
@@ -719,12 +735,6 @@
 # Install required infra support libraries
 install_infra
 
-# Pre-build some problematic wheels
-if [[ -n ${WHEELHOUSE:-} && ! -d ${WHEELHOUSE:-} ]]; then
-    source $TOP_DIR/tools/build_wheels.sh
-fi
-
-
 # Extras Pre-install
 # ------------------
 # Phase: pre-install
@@ -732,8 +742,14 @@
 
 install_rpc_backend
 
+# NOTE(sdague): dlm install is conditional on one being enabled by configuration
+install_dlm
+configure_dlm
+
 if is_service_enabled $DATABASE_BACKENDS; then
     install_database
+fi
+if [ -n "$DATABASE_TYPE" ]; then
     install_database_python
 fi
 
@@ -824,13 +840,6 @@
     configure_horizon
 fi
 
-if is_service_enabled ceilometer; then
-    install_ceilometerclient
-    stack_install_service ceilometer
-    echo_summary "Configuring Ceilometer"
-    configure_ceilometer
-fi
-
 if is_service_enabled heat; then
     stack_install_service heat
     install_heat_other
@@ -916,8 +925,8 @@
 restart_rpc_backend
 
 
-# Export Certicate Authority Bundle
-# ---------------------------------
+# Export Certificate Authority Bundle
+# -----------------------------------
 
 # If certificates were used and written to the SSL bundle file then these
 # should be exported so clients can validate their connections.
@@ -987,13 +996,15 @@
         start_keystone
     fi
 
+    export OS_IDENTITY_API_VERSION=3
+
     # Set up a temporary admin URI for Keystone
-    SERVICE_ENDPOINT=$KEYSTONE_AUTH_URI/v2.0
+    SERVICE_ENDPOINT=$KEYSTONE_AUTH_URI/v3
 
     if is_service_enabled tls-proxy; then
         export OS_CACERT=$INT_CA_DIR/ca-chain.pem
         # Until the client support is fixed, just use the internal endpoint
-        SERVICE_ENDPOINT=http://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT_INT/v2.0
+        SERVICE_ENDPOINT=http://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT_INT/v3
     fi
 
     # Setup OpenStackClient token-endpoint auth
@@ -1006,10 +1017,6 @@
     create_cinder_accounts
     create_neutron_accounts
 
-    if is_service_enabled ceilometer; then
-        create_ceilometer_accounts
-    fi
-
     if is_service_enabled swift; then
         create_swift_accounts
     fi
@@ -1021,17 +1028,32 @@
     # Begone token auth
     unset OS_TOKEN OS_URL
 
-    # force set to use v2 identity authentication even with v3 commands
-    export OS_AUTH_TYPE=v2password
+    # Rather than just export these, we write them out to a
+    # intermediate userrc file that can also be used to debug if
+    # something goes wrong between here and running
+    # tools/create_userrc.sh (this script relies on services other
+    # than keystone being available, so we can't call it right now)
+    cat > $TOP_DIR/userrc_early <<EOF
+# Use this for debugging issues before files in accrc are created
 
-    # Set up password auth credentials now that Keystone is bootstrapped
-    export OS_AUTH_URL=$SERVICE_ENDPOINT
-    export OS_TENANT_NAME=admin
-    export OS_USERNAME=admin
-    export OS_PASSWORD=$ADMIN_PASSWORD
-    export OS_REGION_NAME=$REGION_NAME
+# Set up password auth credentials now that Keystone is bootstrapped
+export OS_AUTH_URL=$KEYSTONE_AUTH_URI
+export OS_USERNAME=admin
+export OS_USER_DOMAIN_ID=default
+export OS_PASSWORD=$ADMIN_PASSWORD
+export OS_PROJECT_NAME=admin
+export OS_PROJECT_DOMAIN_ID=default
+export OS_REGION_NAME=$REGION_NAME
+
+EOF
+
+    source $TOP_DIR/userrc_early
+
 fi
 
+# Write a clouds.yaml file
+write_clouds_yaml
+
 # Horizon
 # -------
 
@@ -1181,8 +1203,6 @@
 # See https://help.ubuntu.com/community/CloudInit for more on ``cloud-init``
 
 if is_service_enabled g-reg; then
-    TOKEN=$(openstack token issue -c id -f value)
-    die_if_not_set $LINENO TOKEN "Keystone fail to get token"
 
     echo_summary "Uploading images"
 
@@ -1192,7 +1212,7 @@
     fi
 
     for image_url in ${IMAGE_URLS//,/ }; do
-        upload_image $image_url $TOKEN
+        upload_image $image_url
     done
 fi
 
@@ -1253,11 +1273,6 @@
     start_cinder
     create_volume_types
 fi
-if is_service_enabled ceilometer; then
-    echo_summary "Starting Ceilometer"
-    init_ceilometer
-    start_ceilometer
-fi
 
 # Configure and launch Heat engine, api and metadata
 if is_service_enabled heat; then
@@ -1298,43 +1313,6 @@
 # Save some values we generated for later use
 save_stackenv
 
-# Update/create user clouds.yaml file.
-# clouds.yaml will have
-# - A `devstack` entry for the `demo` user for the `demo` project.
-# - A `devstack-admin` entry for the `admin` user for the `admin` project.
-
-# The location is a variable to allow for easier refactoring later to make it
-# overridable. There is currently no usecase where doing so makes sense, so
-# it's not currently configurable.
-CLOUDS_YAML=~/.config/openstack/clouds.yaml
-
-mkdir -p $(dirname $CLOUDS_YAML)
-
-CA_CERT_ARG=''
-if [ -f "$SSL_BUNDLE_FILE" ]; then
-    CA_CERT_ARG="--os-cacert $SSL_BUNDLE_FILE"
-fi
-$TOP_DIR/tools/update_clouds_yaml.py \
-    --file $CLOUDS_YAML \
-    --os-cloud devstack \
-    --os-region-name $REGION_NAME \
-    --os-identity-api-version $IDENTITY_API_VERSION \
-    $CA_CERT_ARG \
-    --os-auth-url $KEYSTONE_AUTH_URI/v$IDENTITY_API_VERSION \
-    --os-username demo \
-    --os-password $ADMIN_PASSWORD \
-    --os-project-name demo
-$TOP_DIR/tools/update_clouds_yaml.py \
-    --file $CLOUDS_YAML \
-    --os-cloud devstack-admin \
-    --os-region-name $REGION_NAME \
-    --os-identity-api-version $IDENTITY_API_VERSION \
-    $CA_CERT_ARG \
-    --os-auth-url $KEYSTONE_AUTH_URI/v$IDENTITY_API_VERSION \
-    --os-username admin \
-    --os-password $ADMIN_PASSWORD \
-    --os-project-name admin
-
 
 # Wrapup configuration
 # ====================
@@ -1371,9 +1349,16 @@
     $TOP_DIR/local.sh
 fi
 
+# Sanity checks
+# =============
+
 # Check the status of running services
 service_check
 
+# ensure that all the libraries we think we installed from git,
+# actually were.
+check_libs_from_git
+
 
 # Bash completion
 # ===============
@@ -1406,6 +1391,8 @@
     exec 1>&3
 fi
 
+# Dump out the time totals
+time_totals
 
 # Using the cloud
 # ===============
@@ -1421,7 +1408,7 @@
 # If you installed Horizon on this server you should be able
 # to access the site using your browser.
 if is_service_enabled horizon; then
-    echo "Horizon is now available at http://$SERVICE_HOST/"
+    echo "Horizon is now available at http://$SERVICE_HOST$HORIZON_APACHE_ROOT"
 fi
 
 # If Keystone is present you can point ``nova`` cli to this server
diff --git a/stackrc b/stackrc
index 8beef96..23a4a7c 100644
--- a/stackrc
+++ b/stackrc
@@ -2,6 +2,18 @@
 #
 # stackrc
 #
+
+# ensure we don't re-source this in the same environment
+[[ -z "$_DEVSTACK_STACKRC" ]] || return 0
+declare -r _DEVSTACK_STACKRC=1
+
+# Sanitize language settings to avoid commands bailing out
+# with "unsupported locale setting" errors.
+unset LANG
+unset LANGUAGE
+LC_ALL=C
+export LC_ALL
+
 # Find the other rc files
 RC_DIR=$(cd $(dirname "${BASH_SOURCE:-$0}") && pwd)
 
@@ -78,21 +90,33 @@
 # services will rely on the local toggle variable (e.g. ``KEYSTONE_USE_MOD_WSGI``)
 ENABLE_HTTPD_MOD_WSGI_SERVICES=True
 
-# Tell Tempest which services are available.  The default is set here as
-# Tempest falls late in the configuration sequence.  This differs from
-# ``ENABLED_SERVICES`` in that the project names are used here rather than
-# the service names, i.e.: ``TEMPEST_SERVICES="key,glance,nova"``
-TEMPEST_SERVICES=""
-
 # Set the default Nova APIs to enable
 NOVA_ENABLED_APIS=ec2,osapi_compute,metadata
 
+# Set the root URL for Horizon
+HORIZON_APACHE_ROOT="/dashboard"
+
 # Whether to use 'dev mode' for screen windows. Dev mode works by
 # stuffing text into the screen windows so that a developer can use
 # ctrl-c, up-arrow, enter to restart the service. Starting services
 # this way is slightly unreliable, and a bit slower, so this can
 # be disabled for automated testing by setting this value to False.
-USE_SCREEN=True
+USE_SCREEN=$(trueorfalse True USE_SCREEN)
+
+# When using screen, should we keep a log file on disk?  You might
+# want this False if you have a long-running setup where verbose logs
+# can fill-up the host.
+# XXX: Ideally screen itself would be configured to log but just not
+# activate.  This isn't possible with the screerc syntax.  Temporary
+# logging can still be used by a developer with:
+#    C-a : logfile foo
+#    C-a : log on
+SCREEN_IS_LOGGING=$(trueorfalse True SCREEN_IS_LOGGING)
+
+# Passwords generated by interactive devstack runs
+if [[ -r $RC_DIR/.localrc.password ]]; then
+    source $RC_DIR/.localrc.password
+fi
 
 # allow local overrides of env variables, including repo config
 if [[ -f $RC_DIR/localrc ]]; then
@@ -129,11 +153,6 @@
 # requirmenets files here, in a comma-separated list
 ADDITIONAL_VENV_PACKAGES=${ADITIONAL_VENV_PACKAGES:-""}
 
-# Configure wheel cache location
-export WHEELHOUSE=${WHEELHOUSE:-$DEST/.wheelhouse}
-export PIP_WHEEL_DIR=${PIP_WHEEL_DIR:-$WHEELHOUSE}
-export PIP_FIND_LINKS=${PIP_FIND_LINKS:-file://$WHEELHOUSE}
-
 # This can be used to turn database query logging on and off
 # (currently only implemented for MySQL backend)
 DATABASE_QUERY_LOGGING=$(trueorfalse False DATABASE_QUERY_LOGGING)
@@ -149,13 +168,6 @@
 # Zero disables timeouts
 GIT_TIMEOUT=${GIT_TIMEOUT:-0}
 
-# Constraints mode
-# - False (default) : update git projects dependencies from global-requirements.
-#
-# - True : use upper-constraints.txt to constrain versions of packages intalled
-#          and do not edit projects at all.
-USE_CONSTRAINTS=$(trueorfalse False USE_CONSTRAINTS)
-
 # Repositories
 # ------------
 
@@ -163,6 +175,9 @@
 # Another option is https://git.openstack.org
 GIT_BASE=${GIT_BASE:-git://git.openstack.org}
 
+# The location of REQUIREMENTS once cloned
+REQUIREMENTS_DIR=$DEST/requirements
+
 # Which libraries should we install from git instead of using released
 # versions on pypi?
 #
@@ -183,10 +198,6 @@
 #
 ##############
 
-# telemetry service
-CEILOMETER_REPO=${CEILOMETER_REPO:-${GIT_BASE}/openstack/ceilometer.git}
-CEILOMETER_BRANCH=${CEILOMETER_BRANCH:-master}
-
 # block storage service
 CINDER_REPO=${CINDER_REPO:-${GIT_BASE}/openstack/cinder.git}
 CINDER_BRANCH=${CINDER_BRANCH:-master}
@@ -256,14 +267,10 @@
 
 ##############
 #
-#  OpenStack Client Library Componets
+#  OpenStack Client Library Components
 #
 ##############
 
-# ceilometer client library
-GITREPO["python-ceilometerclient"]=${CEILOMETERCLIENT_REPO:-${GIT_BASE}/openstack/python-ceilometerclient.git}
-GITBRANCH["python-ceilometerclient"]=${CEILOMETERCLIENT_BRANCH:-master}
-
 # volume client
 GITREPO["python-cinderclient"]=${CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-cinderclient.git}
 GITBRANCH["python-cinderclient"]=${CINDERCLIENT_BRANCH:-master}
@@ -369,6 +376,10 @@
 GITREPO["oslo.policy"]=${OSLOPOLICY_REPO:-${GIT_BASE}/openstack/oslo.policy.git}
 GITBRANCH["oslo.policy"]=${OSLOPOLICY_BRANCH:-master}
 
+# oslo.privsep
+GITREPO["oslo.privsep"]=${OSLOPRIVSEP_REPO:-${GIT_BASE}/openstack/oslo.privsep.git}
+GITBRANCH["oslo.privsep"]=${OSLOPRIVSEP_BRANCH:-master}
+
 # oslo.reports
 GITREPO["oslo.reports"]=${OSLOREPORTS_REPO:-${GIT_BASE}/openstack/oslo.reports.git}
 GITBRANCH["oslo.reports"]=${OSLOREPORTS_BRANCH:-master}
@@ -445,17 +456,22 @@
 GITBRANCH["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_BRANCH:-master}
 
 # s3 support for swift
-SWIFT3_REPO=${SWIFT3_REPO:-${GIT_BASE}/stackforge/swift3.git}
+SWIFT3_REPO=${SWIFT3_REPO:-${GIT_BASE}/openstack/swift3.git}
 SWIFT3_BRANCH=${SWIFT3_BRANCH:-master}
 
 # ceilometer middleware
 GITREPO["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_REPO:-${GIT_BASE}/openstack/ceilometermiddleware.git}
 GITBRANCH["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_BRANCH:-master}
+GITDIR["ceilometermiddleware"]=$DEST/ceilometermiddleware
 
 # os-brick library to manage local volume attaches
 GITREPO["os-brick"]=${OS_BRICK_REPO:-${GIT_BASE}/openstack/os-brick.git}
 GITBRANCH["os-brick"]=${OS_BRICK_BRANCH:-master}
 
+# ironic common lib
+GITREPO["ironic-lib"]=${IRONIC_LIB_REPO:-${GIT_BASE}/openstack/ironic-lib.git}
+GITBRANCH["ironic-lib"]=${IRONIC_LIB_BRANCH:-master}
+
 
 ##################
 #
@@ -561,40 +577,47 @@
 # Set default image based on ``VIRT_DRIVER`` and ``LIBVIRT_TYPE``, either of
 # which may be set in ``local.conf``.  Also allow ``DEFAULT_IMAGE_NAME`` and
 # ``IMAGE_URLS`` to be set in the `localrc` section of ``local.conf``.
-case "$VIRT_DRIVER" in
-    openvz)
-        DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ubuntu-12.04-x86_64}
-        IMAGE_URLS=${IMAGE_URLS:-"http://download.openvz.org/template/precreated/ubuntu-12.04-x86_64.tar.gz"};;
-    libvirt)
-        case "$LIBVIRT_TYPE" in
-            lxc) # the cirros root disk in the uec tarball is empty, so it will not work for lxc
-                DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-rootfs}
-                IMAGE_URLS=${IMAGE_URLS:-"http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-rootfs.img.gz"};;
-            *) # otherwise, use the uec style image (with kernel, ramdisk, disk)
-                DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec}
-                IMAGE_URLS=${IMAGE_URLS:-"http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec.tar.gz"};;
-        esac
-        ;;
-    vsphere)
-        DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-0.3.2-i386-disk.vmdk}
-        IMAGE_URLS=${IMAGE_URLS:-"http://partnerweb.vmware.com/programs/vmdkimage/cirros-0.3.2-i386-disk.vmdk"};;
-    xenserver)
-        DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-0.3.4-x86_64-disk}
-        IMAGE_URLS=${IMAGE_URLS:-"http://ca.downloads.xensource.com/OpenStack/cirros-0.3.4-x86_64-disk.vhd.tgz"}
-        IMAGE_URLS+=",http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-uec.tar.gz";;
-    ironic)
-        # Ironic can do both partition and full disk images, depending on the driver
-        if [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]]; then
-            DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-x86_64-disk}
-        else
-            DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-x86_64-uec}
-        fi
-        IMAGE_URLS=${IMAGE_URLS:-"http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-uec.tar.gz"}
-        IMAGE_URLS+=",http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-disk.img";;
-    *) # Default to Cirros with kernel, ramdisk and disk image
-        DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec}
-        IMAGE_URLS=${IMAGE_URLS:-"http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec.tar.gz"};;
-esac
+DOWNLOAD_DEFAULT_IMAGES=$(trueorfalse True DOWNLOAD_DEFAULT_IMAGES)
+if [[ "$DOWNLOAD_DEFAULT_IMAGES" == "True" ]]; then
+    if [[ -n "$IMAGE_URLS" ]]; then
+        IMAGE_URLS+=","
+    fi
+    case "$VIRT_DRIVER" in
+        openvz)
+            DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ubuntu-12.04-x86_64}
+            IMAGE_URLS+="http://download.openvz.org/template/precreated/ubuntu-12.04-x86_64.tar.gz";;
+        libvirt)
+            case "$LIBVIRT_TYPE" in
+                lxc) # the cirros root disk in the uec tarball is empty, so it will not work for lxc
+                    DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-rootfs}
+                    IMAGE_URLS+="http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-rootfs.img.gz";;
+                *) # otherwise, use the uec style image (with kernel, ramdisk, disk)
+                    DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec}
+                    IMAGE_URLS+="http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec.tar.gz";;
+                esac
+            ;;
+        vsphere)
+            DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-0.3.2-i386-disk.vmdk}
+            IMAGE_URLS+="http://partnerweb.vmware.com/programs/vmdkimage/cirros-0.3.2-i386-disk.vmdk";;
+        xenserver)
+            DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-0.3.4-x86_64-disk}
+            IMAGE_URLS+="http://ca.downloads.xensource.com/OpenStack/cirros-0.3.4-x86_64-disk.vhd.tgz"
+            IMAGE_URLS+=",http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-uec.tar.gz";;
+        ironic)
+            # Ironic can do both partition and full disk images, depending on the driver
+            if [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]]; then
+                DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-x86_64-disk}
+            else
+                DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-x86_64-uec}
+            fi
+            IMAGE_URLS+="http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-uec.tar.gz"
+            IMAGE_URLS+=",http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-disk.img";;
+        *) # Default to Cirros with kernel, ramdisk and disk image
+            DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec}
+            IMAGE_URLS+="http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec.tar.gz";;
+    esac
+    DOWNLOAD_DEFAULT_IMAGES=False
+fi
 
 # Staging Area for New Images, have them here for at least 24hrs for nodepool
 # to cache them otherwise the failure rates in the gate are too high
@@ -607,6 +630,13 @@
     fi
 fi
 
+# Detect duplicate values in IMAGE_URLS
+for image_url in ${IMAGE_URLS//,/ }; do
+    if [ $(echo "$IMAGE_URLS" | grep -o -F "$image_url" | wc -l) -gt 1 ]; then
+        die $LINENO "$image_url is duplicate, please remove it from IMAGE_URLS."
+    fi
+done
+
 # 10Gb default volume backing file size
 VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE:-10250M}
 
@@ -621,15 +651,9 @@
 PRIVATE_NETWORK_NAME=${PRIVATE_NETWORK_NAME:-"private"}
 PUBLIC_NETWORK_NAME=${PUBLIC_NETWORK_NAME:-"public"}
 
-# Compatibility until it's eradicated from CI
-USE_SCREEN=${SCREEN_DEV:-$USE_SCREEN}
-
 # Set default screen name
 SCREEN_NAME=${SCREEN_NAME:-stack}
 
-# Undo requirements changes by global requirements
-UNDO_REQUIREMENTS=${UNDO_REQUIREMENTS:-True}
-
 # Allow the use of an alternate protocol (such as https) for service endpoints
 SERVICE_PROTOCOL=${SERVICE_PROTOCOL:-http}
 
@@ -637,7 +661,12 @@
 # the memory used where there are a large number of CPUs present
 # (the default number of workers for many services is the number of CPUs)
 # Also sets the minimum number of workers to 2.
-API_WORKERS=${API_WORKERS:=$(( ($(nproc)/2)<2 ? 2 : ($(nproc)/2) ))}
+if [[ "$VIRT_DRIVER" = 'fake' ]]; then
+    # we need more workers for the large ops job
+    API_WORKERS=${API_WORKERS:=$(( ($(nproc)/2)<2 ? 2 : ($(nproc)/2) ))}
+else
+    API_WORKERS=${API_WORKERS:=$(( ($(nproc)/4)<2 ? 2 : ($(nproc)/4) ))}
+fi
 
 # Service startup timeout
 SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}
@@ -737,6 +766,16 @@
 # Use native SSL for servers in ``SSL_ENABLED_SERVICES``
 USE_SSL=$(trueorfalse False USE_SSL)
 
+# ebtables is inherently racey. If you run it by two or more processes
+# simultaneously it will collide, badly, in the kernel and produce
+# failures or corruption of ebtables. The only way around it is for
+# all tools running ebtables to only ever do so with the --concurrent
+# flag. This requires libvirt >= 1.2.11.
+#
+# If you don't have this then the following work around will replace
+# ebtables with a wrapper script so that it is safe to run without
+# that flag.
+EBTABLES_RACE_FIX=$(trueorfalse False EBTABLES_RACE_FIX)
 
 # Following entries need to be last items in file
 
diff --git a/tests/test_functions.sh b/tests/test_functions.sh
index f555de8..be8dc5e 100755
--- a/tests/test_functions.sh
+++ b/tests/test_functions.sh
@@ -245,4 +245,33 @@
     passed "OK"
 fi
 
+function test_export_proxy_variables {
+    echo "Testing export_proxy_variables()"
+
+    local expected results
+
+    http_proxy=http_proxy_test
+    https_proxy=https_proxy_test
+    no_proxy=no_proxy_test
+
+    export_proxy_variables
+    expected=$(echo -e "http_proxy=$http_proxy\nhttps_proxy=$https_proxy\nno_proxy=$no_proxy")
+    results=$(env | egrep '(http(s)?|no)_proxy=')
+    if [[ $expected = $results ]]; then
+        passed "OK: Proxy variables are exported when proxy variables are set"
+    else
+        failed "Expected: $expected, Failed: $results"
+    fi
+
+    unset http_proxy https_proxy no_proxy
+    export_proxy_variables
+    results=$(env | egrep '(http(s)?|no)_proxy=')
+    if [[ "" = $results ]]; then
+        passed "OK: Proxy variables aren't exported when proxy variables aren't set"
+    else
+        failed "Expected: '', Failed: $results"
+    fi
+}
+test_export_proxy_variables
+
 report_results
diff --git a/tests/test_ini_config.sh b/tests/test_ini_config.sh
index b2529ac..d9cb8d8 100755
--- a/tests/test_ini_config.sh
+++ b/tests/test_ini_config.sh
@@ -13,7 +13,13 @@
 
 echo "Testing INI functions"
 
-cat >test.ini <<EOF
+INI_TMP_DIR=$(mktemp -d)
+INI_TMP_ETC_DIR=$INI_TMP_DIR/etc
+TEST_INI=${INI_TMP_ETC_DIR}/test.ini
+mkdir ${INI_TMP_ETC_DIR}
+
+echo "Creating $TEST_INI"
+cat >${TEST_INI} <<EOF
 [default]
 # comment an option
 #log_file=./log.conf
@@ -65,206 +71,108 @@
 
 EOF
 
+# set TEST_SUDO to test writing to root-owned files
+SUDO_ARG=""
+SUDO=""
+if [ -n "$TEST_SUDO" ]; then
+    SUDO="sudo "
+    SUDO_ARG="-sudo "
+    sudo chown -R root:root ${INI_TMP_ETC_DIR}
+fi
+
 # Test with missing arguments
+BEFORE=$(cat ${TEST_INI})
 
-BEFORE=$(cat test.ini)
+iniset ${SUDO_ARG} ${TEST_INI} aaa
+NO_ATTRIBUTE=$(cat ${TEST_INI})
+assert_equal "$BEFORE" "$NO_ATTRIBUTE" "test missing attribute argument"
 
-echo -n "iniset: test missing attribute argument: "
-iniset test.ini aaa
-NO_ATTRIBUTE=$(cat test.ini)
-if [[ "$BEFORE" == "$NO_ATTRIBUTE" ]]; then
-    passed
-else
-    failed "failed"
-fi
+iniset ${SUDO_ARG} ${TEST_INI}
+NO_SECTION=$(cat ${TEST_INI})
+assert_equal "$BEFORE" "$NO_SECTION" "missing section argument"
 
-echo -n "iniset: test missing section argument: "
-iniset test.ini
-NO_SECTION=$(cat test.ini)
-if [[ "$BEFORE" == "$NO_SECTION" ]]; then
-    passed
-else
-    failed "failed"
-fi
+# Test with spaces in values
+VAL=$(iniget ${TEST_INI} aaa handlers)
+assert_equal "$VAL" "aa, bb" "iniget spaces in option"
 
-# Test with spaces
-
-VAL=$(iniget test.ini aaa handlers)
-if [[ "$VAL" == "aa, bb" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
-
-iniset test.ini aaa handlers "11, 22"
-
-VAL=$(iniget test.ini aaa handlers)
-if [[ "$VAL" == "11, 22" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
+iniset ${SUDO_ARG} ${TEST_INI} aaa handlers "11, 22"
+VAL=$(iniget ${TEST_INI} aaa handlers)
+assert_equal "$VAL" "11, 22" "iniset spaces in option"
 
 # Test with spaces in section header
+VAL=$(iniget ${TEST_INI} " ccc " spaces)
+assert_equal "$VAL" "yes" "iniget with section header space"
 
-VAL=$(iniget test.ini " ccc " spaces)
-if [[ "$VAL" == "yes" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
-
-iniset test.ini "b b" opt_ion 42
-
-VAL=$(iniget test.ini "b b" opt_ion)
-if [[ "$VAL" == "42" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
+iniset ${SUDO_ARG} ${TEST_INI} "b b" opt_ion 42
+VAL=$(iniget ${TEST_INI} "b b" opt_ion)
+assert_equal "$VAL" "42" "iniset with section header space"
 
 # Test without spaces, end of file
+VAL=$(iniget ${TEST_INI} bbb handlers)
+assert_equal "$VAL" "ee,ff" "iniget at EOF"
 
-VAL=$(iniget test.ini bbb handlers)
-if [[ "$VAL" == "ee,ff" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
-
-iniset test.ini bbb handlers "33,44"
-
-VAL=$(iniget test.ini bbb handlers)
-if [[ "$VAL" == "33,44" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
+iniset ${SUDO_ARG} ${TEST_INI} bbb handlers "33,44"
+VAL=$(iniget ${TEST_INI} bbb handlers)
+assert_equal "$VAL" "33,44" "inset at EOF"
 
 # test empty option
-if ini_has_option test.ini ddd empty; then
-    passed "OK: ddd.empty present"
+if ini_has_option ${TEST_INI} ddd empty; then
+    passed "ini_has_option: ddd.empty present"
 else
     failed "ini_has_option failed: ddd.empty not found"
 fi
 
 # test non-empty option
-if ini_has_option test.ini bbb handlers; then
-    passed "OK: bbb.handlers present"
+if ini_has_option ${TEST_INI} bbb handlers; then
+    passed "ini_has_option: bbb.handlers present"
 else
     failed "ini_has_option failed: bbb.handlers not found"
 fi
 
 # test changing empty option
-iniset test.ini ddd empty "42"
-
-VAL=$(iniget test.ini ddd empty)
-if [[ "$VAL" == "42" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
+iniset ${SUDO_ARG} ${TEST_INI} ddd empty "42"
+VAL=$(iniget ${TEST_INI} ddd empty)
+assert_equal "$VAL" "42" "change empty option"
 
 # test pipe in option
-iniset test.ini aaa handlers "a|b"
-
-VAL=$(iniget test.ini aaa handlers)
-if [[ "$VAL" == "a|b" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
-
-# test space in option
-iniset test.ini aaa handlers "a b"
-
-VAL="$(iniget test.ini aaa handlers)"
-if [[ "$VAL" == "a b" ]]; then
-    passed "OK: $VAL"
-else
-    failed "iniget failed: $VAL"
-fi
+iniset ${SUDO_ARG} ${TEST_INI} aaa handlers "a|b"
+VAL=$(iniget ${TEST_INI} aaa handlers)
+assert_equal "$VAL" "a|b" "pipe in option"
 
 # Test section not exist
-
-VAL=$(iniget test.ini zzz handlers)
-if [[ -z "$VAL" ]]; then
-    passed "OK: zzz not present"
-else
-    failed "iniget failed: $VAL"
-fi
-
-iniset test.ini zzz handlers "999"
-
-VAL=$(iniget test.ini zzz handlers)
-if [[ -n "$VAL" ]]; then
-    passed "OK: zzz not present"
-else
-    failed "iniget failed: $VAL"
-fi
+VAL=$(iniget ${TEST_INI} zzz handlers)
+assert_empty VAL "section does not exist"
 
 # Test option not exist
+VAL=$(iniget ${TEST_INI} aaa debug)
+assert_empty VAL "option does not exist"
 
-VAL=$(iniget test.ini aaa debug)
-if [[ -z "$VAL" ]]; then
-    passed "OK aaa.debug not present"
-else
-    failed "iniget failed: $VAL"
-fi
-
-if ! ini_has_option test.ini aaa debug; then
-    passed "OK aaa.debug not present"
+if ! ini_has_option ${TEST_INI} aaa debug; then
+    passed "ini_has_option: aaa.debug not present"
 else
     failed "ini_has_option failed: aaa.debug"
 fi
 
-iniset test.ini aaa debug "999"
-
-VAL=$(iniget test.ini aaa debug)
-if [[ -n "$VAL" ]]; then
-    passed "OK aaa.debug present"
-else
-    failed "iniget failed: $VAL"
-fi
-
 # Test comments
-
-inicomment test.ini aaa handlers
-
-VAL=$(iniget test.ini aaa handlers)
-if [[ -z "$VAL" ]]; then
-    passed "OK"
-else
-    failed "inicomment failed: $VAL"
-fi
+inicomment ${SUDO_ARG} ${TEST_INI} aaa handlers
+VAL=$(iniget ${TEST_INI} aaa handlers)
+assert_empty VAL "test inicomment"
 
 # Test multiple line iniset/iniget
-iniset_multiline test.ini eee multi bar1 bar2
+iniset_multiline ${SUDO_ARG} ${TEST_INI} eee multi bar1 bar2
 
-VAL=$(iniget_multiline test.ini eee multi)
-if [[ "$VAL" == "bar1 bar2" ]]; then
-    echo "OK: iniset_multiline"
-else
-    failed "iniset_multiline failed: $VAL"
-fi
+VAL=$(iniget_multiline ${TEST_INI} eee multi)
+assert_equal "$VAL" "bar1 bar2" "iniget_multiline"
 
 # Test iniadd with exiting values
-iniadd test.ini eee multi bar3
-VAL=$(iniget_multiline test.ini eee multi)
-if [[ "$VAL" == "bar1 bar2 bar3" ]]; then
-    passed "OK: iniadd"
-else
-    failed "iniadd failed: $VAL"
-fi
+iniadd ${SUDO_ARG} ${TEST_INI} eee multi bar3
+VAL=$(iniget_multiline ${TEST_INI} eee multi)
+assert_equal "$VAL" "bar1 bar2 bar3" "iniadd with existing values"
 
 # Test iniadd with non-exiting values
-iniadd test.ini eee non-multi foobar1 foobar2
-VAL=$(iniget_multiline test.ini eee non-multi)
-if [[ "$VAL" == "foobar1 foobar2" ]]; then
-    passed "OK: iniadd with non-exiting value"
-else
-    failed "iniadd with non-exsting failed: $VAL"
-fi
+iniadd ${SUDO_ARG} ${TEST_INI} eee non-multi foobar1 foobar2
+VAL=$(iniget_multiline ${TEST_INI} eee non-multi)
+assert_equal "$VAL" "foobar1 foobar2" "iniadd non-existing values"
 
 # Test inidelete
 del_cases="
@@ -276,25 +184,26 @@
     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
-        passed "OK: inidelete $x"
-    else
-        failed "inidelete $x failed: $VAL"
-    fi
+    inidelete ${SUDO_ARG} ${TEST_INI} $x a
+    VAL=$(iniget_multiline ${TEST_INI} $x a)
+    assert_empty VAL "inidelete $x"
     if [ "$x" = "del_separate_options" -o \
         "$x" = "del_missing_option" -o \
         "$x" = "del_missing_option_multi" ]; then
-        VAL=$(iniget_multiline test.ini $x b)
+        VAL=$(iniget_multiline ${TEST_INI} $x b)
         if [ "$VAL" = "c" -o "$VAL" = "c d" ]; then
-            passed "OK: inidelete other_options $x"
+            passed "inidelete other_options $x"
         else
-            failed "inidelete other_option $x failed: $VAL"
+            failed "inidelete other_option $x: $VAL"
         fi
     fi
 done
 
-rm test.ini
+# test file-creation
+iniset $SUDO_ARG ${INI_TMP_ETC_DIR}/test.new.ini test foo bar
+VAL=$(iniget ${INI_TMP_ETC_DIR}/test.new.ini test foo)
+assert_equal "$VAL" "bar" "iniset created file"
+
+$SUDO rm -rf ${INI_TMP_DIR}
 
 report_results
diff --git a/tests/test_libs_from_pypi.sh b/tests/test_libs_from_pypi.sh
index d10cd0e..f31560a 100755
--- a/tests/test_libs_from_pypi.sh
+++ b/tests/test_libs_from_pypi.sh
@@ -37,11 +37,11 @@
 ALL_LIBS+=" oslo.versionedobjects oslo.vmware keystonemiddleware"
 ALL_LIBS+=" oslo.serialization django_openstack_auth"
 ALL_LIBS+=" python-openstackclient oslo.rootwrap oslo.i18n"
-ALL_LIBS+=" python-ceilometerclient oslo.utils python-swiftclient"
+ALL_LIBS+=" oslo.utils python-swiftclient"
 ALL_LIBS+=" python-neutronclient tooz ceilometermiddleware oslo.policy"
 ALL_LIBS+=" debtcollector os-brick automaton futurist oslo.service"
 ALL_LIBS+=" oslo.cache oslo.reports"
-ALL_LIBS+=" keystoneauth"
+ALL_LIBS+=" keystoneauth ironic-lib oslo.privsep"
 
 # Generate the above list with
 # echo ${!GITREPO[@]}
diff --git a/tests/test_meta_config.sh b/tests/test_meta_config.sh
index a04c081..327fb56 100755
--- a/tests/test_meta_config.sh
+++ b/tests/test_meta_config.sh
@@ -23,6 +23,12 @@
     fi
 }
 
+# mock function-common:die so that it does not
+# interrupt our test script
+function die {
+    exit -1
+}
+
 TEST_1C_ADD="[eee]
 type=new
 multi = foo2"
@@ -110,6 +116,15 @@
 [DEFAULT]
 servers=10.11.12.13:80
 
+[[test8|/permission-denied.conf]]
+foo=bar
+
+[[test9|\$UNDEF]]
+foo=bar
+
+[[test10|does-not-exist-dir/test.conf]]
+foo=bar
+
 [[test-multi-sections|test-multi-sections.conf]]
 [sec-1]
 cfg_item1 = abcd
@@ -340,6 +355,36 @@
 servers = 10.11.12.13:80"
 check_result "$VAL" "$EXPECT_VAL"
 
+echo "merge_config_file test8 non-touchable conf file: "
+set +e
+# function is expected to fail and exit, running it
+# in a subprocess to let this script proceed
+(merge_config_file test.conf test8 /permission-denied.conf)
+VAL=$?
+EXPECT_VAL=255
+check_result "$VAL" "$EXPECT_VAL"
+set -e
+
+echo -n "merge_config_group test9 undefined conf file: "
+set +e
+# function is expected to fail and exit, running it
+# in a subprocess to let this script proceed
+(merge_config_group test.conf test9)
+VAL=$?
+EXPECT_VAL=255
+check_result "$VAL" "$EXPECT_VAL"
+set -e
+
+echo -n "merge_config_group test10 not directory: "
+set +e
+# function is expected to fail and exit, running it
+# in a subprocess to let this script proceed
+(merge_config_group test.conf test10)
+VAL=$?
+EXPECT_VAL=255
+check_result "$VAL" "$EXPECT_VAL"
+set -e
+
 rm -f test.conf test1c.conf test2a.conf \
     test-space.conf test-equals.conf test-strip.conf \
     test-colon.conf test-env.conf test-multiline.conf \
diff --git a/tests/test_truefalse.sh b/tests/test_truefalse.sh
index 2689589..03996ce 100755
--- a/tests/test_truefalse.sh
+++ b/tests/test_truefalse.sh
@@ -8,6 +8,14 @@
 source $TOP/functions
 source $TOP/tests/unittest.sh
 
+# common mistake is to use $FOO instead of "FOO"; in that case we
+# should die
+bash -c "source $TOP/functions-common; VAR=\$(trueorfalse False \$FOO)" &> /dev/null
+assert_equal 1 $? "missing test-value"
+
+VAL=$(trueorfalse False MISSING_VARIABLE)
+assert_equal "False" $VAL "blank test-value"
+
 function test_trueorfalse {
     local one=1
     local captrue=True
diff --git a/tests/unittest.sh b/tests/unittest.sh
index 93aa5fc..26b5b8e 100644
--- a/tests/unittest.sh
+++ b/tests/unittest.sh
@@ -20,21 +20,25 @@
 # pass a test, printing out MSG
 #  usage: passed message
 function passed {
-    local lineno=$(caller 0 | awk '{print $1}')
-    local function=$(caller 0 | awk '{print $2}')
+    local lineno
+    lineno=$(caller 0 | awk '{print $1}')
+    local function
+    function=$(caller 0 | awk '{print $2}')
     local msg="$1"
     if [ -z "$msg" ]; then
         msg="OK"
     fi
     PASS=$((PASS+1))
-    echo "PASS: $function:L$lineno $msg"
+    echo "PASS: $function:L$lineno - $msg"
 }
 
 # fail a test, printing out MSG
 #  usage: failed message
 function failed {
-    local lineno=$(caller 0 | awk '{print $1}')
-    local function=$(caller 0 | awk '{print $2}')
+    local lineno
+    lineno=$(caller 0 | awk '{print $1}')
+    local function
+    function=$(caller 0 | awk '{print $2}')
     local msg="$1"
     FAILED_FUNCS+="$function:L$lineno\n"
     echo "ERROR: $function:L$lineno!"
@@ -42,11 +46,13 @@
     ERROR=$((ERROR+1))
 }
 
-# assert string comparision of val1 equal val2, printing out msg
+# assert string comparison of val1 equal val2, printing out msg
 #  usage: assert_equal val1 val2 msg
 function assert_equal {
-    local lineno=`caller 0 | awk '{print $1}'`
-    local function=`caller 0 | awk '{print $2}'`
+    local lineno
+    lineno=`caller 0 | awk '{print $1}'`
+    local function
+    function=`caller 0 | awk '{print $2}'`
     local msg=$3
 
     if [ -z "$msg" ]; then
@@ -63,16 +69,40 @@
     fi
 }
 
-# print a summary of passing and failing tests, exiting
-# with an error if we have failed tests
+# assert variable is empty/blank, printing out msg
+#  usage: assert_empty VAR msg
+function assert_empty {
+    local lineno
+    lineno=`caller 0 | awk '{print $1}'`
+    local function
+    function=`caller 0 | awk '{print $2}'`
+    local msg=$2
+
+    if [ -z "$msg" ]; then
+        msg="OK"
+    fi
+    if [[ ! -z ${!1} ]]; then
+        FAILED_FUNCS+="$function:L$lineno\n"
+        echo "ERROR: $1 not empty in $function:L$lineno!"
+        echo "  $msg"
+        ERROR=$((ERROR+1))
+    else
+        PASS=$((PASS+1))
+        echo "PASS: $function:L$lineno - $msg"
+    fi
+}
+
+# Print a summary of passing and failing tests and exit
+# (with an error if we have failed tests)
 #  usage: report_results
 function report_results {
     echo "$PASS Tests PASSED"
-    if [[ $ERROR -gt 1 ]]; then
+    if [[ $ERROR -gt 0 ]]; then
         echo
         echo "The following $ERROR tests FAILED"
         echo -e "$FAILED_FUNCS"
         echo "---"
         exit 1
     fi
+    exit 0
 }
diff --git a/tools/build_docs.sh b/tools/build_docs.sh
index fa84343..7dc492e 100755
--- a/tools/build_docs.sh
+++ b/tools/build_docs.sh
@@ -81,7 +81,7 @@
     mkdir -p $FQ_HTML_BUILD/`dirname $f`;
     $SHOCCO $f > $FQ_HTML_BUILD/$f.html
 done
-for f in $(find functions functions-common inc lib pkg samples -type f -name \*); do
+for f in $(find functions functions-common inc lib pkg samples -type f -name \* ! -name *.md ! -name *.conf); do
     echo $f
     FILES+="$f "
     mkdir -p $FQ_HTML_BUILD/`dirname $f`;
diff --git a/tools/build_wheels.sh b/tools/build_wheels.sh
deleted file mode 100755
index 14c2999..0000000
--- a/tools/build_wheels.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env bash
-#
-# **tools/build_wheels.sh** - Build a cache of Python wheels
-#
-# build_wheels.sh [package [...]]
-#
-# System package prerequisites listed in ``files/*/devlibs`` will be installed
-#
-# Builds wheels for all virtual env requirements listed in
-# ``venv-requirements.txt`` plus any supplied on the command line.
-#
-# Assumes:
-# - ``tools/install_pip.sh`` has been run and a suitable ``pip/setuptools`` is available.
-
-# If ``TOP_DIR`` is set we're being sourced rather than running stand-alone
-# or in a sub-shell
-if [[ -z "$TOP_DIR" ]]; then
-
-    set -o errexit
-    set -o nounset
-
-    # Keep track of the DevStack directory
-    TOP_DIR=$(cd $(dirname "$0")/.. && pwd)
-    FILES=$TOP_DIR/files
-
-    # Import common functions
-    source $TOP_DIR/functions
-
-    GetDistro
-
-    source $TOP_DIR/stackrc
-
-    trap err_trap ERR
-
-fi
-
-# Get additional packages to build
-MORE_PACKAGES="$@"
-
-# Exit on any errors so that errors don't compound
-function err_trap {
-    local r=$?
-    set +o xtrace
-
-    rm -rf $TMP_VENV_PATH
-
-    exit $r
-}
-
-# Get system prereqs
-install_package $(get_packages devlibs)
-
-# Get a modern ``virtualenv``
-pip_install virtualenv
-
-# Prepare the workspace
-TMP_VENV_PATH=$(mktemp -d tmp-venv-XXXX)
-virtualenv $TMP_VENV_PATH
-
-# Install modern pip and wheel
-PIP_VIRTUAL_ENV=$TMP_VENV_PATH pip_install -U pip wheel
-
-# BUG: cffi has a lot of issues. It has no stable ABI, if installed
-# code is built with a different ABI than the one that's detected at
-# load time, it tries to compile on the fly for the new ABI in the
-# install location (which will probably be /usr and not
-# writable). Also cffi is often included via setup_requires by
-# packages, which have different install rules (allowing betas) than
-# pip has.
-#
-# Because of this we must pip install cffi into the venv to build
-# wheels.
-PIP_VIRTUAL_ENV=$TMP_VENV_PATH pip_install_gr cffi
-
-# ``VENV_PACKAGES`` is a list of packages we want to pre-install
-VENV_PACKAGE_FILE=$FILES/venv-requirements.txt
-if [[ -r $VENV_PACKAGE_FILE ]]; then
-    VENV_PACKAGES=$(grep -v '^#' $VENV_PACKAGE_FILE)
-fi
-
-for pkg in ${VENV_PACKAGES,/ } ${MORE_PACKAGES}; do
-    $TMP_VENV_PATH/bin/pip wheel $pkg
-done
-
-# Clean up wheel workspace
-rm -rf $TMP_VENV_PATH
diff --git a/tools/create_userrc.sh b/tools/create_userrc.sh
index c2dbe1a..25f713c 100755
--- a/tools/create_userrc.sh
+++ b/tools/create_userrc.sh
@@ -158,12 +158,12 @@
 
 export -n SERVICE_TOKEN SERVICE_ENDPOINT OS_SERVICE_TOKEN OS_SERVICE_ENDPOINT
 
-EC2_URL=$(openstack endpoint show -f value -c publicurl ec2 || true)
+EC2_URL=$(openstack endpoint list --service ec2 --interface public --os-identity-api-version=3 -c URL -f value || true)
 if [[ -z $EC2_URL ]]; then
     EC2_URL=http://localhost:8773/
 fi
 
-S3_URL=$(openstack endpoint show -f value -c publicurl s3 || true)
+S3_URL=$(openstack endpoint list --service s3 --interface public --os-identity-api-version=3 -c URL -f value || true)
 if [[ -z $S3_URL ]]; then
     S3_URL=http://localhost:3333
 fi
@@ -190,7 +190,8 @@
     local user_passwd=$5
 
     # The admin user can see all user's secret AWS keys, it does not looks good
-    local line=`openstack ec2 credentials list --user $user_id | grep " $project_id "`
+    local line
+    line=$(openstack ec2 credentials list --user $user_id | grep " $project_id " || true)
     if [ -z "$line" ]; then
         openstack ec2 credentials create --user $user_id --project $project_id 1>&2
         line=`openstack ec2 credentials list --user $user_id | grep " $project_id "`
diff --git a/tools/dstat.sh b/tools/dstat.sh
new file mode 100755
index 0000000..6ba4515
--- /dev/null
+++ b/tools/dstat.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# **tools/dstat.sh** - Execute instances of DStat to log system load info
+#
+# Multiple instances of DStat are executed in order to take advantage of
+# incompatible features, particularly CSV output and the "top-cpu-adv" and
+# "top-io-adv" flags.
+#
+# Assumes:
+#  - dstat command is installed
+
+# Retreive log directory as argument from calling script.
+LOGDIR=$1
+
+# Command line arguments for primary DStat process.
+DSTAT_OPTS="-tcmndrylpg --top-cpu-adv --top-io-adv"
+
+# Command-line arguments for secondary background DStat process.
+DSTAT_CSV_OPTS="-tcmndrylpg --output $LOGDIR/dstat-csv.log"
+
+# Execute and background the secondary dstat process and discard its output.
+dstat $DSTAT_CSV_OPTS >& /dev/null &
+
+# Execute and background the primary dstat process, but keep its output in this
+# TTY.
+dstat $DSTAT_OPTS &
+
+# Catch any exit signals, making sure to also terminate any child processes.
+trap "kill -- -$$" EXIT
+
+# Keep this script running as long as child dstat processes are alive.
+wait
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 4fff57f..9ae2ae7 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -108,7 +108,7 @@
         sudo setenforce 0
     fi
 
-    FORCE_FIREWALLD=$(trueorfalse False $FORCE_FIREWALLD)
+    FORCE_FIREWALLD=$(trueorfalse False FORCE_FIREWALLD)
     if [[ $FORCE_FIREWALLD == "False" ]]; then
         # On Fedora 20 firewalld interacts badly with libvirt and
         # slows things down significantly (this issue was fixed in
@@ -134,6 +134,31 @@
             sudo systemctl start iptables
         fi
     fi
+
+    if  [[ "$os_VENDOR" == "Fedora" ]] && [[ "$os_RELEASE" -ge "21" ]]; then
+        # requests ships vendored version of chardet/urllib3, but on
+        # fedora these are symlinked back to the primary versions to
+        # avoid duplication of code on disk.  This is fine when
+        # maintainers keep things in sync, but since devstack takes
+        # over and installs later versions via pip we can end up with
+        # incompatible versions.
+        #
+        # The rpm package is not removed to preserve the dependent
+        # packages like cloud-init; rather we remove the symlinks and
+        # force a re-install of requests so the vendored versions it
+        # wants are present.
+        #
+        # Realted issues:
+        # https://bugs.launchpad.net/glance/+bug/1476770
+        # https://bugzilla.redhat.com/show_bug.cgi?id=1253823
+
+        base_path=$(get_package_path requests)/packages
+        if [ -L $base_path/chardet -o -L $base_path/urllib3 ]; then
+            sudo rm -f $base_path/{chardet,urllib3}
+            # install requests with the bundled urllib3 to avoid conflicts
+            pip_install --upgrade --force-reinstall requests
+        fi
+    fi
 fi
 
 # The version of pip(1.5.4) supported by python-virtualenv(1.11.4) has
diff --git a/tools/install_ebtables_workaround.sh b/tools/install_ebtables_workaround.sh
new file mode 100755
index 0000000..45ced87
--- /dev/null
+++ b/tools/install_ebtables_workaround.sh
@@ -0,0 +1,31 @@
+#!/bin/bash -eu
+#
+# Copyright 2015 Hewlett-Packard Development Company, L.P.
+#
+# 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 replaces the ebtables on your system with a wrapper script that
+# does implicit locking. This is needed if libvirt < 1.2.11 on your platform.
+
+EBTABLES=/sbin/ebtables
+EBTABLESREAL=/sbin/ebtables.real
+FILES=$TOP_DIR/files
+
+if [[ -f "$EBTABLES" ]]; then
+    if file $EBTABLES | grep ELF; then
+        sudo mv $EBTABLES $EBTABLESREAL
+        sudo install -m 0755 $FILES/ebtables.workaround $EBTABLES
+        echo "Replaced ebtables with locking workaround"
+    fi
+fi
diff --git a/tools/install_pip.sh b/tools/install_pip.sh
index 0f7c962..ab5efb2 100755
--- a/tools/install_pip.sh
+++ b/tools/install_pip.sh
@@ -20,7 +20,7 @@
 cd $TOP_DIR
 
 # Import common functions
-source $TOP_DIR/functions
+source $TOP_DIR/stackrc
 
 FILES=$TOP_DIR/files
 
@@ -42,6 +42,15 @@
 
 
 function install_get_pip {
+    # If get-pip.py isn't python, delete it. This was probably an
+    # outage on the server.
+    if [[ -r $LOCAL_PIP ]]; then
+        if ! head -1 $LOCAL_PIP | grep -q '#!/usr/bin/env python'; then
+            echo "WARNING: Corrupt $LOCAL_PIP found removing"
+            rm $LOCAL_PIP
+        fi
+    fi
+
     # The OpenStack gate and others put a cached version of get-pip.py
     # for this to find, explicitly to avoid download issues.
     #
@@ -53,8 +62,15 @@
     # since and only download if a new version is out -- but only if
     # it seems we downloaded the file originally.
     if [[ ! -r $LOCAL_PIP || -r $LOCAL_PIP.downloaded ]]; then
-        curl --retry 6 --retry-delay 5 \
-            -z $LOCAL_PIP -o $LOCAL_PIP $PIP_GET_PIP_URL || \
+        # only test freshness if LOCAL_PIP is actually there,
+        # otherwise we generate a scary warning.
+        local timecond=""
+        if [[ -r $LOCAL_PIP ]]; then
+            timecond="-z $LOCAL_PIP"
+        fi
+
+        curl -f --retry 6 --retry-delay 5 \
+            $timecond -o $LOCAL_PIP $PIP_GET_PIP_URL || \
             die $LINENO "Download of get-pip.py failed"
         touch $LOCAL_PIP.downloaded
     fi
@@ -94,7 +110,11 @@
 # Do pip
 
 # Eradicate any and all system packages
-uninstall_package python-pip
+
+# python in f23 depends on the python-pip package
+if ! { is_fedora && [[ $DISTRO == "f23" ]]; }; then
+    uninstall_package python-pip
+fi
 
 install_get_pip
 
diff --git a/tools/install_prereqs.sh b/tools/install_prereqs.sh
index a07e58d..38452cd 100755
--- a/tools/install_prereqs.sh
+++ b/tools/install_prereqs.sh
@@ -61,7 +61,7 @@
 # ================
 
 # Install package requirements
-PACKAGES=$(get_packages general $ENABLED_SERVICES)
+PACKAGES=$(get_packages general,$ENABLED_SERVICES)
 PACKAGES="$PACKAGES $(get_plugin_packages)"
 
 if is_ubuntu && echo $PACKAGES | grep -q dkms ; then
diff --git a/tools/make_cert.sh b/tools/make_cert.sh
index cb93e57..2628b40 100755
--- a/tools/make_cert.sh
+++ b/tools/make_cert.sh
@@ -5,7 +5,7 @@
 # Create a CA hierarchy (if necessary) and server certificate
 #
 # This mimics the CA structure that DevStack sets up when ``tls_proxy`` is enabled
-# but in the curent directory unless ``DATA_DIR`` is set
+# but in the current directory unless ``DATA_DIR`` is set
 
 ENABLE_TLS=True
 DATA_DIR=${DATA_DIR:-`pwd`/ca-data}
diff --git a/tools/peakmem_tracker.sh b/tools/peakmem_tracker.sh
index 0d5728a..ecbd79a 100755
--- a/tools/peakmem_tracker.sh
+++ b/tools/peakmem_tracker.sh
@@ -41,10 +41,12 @@
 # snapshot of current usage; i.e. checking the latest entry in the
 # file will give the peak-memory usage
 function tracker {
-    local low_point=$(get_mem_available)
+    local low_point
+    low_point=$(get_mem_available)
     while [ 1 ]; do
 
-        local mem_available=$(get_mem_available)
+        local mem_available
+        mem_available=$(get_mem_available)
 
         if [[ $mem_available -lt $low_point ]]; then
             low_point=$mem_available
diff --git a/tools/ping_neutron.sh b/tools/ping_neutron.sh
index d36b7f6..dba7502 100755
--- a/tools/ping_neutron.sh
+++ b/tools/ping_neutron.sh
@@ -51,15 +51,15 @@
     usage
 fi
 
-REMANING_ARGS="${@:2}"
+REMAINING_ARGS="${@:2}"
 
 # BUG: with duplicate network names, this fails pretty hard.
-NET_ID=$(neutron net-list $NET_NAME | grep "$NET_NAME" | awk '{print $2}')
+NET_ID=$(neutron net-list | grep "$NET_NAME" | awk '{print $2}')
 PROBE_ID=$(neutron-debug probe-list -c id -c network_id | grep "$NET_ID" | awk '{print $2}' | head -n 1)
 
 # This runs a command inside the specific netns
 NET_NS_CMD="ip netns exec qprobe-$PROBE_ID"
 
-PING_CMD="sudo $NET_NS_CMD ping $REMAING_ARGS"
+PING_CMD="sudo $NET_NS_CMD ping $REMAINING_ARGS"
 echo "Running $PING_CMD"
 $PING_CMD
diff --git a/tools/worlddump.py b/tools/worlddump.py
index e4ba02b..97e4d94 100755
--- a/tools/worlddump.py
+++ b/tools/worlddump.py
@@ -31,12 +31,19 @@
     parser.add_argument('-d', '--dir',
                         default='.',
                         help='Output directory for worlddump')
+    parser.add_argument('-n', '--name',
+                        default='',
+                        help='Additional name to tag into file')
     return parser.parse_args()
 
 
-def filename(dirname):
+def filename(dirname, name=""):
     now = datetime.datetime.utcnow()
-    return os.path.join(dirname, now.strftime("worlddump-%Y-%m-%d-%H%M%S.txt"))
+    fmt = "worlddump-%Y-%m-%d-%H%M%S"
+    if name:
+        fmt += "-" + name
+    fmt += ".txt"
+    return os.path.join(dirname, now.strftime(fmt))
 
 
 def warn(msg):
@@ -78,6 +85,13 @@
     print dfraw
 
 
+def ebtables_dump():
+    tables = ['filter', 'nat', 'broute']
+    _header("EB Tables Dump")
+    for table in tables:
+        _dump_cmd("sudo ebtables -t %s -L" % table)
+
+
 def iptables_dump():
     tables = ['filter', 'nat', 'mangle']
     _header("IP Tables Dump")
@@ -125,7 +139,7 @@
 
 def main():
     opts = get_options()
-    fname = filename(opts.dir)
+    fname = filename(opts.dir, opts.name)
     print "World dumping... see %s for details" % fname
     sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
     with open(fname, 'w') as f:
@@ -134,6 +148,7 @@
         process_list()
         network_dump()
         iptables_dump()
+        ebtables_dump()
         compute_consoles()
         guru_meditation_report()
 
diff --git a/tools/xen/README.md b/tools/xen/README.md
index 61694e9..a1adf59 100644
--- a/tools/xen/README.md
+++ b/tools/xen/README.md
@@ -77,7 +77,7 @@
     # NOTE: these need to be specified, otherwise devstack will try
     # to prompt for these passwords, blocking the install process.
 
-    MYSQL_PASSWORD=my_super_secret
+    DATABASE_PASSWORD=my_super_secret
     SERVICE_TOKEN=my_super_secret
     ADMIN_PASSWORD=my_super_secret
     SERVICE_PASSWORD=my_super_secret
@@ -94,11 +94,6 @@
     XENAPI_CONNECTION_URL="http://address_of_your_xenserver"
     VNCSERVER_PROXYCLIENT_ADDRESS=address_of_your_xenserver
 
-    # Download a vhd and a uec image
-    IMAGE_URLS="\
-    https://github.com/downloads/citrix-openstack/warehouse/cirros-0.3.0-x86_64-disk.vhd.tgz,\
-    http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-uec.tar.gz"
-
     # Explicitly set virt driver
     VIRT_DRIVER=xenserver
 
diff --git a/tools/xen/functions b/tools/xen/functions
index 4e9fede..8c674dc 100644
--- a/tools/xen/functions
+++ b/tools/xen/functions
@@ -179,7 +179,8 @@
     local bridge_or_net_name
     bridge_or_net_name=$1
 
-    ifconfig $(bridge_for "$bridge_or_net_name") | grep "inet addr" | cut -d ":" -f2 | sed "s/ .*//"
+    ip -4 addr show $(bridge_for "$bridge_or_net_name") |\
+    awk '/inet/{split($2, ip, "/"); print ip[1];}'
 }
 
 function xenapi_is_listening_on {
diff --git a/tools/xen/install_os_domU.sh b/tools/xen/install_os_domU.sh
index b49347e..e24d9ed 100755
--- a/tools/xen/install_os_domU.sh
+++ b/tools/xen/install_os_domU.sh
@@ -193,7 +193,10 @@
             TMP_DIR=/tmp/temp.$RANDOM
             mkdir -p $TMP_DIR
             mount -o loop $TOOLS_ISO $TMP_DIR
-            DEB_FILE=$(ls $TMP_DIR/Linux/*amd64.deb)
+            # the target deb package maybe *amd64.deb or *all.deb,
+            # so use *amd64.deb by default. If it doesn't exist,
+            # then use *all.deb.
+            DEB_FILE=$(ls $TMP_DIR/Linux/*amd64.deb || ls $TMP_DIR/Linux/*all.deb)
             cp $DEB_FILE $HTTP_SERVER_LOCATION
             umount $TMP_DIR
             rmdir $TMP_DIR
diff --git a/tools/xen/scripts/install-os-vpx.sh b/tools/xen/scripts/install-os-vpx.sh
index 1ebbeaf..66f7ef4 100755
--- a/tools/xen/scripts/install-os-vpx.sh
+++ b/tools/xen/scripts/install-os-vpx.sh
@@ -100,7 +100,8 @@
 {
     local v="$1"
     echo "Installing VM interface on [$BRIDGE]"
-    local out_network_uuid=$(find_network "$BRIDGE")
+    local out_network_uuid
+    out_network_uuid=$(find_network "$BRIDGE")
     xe vif-create vm-uuid="$v" network-uuid="$out_network_uuid" device="0"
 }
 
diff --git a/tools/xen/scripts/uninstall-os-vpx.sh b/tools/xen/scripts/uninstall-os-vpx.sh
index 1ed2494..96dad7e 100755
--- a/tools/xen/scripts/uninstall-os-vpx.sh
+++ b/tools/xen/scripts/uninstall-os-vpx.sh
@@ -35,9 +35,12 @@
 destroy_vdi()
 {
     local vbd_uuid="$1"
-    local type=$(xe_min vbd-list uuid=$vbd_uuid params=type)
-    local dev=$(xe_min vbd-list uuid=$vbd_uuid params=userdevice)
-    local vdi_uuid=$(xe_min vbd-list uuid=$vbd_uuid params=vdi-uuid)
+    local type
+    type=$(xe_min vbd-list uuid=$vbd_uuid params=type)
+    local dev
+    dev=$(xe_min vbd-list uuid=$vbd_uuid params=userdevice)
+    local vdi_uuid
+    vdi_uuid=$(xe_min vbd-list uuid=$vbd_uuid params=vdi-uuid)
 
     if [ "$type" == 'Disk' ] && [ "$dev" != 'xvda' ] && [ "$dev" != '0' ]; then
         xe vdi-destroy uuid=$vdi_uuid
@@ -47,7 +50,8 @@
 uninstall()
 {
     local vm_uuid="$1"
-    local power_state=$(xe_min vm-list uuid=$vm_uuid params=power-state)
+    local power_state
+    power_state=$(xe_min vm-list uuid=$vm_uuid params=power-state)
 
     if [ "$power_state" != "halted" ]; then
         xe vm-shutdown vm=$vm_uuid force=true
diff --git a/tools/xen/test_functions.sh b/tools/xen/test_functions.sh
index 924e773..324e6a1 100755
--- a/tools/xen/test_functions.sh
+++ b/tools/xen/test_functions.sh
@@ -165,7 +165,8 @@
 function test_get_local_sr {
     setup_xe_response "uuid123"
 
-    local RESULT=$(. mocks && get_local_sr)
+    local RESULT
+    RESULT=$(. mocks && get_local_sr)
 
     [ "$RESULT" == "uuid123" ]
 
@@ -173,7 +174,8 @@
 }
 
 function test_get_local_sr_path {
-    local RESULT=$(mock_out get_local_sr "uuid1" && get_local_sr_path)
+    local RESULT
+    RESULT=$(mock_out get_local_sr "uuid1" && get_local_sr_path)
 
     [ "/var/run/sr-mount/uuid1" == "$RESULT" ]
 }
diff --git a/tox.ini b/tox.ini
index 788fea9..9279455 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,7 +8,8 @@
 install_command = pip install {opts} {packages}
 
 [testenv:bashate]
-deps = bashate
+deps =
+   {env:BASHATE_INSTALL_PATH:bashate==0.3.2}
 whitelist_externals = bash
 commands = bash -c "find {toxinidir}             \
          -not \( -type d -name .?\* -prune \)    \ # prune all 'dot' dirs
@@ -19,12 +20,13 @@
          -not -name \*.md                        \
          \(                                      \
           -name \*.sh -or                        \
-          -name \*rc -or                         \
+          -name \*.orig -or                      \
+          -name \*rc -or                         \ # openrc files, etc
           -name functions\* -or                  \
           -wholename \*/inc/\* -or               \ # /inc files and
           -wholename \*/lib/\*                   \ # /lib files are shell, but
          \)                                      \ #   have no extension
-         -print0 | xargs -0 bashate -v"
+         -print0 | xargs -0 bashate -v -iE006 -eE005,E042"
 
 [testenv:docs]
 deps =
diff --git a/unstack.sh b/unstack.sh
index 10e5958..8eded83 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -45,7 +45,7 @@
 # Configure Projects
 # ==================
 
-# Plugin Phase 0: override_defaults - allow pluggins to override
+# Plugin Phase 0: override_defaults - allow plugins to override
 # defaults before other services are run
 run_phase override_defaults
 
@@ -65,11 +65,11 @@
 source $TOP_DIR/lib/nova
 source $TOP_DIR/lib/cinder
 source $TOP_DIR/lib/swift
-source $TOP_DIR/lib/ceilometer
 source $TOP_DIR/lib/heat
 source $TOP_DIR/lib/neutron-legacy
 source $TOP_DIR/lib/ldap
 source $TOP_DIR/lib/dstat
+source $TOP_DIR/lib/dlm
 
 # Extras Source
 # --------------
@@ -104,10 +104,6 @@
     stop_heat
 fi
 
-if is_service_enabled ceilometer; then
-    stop_ceilometer
-fi
-
 if is_service_enabled nova; then
     stop_nova
 fi
@@ -177,6 +173,10 @@
     stop_dstat
 fi
 
+if is_service_enabled zookeeper; then
+    stop_zookeeper
+fi
+
 # Clean up the remainder of the screen processes
 SCREEN=$(which screen)
 if [[ -n "$SCREEN" ]]; then