Merge "remove tools/jenkins"
diff --git a/.gitignore b/.gitignore
index b80b476..b0a65f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
 *.pem
 .localrc.auto
 .prereqs
+.tox
 .stackenv
 accrc
 docs/files
diff --git a/.mailmap b/.mailmap
index 29be995..43e4e6e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -4,3 +4,4 @@
 Jiajun Liu <jiajun@unitedstack.com> <iamljj@gmail.com>
 Jian Wen <jian.wen@canonical.com> <wenjianhn@gmail.com>
 Joe Gordon <joe.gordon0@gmail.com> <jogo@cloudscaling.com>
+Sean Dague <sean.dague@samsung.com> <sdague@linux.vnet.ibm.com> <sean@dague.net>
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index 04bff48..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,51 +0,0 @@
-Aaron Lee <aaron.lee@rackspace.com>
-Aaron Rosen <arosen@nicira.com>
-Adam Gandelman <adamg@canonical.com>
-Akihiro MOTOKI <motoki@da.jp.nec.com>
-Andrew Laski <andrew.laski@rackspace.com>
-Andy Smith <github@anarkystic.com>
-Anthony Young <sleepsonthefloor@gmail.com>
-Armando Migliaccio <armando.migliaccio@eu.citrix.com>
-Brad Hall <brad@nicira.com>
-Chmouel Boudjnah <chmouel@chmouel.com>
-Dan Prince <dprince@redhat.com>
-Dean Troyer <dtroyer@gmail.com>
-Devin Carlen <devin.carlen@gmail.com>
-Doug hellmann <doug.hellmann@dreamhost.com>
-Eddie Hebert <edhebert@gmail.com>
-Edgar Magana <emagana@gmail.com>
-Eoghan Glynn <eglynn@redhat.com>
-Eric Windisch <ewindisch@cloudscaling.com>
-Gabriel Hurley <gabriel@strikeawe.com>
-Gary Kotton <gkotton@redhat.com>
-Hengqing Hu <hudayou@hotmail.com>
-Hua ZHANG <zhuadl@cn.ibm.com>
-Isaku Yamahata <yamahata@private.email.ne.jp>
-Jake Dahn <admin@jakedahn.com>
-James E. Blair <james.blair@rackspace.com>
-Jason Cannavale <jason.cannavale@rackspace.com>
-Jay Pipes <jaypipes@gmail.com>
-Jesse Andrews <anotherjesse@gmail.com>
-Jian Wen <jian.wen@canonical.com>
-Joe Gordon <jogo@cloudscaling.com>
-Johannes Erdfelt <johannes.erdfelt@rackspace.com>
-John Postlethwait <john.postlethwait@nebula.com>
-Josh Kearney <josh@jk0.org>
-Justin Shepherd <galstrom21@gmail.com>
-Ken Pepple <ken.pepple@rabbityard.com>
-Kiall Mac Innes <kiall@managedit.ie>
-Matt Joyce <matt.joyce@cloudscaling.com>
-Osamu Habuka <xiu.yushen@gmail.com>
-Russell Bryant <rbryant@redhat.com>
-Scott Moser <smoser@ubuntu.com>
-Sean Dague <sdague@linux.vnet.ibm.com>
-Sumit Naiksatam <sumitnaiksatam@gmail.com>
-Thierry Carrez <thierry@openstack.org>
-Todd Willey <xtoddx@gmail.com>
-Tres Henry <tres@treshenry.net>
-Vincent Untz <vuntz@suse.com>
-Vishvananda Ishaya <vishvananda@gmail.com>
-Yun Mao <yunmao@gmail.com>
-Yong Sheng Gong <gongysh@cn.ibm.com>
-Zhongyue Luo <lzyeval@gmail.com>
-Zhenguo Niu <niu.zglinux@gmail.com>
diff --git a/MAINTAINERS.rst b/MAINTAINERS.rst
new file mode 100644
index 0000000..bdd9e78
--- /dev/null
+++ b/MAINTAINERS.rst
@@ -0,0 +1,81 @@
+MAINTAINERS
+===========
+
+
+Overview
+--------
+
+The following is a list of people known to have interests in
+particular areas or sub-systems of devstack.
+
+It is a rather general guide intended to help seed the initial
+reviewers list of a change.  A +1 on a review from someone identified
+as being a maintainer of its affected area is a very positive flag to
+the core team for the veracity of the change.
+
+The ``devstack-core`` group can still be added to all reviews.
+
+
+Format
+~~~~~~
+
+The format of the file is the name of the maintainer and their
+gerrit-registered email.
+
+
+Maintainers
+-----------
+
+.. contents:: :local:
+
+
+Ceph
+~~~~
+
+* Sebastien Han <sebastien.han@enovance.com>
+
+Cinder
+~~~~~~
+
+Fedora/CentOS/RHEL
+~~~~~~~~~~~~~~~~~~
+
+* Ian Wienand <iwienand@redhat.com>
+
+Neutron
+~~~~~~~
+
+OpenDaylight
+~~~~~~~~~~~~
+
+* Kyle Mestery <kmestery@cisco.com>
+
+Sahara
+~~~~~~
+
+* Sergey Lukjanov <slukjanov@mirantis.com>
+
+SUSE
+~~~~
+
+* Ralf Haferkamp <rhafer@suse.de>
+* Vincent Untz <vuntz@suse.com>
+
+Tempest
+~~~~~~~
+
+Trove
+~~~~~
+
+* Nikhil Manchanda <SlickNik@gmail.com>
+* Michael Basnight <mbasnight@gmail.com>
+
+Xen
+~~~
+* Bob Ball <bob.ball@citrix.com>
+
+Zaqar (Marconi)
+~~~~~~~~~~~~~~~
+
+* Flavio Percoco <flaper87@gmail.com>
+* Malini Kamalambal <malini.kamalambal@rackspace.com>
diff --git a/clean.sh b/clean.sh
index e2374e7..db1a1e4 100755
--- a/clean.sh
+++ b/clean.sh
@@ -84,7 +84,6 @@
 fi
 
 # Clean projects
-cleanup_oslo
 cleanup_cinder
 cleanup_glance
 cleanup_keystone
diff --git a/docs/source/guides/multinode-lab.html b/docs/source/guides/multinode-lab.html
index 2e52379..a286954 100644
--- a/docs/source/guides/multinode-lab.html
+++ b/docs/source/guides/multinode-lab.html
@@ -184,7 +184,13 @@
 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-sch,c-api,c-vol</pre>
+ENABLED_SERVICES=n-cpu,n-net,n-api,c-sch,c-api,c-vol
+NOVA_VNC_ENABLED=True
+NOVNCPROXY_URL="http://192.168.42.11:6080/vnc_auto.html"
+VNCSERVER_LISTEN=$HOST_IP
+VNCSERVER_PROXYCLIENT_ADDRESS=$VNCSERVER_LISTEN
+</pre>
+
 <!-- save for vlan
 FLAT_INTERFACE=eth0.926
 -->
diff --git a/docs/source/index.html b/docs/source/index.html
index 1a31df1..dada57d 100644
--- a/docs/source/index.html
+++ b/docs/source/index.html
@@ -287,8 +287,8 @@
                 <td><a href="lib/ldap.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
               </tr>
               <tr>
-                <td>lib/marconi</td>
-                <td><a href="lib/marconi.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
+                <td>lib/zaqar</td>
+                <td><a href="lib/zaqar.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
               </tr>
               <tr>
                 <td>lib/neutron</td>
@@ -351,8 +351,8 @@
                 <td><a href="extras.d/50-ironic.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
               </tr>
               <tr>
-                <td>extras.d/70-marconi.sh</td>
-                <td><a href="extras.d/70-marconi.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
+                <td>extras.d/70-zaqar.sh</td>
+                <td><a href="extras.d/70-zaqar.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
               </tr>
               <tr>
                 <td>extras.d/70-sahara.sh</td>
@@ -517,9 +517,6 @@
                 <td>exercises/horizon.sh</td>
                 <td><a href="exercises/horizon.sh.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
               </tr>
-                <td>exercises/marconi.sh</td>
-                <td><a href="exercises/marconi.sh.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
-              </tr>
               <tr>
                 <td>exercises/neutron-adv-test.sh</td>
                 <td><a href="exercises/neutron-adv-test.sh.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
@@ -545,6 +542,10 @@
                 <td>exercises/volumes.sh</td>
                 <td><a href="exercises/volumes.sh.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
               </tr>
+              <tr>
+                <td>exercises/zaqar.sh</td>
+                <td><a href="exercises/zaqar.sh.html" class="btn btn-small btn-primary table-action">Read &raquo;</a></td>
+              </tr>
             </tbody>
           </table>
 
diff --git a/exercises/boot_from_volume.sh b/exercises/boot_from_volume.sh
index d756685..a2ae275 100755
--- a/exercises/boot_from_volume.sh
+++ b/exercises/boot_from_volume.sh
@@ -71,10 +71,10 @@
 # ------
 
 # List the images available
-glance image-list
+openstack image list
 
 # Grab the id of the image to launch
-IMAGE=$(glance image-list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
+IMAGE=$(openstack image list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
 die_if_not_set $LINENO IMAGE "Failure getting image $DEFAULT_IMAGE_NAME"
 
 # Security Groups
diff --git a/exercises/client-args.sh b/exercises/client-args.sh
index b360f1e..2f85d98 100755
--- a/exercises/client-args.sh
+++ b/exercises/client-args.sh
@@ -122,7 +122,7 @@
         STATUS_GLANCE="Skipped"
     else
         echo -e "\nTest Glance"
-        if glance $TENANT_ARG $ARGS image-list; then
+        if openstack $TENANT_ARG $ARGS image list; then
             STATUS_GLANCE="Succeeded"
         else
             STATUS_GLANCE="Failed"
diff --git a/exercises/client-env.sh b/exercises/client-env.sh
index cc518d9..4a0609a 100755
--- a/exercises/client-env.sh
+++ b/exercises/client-env.sh
@@ -132,7 +132,7 @@
         STATUS_GLANCE="Skipped"
     else
         echo -e "\nTest Glance"
-        if glance image-list; then
+        if openstack image list; then
             STATUS_GLANCE="Succeeded"
         else
             STATUS_GLANCE="Failed"
diff --git a/exercises/floating_ips.sh b/exercises/floating_ips.sh
index 7e90e5a..57f48e0 100755
--- a/exercises/floating_ips.sh
+++ b/exercises/floating_ips.sh
@@ -71,10 +71,10 @@
 # ------
 
 # List the images available
-glance image-list
+openstack image list
 
 # Grab the id of the image to launch
-IMAGE=$(glance image-list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
+IMAGE=$(openstack image list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
 die_if_not_set $LINENO IMAGE "Failure getting image $DEFAULT_IMAGE_NAME"
 
 # Security Groups
diff --git a/exercises/neutron-adv-test.sh b/exercises/neutron-adv-test.sh
index 6679670..5b3281b 100755
--- a/exercises/neutron-adv-test.sh
+++ b/exercises/neutron-adv-test.sh
@@ -134,7 +134,7 @@
 }
 
 function get_image_id {
-    local IMAGE_ID=$(glance image-list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
+    local 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"
 }
diff --git a/exercises/volumes.sh b/exercises/volumes.sh
index 1dff6a4..504fba1 100755
--- a/exercises/volumes.sh
+++ b/exercises/volumes.sh
@@ -68,10 +68,10 @@
 # ------
 
 # List the images available
-glance image-list
+openstack image list
 
 # Grab the id of the image to launch
-IMAGE=$(glance image-list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
+IMAGE=$(openstack image list | egrep " $DEFAULT_IMAGE_NAME " | get_field 1)
 die_if_not_set $LINENO IMAGE "Failure getting image $DEFAULT_IMAGE_NAME"
 
 # Security Groups
diff --git a/exercises/marconi.sh b/exercises/zaqar.sh
similarity index 86%
rename from exercises/marconi.sh
rename to exercises/zaqar.sh
index 9d83a99..6996f34 100755
--- a/exercises/marconi.sh
+++ b/exercises/zaqar.sh
@@ -1,8 +1,8 @@
 #!/usr/bin/env bash
 
-# **marconi.sh**
+# **zaqar.sh**
 
-# Sanity check that Marconi started if enabled
+# Sanity check that Zaqar started if enabled
 
 echo "*********************************************************************"
 echo "Begin DevStack Exercise: $0"
@@ -33,9 +33,9 @@
 # Import exercise configuration
 source $TOP_DIR/exerciserc
 
-is_service_enabled marconi-server || exit 55
+is_service_enabled zaqar-server || exit 55
 
-curl http://$SERVICE_HOST:8888/v1/ 2>/dev/null | grep -q 'queue_name' || die $LINENO "Marconi API not functioning!"
+curl http://$SERVICE_HOST:8888/v1/ 2>/dev/null | grep -q 'queue_name' || die $LINENO "Zaqar API not functioning!"
 
 set +o xtrace
 echo "*********************************************************************"
diff --git a/extras.d/40-dib.sh b/extras.d/40-dib.sh
new file mode 100644
index 0000000..fdae011
--- /dev/null
+++ b/extras.d/40-dib.sh
@@ -0,0 +1,27 @@
+# dib.sh - Devstack extras script to install diskimage-builder
+
+if is_service_enabled dib; then
+    if [[ "$1" == "source" ]]; then
+        # Initial source
+        source $TOP_DIR/lib/dib
+    elif [[ "$1" == "stack" && "$2" == "install" ]]; then
+        echo_summary "Installing diskimage-builder"
+        install_dib
+    elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
+        # no-op
+        :
+    elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
+        # no-op
+        :
+    fi
+
+    if [[ "$1" == "unstack" ]]; then
+        # no-op
+        :
+    fi
+
+    if [[ "$1" == "clean" ]]; then
+        # no-op
+        :
+    fi
+fi
diff --git a/extras.d/70-marconi.sh b/extras.d/70-marconi.sh
deleted file mode 100644
index a96a4c5..0000000
--- a/extras.d/70-marconi.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-# marconi.sh - Devstack extras script to install Marconi
-
-if is_service_enabled marconi-server; then
-    if [[ "$1" == "source" ]]; then
-        # Initial source
-        source $TOP_DIR/lib/marconi
-    elif [[ "$1" == "stack" && "$2" == "install" ]]; then
-        echo_summary "Installing Marconi"
-        install_marconiclient
-        install_marconi
-    elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
-        echo_summary "Configuring Marconi"
-        configure_marconi
-        configure_marconiclient
-
-        if is_service_enabled key; then
-            create_marconi_accounts
-        fi
-
-    elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
-        echo_summary "Initializing Marconi"
-        init_marconi
-        start_marconi
-    fi
-
-    if [[ "$1" == "unstack" ]]; then
-        stop_marconi
-    fi
-fi
diff --git a/extras.d/70-sahara.sh b/extras.d/70-sahara.sh
index 80e07ff..1a67c29 100644
--- a/extras.d/70-sahara.sh
+++ b/extras.d/70-sahara.sh
@@ -8,17 +8,12 @@
     elif [[ "$1" == "stack" && "$2" == "install" ]]; then
         echo_summary "Installing sahara"
         install_sahara
+        install_python_saharaclient
         cleanup_sahara
-        if is_service_enabled horizon; then
-            install_sahara_dashboard
-        fi
     elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
         echo_summary "Configuring sahara"
         configure_sahara
         create_sahara_accounts
-        if is_service_enabled horizon; then
-            configure_sahara_dashboard
-        fi
     elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
         echo_summary "Initializing sahara"
         start_sahara
@@ -26,9 +21,6 @@
 
     if [[ "$1" == "unstack" ]]; then
         stop_sahara
-        if is_service_enabled horizon; then
-            cleanup_sahara_dashboard
-        fi
     fi
 
     if [[ "$1" == "clean" ]]; then
diff --git a/extras.d/70-zaqar.sh b/extras.d/70-zaqar.sh
new file mode 100644
index 0000000..63c4fd5
--- /dev/null
+++ b/extras.d/70-zaqar.sh
@@ -0,0 +1,29 @@
+# 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/files/apache-dib-pip-repo.template b/files/apache-dib-pip-repo.template
new file mode 100644
index 0000000..5d2379b
--- /dev/null
+++ b/files/apache-dib-pip-repo.template
@@ -0,0 +1,15 @@
+Listen %DIB_PIP_REPO_PORT%
+
+<VirtualHost *:%DIB_PIP_REPO_PORT%>
+    DocumentRoot %DIB_PIP_REPO%
+    <Directory %DIB_PIP_REPO%>
+        DirectoryIndex index.html
+        Require all granted
+        Order allow,deny
+        allow from all
+    </Directory>
+
+    ErrorLog /var/log/%APACHE_NAME%/dib_pip_repo_error.log
+    LogLevel warn
+    CustomLog /var/log/%APACHE_NAME%/dib_pip_repo_access.log combined
+</VirtualHost>
diff --git a/files/apache-horizon.template b/files/apache-horizon.template
index af880c4..c1dd693 100644
--- a/files/apache-horizon.template
+++ b/files/apache-horizon.template
@@ -1,6 +1,6 @@
 <VirtualHost *:80>
     WSGIScriptAlias / %HORIZON_DIR%/openstack_dashboard/wsgi/django.wsgi
-    WSGIDaemonProcess horizon user=%USER% group=%GROUP% processes=3 threads=10 home=%HORIZON_DIR%
+    WSGIDaemonProcess horizon user=%USER% group=%GROUP% processes=3 threads=10 home=%HORIZON_DIR% display-name=%{GROUP}
     WSGIApplicationGroup %{GLOBAL}
 
     SetEnv APACHE_RUN_USER %USER%
diff --git a/files/apache-ironic.template b/files/apache-ironic.template
new file mode 100644
index 0000000..8864194
--- /dev/null
+++ b/files/apache-ironic.template
@@ -0,0 +1,12 @@
+Listen %PUBLICPORT%
+
+<VirtualHost *:%PUBLICPORT%>
+    DocumentRoot "%HTTPROOT%"
+    <Directory "%HTTPROOT%">
+        Options Indexes FollowSymLinks
+        AllowOverride None
+        Order allow,deny
+        Allow from all
+        Require all granted
+    </Directory>
+</VirtualHost>
diff --git a/files/apache-keystone.template b/files/apache-keystone.template
index 805e7b8..a9d9cc3 100644
--- a/files/apache-keystone.template
+++ b/files/apache-keystone.template
@@ -2,22 +2,20 @@
 Listen %ADMINPORT%
 
 <VirtualHost *:%PUBLICPORT%>
-    WSGIDaemonProcess keystone-public processes=5 threads=1 user=%USER%
+    WSGIDaemonProcess keystone-public processes=5 threads=1 user=%USER% display-name=%{GROUP}
     WSGIProcessGroup keystone-public
     WSGIScriptAlias / %PUBLICWSGI%
     WSGIApplicationGroup %{GLOBAL}
-    ErrorLog /var/log/%APACHE_NAME%/keystone
-    LogLevel debug
+    ErrorLog /var/log/%APACHE_NAME%/keystone.log
     CustomLog /var/log/%APACHE_NAME%/access.log combined
 </VirtualHost>
 
 <VirtualHost *:%ADMINPORT%>
-    WSGIDaemonProcess keystone-admin processes=5 threads=1 user=%USER%
+    WSGIDaemonProcess keystone-admin processes=5 threads=1 user=%USER% display-name=%{GROUP}
     WSGIProcessGroup keystone-admin
     WSGIScriptAlias / %ADMINWSGI%
     WSGIApplicationGroup %{GLOBAL}
-    ErrorLog /var/log/%APACHE_NAME%/keystone
-    LogLevel debug
+    ErrorLog /var/log/%APACHE_NAME%/keystone.log
     CustomLog /var/log/%APACHE_NAME%/access.log combined
 </VirtualHost>
 
diff --git a/files/apts/ironic b/files/apts/ironic
index fe9c07f..8674d9f 100644
--- a/files/apts/ironic
+++ b/files/apts/ironic
@@ -1,5 +1,6 @@
 ipmitool
 iptables
+ipxe
 libguestfs0
 libvirt-bin
 openssh-client
diff --git a/files/apts/keystone b/files/apts/keystone
index b7218b7..d316a42 100644
--- a/files/apts/keystone
+++ b/files/apts/keystone
@@ -6,6 +6,7 @@
 python-pysqlite2
 python-sqlalchemy
 python-mysqldb
+python-mysql.connector
 python-webob
 python-greenlet
 python-routes
diff --git a/files/apts/neutron b/files/apts/neutron
index 648716a..23dd65b 100644
--- a/files/apts/neutron
+++ b/files/apts/neutron
@@ -2,6 +2,7 @@
 iptables
 iputils-ping
 iputils-arping
+libmysqlclient-dev  # testonly
 mysql-server #NOPRIME
 sudo
 python-boto
@@ -15,6 +16,7 @@
 python-eventlet
 python-sqlalchemy
 python-mysqldb
+python-mysql.connector
 python-pyudev
 python-qpid # dist:precise
 dnsmasq-base
@@ -23,3 +25,4 @@
 qpidd # NOPRIME
 sqlite3
 vlan
+radvd # NOPRIME
diff --git a/files/apts/nova b/files/apts/nova
index e779849..114194e 100644
--- a/files/apts/nova
+++ b/files/apts/nova
@@ -4,8 +4,10 @@
 kpartx
 parted
 iputils-arping
+libmysqlclient-dev  # testonly
 mysql-server # NOPRIME
 python-mysqldb
+python-mysql.connector
 python-xattr # needed for glance which is needed for nova --- this shouldn't be here
 python-lxml # needed for glance which is needed for nova --- this shouldn't be here
 gawk
diff --git a/files/apts/q-l3 b/files/apts/q-l3
new file mode 100644
index 0000000..b98b628
--- /dev/null
+++ b/files/apts/q-l3
@@ -0,0 +1,2 @@
+conntrackd
+keepalived
diff --git a/files/apts/marconi-server b/files/apts/zaqar-server
similarity index 100%
rename from files/apts/marconi-server
rename to files/apts/zaqar-server
diff --git a/files/rpms-suse/keystone b/files/rpms-suse/keystone
index 403d82f..a734cb9 100644
--- a/files/rpms-suse/keystone
+++ b/files/rpms-suse/keystone
@@ -10,5 +10,6 @@
 python-greenlet
 python-lxml
 python-mysql
+python-mysql.connector
 python-pysqlite
 sqlite3
diff --git a/files/rpms-suse/neutron b/files/rpms-suse/neutron
index d4841b1..79f5bff 100644
--- a/files/rpms-suse/neutron
+++ b/files/rpms-suse/neutron
@@ -10,6 +10,7 @@
 python-iso8601
 python-kombu
 python-mysql
+python-mysql.connector
 python-Paste
 python-PasteDeploy
 python-pyudev
@@ -20,6 +21,7 @@
 sqlite3
 sudo
 vlan
+radvd # NOPRIME
 
 # FIXME: qpid is not part of openSUSE, those names are tentative
 python-qpid # NOPRIME
diff --git a/files/rpms-suse/nova b/files/rpms-suse/nova
index 7a1160e..2a210e5 100644
--- a/files/rpms-suse/nova
+++ b/files/rpms-suse/nova
@@ -35,6 +35,7 @@
 python-lxml # needed for glance which is needed for nova --- this shouldn't be here
 python-mox
 python-mysql
+python-mysql.connector
 python-numpy # needed by websockify for spice console
 python-paramiko
 python-sqlalchemy-migrate
diff --git a/files/rpms/ironic b/files/rpms/ironic
index 0c81081..959ac3c 100644
--- a/files/rpms/ironic
+++ b/files/rpms/ironic
@@ -1,5 +1,6 @@
 ipmitool
 iptables
+ipxe-bootimgs
 libguestfs
 libvirt
 libvirt-python
diff --git a/files/rpms/neutron b/files/rpms/neutron
index 15ed973..c56e6e2 100644
--- a/files/rpms/neutron
+++ b/files/rpms/neutron
@@ -4,6 +4,8 @@
 ebtables
 iptables
 iputils
+mysql-connector-python
+mysql-devel  # testonly
 mysql-server # NOPRIME
 openvswitch # NOPRIME
 python-boto
@@ -22,3 +24,4 @@
 qpid-cpp-server        # NOPRIME
 sqlite
 sudo
+radvd # NOPRIME
diff --git a/files/rpms/nova b/files/rpms/nova
index 6097991..4c2ee57 100644
--- a/files/rpms/nova
+++ b/files/rpms/nova
@@ -15,6 +15,8 @@
 libxml2-python
 numpy # needed by websockify for spice console
 m2crypto
+mysql-connector-python
+mysql-devel  # testonly
 mysql-server # NOPRIME
 parted
 polkit
diff --git a/files/rpms/q-l3 b/files/rpms/q-l3
new file mode 100644
index 0000000..a7a190c
--- /dev/null
+++ b/files/rpms/q-l3
@@ -0,0 +1,2 @@
+conntrack-tools
+keepalived
diff --git a/files/rpms/marconi-server b/files/rpms/zaqar-server
similarity index 100%
rename from files/rpms/marconi-server
rename to files/rpms/zaqar-server
diff --git a/functions b/functions
index 1fa6346..76f7047 100644
--- a/functions
+++ b/functions
@@ -85,7 +85,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}"
-        glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT image-create --name "$image_name" --is-public=True --container-format ami --disk-format ami < "${image}"
+        openstack --os-token $token --os-url http://$GLANCE_HOSTPORT image create "$image_name" --public --container-format ami --disk-format ami < "${image}"
         return
     fi
 
@@ -196,7 +196,7 @@
         vmdk_adapter_type="${props[1]:-$vmdk_adapter_type}"
         vmdk_net_adapter="${props[2]:-$vmdk_net_adapter}"
 
-        glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT image-create --name "$image_name" --is-public=True --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-token $token --os-url http://$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}"
         return
     fi
 
@@ -212,11 +212,11 @@
             # directly from volume.
             force_vm_mode="--property vm_mode=xen"
         fi
-        glance \
-            --os-auth-token $token \
-            --os-image-url http://$GLANCE_HOSTPORT \
-            image-create \
-            --name "$image_name" --is-public=True \
+        openstack \
+            --os-token $token \
+            --os-url http://$GLANCE_HOSTPORT \
+            image create \
+            "$image_name" --public \
             --container-format=ovf --disk-format=vhd \
             $force_vm_mode < "${image}"
         return
@@ -227,11 +227,11 @@
     # Setting metadata, so PV mode is used.
     if [[ "$image_url" =~ '.xen-raw.tgz' ]]; then
         image_name="${image_fname%.xen-raw.tgz}"
-        glance \
-            --os-auth-token $token \
-            --os-image-url http://$GLANCE_HOSTPORT \
-            image-create \
-            --name "$image_name" --is-public=True \
+        openstack \
+            --os-token $token \
+            --os-url http://$GLANCE_HOSTPORT \
+            image create \
+            "$image_name" --public \
             --container-format=tgz --disk-format=raw \
             --property vm_mode=xen < "${image}"
         return
@@ -289,6 +289,15 @@
             disk_format=iso
             container_format=bare
             ;;
+        *.vhd|*.vhdx|*.vhd.gz|*.vhdx.gz)
+            local extension="${image_fname#*.}"
+            image_name=$(basename "$image" ".$extension")
+            disk_format=vhd
+            container_format=bare
+            if [ "${image_fname##*.}" == "gz" ]; then
+                unpack=zcat
+            fi
+            ;;
         *) echo "Do not know what to do with $image_fname"; false;;
     esac
 
@@ -298,9 +307,9 @@
 
     if [ "$container_format" = "bare" ]; then
         if [ "$unpack" = "zcat" ]; then
-            glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT image-create --name "$image_name" $img_property --is-public True --container-format=$container_format --disk-format $disk_format < <(zcat --force "${image}")
+            openstack --os-token $token --os-url http://$GLANCE_HOSTPORT image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < <(zcat --force "${image}")
         else
-            glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT image-create --name "$image_name" $img_property --is-public True --container-format=$container_format --disk-format $disk_format < "${image}"
+            openstack --os-token $token --os-url http://$GLANCE_HOSTPORT 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.
@@ -308,12 +317,12 @@
         # kernel for use when uploading the root filesystem.
         local kernel_id="" ramdisk_id="";
         if [ -n "$kernel" ]; then
-            kernel_id=$(glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT image-create --name "$image_name-kernel" $img_property --is-public True --container-format aki --disk-format aki < "$kernel" | grep ' id ' | get_field 2)
+            kernel_id=$(openstack --os-token $token --os-url http://$GLANCE_HOSTPORT 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=$(glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT image-create --name "$image_name-ramdisk" $img_property --is-public True --container-format ari --disk-format ari < "$ramdisk" | grep ' id ' | get_field 2)
+            ramdisk_id=$(openstack --os-token $token --os-url http://$GLANCE_HOSTPORT image create "$image_name-ramdisk" $img_property --public --container-format ari --disk-format ari < "$ramdisk" | grep ' id ' | get_field 2)
         fi
-        glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT image-create --name "${image_name%.img}" $img_property --is-public True --container-format ami --disk-format ami ${kernel_id:+--property kernel_id=$kernel_id} ${ramdisk_id:+--property ramdisk_id=$ramdisk_id} < "${image}"
+        openstack --os-token $token --os-url http://$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}"
     fi
 }
 
diff --git a/functions-common b/functions-common
index 5284056..c096664 100644
--- a/functions-common
+++ b/functions-common
@@ -466,6 +466,13 @@
     [[ "$(uname -m)" == "$1" ]]
 }
 
+# Quick check for a rackspace host; n.b. rackspace provided images
+# have these Xen tools installed but a custom image may not.
+function is_rackspace {
+    [ -f /usr/bin/xenstore-ls ] && \
+        sudo /usr/bin/xenstore-ls vm-data | grep -q "Rackspace"
+}
+
 # Determine if current distribution is a Fedora-based distribution
 # (Fedora, RHEL, CentOS, etc).
 # is_fedora
diff --git a/lib/baremetal b/lib/baremetal
index adcbe4c..af90c06 100644
--- a/lib/baremetal
+++ b/lib/baremetal
@@ -127,10 +127,6 @@
 BM_FLAVOR_ARCH=${BM_FLAVOR_ARCH:-$BM_CPU_ARCH}
 
 
-# Below this, we set some path and filenames.
-# Defaults are probably sufficient.
-DIB_DIR=${DIB_DIR:-$DEST/diskimage-builder}
-
 # Use DIB to create deploy ramdisk and kernel.
 BM_BUILD_DEPLOY_RAMDISK=`trueorfalse True $BM_BUILD_DEPLOY_RAMDISK`
 # If not use DIB, these files are used as deploy ramdisk/kernel.
@@ -165,8 +161,9 @@
 # Install diskimage-builder and shell-in-a-box
 # so that we can build the deployment kernel & ramdisk
 function prepare_baremetal_toolchain {
-    git_clone $DIB_REPO $DIB_DIR $DIB_BUILD_BRANCH
-
+    if [[ $(type -P ramdisk-image-create) == "" ]]; then
+        pip_install diskimage_builder
+    fi
     local shellinabox_basename=$(basename $BM_SHELL_IN_A_BOX)
     if [[ ! -e $DEST/$shellinabox_basename ]]; then
         cd $DEST
@@ -223,25 +220,27 @@
         BM_DEPLOY_KERNEL=bm-deploy.kernel
         BM_DEPLOY_RAMDISK=bm-deploy.initramfs
         if [ ! -e "$TOP_DIR/files/$BM_DEPLOY_KERNEL" -o ! -e "$TOP_DIR/files/$BM_DEPLOY_RAMDISK" ]; then
-            $DIB_DIR/bin/ramdisk-image-create $BM_DEPLOY_FLAVOR \
+            ramdisk-image-create $BM_DEPLOY_FLAVOR \
                 -o $TOP_DIR/files/bm-deploy
         fi
     fi
 
     # load them into glance
-    BM_DEPLOY_KERNEL_ID=$(glance \
-        --os-auth-token $token \
-        --os-image-url http://$GLANCE_HOSTPORT \
-        image-create \
-        --name $BM_DEPLOY_KERNEL \
-        --is-public True --disk-format=aki \
+    BM_DEPLOY_KERNEL_ID=$(openstack \
+        --os-token $token \
+        --os-url http://$GLANCE_HOSTPORT \
+        image create \
+        $BM_DEPLOY_KERNEL \
+        --public --disk-format=aki \
+        --container-format=aki \
         < $TOP_DIR/files/$BM_DEPLOY_KERNEL  | grep ' id ' | get_field 2)
-    BM_DEPLOY_RAMDISK_ID=$(glance \
-        --os-auth-token $token \
-        --os-image-url http://$GLANCE_HOSTPORT \
-        image-create \
-        --name $BM_DEPLOY_RAMDISK \
-        --is-public True --disk-format=ari \
+    BM_DEPLOY_RAMDISK_ID=$(openstack \
+        --os-token $token \
+        --os-url http://$GLANCE_HOSTPORT \
+        image create \
+        $BM_DEPLOY_RAMDISK \
+        --public --disk-format=ari \
+        --container-format=ari \
         < $TOP_DIR/files/$BM_DEPLOY_RAMDISK  | grep ' id ' | get_field 2)
 }
 
@@ -271,7 +270,7 @@
     image_name=$(basename "$file" ".qcow2")
 
     # this call returns the file names as "$kernel,$ramdisk"
-    out=$($DIB_DIR/bin/disk-image-get-kernel \
+    out=$(disk-image-get-kernel \
             -x -d $TOP_DIR/files -o bm-deploy -i $file)
     if [ $? -ne 0 ]; then
         die $LINENO "Failed to get kernel and ramdisk from $file"
@@ -284,19 +283,21 @@
     OUT_RAMDISK=${out##*,}
 
     # load them into glance
-    KERNEL_ID=$(glance \
-        --os-auth-token $token \
-        --os-image-url http://$GLANCE_HOSTPORT \
-        image-create \
-        --name $image_name-kernel \
-        --is-public True --disk-format=aki \
+    KERNEL_ID=$(openstack \
+        --os-token $token \
+        --os-url http://$GLANCE_HOSTPORT \
+        image create \
+        $image_name-kernel \
+        --public --disk-format=aki \
+        --container-format=aki \
         < $TOP_DIR/files/$OUT_KERNEL | grep ' id ' | get_field 2)
-    RAMDISK_ID=$(glance \
-        --os-auth-token $token \
-        --os-image-url http://$GLANCE_HOSTPORT \
-        image-create \
-        --name $image_name-initrd \
-        --is-public True --disk-format=ari \
+    RAMDISK_ID=$(openstack \
+        --os-token $token \
+        --os-url http://$GLANCE_HOSTPORT \
+        image create \
+        $image_name-initrd \
+        --public --disk-format=ari \
+        --container-format=ari \
         < $TOP_DIR/files/$OUT_RAMDISK | grep ' id ' | get_field 2)
 }
 
@@ -362,18 +363,18 @@
     if [ "$CONTAINER_FORMAT" = "bare" ]; then
         extract_and_upload_k_and_r_from_image $token $IMAGE
     elif [ "$CONTAINER_FORMAT" = "ami" ]; then
-        KERNEL_ID=$(glance \
-            --os-auth-token $token \
-            --os-image-url http://$GLANCE_HOSTPORT \
-            image-create \
-            --name "$IMAGE_NAME-kernel" --is-public True \
+        KERNEL_ID=$(openstack \
+            --os-token $token \
+            --os-url http://$GLANCE_HOSTPORT \
+            image create \
+            "$IMAGE_NAME-kernel" --public \
             --container-format aki \
             --disk-format aki < "$KERNEL" | grep ' id ' | get_field 2)
-        RAMDISK_ID=$(glance \
-            --os-auth-token $token \
-            --os-image-url http://$GLANCE_HOSTPORT \
-            image-create \
-            --name "$IMAGE_NAME-ramdisk" --is-public True \
+        RAMDISK_ID=$(openstack \
+            --os-token $token \
+            --os-url http://$GLANCE_HOSTPORT \
+            image create \
+            "$IMAGE_NAME-ramdisk" --public \
             --container-format ari \
             --disk-format ari < "$RAMDISK" | grep ' id ' | get_field 2)
     else
@@ -381,11 +382,11 @@
         return
     fi
 
-    glance \
-        --os-auth-token $token \
-        --os-image-url http://$GLANCE_HOSTPORT \
-        image-create \
-        --name "${IMAGE_NAME%.img}" --is-public True \
+    openstack \
+        --os-token $token \
+        --os-url http://$GLANCE_HOSTPORT \
+        image create \
+        "${IMAGE_NAME%.img}" --public \
         --container-format $CONTAINER_FORMAT \
         --disk-format $DISK_FORMAT \
         ${KERNEL_ID:+--property kernel_id=$KERNEL_ID} \
diff --git a/lib/ceilometer b/lib/ceilometer
index 54d95c5..340acb9 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -79,19 +79,19 @@
 
 create_ceilometer_accounts() {
 
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+    local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
 
     # Ceilometer
     if [[ "$ENABLED_SERVICES" =~ "ceilometer-api" ]]; then
-        CEILOMETER_USER=$(get_or_create_user "ceilometer" \
-            "$SERVICE_PASSWORD" $SERVICE_TENANT)
-        get_or_add_user_role $ADMIN_ROLE $CEILOMETER_USER $SERVICE_TENANT
+        local ceilometer_user=$(get_or_create_user "ceilometer" \
+            "$SERVICE_PASSWORD" $service_tenant)
+        get_or_add_user_role $admin_role $ceilometer_user $service_tenant
 
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
-            CEILOMETER_SERVICE=$(get_or_create_service "ceilometer" \
+            local ceilometer_service=$(get_or_create_service "ceilometer" \
                 "metering" "OpenStack Telemetry Service")
-            get_or_create_endpoint $CEILOMETER_SERVICE \
+            get_or_create_endpoint $ceilometer_service \
                 "$REGION_NAME" \
                 "$CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/" \
                 "$CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/" \
@@ -154,6 +154,7 @@
 
     if [ "$CEILOMETER_BACKEND" = 'mysql' ] || [ "$CEILOMETER_BACKEND" = 'postgresql' ] ; then
         iniset $CEILOMETER_CONF database connection `database_connection_url ceilometer`
+        iniset $CEILOMETER_CONF DEFAULT collector_workers $(( ($(nproc) + 1) / 2 ))
     else
         iniset $CEILOMETER_CONF database connection mongodb://localhost:27017/ceilometer
         configure_mongodb
@@ -223,16 +224,19 @@
 
 # start_ceilometer() - Start running processes, including screen
 function start_ceilometer {
+    screen_it ceilometer-acentral "cd ; ceilometer-agent-central --config-file $CEILOMETER_CONF"
+    screen_it ceilometer-anotification "cd ; ceilometer-agent-notification --config-file $CEILOMETER_CONF"
+    screen_it ceilometer-collector "cd ; ceilometer-collector --config-file $CEILOMETER_CONF"
+    screen_it ceilometer-api "cd ; ceilometer-api -d -v --log-dir=$CEILOMETER_API_LOG_DIR --config-file $CEILOMETER_CONF"
+
+    # 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
         screen_it ceilometer-acompute "cd ; sg $LIBVIRT_GROUP 'ceilometer-agent-compute --config-file $CEILOMETER_CONF'"
     fi
     if [[ "$VIRT_DRIVER" = 'vsphere' ]]; then
         screen_it ceilometer-acompute "cd ; ceilometer-agent-compute --config-file $CEILOMETER_CONF"
     fi
-    screen_it ceilometer-acentral "cd ; ceilometer-agent-central --config-file $CEILOMETER_CONF"
-    screen_it ceilometer-anotification "cd ; ceilometer-agent-notification --config-file $CEILOMETER_CONF"
-    screen_it ceilometer-collector "cd ; ceilometer-collector --config-file $CEILOMETER_CONF"
-    screen_it ceilometer-api "cd ; ceilometer-api -d -v --log-dir=$CEILOMETER_API_LOG_DIR --config-file $CEILOMETER_CONF"
 
     # only die on API if it was actually intended to be turned on
     if is_service_enabled ceilometer-api; then
diff --git a/lib/cinder b/lib/cinder
index 38ce4d6..ce13b86 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -96,10 +96,10 @@
 # Source the enabled backends
 if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
     for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
-        BE_TYPE=${be%%:*}
-        BE_NAME=${be##*:}
-        if [[ -r $CINDER_BACKENDS/${BE_TYPE} ]]; then
-            source $CINDER_BACKENDS/${BE_TYPE}
+        be_type=${be%%:*}
+        be_name=${be##*:}
+        if [[ -r $CINDER_BACKENDS/${be_type} ]]; then
+            source $CINDER_BACKENDS/${be_type}
         fi
     done
 fi
@@ -120,7 +120,7 @@
 function cleanup_cinder {
     # ensure the volume group is cleared up because fails might
     # leave dead volumes in the group
-    TARGETS=$(sudo tgtadm --op show --mode target)
+    local 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
@@ -130,11 +130,11 @@
         else
             restart_service tgtd
         fi
-        TARGETS=$(sudo tgtadm --op show --mode target)
+        targets=$(sudo tgtadm --op show --mode target)
     fi
 
-    if [[ -n "$TARGETS" ]]; then
-        iqn_list=( $(grep --no-filename -r iqn $SCSI_PERSIST_DIR | sed 's/<target //' | sed 's/>//') )
+    if [[ -n "$targets" ]]; then
+        local iqn_list=( $(grep --no-filename -r iqn $SCSI_PERSIST_DIR | sed 's/<target //' | sed 's/>//') )
         for i in "${iqn_list[@]}"; do
             echo removing iSCSI target: $i
             sudo tgt-admin --delete $i
@@ -148,11 +148,12 @@
     fi
 
     if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
+        local be be_name be_type
         for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
-            BE_TYPE=${be%%:*}
-            BE_NAME=${be##*:}
-            if type cleanup_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
-                cleanup_cinder_backend_${BE_TYPE} ${BE_NAME}
+            be_type=${be%%:*}
+            be_name=${be##*:}
+            if type cleanup_cinder_backend_${be_type} >/dev/null 2>&1; then
+                cleanup_cinder_backend_${be_type} ${be_name}
             fi
         done
     fi
@@ -161,7 +162,7 @@
 # configure_cinder_rootwrap() - configure Cinder's rootwrap
 function configure_cinder_rootwrap {
     # Set the paths of certain binaries
-    CINDER_ROOTWRAP=$(get_rootwrap_location cinder)
+    local cinder_rootwrap=$(get_rootwrap_location cinder)
 
     # Deploy new rootwrap filters files (owned by root).
     # Wipe any existing rootwrap.d files first
@@ -179,14 +180,14 @@
     sudo chown root:root $CINDER_CONF_DIR/rootwrap.conf
     sudo chmod 0644 $CINDER_CONF_DIR/rootwrap.conf
     # Specify rootwrap.conf as first parameter to rootwrap
-    ROOTWRAP_CSUDOER_CMD="$CINDER_ROOTWRAP $CINDER_CONF_DIR/rootwrap.conf *"
+    ROOTWRAP_CSUDOER_CMD="$cinder_rootwrap $CINDER_CONF_DIR/rootwrap.conf *"
 
     # Set up the rootwrap sudoers for cinder
-    TEMPFILE=`mktemp`
-    echo "$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_CSUDOER_CMD" >$TEMPFILE
-    chmod 0440 $TEMPFILE
-    sudo chown root:root $TEMPFILE
-    sudo mv $TEMPFILE /etc/sudoers.d/cinder-rootwrap
+    local tempfile=`mktemp`
+    echo "$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_CSUDOER_CMD" >$tempfile
+    chmod 0440 $tempfile
+    sudo chown root:root $tempfile
+    sudo mv $tempfile /etc/sudoers.d/cinder-rootwrap
 }
 
 # configure_cinder() - Set config files, create data dirs, etc
@@ -237,18 +238,19 @@
     iniset $CINDER_CONF DEFAULT enable_v1_api true
 
     if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
-        enabled_backends=""
-        default_name=""
+        local enabled_backends=""
+        local default_name=""
+        local be be_name be_type
         for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
-            BE_TYPE=${be%%:*}
-            BE_NAME=${be##*:}
-            if type configure_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
-                configure_cinder_backend_${BE_TYPE} ${BE_NAME}
+            be_type=${be%%:*}
+            be_name=${be##*:}
+            if type configure_cinder_backend_${be_type} >/dev/null 2>&1; then
+                configure_cinder_backend_${be_type} ${be_name}
             fi
-            if [[ -z "$default_name" ]]; then
-                default_name=$BE_NAME
+            if [[ -z "$default_type" ]]; then
+                default_name=$be_type
             fi
-            enabled_backends+=$BE_NAME,
+            enabled_backends+=$be_name,
         done
         iniset $CINDER_CONF DEFAULT enabled_backends ${enabled_backends%,*}
         if [[ -n "$default_name" ]]; then
@@ -316,28 +318,28 @@
 # Migrated from keystone_data.sh
 function create_cinder_accounts {
 
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+    local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
 
     # Cinder
     if [[ "$ENABLED_SERVICES" =~ "c-api" ]]; then
 
-        CINDER_USER=$(get_or_create_user "cinder" \
-            "$SERVICE_PASSWORD" $SERVICE_TENANT)
-        get_or_add_user_role $ADMIN_ROLE $CINDER_USER $SERVICE_TENANT
+        local cinder_user=$(get_or_create_user "cinder" \
+            "$SERVICE_PASSWORD" $service_tenant)
+        get_or_add_user_role $admin_role $cinder_user $service_tenant
 
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
-            CINDER_SERVICE=$(get_or_create_service "cinder" \
+            local cinder_service=$(get_or_create_service "cinder" \
                 "volume" "Cinder Volume Service")
-            get_or_create_endpoint $CINDER_SERVICE "$REGION_NAME" \
+            get_or_create_endpoint $cinder_service "$REGION_NAME" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/\$(tenant_id)s" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/\$(tenant_id)s" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/\$(tenant_id)s"
 
-            CINDER_V2_SERVICE=$(get_or_create_service "cinderv2" \
+            local cinder_v2_service=$(get_or_create_service "cinderv2" \
                 "volumev2" "Cinder Volume Service V2")
-            get_or_create_endpoint $CINDER_V2_SERVICE "$REGION_NAME" \
+            get_or_create_endpoint $cinder_v2_service "$REGION_NAME" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v2/\$(tenant_id)s" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v2/\$(tenant_id)s" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v2/\$(tenant_id)s"
@@ -354,6 +356,7 @@
 }
 
 # init_cinder() - Initialize database and volume group
+# Uses global ``NOVA_ENABLED_APIS``
 function init_cinder {
     # Force nova volumes off
     NOVA_ENABLED_APIS=$(echo $NOVA_ENABLED_APIS | sed "s/osapi_volume,//")
@@ -367,11 +370,12 @@
     fi
 
     if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
+        local be be_name be_type
         for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
-            BE_TYPE=${be%%:*}
-            BE_NAME=${be##*:}
-            if type init_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
-                init_cinder_backend_${BE_TYPE} ${BE_NAME}
+            be_type=${be%%:*}
+            be_name=${be##*:}
+            if type init_cinder_backend_${be_type} >/dev/null 2>&1; then
+                init_cinder_backend_${be_type} ${be_name}
             fi
         done
     fi
@@ -450,6 +454,7 @@
 # stop_cinder() - Stop running processes
 function stop_cinder {
     # Kill the cinder screen windows
+    local serv
     for serv in c-api c-bak c-sch c-vol; do
         screen_stop $serv
     done
@@ -467,14 +472,13 @@
 function create_volume_types {
     # Create volume types
     if is_service_enabled c-api && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
+        local be be_name be_type
         for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
-            BE_TYPE=${be%%:*}
-            BE_NAME=${be##*:}
-            if type configure_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
-                # openstack volume type create --property volume_backend_name="${BE_TYPE}" ${BE_NAME}
-                cinder type-create ${BE_NAME} && \
-                    cinder type-key ${BE_NAME} set volume_backend_name="${BE_NAME}"
-            fi
+            be_type=${be%%:*}
+            be_name=${be##*:}
+            # openstack volume type create --property volume_backend_name="${be_type}" ${be_name}
+            cinder type-create ${be_name} && \
+                cinder type-key ${be_name} set volume_backend_name="${be_name}"
         done
     fi
 }
diff --git a/lib/cinder_backends/lvm b/lib/cinder_backends/lvm
index 324c323..8f8ab79 100644
--- a/lib/cinder_backends/lvm
+++ b/lib/cinder_backends/lvm
@@ -112,6 +112,7 @@
     local lv_prefix=$2
 
     # Clean out existing volumes
+    local lv
     for lv in $(sudo lvs --noheadings -o lv_name $vg 2>/dev/null); do
         # lv_prefix prefixes the LVs we want
         if [[ "${lv#$lv_prefix}" != "$lv" ]]; then
@@ -132,9 +133,9 @@
     # of the backing file
     if [[ -z "$(sudo lvs --noheadings -o lv_name $vg 2>/dev/null)" ]]; then
         # if the backing physical device is a loop device, it was probably setup by devstack
-        VG_DEV=$(sudo losetup -j $backing_file | awk -F':' '/backing-file/ { print $1}')
-        if [[ -n "$VG_DEV" ]] && [[ -e "$VG_DEV" ]]; then
-            sudo losetup -d $VG_DEV
+        local vg_dev=$(sudo losetup -j $backing_file | awk -F':' '/backing-file/ { print $1}')
+        if [[ -n "$vg_dev" ]] && [[ -e "$vg_dev" ]]; then
+            sudo losetup -d $vg_dev
             rm -f $backing_file
         fi
     fi
@@ -159,11 +160,11 @@
         if [ -z "$VOLUME_BACKING_DEVICE" ]; then
             # Only create if the file doesn't already exists
             [[ -f $backing_file ]] || truncate -s $VOLUME_BACKING_FILE_SIZE $backing_file
-            DEV=`sudo losetup -f --show $backing_file`
+            local vg_dev=`sudo losetup -f --show $backing_file`
 
             # Only create if the loopback device doesn't contain $VOLUME_GROUP
             if ! sudo vgs $vg_name; then
-                sudo vgcreate $vg_name $DEV
+                sudo vgcreate $vg_name $vg_dev
             fi
         else
             sudo vgcreate $vg_name $VOLUME_BACKING_DEVICE
diff --git a/lib/cinder_backends/xiv b/lib/cinder_backends/xiv
new file mode 100644
index 0000000..dbdb96c
--- /dev/null
+++ b/lib/cinder_backends/xiv
@@ -0,0 +1,84 @@
+# Copyright 2014 IBM Corp.
+# Copyright (c) 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+#    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.
+#
+# Authors:
+#   Alon Marx <alonma@il.ibm.com>
+
+# lib/cinder_plugins/xiv
+# Configure the xiv_ds8k driver for xiv testing
+
+# Enable xiv_ds8k driver for xiv with:
+#
+#   CINDER_ENABLED_BACKENDS+=,xiv:<volume-type-name>
+#   XIV_DRIVER_VERSION=<version-string>
+#   SAN_IP=<storage-ip-or-hostname>
+#   SAN_LOGIN=<storage-admin-account>
+#   SAN_PASSWORD=<storage-admin-password>
+#   SAN_CLUSTERNAME=<cluster-name>
+#   CONNECTION_TYPE=<connection-type> iscsi|fc
+#   XIV_CHAP=<chap-type> disabled|enabled
+
+# Dependencies:
+#
+# - ``functions`` file
+# - ``cinder`` configurations
+
+# configure_cinder_backend_xiv - Configure Cinder for xiv backends
+
+# Save trace setting
+XIV_XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+# Defaults
+# --------
+# Set up default directories
+
+
+# Entry Points
+# ------------
+
+# configure_cinder_backend_xiv - Set config files, create data dirs, etc
+function configure_cinder_backend_xiv {
+
+    local be_name=$1
+
+    python -c 'from xiv_ds8k_openstack.xiv_nova_proxy import XIVNovaProxy'
+    if [ $? -ne 0 ]; then
+        die $LINENO "XIV_DS8K driver is missing. Please install first"
+    fi
+
+    # For reference:
+    # XIV_DS8K_BACKEND='IBM-XIV_'${SAN_IP}'_'${SAN_CLUSTERNAME}'_'${CONNECTION_TYPE}
+    iniset $CINDER_CONF DEFAULT xiv_ds8k_driver_version $XIV_DRIVER_VERSION
+
+    iniset $CINDER_CONF $be_name san_ip $SAN_IP
+    iniset $CINDER_CONF $be_name san_login $SAN_LOGIN
+    iniset $CINDER_CONF $be_name san_password $SAN_PASSWORD
+    iniset $CINDER_CONF $be_name san_clustername $SAN_CLUSTERNAME
+    iniset $CINDER_CONF $be_name xiv_ds8k_connection_type $CONNECTION_TYPE
+    iniset $CINDER_CONF $be_name volume_backend_name $be_name
+    iniset $CINDER_CONF $be_name volume_driver 'cinder.volume.drivers.ibm.xiv_ds8k.XIVDS8KDriver'
+    iniset $CINDER_CONF $be_name xiv_ds8k_proxy 'xiv_ds8k_openstack.xiv_nova_proxy.XIVNovaProxy'
+    iniset $CINDER_CONF $be_name xiv_chap $XIV_CHAP
+}
+
+# Restore xtrace
+$XIV_XTRACE
+
+# Local variables:
+# mode: shell-script
+# End:
+
diff --git a/lib/config b/lib/config
index 67d788c..0baa4cc 100644
--- a/lib/config
+++ b/lib/config
@@ -110,6 +110,7 @@
 
     [[ -r $localfile ]] || return 0
 
+    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
diff --git a/lib/database b/lib/database
index 0661049..e226515 100644
--- a/lib/database
+++ b/lib/database
@@ -89,7 +89,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:-${DATABASE_TYPE}://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST}
+    BASE_SQL_CONN=${BASE_SQL_CONN:-$(get_database_type)://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST}
 
     return 0
 }
@@ -120,6 +120,14 @@
     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/dib b/lib/dib
new file mode 100644
index 0000000..3a1167f
--- /dev/null
+++ b/lib/dib
@@ -0,0 +1,133 @@
+# lib/dib
+# Install and build images with **diskimage-builder**
+
+# Dependencies:
+#
+# - functions
+# - DEST, DATA_DIR must be defined
+
+# stack.sh
+# ---------
+# - install_dib
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+# Defaults
+# --------
+
+# set up default directories
+DIB_DIR=$DEST/diskimage-builder
+TIE_DIR=$DEST/tripleo-image-elements
+DIB_IMAGE_CACHE=$DATA_DIR/diskimage-builder/image-create
+DIB_PIP_REPO=$DATA_DIR/diskimage-builder/pip-repo
+DIB_PIP_REPO_PORT=${DIB_PIP_REPO_PORT:-8899}
+OCC_DIR=$DEST/os-collect-config
+ORC_DIR=$DEST/os-refresh-config
+OAC_DIR=$DEST/os-apply-config
+
+# Functions
+# ---------
+
+# install_dib() - Collect source and prepare
+function install_dib {
+    git_clone $DIB_REPO $DIB_DIR $DIB_BRANCH
+    pushd $DIB_DIR
+    pip_install ./
+    popd
+
+    git_clone $TIE_REPO $TIE_DIR $TIE_BRANCH
+    git_clone $OCC_REPO $OCC_DIR $OCC_BRANCH
+    git_clone $ORC_REPO $ORC_DIR $ORC_BRANCH
+    git_clone $OAC_REPO $OAC_DIR $OAC_BRANCH
+    mkdir -p $DIB_IMAGE_CACHE
+}
+
+# build_dib_pip_repo() - Builds a local pip repo from local projects
+function build_dib_pip_repo {
+    local project_dirs=$1
+    local projpath proj package
+
+    rm -rf $DIB_PIP_REPO
+    mkdir -p $DIB_PIP_REPO
+
+    echo "<html><body>" > $DIB_PIP_REPO/index.html
+    for projpath in $project_dirs; do
+        proj=$(basename $projpath)
+        mkdir -p $DIB_PIP_REPO/$proj
+        pushd $projpath
+        rm -rf dist
+        python setup.py sdist
+        pushd dist
+        package=$(ls *)
+        mv $package $DIB_PIP_REPO/$proj/$package
+        popd
+
+        echo "<html><body><a href=\"$package\">$package</a></body></html>" > $DIB_PIP_REPO/$proj/index.html
+        echo "<a href=\"$proj\">$proj</a><br/>" >> $DIB_PIP_REPO/index.html
+
+        popd
+    done
+
+    echo "</body></html>" >> $DIB_PIP_REPO/index.html
+
+    local dib_pip_repo_apache_conf=$(apache_site_config_for dib_pip_repo)
+
+    sudo cp $FILES/apache-dib-pip-repo.template $dib_pip_repo_apache_conf
+    sudo sed -e "
+        s|%DIB_PIP_REPO%|$DIB_PIP_REPO|g;
+        s|%DIB_PIP_REPO_PORT%|$DIB_PIP_REPO_PORT|g;
+        s|%APACHE_NAME%|$APACHE_NAME|g;
+    " -i $dib_pip_repo_apache_conf
+    enable_apache_site dib_pip_repo
+}
+
+# disk_image_create_upload() - Creates and uploads a diskimage-builder built image
+function disk_image_create_upload {
+
+    local image_name=$1
+    local image_elements=$2
+    local elements_path=$3
+
+    local image_path=$TOP_DIR/files/$image_name.qcow2
+
+    # Set the local pip repo as the primary index mirror so the
+    # image is built with local packages
+    local pypi_mirror_url=http://$SERVICE_HOST:$DIB_PIP_REPO_PORT/
+    local pypi_mirror_url_1
+
+    if [ -a $HOME/.pip/pip.conf ]; then
+        # Add the current pip.conf index-url as an extra-index-url
+        # in the image build
+        pypi_mirror_url_1=$(iniget $HOME/.pip/pip.conf global index-url)
+    else
+        # If no pip.conf, set upstream pypi as an extra mirror
+        # (this also sets the .pydistutils.cfg index-url)
+        pypi_mirror_url_1=http://pypi.python.org/simple
+    fi
+
+    # The disk-image-create command to run
+    ELEMENTS_PATH=$elements_path \
+    PYPI_MIRROR_URL=$pypi_mirror_url \
+    PYPI_MIRROR_URL_1=$pypi_mirror_url_1 \
+    disk-image-create -a amd64 $image_elements \
+        --image-cache $DIB_IMAGE_CACHE \
+        -o $image_path
+
+    local token=$(keystone token-get | grep ' id ' | get_field 2)
+    die_if_not_set $LINENO token "Keystone fail to get token"
+
+    glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT \
+        image-create --name $image_name --is-public True \
+        --container-format=bare --disk-format qcow2 \
+        < $image_path
+}
+
+# Restore xtrace
+$XTRACE
+
+# Tell emacs to use shell-script-mode
+## Local variables:
+## mode: shell-script
+## End:
diff --git a/lib/glance b/lib/glance
index 92577d9..1dea6cf 100644
--- a/lib/glance
+++ b/lib/glance
@@ -96,6 +96,10 @@
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_user glance
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken signing_dir $GLANCE_AUTH_CACHE_DIR/registry
+    if is_service_enabled qpid || [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; then
+        iniset $GLANCE_REGISTRY_CONF DEFAULT notification_driver messaging
+    fi
+    iniset_rpc_backend glance $GLANCE_REGISTRY_CONF DEFAULT
 
     cp $GLANCE_DIR/etc/glance-api.conf $GLANCE_API_CONF
     iniset $GLANCE_API_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
@@ -165,23 +169,23 @@
 function create_glance_accounts {
     if is_service_enabled g-api; then
 
-        GLANCE_USER=$(get_or_create_user "glance" \
+        local glance_user=$(get_or_create_user "glance" \
             "$SERVICE_PASSWORD" $SERVICE_TENANT_NAME)
-        get_or_add_user_role service $GLANCE_USER $SERVICE_TENANT_NAME
+        get_or_add_user_role service $glance_user $SERVICE_TENANT_NAME
 
         # required for swift access
         if is_service_enabled s-proxy; then
 
-            GLANCE_SWIFT_USER=$(get_or_create_user "glance-swift" \
+            local glance_swift_user=$(get_or_create_user "glance-swift" \
                 "$SERVICE_PASSWORD" $SERVICE_TENANT_NAME "glance-swift@example.com")
-            get_or_add_user_role "ResellerAdmin" $GLANCE_SWIFT_USER $SERVICE_TENANT_NAME
+            get_or_add_user_role "ResellerAdmin" $glance_swift_user $SERVICE_TENANT_NAME
         fi
 
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
-            GLANCE_SERVICE=$(get_or_create_service "glance" \
+            local glance_service=$(get_or_create_service "glance" \
                 "image" "Glance Image Service")
-            get_or_create_endpoint $GLANCE_SERVICE \
+            get_or_create_endpoint $glance_service \
                 "$REGION_NAME" \
                 "http://$GLANCE_HOSTPORT" \
                 "http://$GLANCE_HOSTPORT" \
diff --git a/lib/heat b/lib/heat
index 510b683..bd99d6b 100644
--- a/lib/heat
+++ b/lib/heat
@@ -31,6 +31,8 @@
 # set up default directories
 HEAT_DIR=$DEST/heat
 HEATCLIENT_DIR=$DEST/python-heatclient
+HEAT_CFNTOOLS_DIR=$DEST/heat-cfntools
+HEAT_TEMPLATES_REPO_DIR=$DEST/heat-templates
 HEAT_AUTH_CACHE_DIR=${HEAT_AUTH_CACHE_DIR:-/var/cache/heat}
 HEAT_STANDALONE=`trueorfalse False $HEAT_STANDALONE`
 HEAT_CONF_DIR=/etc/heat
@@ -179,6 +181,12 @@
     git_clone $HEAT_REPO $HEAT_DIR $HEAT_BRANCH
 }
 
+# install_heat_other() - Collect source and prepare
+function install_heat_other {
+    git_clone $HEAT_CFNTOOLS_REPO $HEAT_CFNTOOLS_DIR $HEAT_CFNTOOLS_BRANCH
+    git_clone $HEAT_TEMPLATES_REPO $HEAT_TEMPLATES_REPO_DIR $HEAT_TEMPLATES_BRANCH
+}
+
 # start_heat() - Start running processes, including screen
 function start_heat {
     screen_it h-eng "cd $HEAT_DIR; bin/heat-engine --config-file=$HEAT_CONF"
@@ -190,49 +198,35 @@
 # 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
         screen_stop $serv
     done
 }
 
-function disk_image_create {
-    local elements_path=$1
-    local elements=$2
-    local arch=$3
-    local output=$TOP_DIR/files/$4
-    if [[ -f "$output.qcow2" ]]; then
-        echo "Image file already exists: $output_file"
-    else
-        ELEMENTS_PATH=$elements_path disk-image-create \
-            $elements -a $arch -o $output
-    fi
-    # upload with fake URL so that image in $TOP_DIR/files is used
-    upload_image "http://localhost/$output.qcow2" $TOKEN
-}
-
 # create_heat_accounts() - Set up common required heat accounts
 function create_heat_accounts {
     # migrated from files/keystone_data.sh
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+    local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
 
-    HEAT_USER=$(get_or_create_user "heat" \
-        "$SERVICE_PASSWORD" $SERVICE_TENANT)
-    get_or_add_user_role $ADMIN_ROLE $HEAT_USER $SERVICE_TENANT
+    local heat_user=$(get_or_create_user "heat" \
+        "$SERVICE_PASSWORD" $service_tenant)
+    get_or_add_user_role $admin_role $heat_user $service_tenant
 
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
-        HEAT_SERVICE=$(get_or_create_service "heat" \
+        local heat_service=$(get_or_create_service "heat" \
                 "orchestration" "Heat Orchestration Service")
-        get_or_create_endpoint $HEAT_SERVICE \
+        get_or_create_endpoint $heat_service \
             "$REGION_NAME" \
             "$SERVICE_PROTOCOL://$HEAT_API_HOST:$HEAT_API_PORT/v1/\$(tenant_id)s" \
             "$SERVICE_PROTOCOL://$HEAT_API_HOST:$HEAT_API_PORT/v1/\$(tenant_id)s" \
             "$SERVICE_PROTOCOL://$HEAT_API_HOST:$HEAT_API_PORT/v1/\$(tenant_id)s"
 
-        HEAT_CFN_SERVICE=$(get_or_create_service "heat-cfn" \
+        local heat_cfn_service=$(get_or_create_service "heat-cfn" \
                 "cloudformation" "Heat CloudFormation Service")
-        get_or_create_endpoint $HEAT_CFN_SERVICE \
+        get_or_create_endpoint $heat_cfn_service \
             "$REGION_NAME" \
             "$SERVICE_PROTOCOL://$HEAT_API_CFN_HOST:$HEAT_API_CFN_PORT/v1" \
             "$SERVICE_PROTOCOL://$HEAT_API_CFN_HOST:$HEAT_API_CFN_PORT/v1" \
@@ -247,36 +241,36 @@
         # heat_stack_owner role is given to users who create Heat stacks,
         # it's the default role used by heat to delegate to the heat service
         # user (for performing deferred operations via trusts), see heat.conf
-        HEAT_OWNER_ROLE=$(get_or_create_role "heat_stack_owner")
+        local heat_owner_role=$(get_or_create_role "heat_stack_owner")
 
         # Give the role to the demo and admin users so they can create stacks
         # in either of the projects created by devstack
-        get_or_add_user_role $HEAT_OWNER_ROLE demo demo
-        get_or_add_user_role $HEAT_OWNER_ROLE admin demo
-        get_or_add_user_role $HEAT_OWNER_ROLE admin admin
+        get_or_add_user_role $heat_owner_role demo demo
+        get_or_add_user_role $heat_owner_role admin demo
+        get_or_add_user_role $heat_owner_role admin admin
         iniset $HEAT_CONF DEFAULT deferred_auth_method trusts
     fi
 
     if [[ "$HEAT_STACK_DOMAIN" == "True" ]]; then
         # Note we have to pass token/endpoint here because the current endpoint and
         # version negotiation in OSC means just --os-identity-api-version=3 won't work
-        KS_ENDPOINT_V3="$KEYSTONE_SERVICE_URI/v3"
+        local ks_endpoint_v3="$KEYSTONE_SERVICE_URI/v3"
 
-        D_ID=$(openstack --os-token $OS_TOKEN --os-url=$KS_ENDPOINT_V3 \
+        D_ID=$(openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
             --os-identity-api-version=3 domain list | grep ' heat ' | get_field 1)
 
         if [[ -z "$D_ID" ]]; then
-            D_ID=$(openstack --os-token $OS_TOKEN --os-url=$KS_ENDPOINT_V3 \
+            D_ID=$(openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
                 --os-identity-api-version=3 domain create heat \
                 --description "Owns users and projects created by heat" \
                 | grep ' id ' | get_field 2)
             iniset $HEAT_CONF DEFAULT stack_user_domain ${D_ID}
 
-            openstack --os-token $OS_TOKEN --os-url=$KS_ENDPOINT_V3 \
+            openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
                 --os-identity-api-version=3 user create --password $SERVICE_PASSWORD \
                 --domain $D_ID heat_domain_admin \
                 --description "Manages users and projects created by heat"
-            openstack --os-token $OS_TOKEN --os-url=$KS_ENDPOINT_V3 \
+            openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
                 --os-identity-api-version=3 role add \
                 --user heat_domain_admin --domain ${D_ID} admin
             iniset $HEAT_CONF DEFAULT stack_domain_admin heat_domain_admin
@@ -285,6 +279,23 @@
     fi
 }
 
+# build_heat_functional_test_image() - Build and upload functional test image
+function build_heat_functional_test_image {
+    build_dib_pip_repo "$OCC_DIR $OAC_DIR $ORC_DIR $HEAT_CFNTOOLS_DIR"
+    local image_name=heat-functional-tests-image
+
+    # The elements to invoke disk-image-create with
+    local image_elements="vm fedora selinux-permissive pypi \
+        os-collect-config os-refresh-config os-apply-config heat-cfntools \
+        heat-config heat-config-cfn-init heat-config-puppet heat-config-script"
+
+    # Elements path for tripleo-image-elements and heat-templates software-config
+    local elements_path=$TIE_DIR/elements:$HEAT_TEMPLATES_REPO_DIR/hot/software-config/elements
+
+    disk_image_create_upload "$image_name" "$image_elements" "$elements_path"
+    iniset $TEMPEST_CONFIG orchestration image_ref $image_name
+}
+
 # Restore xtrace
 $XTRACE
 
diff --git a/lib/horizon b/lib/horizon
index a65b243..614a0c8 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -50,7 +50,7 @@
         sed -e "/^$option/d" -i $local_settings
         echo -e "\n$option=$value" >> $file
     elif grep -q "^$section" $file; then
-        line=$(sed -ne "/^$section/,/^}/ { /^ *'$option':/ p; }" $file)
+        local line=$(sed -ne "/^$section/,/^}/ { /^ *'$option':/ p; }" $file)
         if [ -n "$line" ]; then
             sed -i -e "/^$section/,/^}/ s/^\( *'$option'\) *:.*$/\1: $value,/" $file
         else
@@ -89,7 +89,7 @@
 # init_horizon() - Initialize databases, etc.
 function init_horizon {
     # ``local_settings.py`` is used to override horizon default settings.
-    local_settings=$HORIZON_DIR/openstack_dashboard/local/local_settings.py
+    local local_settings=$HORIZON_DIR/openstack_dashboard/local/local_settings.py
     cp $HORIZON_SETTINGS $local_settings
 
     if is_service_enabled neutron; then
@@ -121,9 +121,9 @@
     sudo mkdir -p $HORIZON_DIR/.blackhole
 
     # Apache 2.4 uses mod_authz_host for access control now (instead of "Allow")
-    HORIZON_REQUIRE=''
+    local horizon_require=''
     if check_apache_version "2.4" ; then
-        HORIZON_REQUIRE='Require all granted'
+        horizon_require='Require all granted'
     fi
 
     local horizon_conf=$(apache_site_config_for horizon)
@@ -135,7 +135,7 @@
         s,%HORIZON_DIR%,$HORIZON_DIR,g;
         s,%APACHE_NAME%,$APACHE_NAME,g;
         s,%DEST%,$DEST,g;
-        s,%HORIZON_REQUIRE%,$HORIZON_REQUIRE,g;
+        s,%HORIZON_REQUIRE%,$horizon_require,g;
     \" $FILES/apache-horizon.template >$horizon_conf"
 
     if is_ubuntu; then
diff --git a/lib/ironic b/lib/ironic
index b56abcb..469f3a3 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -66,8 +66,6 @@
 IRONIC_VM_LOG_CONSOLE=${IRONIC_VM_LOG_CONSOLE:-True}
 IRONIC_VM_LOG_DIR=${IRONIC_VM_LOG_DIR:-$IRONIC_DATA_DIR/logs/}
 
-DIB_DIR=${DIB_DIR:-$DEST/diskimage-builder}
-
 # 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.
@@ -76,6 +74,12 @@
 IRONIC_DEPLOY_KERNEL=${IRONIC_DEPLOY_KERNEL:-}
 IRONIC_DEPLOY_ELEMENT=${IRONIC_DEPLOY_ELEMENT:-deploy-ironic}
 
+IRONIC_AGENT_TARBALL=${IRONIC_AGENT_TARBALL:-http://tarballs.openstack.org/ironic-python-agent/coreos/ipa-coreos.tar.gz}
+
+# Which deploy driver to use - valid choices right now
+# are 'pxe_ssh' and 'agent_ssh'.
+IRONIC_DEPLOY_DRIVER=${IRONIC_DEPLOY_DRIVER:-pxe_ssh}
+
 #TODO(agordeev): replace 'ubuntu' with host distro name getting
 IRONIC_DEPLOY_FLAVOR=${IRONIC_DEPLOY_FLAVOR:-ubuntu $IRONIC_DEPLOY_ELEMENT}
 
@@ -89,6 +93,32 @@
 # 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}
+IRONIC_HTTP_SERVER=${IRONIC_HTTP_SERVER:-$HOST_IP}
+IRONIC_HTTP_PORT=${IRONIC_HTTP_PORT:-8088}
+
+# get_pxe_boot_file() - Get the PXE/iPXE boot file path
+function get_pxe_boot_file {
+    local relpath=syslinux/pxelinux.0
+    if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+        relpath=ipxe/undionly.kpxe
+    fi
+
+    local pxe_boot_file
+    if is_ubuntu; then
+        pxe_boot_file=/usr/lib/$relpath
+    elif is_fedora || is_suse; then
+        pxe_boot_file=/usr/share/$relpath
+    fi
+
+    echo $pxe_boot_file
+}
+
+# PXE boot image
+IRONIC_PXE_BOOT_IMAGE=${IRONIC_PXE_BOOT_IMAGE:-$(get_pxe_boot_file)}
+
 
 # Functions
 # ---------
@@ -102,8 +132,18 @@
 
 # install_ironic() - Collect source and prepare
 function install_ironic {
+    # make sure all needed service were enabled
+    for srv in nova glance key; do
+        if ! is_service_enabled "$srv"; then
+            die $LINENO "$srv should be enabled for Ironic."
+        fi
+    done
     git_clone $IRONIC_REPO $IRONIC_DIR $IRONIC_BRANCH
     setup_develop $IRONIC_DIR
+
+    if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+        install_apache_wsgi
+    fi
 }
 
 # install_ironicclient() - Collect sources and prepare
@@ -113,17 +153,60 @@
     sudo install -D -m 0644 -o $STACK_USER {$IRONICCLIENT_DIR/tools/,/etc/bash_completion.d/}ironic.bash_completion
 }
 
+# _cleanup_ironic_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
+function _cleanup_ironic_apache_wsgi {
+    sudo rm -rf $IRONIC_HTTP_DIR
+    disable_apache_site ironic
+    sudo rm -f $(apache_site_config_for ironic)
+    restart_apache_server
+}
+
+# _config_ironic_apache_wsgi() - Set WSGI config files of Ironic
+function _config_ironic_apache_wsgi {
+    local 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;
+        s|%HTTPROOT%|$IRONIC_HTTP_DIR|g;
+    " -i $ironic_apache_conf
+    enable_apache_site ironic
+}
+
 # cleanup_ironic() - Remove residual data files, anything left over from previous
 # runs that would need to clean up.
 function cleanup_ironic {
     sudo rm -rf $IRONIC_AUTH_CACHE_DIR
 }
 
-# configure_ironic() - Set config files, create data dirs, etc
-function configure_ironic {
+# configure_ironic_dirs() - Create all directories required by Ironic and
+# associated services.
+function configure_ironic_dirs {
     if [[ ! -d $IRONIC_CONF_DIR ]]; then
         sudo mkdir -p $IRONIC_CONF_DIR
     fi
+
+    if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+        sudo mkdir -p $IRONIC_HTTP_DIR
+        sudo chown -R $STACK_USER:$LIBVIRT_GROUP $IRONIC_HTTP_DIR
+    fi
+
+    sudo mkdir -p $IRONIC_DATA_DIR
+    sudo mkdir -p $IRONIC_STATE_PATH
+    sudo mkdir -p $IRONIC_TFTPBOOT_DIR
+    sudo chown -R $STACK_USER $IRONIC_DATA_DIR $IRONIC_STATE_PATH
+    sudo chown -R $STACK_USER:$LIBVIRT_GROUP $IRONIC_TFTPBOOT_DIR
+    mkdir -p $IRONIC_TFTPBOOT_DIR/pxelinux.cfg
+
+    if [ ! -f $IRONIC_PXE_BOOT_IMAGE ]; then
+        die $LINENO "PXE boot file $IRONIC_PXE_BOOT_IMAGE not found."
+    fi
+
+    cp $IRONIC_PXE_BOOT_IMAGE $IRONIC_TFTPBOOT_DIR
+}
+
+# configure_ironic() - Set config files, create data dirs, etc
+function configure_ironic {
+    configure_ironic_dirs
     sudo chown $STACK_USER $IRONIC_CONF_DIR
 
     # Copy over ironic configuration file and configure common parameters.
@@ -148,8 +231,8 @@
         setup_colorized_logging $IRONIC_CONF_FILE DEFAULT
     fi
 
-    if [[ "$IRONIC_BAREMETAL_BASIC_OPS" == "True" ]]; then
-        configure_ironic_auxiliary
+    if [[ "$IRONIC_IPXE_ENABLED" == "True" ]]; then
+        _config_ironic_apache_wsgi
     fi
 }
 
@@ -175,15 +258,15 @@
 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
-    IRONIC_ROOTWRAP=$(get_rootwrap_location ironic)
-    ROOTWRAP_ISUDOER_CMD="$IRONIC_ROOTWRAP $IRONIC_CONF_DIR/rootwrap.conf *"
+    local ironic_rootwrap=$(get_rootwrap_location ironic)
+    local rootwrap_isudoer_cmd="$ironic_rootwrap $IRONIC_CONF_DIR/rootwrap.conf *"
 
     # Set up the rootwrap sudoers for ironic
-    TEMPFILE=`mktemp`
-    echo "$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_ISUDOER_CMD" >$TEMPFILE
-    chmod 0440 $TEMPFILE
-    sudo chown root:root $TEMPFILE
-    sudo mv $TEMPFILE /etc/sudoers.d/ironic-rootwrap
+    local tempfile=`mktemp`
+    echo "$STACK_USER ALL=(root) NOPASSWD: $rootwrap_isudoer_cmd" >$tempfile
+    chmod 0440 $tempfile
+    sudo chown root:root $tempfile
+    sudo mv $tempfile /etc/sudoers.d/ironic-rootwrap
 
     iniset $IRONIC_CONF_FILE DEFAULT rootwrap_config $IRONIC_ROOTWRAP_CONF
     iniset $IRONIC_CONF_FILE DEFAULT enabled_drivers $IRONIC_ENABLED_DRIVERS
@@ -194,6 +277,31 @@
     if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
         iniset $IRONIC_CONF_FILE pxe pxe_append_params "nofb nomodeset vga=normal console=ttyS0"
     fi
+    if [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]] ; then
+        if [[ "$SWIFT_ENABLE_TEMPURLS" == "True" ]] ; then
+            iniset $IRONIC_CONF_FILE glance swift_temp_url_key $SWIFT_TEMPURL_KEY
+        else
+            die $LINENO "SWIFT_ENABLE_TEMPURLS must be True to use agent_ssh driver in Ironic."
+        fi
+        iniset $IRONIC_CONF_FILE glance swift_endpoint_url http://${HOST_IP}:8080
+        iniset $IRONIC_CONF_FILE glance swift_api_version v1
+        iniset $IRONIC_CONF_FILE glance swift_account AUTH_${SERVICE_TENANT}
+        iniset $IRONIC_CONF_FILE glance swift_container glance
+        iniset $IRONIC_CONF_FILE glance swift_temp_url_duration 3600
+        iniset $IRONIC_CONF_FILE agent heartbeat_timeout 30
+        if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
+            iniset $IRONIC_CONF_FILE agent agent_pxe_append_params "nofb nomodeset vga=normal console=ttyS0"
+        fi
+    fi
+
+    if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+        local 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
+        iniset $IRONIC_CONF_FILE pxe http_root $IRONIC_HTTP_DIR
+        iniset $IRONIC_CONF_FILE pxe http_url "http://$IRONIC_HTTP_SERVER:$IRONIC_HTTP_PORT"
+    fi
 }
 
 # create_ironic_cache_dir() - Part of the init_ironic() process
@@ -214,22 +322,22 @@
 # service              ironic     admin        # if enabled
 function create_ironic_accounts {
 
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+    local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
 
     # Ironic
     if [[ "$ENABLED_SERVICES" =~ "ir-api" ]]; then
         # Get ironic user if exists
 
-        IRONIC_USER=$(get_or_create_user "ironic" \
-            "$SERVICE_PASSWORD" $SERVICE_TENANT)
-        get_or_add_user_role $ADMIN_ROLE $IRONIC_USER $SERVICE_TENANT
+        local ironic_user=$(get_or_create_user "ironic" \
+            "$SERVICE_PASSWORD" $service_tenant)
+        get_or_add_user_role $admin_role $ironic_user $service_tenant
 
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
-            IRONIC_SERVICE=$(get_or_create_service "ironic" \
+            local ironic_service=$(get_or_create_service "ironic" \
                 "baremetal" "Ironic baremetal provisioning service")
-            get_or_create_endpoint $IRONIC_SERVICE \
+            get_or_create_endpoint $ironic_service \
                 "$REGION_NAME" \
                 "$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT" \
                 "$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT" \
@@ -261,6 +369,11 @@
     if is_service_enabled ir-cond; then
         start_ironic_conductor
     fi
+
+    # Start Apache if iPXE is enabled
+    if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+        restart_apache_server
+    fi
 }
 
 # start_ironic_api() - Used by start_ironic().
@@ -285,6 +398,11 @@
     # Kill the Ironic screen windows
     screen -S $SCREEN_NAME -p ir-api -X kill
     screen -S $SCREEN_NAME -p ir-cond -X kill
+
+    # Cleanup the WSGI files
+    if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+        _cleanup_ironic_apache_wsgi
+    fi
 }
 
 function is_ironic {
@@ -294,58 +412,72 @@
     return 1
 }
 
-function configure_ironic_dirs {
-    sudo mkdir -p $IRONIC_DATA_DIR
-    sudo mkdir -p $IRONIC_STATE_PATH
-    sudo mkdir -p $IRONIC_TFTPBOOT_DIR
-    sudo chown -R $STACK_USER $IRONIC_DATA_DIR $IRONIC_STATE_PATH
-    sudo chown -R $STACK_USER:$LIBVIRT_GROUP $IRONIC_TFTPBOOT_DIR
-    if is_ubuntu; then
-        PXEBIN=/usr/lib/syslinux/pxelinux.0
-    elif is_fedora; then
-        PXEBIN=/usr/share/syslinux/pxelinux.0
-    fi
-    if [ ! -f $PXEBIN ]; then
-        die $LINENO "pxelinux.0 (from SYSLINUX) not found."
-    fi
+function create_ovs_taps {
+    local ironic_net_id=$(neutron net-list | grep private | get_field 1)
 
-    cp $PXEBIN $IRONIC_TFTPBOOT_DIR
-    mkdir -p $IRONIC_TFTPBOOT_DIR/pxelinux.cfg
+    # 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)
+
+    # intentional sleep to make sure the tag has been set to port
+    sleep 10
+
+    local tapdev=$(sudo ip netns exec qdhcp-${ironic_net_id} ip link list | grep tap | cut -d':' -f2 | cut -b2-)
+    local 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
+    sudo ip link show brbm-tap1 && sudo ip link delete brbm-tap1
+    # create veth pair for future interconnection between br-int and brbm
+    sudo ip link add brbm-tap1 type veth peer name ovs-tap1
+    sudo ip link set dev brbm-tap1 up
+    sudo ip link set dev ovs-tap1 up
+
+    sudo ovs-vsctl -- --if-exists del-port ovs-tap1 -- add-port br-int ovs-tap1 tag=$tag_id
+    sudo ovs-vsctl -- --if-exists del-port brbm-tap1 -- add-port $IRONIC_VM_NETWORK_BRIDGE brbm-tap1
+
+    # Remove the port needed only for workaround.
+    neutron port-delete $port_id
+
+    # Finally, share the fixed tenant network across all tenants.  This allows the host
+    # to serve TFTP to a single network namespace via the tap device created above.
+    neutron net-update $ironic_net_id --shared true
 }
 
 function create_bridge_and_vms {
     # Call libvirt setup scripts in a new shell to ensure any new group membership
     sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/setup-network"
     if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
-        LOG_ARG="$IRONIC_VM_LOG_DIR"
+        local log_arg="$IRONIC_VM_LOG_DIR"
     else
-        LOG_ARG=""
+        local log_arg=""
     fi
     sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/create-nodes \
         $IRONIC_VM_SPECS_CPU $IRONIC_VM_SPECS_RAM $IRONIC_VM_SPECS_DISK \
         amd64 $IRONIC_VM_COUNT $IRONIC_VM_NETWORK_BRIDGE $IRONIC_VM_EMULATOR \
-        $LOG_ARG" >> $IRONIC_VM_MACS_CSV_FILE
+        $log_arg" >> $IRONIC_VM_MACS_CSV_FILE
+    create_ovs_taps
 }
 
 function enroll_vms {
-
-    CHASSIS_ID=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
-    IRONIC_NET_ID=$(neutron net-list | grep private | get_field 1)
+    local chassis_id=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
     local idx=0
 
-    # work around; need to know what netns neutron uses for private network.
-    # Without knowing how to interconnect the networks, PXE won't work properly
-    # for fake baremetal instances. The network should be configured prior all
-    # the instances operation. If we don't do this, the first port creation
-    # only happens in the middle of fake baremetal instance's spawning by nova,
-    # so we'll end up with unbootable fake baremetal VM due to broken PXE.
-    PORT_ID=$(neutron port-create private | grep " id " | get_field 2)
+    if [[ "$IRONIC_DEPLOY_DRIVER" == "pxe_ssh" ]] ; then
+        local _IRONIC_DEPLOY_KERNEL_KEY=pxe_deploy_kernel
+        local _IRONIC_DEPLOY_RAMDISK_KEY=pxe_deploy_ramdisk
+    elif [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]] ; then
+        local _IRONIC_DEPLOY_KERNEL_KEY=deploy_kernel
+        local _IRONIC_DEPLOY_RAMDISK_KEY=deploy_ramdisk
+    fi
 
     while read MAC; do
 
-        NODE_ID=$(ironic node-create --chassis_uuid $CHASSIS_ID --driver pxe_ssh \
-            -i pxe_deploy_kernel=$IRONIC_DEPLOY_KERNEL_ID \
-            -i pxe_deploy_ramdisk=$IRONIC_DEPLOY_RAMDISK_ID \
+        local node_id=$(ironic node-create --chassis_uuid $chassis_id \
+            --driver $IRONIC_DEPLOY_DRIVER \
+            -i $_IRONIC_DEPLOY_KERNEL_KEY=$IRONIC_DEPLOY_KERNEL_ID \
+            -i $_IRONIC_DEPLOY_RAMDISK_KEY=$IRONIC_DEPLOY_RAMDISK_ID \
             -i ssh_virt_type=$IRONIC_SSH_VIRT_TYPE \
             -i ssh_address=$IRONIC_VM_SSH_ADDRESS \
             -i ssh_port=$IRONIC_VM_SSH_PORT \
@@ -357,40 +489,19 @@
             -p cpu_arch=x86_64 \
             | grep " uuid " | get_field 2)
 
-        ironic port-create --address $MAC --node_uuid $NODE_ID
+        ironic port-create --address $MAC --node_uuid $node_id
 
         idx=$((idx+1))
-
     done < $IRONIC_VM_MACS_CSV_FILE
 
     # create the nova flavor
-    adjusted_disk=$(($IRONIC_VM_SPECS_DISK - $IRONIC_VM_EPHEMERAL_DISK))
+    local adjusted_disk=$(($IRONIC_VM_SPECS_DISK - $IRONIC_VM_EPHEMERAL_DISK))
     nova flavor-create --ephemeral $IRONIC_VM_EPHEMERAL_DISK baremetal auto $IRONIC_VM_SPECS_RAM $adjusted_disk $IRONIC_VM_SPECS_CPU
     # TODO(lucasagomes): Remove the 'baremetal:deploy_kernel_id'
     # and 'baremetal:deploy_ramdisk_id' parameters
     # from the flavor after the completion of
     # https://blueprints.launchpad.net/ironic/+spec/add-node-instance-info
     nova flavor-key baremetal set "cpu_arch"="x86_64" "baremetal:deploy_kernel_id"="$IRONIC_DEPLOY_KERNEL_ID" "baremetal:deploy_ramdisk_id"="$IRONIC_DEPLOY_RAMDISK_ID"
-
-    # intentional sleep to make sure the tag has been set to port
-    sleep 10
-    TAPDEV=$(sudo ip netns exec qdhcp-${IRONIC_NET_ID} ip link list | grep tap | cut -d':' -f2 | cut -b2-)
-    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
-    sudo ip link show brbm-tap1 && sudo ip link delete brbm-tap1
-    # create veth pair for future interconnection between br-int and brbm
-    sudo ip link add brbm-tap1 type veth peer name ovs-tap1
-    sudo ip link set dev brbm-tap1 up
-    sudo ip link set dev ovs-tap1 up
-
-    sudo ovs-vsctl -- --if-exists del-port ovs-tap1 -- add-port br-int ovs-tap1 tag=$TAG_ID
-    sudo ovs-vsctl -- --if-exists del-port brbm-tap1 -- add-port $IRONIC_VM_NETWORK_BRIDGE brbm-tap1
-
-    # Remove the port needed only for workaround. For additional info read the
-    # comment at the beginning of this function
-    neutron port-delete $PORT_ID
 }
 
 function configure_iptables {
@@ -403,15 +514,6 @@
 }
 
 function configure_tftpd {
-    if is_ubuntu; then
-        PXEBIN=/usr/lib/syslinux/pxelinux.0
-    elif is_fedora; then
-        PXEBIN=/usr/share/syslinux/pxelinux.0
-    fi
-    if [ ! -f $PXEBIN ]; then
-        die $LINENO "pxelinux.0 (from SYSLINUX) not found."
-    fi
-
     # stop tftpd and setup serving via xinetd
     stop_service tftpd-hpa || true
     [ -f /etc/init/tftpd-hpa.conf ] && echo "manual" | sudo tee /etc/init/tftpd-hpa.override
@@ -441,33 +543,38 @@
 }
 
 function ironic_ssh_check {
-    local KEY_FILE=$1
-    local FLOATING_IP=$2
-    local PORT=$3
-    local DEFAULT_INSTANCE_USER=$4
-    local ACTIVE_TIMEOUT=$5
-    if ! timeout $ACTIVE_TIMEOUT sh -c "while ! ssh -p $PORT -o StrictHostKeyChecking=no -i $KEY_FILE ${DEFAULT_INSTANCE_USER}@$FLOATING_IP echo success; do sleep 1; done"; then
+    local key_file=$1
+    local floating_ip=$2
+    local port=$3
+    local default_instance_user=$4
+    local active_timeout=$5
+    if ! timeout $active_timeout sh -c "while ! ssh -p $port -o StrictHostKeyChecking=no -i $key_file ${default_instance_user}@$floating_ip echo success; do sleep 1; done"; then
         die $LINENO "server didn't become ssh-able!"
     fi
 }
 
 function configure_ironic_auxiliary {
-    configure_ironic_dirs
     configure_ironic_ssh_keypair
     ironic_ssh_check $IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME $IRONIC_VM_SSH_ADDRESS $IRONIC_VM_SSH_PORT $IRONIC_SSH_USERNAME 10
 }
 
 # build deploy kernel+ramdisk, then upload them to glance
-# this function sets IRONIC_DEPLOY_KERNEL_ID and IRONIC_DEPLOY_RAMDISK_ID
+# this function sets ``IRONIC_DEPLOY_KERNEL_ID``, ``IRONIC_DEPLOY_RAMDISK_ID``
 function upload_baremetal_ironic_deploy {
-    token=$1
+    declare -g IRONIC_DEPLOY_KERNEL_ID IRONIC_DEPLOY_RAMDISK_ID
+    echo_summary "Creating and uploading baremetal images for ironic"
+
+    # install diskimage-builder
+    if [[ $(type -P ramdisk-image-create) == "" ]]; then
+        pip_install diskimage_builder
+    fi
 
     if [ -z "$IRONIC_DEPLOY_KERNEL" -o -z "$IRONIC_DEPLOY_RAMDISK" ]; then
-        IRONIC_DEPLOY_KERNEL_PATH=$TOP_DIR/files/ir-deploy.kernel
-        IRONIC_DEPLOY_RAMDISK_PATH=$TOP_DIR/files/ir-deploy.initramfs
+        local IRONIC_DEPLOY_KERNEL_PATH=$TOP_DIR/files/ir-deploy.kernel
+        local IRONIC_DEPLOY_RAMDISK_PATH=$TOP_DIR/files/ir-deploy.initramfs
     else
-        IRONIC_DEPLOY_KERNEL_PATH=$IRONIC_DEPLOY_KERNEL
-        IRONIC_DEPLOY_RAMDISK_PATH=$IRONIC_DEPLOY_RAMDISK
+        local IRONIC_DEPLOY_KERNEL_PATH=$IRONIC_DEPLOY_KERNEL
+        local IRONIC_DEPLOY_RAMDISK_PATH=$IRONIC_DEPLOY_RAMDISK
     fi
 
     if [ ! -e "$IRONIC_DEPLOY_RAMDISK_PATH" -o ! -e "$IRONIC_DEPLOY_KERNEL_PATH" ]; then
@@ -475,70 +582,67 @@
         if [ "$IRONIC_BUILD_DEPLOY_RAMDISK" = "True" ]; then
             # we can build them only if we're not offline
             if [ "$OFFLINE" != "True" ]; then
-                $DIB_DIR/bin/ramdisk-image-create $IRONIC_DEPLOY_FLAVOR \
-                    -o $TOP_DIR/files/ir-deploy
+                if [ "$IRONIC_DEPLOY_RAMDISK" == "agent_ssh" ]; then
+                    die $LINENO "Ironic-python-agent build is not yet supported"
+                else
+                    ramdisk-image-create $IRONIC_DEPLOY_FLAVOR \
+                        -o $TOP_DIR/files/ir-deploy
+                fi
             else
                 die $LINENO "Deploy kernel+ramdisk files don't exist and cannot be build in OFFLINE mode"
             fi
         else
-            die $LINENO "Deploy kernel+ramdisk files don't exist and their building was disabled explicitly by IRONIC_BUILD_DEPLOY_RAMDISK"
+            if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+                # download the agent image tarball
+                wget "$IRONIC_AGENT_TARBALL" -O ironic_agent_tarball.tar.gz
+                tar zxfv ironic_agent_tarball.tar.gz
+                mv UPLOAD/coreos_production_pxe.vmlinuz $IRONIC_DEPLOY_KERNEL_PATH
+                mv UPLOAD/coreos_production_pxe_image-oem.cpio.gz $IRONIC_DEPLOY_RAMDISK_PATH
+                rm -rf UPLOAD
+                rm ironic_agent_tarball.tar.gz
+            else
+                die $LINENO "Deploy kernel+ramdisk files don't exist and their building was disabled explicitly by IRONIC_BUILD_DEPLOY_RAMDISK"
+            fi
         fi
     fi
 
+    local token=$(keystone token-get | grep ' id ' | get_field 2)
+    die_if_not_set $LINENO token "Keystone fail to get token"
+
     # load them into glance
-    IRONIC_DEPLOY_KERNEL_ID=$(glance \
-        --os-auth-token $token \
-        --os-image-url http://$GLANCE_HOSTPORT \
-        image-create \
-        --name $(basename $IRONIC_DEPLOY_KERNEL_PATH) \
-        --is-public True --disk-format=aki \
+    IRONIC_DEPLOY_KERNEL_ID=$(openstack \
+        --os-token $token \
+        --os-url http://$GLANCE_HOSTPORT \
+        image create \
+        $(basename $IRONIC_DEPLOY_KERNEL_PATH) \
+        --public --disk-format=aki \
+        --container-format=aki \
         < $IRONIC_DEPLOY_KERNEL_PATH  | grep ' id ' | get_field 2)
-    IRONIC_DEPLOY_RAMDISK_ID=$(glance \
-        --os-auth-token $token \
-        --os-image-url http://$GLANCE_HOSTPORT \
-        image-create \
-        --name $(basename $IRONIC_DEPLOY_RAMDISK_PATH) \
-        --is-public True --disk-format=ari \
+    IRONIC_DEPLOY_RAMDISK_ID=$(openstack \
+        --os-token $token \
+        --os-url http://$GLANCE_HOSTPORT \
+        image create \
+        $(basename $IRONIC_DEPLOY_RAMDISK_PATH) \
+        --public --disk-format=ari \
+        --container-format=ari \
         < $IRONIC_DEPLOY_RAMDISK_PATH  | grep ' id ' | get_field 2)
 }
 
 function prepare_baremetal_basic_ops {
-
-    # install diskimage-builder
-    git_clone $DIB_REPO $DIB_DIR $DIB_BRANCH
-
-    # make sure all needed service were enabled
-    for srv in nova glance key neutron; do
-        if ! is_service_enabled "$srv"; then
-            die $LINENO "$srv should be enabled for ironic tests"
-        fi
-    done
-
-    TOKEN=$(keystone token-get | grep ' id ' | get_field 2)
-    die_if_not_set $LINENO TOKEN "Keystone fail to get token"
-
-    echo_summary "Creating and uploading baremetal images for ironic"
-
-    # build and upload separate deploy kernel & ramdisk
-    upload_baremetal_ironic_deploy $TOKEN
-
+    upload_baremetal_ironic_deploy
     create_bridge_and_vms
     enroll_vms
     configure_tftpd
     configure_iptables
-
-    # restart nova-compute to ensure its resource tracking is up to
-    # date with newly enrolled nodes
-    stop_nova_compute || true
-    start_nova_compute
+    configure_ironic_auxiliary
 }
 
 function cleanup_baremetal_basic_ops {
     rm -f $IRONIC_VM_MACS_CSV_FILE
     if [ -f $IRONIC_KEY_FILE ]; then
-        KEY=`cat $IRONIC_KEY_FILE.pub`
+        local 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
+        grep -v "$key" $IRONIC_AUTHORIZED_KEYS_FILE > temp && mv temp $IRONIC_AUTHORIZED_KEYS_FILE
         chmod 0600 $IRONIC_AUTHORIZED_KEYS_FILE
     fi
     sudo rm -rf $IRONIC_DATA_DIR $IRONIC_STATE_PATH
diff --git a/lib/keystone b/lib/keystone
index 547646a..c6e17ca 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -328,11 +328,11 @@
 function create_keystone_accounts {
 
     # admin
-    ADMIN_TENANT=$(get_or_create_project "admin")
-    ADMIN_USER=$(get_or_create_user "admin" \
-        "$ADMIN_PASSWORD" "$ADMIN_TENANT")
-    ADMIN_ROLE=$(get_or_create_role "admin")
-    get_or_add_user_role $ADMIN_ROLE $ADMIN_USER $ADMIN_TENANT
+    local admin_tenant=$(get_or_create_project "admin")
+    local admin_user=$(get_or_create_user "admin" \
+        "$ADMIN_PASSWORD" "$admin_tenant")
+    local admin_role=$(get_or_create_role "admin")
+    get_or_add_user_role $admin_role $admin_user $admin_tenant
 
     # Create service project/role
     get_or_create_project "$SERVICE_TENANT_NAME"
@@ -347,25 +347,25 @@
     get_or_create_role ResellerAdmin
 
     # The Member role is used by Horizon and Swift so we need to keep it:
-    MEMBER_ROLE=$(get_or_create_role "Member")
+    local 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!
 
-    ANOTHER_ROLE=$(get_or_create_role "anotherrole")
+    local another_role=$(get_or_create_role "anotherrole")
 
     # invisible tenant - admin can't see this one
-    INVIS_TENANT=$(get_or_create_project "invisible_to_admin")
+    local invis_tenant=$(get_or_create_project "invisible_to_admin")
 
     # demo
-    DEMO_TENANT=$(get_or_create_project "demo")
-    DEMO_USER=$(get_or_create_user "demo" \
-        "$ADMIN_PASSWORD" "$DEMO_TENANT" "demo@example.com")
+    local demo_tenant=$(get_or_create_project "demo")
+    local demo_user=$(get_or_create_user "demo" \
+        "$ADMIN_PASSWORD" "$demo_tenant" "demo@example.com")
 
-    get_or_add_user_role $MEMBER_ROLE $DEMO_USER $DEMO_TENANT
-    get_or_add_user_role $ADMIN_ROLE $ADMIN_USER $DEMO_TENANT
-    get_or_add_user_role $ANOTHER_ROLE $DEMO_USER $DEMO_TENANT
-    get_or_add_user_role $MEMBER_ROLE $DEMO_USER $INVIS_TENANT
+    get_or_add_user_role $member_role $demo_user $demo_tenant
+    get_or_add_user_role $admin_role $admin_user $demo_tenant
+    get_or_add_user_role $another_role $demo_user $demo_tenant
+    get_or_add_user_role $member_role $demo_user $invis_tenant
 
     # Keystone
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -467,7 +467,7 @@
 
     if [ "$KEYSTONE_USE_MOD_WSGI" == "True" ]; then
         restart_apache_server
-        screen_it key "cd $KEYSTONE_DIR && sudo tail -f /var/log/$APACHE_NAME/keystone"
+        screen_it key "cd $KEYSTONE_DIR && sudo tail -f /var/log/$APACHE_NAME/keystone.log"
     else
         # Start Keystone in a screen window
         screen_it key "cd $KEYSTONE_DIR && $KEYSTONE_DIR/bin/keystone-all --config-file $KEYSTONE_CONF --debug"
diff --git a/lib/marconi b/lib/marconi
deleted file mode 100644
index 063ed3d..0000000
--- a/lib/marconi
+++ /dev/null
@@ -1,204 +0,0 @@
-# lib/marconi
-# Install and start **Marconi** service
-
-# To enable a minimal set of Marconi services, add the following to localrc:
-#
-#     enable_service marconi-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_marconi
-# configure_marconi
-# init_marconi
-# start_marconi
-# stop_marconi
-# cleanup_marconi
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Defaults
-# --------
-
-# Set up default directories
-MARCONI_DIR=$DEST/marconi
-MARCONICLIENT_DIR=$DEST/python-marconiclient
-MARCONI_CONF_DIR=/etc/marconi
-MARCONI_CONF=$MARCONI_CONF_DIR/marconi.conf
-MARCONI_API_LOG_DIR=/var/log/marconi
-MARCONI_API_LOG_FILE=$MARCONI_API_LOG_DIR/queues.log
-MARCONI_AUTH_CACHE_DIR=${MARCONI_AUTH_CACHE_DIR:-/var/cache/marconi}
-
-# Support potential entry-points console scripts
-MARCONI_BIN_DIR=$(get_python_exec_prefix)
-
-# Set up database backend
-MARCONI_BACKEND=${MARCONI_BACKEND:-sqlite}
-
-
-# Set Marconi repository
-MARCONI_REPO=${MARCONI_REPO:-${GIT_BASE}/openstack/marconi.git}
-MARCONI_BRANCH=${MARCONI_BRANCH:-master}
-
-# Set client library repository
-MARCONICLIENT_REPO=${MARCONICLIENT_REPO:-${GIT_BASE}/openstack/python-marconiclient.git}
-MARCONICLIENT_BRANCH=${MARCONICLIENT_BRANCH:-master}
-
-# Set Marconi Connection Info
-MARCONI_SERVICE_HOST=${MARCONI_SERVICE_HOST:-$SERVICE_HOST}
-MARCONI_SERVICE_PORT=${MARCONI_SERVICE_PORT:-8888}
-MARCONI_SERVICE_PROTOCOL=${MARCONI_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
-
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,marconi
-
-
-# Functions
-# ---------
-
-# Test if any Marconi services are enabled
-# is_marconi_enabled
-function is_marconi_enabled {
-    [[ ,${ENABLED_SERVICES} =~ ,"marconi-" ]] && return 0
-    return 1
-}
-
-# cleanup_marconi() - Remove residual data files, anything left over from previous
-# runs that a clean run would need to clean up
-function cleanup_marconi {
-    if ! timeout $SERVICE_TIMEOUT sh -c "while ! mongo marconi --eval 'db.dropDatabase();'; do sleep 1; done"; then
-        die $LINENO "Mongo DB did not start"
-    fi
-}
-
-# configure_marconiclient() - Set config files, create data dirs, etc
-function configure_marconiclient {
-    setup_develop $MARCONICLIENT_DIR
-}
-
-# configure_marconi() - Set config files, create data dirs, etc
-function configure_marconi {
-    setup_develop $MARCONI_DIR
-
-    [ ! -d $MARCONI_CONF_DIR ] && sudo mkdir -m 755 -p $MARCONI_CONF_DIR
-    sudo chown $USER $MARCONI_CONF_DIR
-
-    [ ! -d $MARCONI_API_LOG_DIR ] &&  sudo mkdir -m 755 -p $MARCONI_API_LOG_DIR
-    sudo chown $USER $MARCONI_API_LOG_DIR
-
-    iniset $MARCONI_CONF DEFAULT verbose True
-    iniset $MARCONI_CONF DEFAULT use_syslog $SYSLOG
-    iniset $MARCONI_CONF DEFAULT log_file $MARCONI_API_LOG_FILE
-    iniset $MARCONI_CONF 'drivers:transport:wsgi' bind $MARCONI_SERVICE_HOST
-
-    iniset $MARCONI_CONF keystone_authtoken auth_protocol http
-    iniset $MARCONI_CONF keystone_authtoken admin_user marconi
-    iniset $MARCONI_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
-    iniset $MARCONI_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
-    iniset $MARCONI_CONF keystone_authtoken signing_dir $MARCONI_AUTH_CACHE_DIR
-
-    if [ "$MARCONI_BACKEND" = 'mysql' ] || [ "$MARCONI_BACKEND" = 'postgresql' ] ; then
-        iniset $MARCONI_CONF drivers storage sqlalchemy
-        iniset $MARCONI_CONF 'drivers:storage:sqlalchemy' uri `database_connection_url marconi`
-    elif [ "$MARCONI_BACKEND" = 'mongodb' ] ; then
-        iniset $MARCONI_CONF  drivers storage mongodb
-        iniset $MARCONI_CONF 'drivers:storage:mongodb' uri mongodb://localhost:27017/marconi
-        configure_mongodb
-        cleanup_marconi
-    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_marconi() - Initialize etc.
-function init_marconi {
-    # Create cache dir
-    sudo mkdir -p $MARCONI_AUTH_CACHE_DIR
-    sudo chown $STACK_USER $MARCONI_AUTH_CACHE_DIR
-    rm -f $MARCONI_AUTH_CACHE_DIR/*
-}
-
-# install_marconi() - Collect source and prepare
-function install_marconi {
-    git_clone $MARCONI_REPO $MARCONI_DIR $MARCONI_BRANCH
-    setup_develop $MARCONI_DIR
-}
-
-# install_marconiclient() - Collect source and prepare
-function install_marconiclient {
-    git_clone $MARCONICLIENT_REPO $MARCONICLIENT_DIR $MARCONICLIENT_BRANCH
-    setup_develop $MARCONICLIENT_DIR
-}
-
-# start_marconi() - Start running processes, including screen
-function start_marconi {
-    if [[ "$USE_SCREEN" = "False" ]]; then
-        screen_it marconi-server "marconi-server --config-file $MARCONI_CONF --daemon"
-    else
-        screen_it marconi-server "marconi-server --config-file $MARCONI_CONF"
-    fi
-
-    echo "Waiting for Marconi to start..."
-    if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT/v1/health; do sleep 1; done"; then
-        die $LINENO "Marconi did not start"
-    fi
-}
-
-# stop_marconi() - Stop running processes
-function stop_marconi {
-    # Kill the marconi screen windows
-    for serv in marconi-server; do
-        screen -S $SCREEN_NAME -p $serv -X kill
-    done
-}
-
-function create_marconi_accounts {
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
-
-    MARCONI_USER=$(get_or_create_user "marconi" \
-        "$SERVICE_PASSWORD" $SERVICE_TENANT)
-    get_or_add_user_role $ADMIN_ROLE $MARCONI_USER $SERVICE_TENANT
-
-    if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
-
-        MARCONI_SERVICE=$(get_or_create_service "marconi" \
-            "queuing" "Marconi Service")
-        get_or_create_endpoint $MARCONI_SERVICE \
-            "$REGION_NAME" \
-            "$MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT" \
-            "$MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT" \
-            "$MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT"
-    fi
-
-}
-
-
-# Restore xtrace
-$XTRACE
-
-# Local variables:
-# mode: shell-script
-# End:
diff --git a/lib/neutron b/lib/neutron
index 98636b4..a00664e 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -85,6 +85,20 @@
 NEUTRON_CONF=$NEUTRON_CONF_DIR/neutron.conf
 export NEUTRON_TEST_CONFIG_FILE=${NEUTRON_TEST_CONFIG_FILE:-"$NEUTRON_CONF_DIR/debug.ini"}
 
+# Agent binaries.  Note, binary paths for other agents are set in per-service
+# scripts in lib/neutron_plugins/services/
+AGENT_DHCP_BINARY="$NEUTRON_BIN_DIR/neutron-dhcp-agent"
+AGENT_L3_BINARY=${AGENT_L3_BINARY:-"$NEUTRON_BIN_DIR/neutron-l3-agent"}
+AGENT_META_BINARY="$NEUTRON_BIN_DIR/neutron-metadata-agent"
+
+# Agent config files. Note, plugin-specific Q_PLUGIN_CONF_FILE is set and
+# loaded from per-plugin  scripts in lib/neutron_plugins/
+Q_DHCP_CONF_FILE=$NEUTRON_CONF_DIR/dhcp_agent.ini
+Q_L3_CONF_FILE=$NEUTRON_CONF_DIR/l3_agent.ini
+Q_FWAAS_CONF_FILE=$NEUTRON_CONF_DIR/fwaas_driver.ini
+Q_VPN_CONF_FILE=$NEUTRON_CONF_DIR/vpn_agent.ini
+Q_META_CONF_FILE=$NEUTRON_CONF_DIR/metadata_agent.ini
+
 # Default name for Neutron database
 Q_DB_NAME=${Q_DB_NAME:-neutron}
 # Default Neutron Plugin
@@ -120,6 +134,21 @@
 ## Provider Network Information
 PROVIDER_SUBNET_NAME=${PROVIDER_SUBNET_NAME:-"provider_net"}
 
+# Use flat providernet for public network
+#
+# If Q_USE_PROVIDERNET_FOR_PUBLIC=True, use a flat provider network
+# for external interface of neutron l3-agent.  In that case,
+# PUBLIC_PHYSICAL_NETWORK specifies provider:physical_network value
+# used for the network.  In case of openvswitch agent, you should
+# add the corresponding entry to your OVS_BRIDGE_MAPPINGS.
+#
+# eg.
+#    Q_USE_PROVIDERNET_FOR_PUBLIC=True
+#    PUBLIC_PHYSICAL_NETWORK=public
+#    OVS_BRIDGE_MAPPINGS=public:br-ex
+Q_USE_PROVIDERNET_FOR_PUBLIC=${Q_USE_PROVIDERNET_FOR_PUBLIC:-False}
+PUBLIC_PHYSICAL_NETWORK=${PUBLIC_PHYSICAL_NETWORK:-public}
+
 # The next two variables are configured by plugin
 # e.g.  _configure_neutron_l3_agent or lib/neutron_plugins/*
 #
@@ -275,6 +304,51 @@
 # Functions
 # ---------
 
+function _determine_config_server {
+    local cfg_file
+    local opts="--config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE"
+    for cfg_file in ${Q_PLUGIN_EXTRA_CONF_FILES[@]}; do
+        opts+=" --config-file /$cfg_file"
+    done
+    echo "$opts"
+}
+
+function _determine_config_vpn {
+    local cfg_file
+    local opts="--config-file $NEUTRON_CONF --config-file=$Q_L3_CONF_FILE --config-file=$Q_VPN_CONF_FILE"
+    if is_service_enabled q-fwaas; then
+        opts+=" --config-file $Q_FWAAS_CONF_FILE"
+    fi
+    for cfg_file in ${Q_VPN_EXTRA_CONF_FILES[@]}; do
+        opts+=" --config-file $cfg_file"
+    done
+    echo "$opts"
+
+}
+
+function _determine_config_l3 {
+    local opts="--config-file $NEUTRON_CONF --config-file=$Q_L3_CONF_FILE"
+    if is_service_enabled q-fwaas; then
+        opts+=" --config-file $Q_FWAAS_CONF_FILE"
+    fi
+    echo "$opts"
+}
+
+# For services and agents that require it, dynamically construct a list of
+# --config-file arguments that are passed to the binary.
+function determine_config_files {
+    local opts=""
+    case "$1" in
+        "neutron-server") opts="$(_determine_config_server)" ;;
+        "neutron-vpn-agent") opts="$(_determine_config_vpn)" ;;
+        "neutron-l3-agent") opts="$(_determine_config_l3)" ;;
+    esac
+    if [ -z "$opts" ] ; then
+        die $LINENO "Could not determine config files for $1."
+    fi
+    echo "$opts"
+}
+
 # Test if any Neutron services are enabled
 # is_neutron_enabled
 function is_neutron_enabled {
@@ -349,7 +423,7 @@
     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 DEFAULT service_neutron_metadata_proxy "True"
+        iniset $NOVA_CONF neutron service_metadata_proxy "True"
     fi
 
     iniset $NOVA_CONF DEFAULT vif_plugging_is_fatal "$VIF_PLUGGING_IS_FATAL"
@@ -446,7 +520,11 @@
         fi
         neutron router-interface-add $ROUTER_ID $SUBNET_ID
         # Create an external network, and a subnet. Configure the external network as router gw
-        EXT_NET_ID=$(neutron net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True | grep ' id ' | get_field 2)
+        if [ "$Q_USE_PROVIDERNET_FOR_PUBLIC" = "True" ]; then
+            EXT_NET_ID=$(neutron net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True --provider:network_type=flat --provider:physical_network=${PUBLIC_PHYSICAL_NETWORK} | grep ' id ' | get_field 2)
+        else
+            EXT_NET_ID=$(neutron net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True | grep ' id ' | get_field 2)
+        fi
         die_if_not_set $LINENO EXT_NET_ID "Failure creating EXT_NET_ID for $PUBLIC_NETWORK_NAME"
         EXT_GW_IP=$(neutron subnet-create --ip_version 4 ${Q_FLOATING_ALLOCATION_POOL:+--allocation-pool $Q_FLOATING_ALLOCATION_POOL} --gateway $PUBLIC_NETWORK_GATEWAY --name $PUBLIC_SUBNET_NAME $EXT_NET_ID $FLOATING_RANGE -- --enable_dhcp=False | grep 'gateway_ip' | get_field 2)
         die_if_not_set $LINENO EXT_GW_IP "Failure creating EXT_GW_IP"
@@ -455,6 +533,9 @@
         if is_service_enabled q-l3; then
             # logic is specific to using the l3-agent for l3
             if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
+                # Disable in-band as we are going to use local port
+                # to communicate with VMs
+                sudo ovs-vsctl set Bridge $PUBLIC_BRIDGE other_config:disable-in-band=true
                 CIDR_LEN=${FLOATING_RANGE#*/}
                 sudo ip addr add $EXT_GW_IP/$CIDR_LEN dev $PUBLIC_BRIDGE
                 sudo ip link set $PUBLIC_BRIDGE up
@@ -492,6 +573,10 @@
 
 # install_neutron_agent_packages() - Collect source and prepare
 function install_neutron_agent_packages {
+    # radvd doesn't come with the OS. Install it if the l3 service is enabled.
+    if is_service_enabled q-l3; then
+        install_package radvd
+    fi
     # install packages that are specific to plugin agent(s)
     if is_service_enabled q-agt q-dhcp q-l3; then
         neutron_plugin_install_agent_packages
@@ -504,14 +589,9 @@
 
 # Start running processes, including screen
 function start_neutron_service_and_check {
-    # build config-file options
-    local cfg_file
-    local CFG_FILE_OPTIONS="--config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE"
-    for cfg_file in ${Q_PLUGIN_EXTRA_CONF_FILES[@]}; do
-        CFG_FILE_OPTIONS+=" --config-file /$cfg_file"
-    done
+    local cfg_file_options="$(determine_config_files neutron-server)"
     # Start the Neutron service
-    screen_it q-svc "cd $NEUTRON_DIR && python $NEUTRON_BIN_DIR/neutron-server $CFG_FILE_OPTIONS"
+    screen_it q-svc "cd $NEUTRON_DIR && python $NEUTRON_BIN_DIR/neutron-server $cfg_file_options"
     echo "Waiting for Neutron to start..."
     if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- http://$Q_HOST:$Q_PORT; do sleep 1; done"; then
         die $LINENO "Neutron did not start"
@@ -524,8 +604,6 @@
     screen_it q-agt "cd $NEUTRON_DIR && python $AGENT_BINARY --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE"
     screen_it q-dhcp "cd $NEUTRON_DIR && python $AGENT_DHCP_BINARY --config-file $NEUTRON_CONF --config-file=$Q_DHCP_CONF_FILE"
 
-    L3_CONF_FILES="--config-file $NEUTRON_CONF --config-file=$Q_L3_CONF_FILE"
-
     if is_provider_network; then
         sudo ovs-vsctl add-port $OVS_PHYSICAL_BRIDGE $PUBLIC_INTERFACE
         sudo ip link set $OVS_PHYSICAL_BRIDGE up
@@ -533,14 +611,10 @@
         sudo ip link set $PUBLIC_INTERFACE up
     fi
 
-    if is_service_enabled q-fwaas; then
-        L3_CONF_FILES="$L3_CONF_FILES --config-file $Q_FWAAS_CONF_FILE"
-        VPN_CONF_FILES="$VPN_CONF_FILES --config-file $Q_FWAAS_CONF_FILE"
-    fi
     if is_service_enabled q-vpn; then
-        screen_it q-vpn "cd $NEUTRON_DIR && $AGENT_VPN_BINARY $VPN_CONF_FILES"
+        screen_it q-vpn "cd $NEUTRON_DIR && $AGENT_VPN_BINARY $(determine_config_files neutron-vpn-agent)"
     else
-        screen_it q-l3 "cd $NEUTRON_DIR && python $AGENT_L3_BINARY $L3_CONF_FILES"
+        screen_it q-l3 "cd $NEUTRON_DIR && python $AGENT_L3_BINARY $(determine_config_files neutron-l3-agent)"
     fi
 
     screen_it q-meta "cd $NEUTRON_DIR && python $AGENT_META_BINARY --config-file $NEUTRON_CONF --config-file=$Q_META_CONF_FILE"
@@ -677,8 +751,6 @@
 }
 
 function _configure_neutron_dhcp_agent {
-    AGENT_DHCP_BINARY="$NEUTRON_BIN_DIR/neutron-dhcp-agent"
-    Q_DHCP_CONF_FILE=$NEUTRON_CONF_DIR/dhcp_agent.ini
 
     cp $NEUTRON_DIR/etc/dhcp_agent.ini $Q_DHCP_CONF_FILE
 
@@ -698,20 +770,8 @@
     # for l3-agent, only use per tenant router if we have namespaces
     Q_L3_ROUTER_PER_TENANT=$Q_USE_NAMESPACE
 
-    AGENT_L3_BINARY=${AGENT_L3_BINARY:-"$NEUTRON_BIN_DIR/neutron-l3-agent"}
-    Q_L3_CONF_FILE=$NEUTRON_CONF_DIR/l3_agent.ini
-
-    if is_service_enabled q-fwaas; then
-        Q_FWAAS_CONF_FILE=$NEUTRON_CONF_DIR/fwaas_driver.ini
-    fi
-
     if is_service_enabled q-vpn; then
-        Q_VPN_CONF_FILE=$NEUTRON_CONF_DIR/vpn_agent.ini
         cp $NEUTRON_DIR/etc/vpn_agent.ini $Q_VPN_CONF_FILE
-        VPN_CONF_FILES="--config-file $NEUTRON_CONF --config-file=$Q_L3_CONF_FILE --config-file=$Q_VPN_CONF_FILE"
-        for cfg_file in ${Q_VPN_EXTRA_CONF_FILES[@]}; do
-            VPN_CONF_FILES+=" --config-file $cfg_file"
-        done
     fi
 
     cp $NEUTRON_DIR/etc/l3_agent.ini $Q_L3_CONF_FILE
@@ -727,9 +787,6 @@
 }
 
 function _configure_neutron_metadata_agent {
-    AGENT_META_BINARY="$NEUTRON_BIN_DIR/neutron-metadata-agent"
-    Q_META_CONF_FILE=$NEUTRON_CONF_DIR/metadata_agent.ini
-
     cp $NEUTRON_DIR/etc/metadata_agent.ini $Q_META_CONF_FILE
 
     iniset $Q_META_CONF_FILE DEFAULT verbose True
diff --git a/lib/neutron_plugins/linuxbridge_agent b/lib/neutron_plugins/linuxbridge_agent
index 82b5fc9..2638dd3 100644
--- a/lib/neutron_plugins/linuxbridge_agent
+++ b/lib/neutron_plugins/linuxbridge_agent
@@ -47,6 +47,7 @@
         iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
     fi
     AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-linuxbridge-agent"
+    iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
     # Define extra "AGENT" configuration options when q-agt is configured by defining
     # the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
     # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
diff --git a/lib/neutron_plugins/ml2 b/lib/neutron_plugins/ml2
index d270015..44b947f 100644
--- a/lib/neutron_plugins/ml2
+++ b/lib/neutron_plugins/ml2
@@ -6,13 +6,13 @@
 set +o xtrace
 
 # Enable this to simply and quickly enable tunneling with ML2.
-# Select either 'gre', 'vxlan', or '(gre vxlan)'
+# Select either 'gre', 'vxlan', or 'gre,vxlan'
 Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-"vxlan"}
 # This has to be set here since the agent will set this in the config file
-if [[ "$Q_ML2_TENANT_NETWORK_TYPE" != "local" ]]; then
-    Q_AGENT_EXTRA_AGENT_OPTS+=(tunnel_types=$Q_ML2_TENANT_NETWORK_TYPE)
+if [[ "$Q_ML2_TENANT_NETWORK_TYPE" == "gre" || "$Q_ML2_TENANT_NETWORK_TYPE" == "vxlan" ]]; then
+    Q_TUNNEL_TYPES=$Q_ML2_TENANT_NETWORK_TYPE
 elif [[ "$ENABLE_TENANT_TUNNELS" == "True" ]]; then
-    Q_AGENT_EXTRA_AGENT_OPTS+=(tunnel_types=gre)
+    Q_TUNNEL_TYPES=gre
 fi
 
 # Default openvswitch L2 agent
diff --git a/lib/neutron_plugins/ofagent_agent b/lib/neutron_plugins/ofagent_agent
index 66283ad..b4c2ada 100644
--- a/lib/neutron_plugins/ofagent_agent
+++ b/lib/neutron_plugins/ofagent_agent
@@ -71,6 +71,7 @@
     fi
     AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-ofagent-agent"
 
+    iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
     # Define extra "AGENT" configuration options when q-agt is configured by defining
     # defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
     # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
diff --git a/lib/neutron_plugins/openvswitch_agent b/lib/neutron_plugins/openvswitch_agent
index 5adb0c5..835f645 100644
--- a/lib/neutron_plugins/openvswitch_agent
+++ b/lib/neutron_plugins/openvswitch_agent
@@ -67,7 +67,7 @@
 
     if [ "$VIRT_DRIVER" == 'xenserver' ]; then
         # Make a copy of our config for domU
-        sudo cp /$Q_PLUGIN_CONF_FILE "/$Q_PLUGIN_CONF_FILE.domu"
+        sudo cp /$Q_PLUGIN_CONF_FILE "/$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"
@@ -102,6 +102,7 @@
         # 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
     # Define extra "AGENT" configuration options when q-agt is configured by defining
     # defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
     # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
diff --git a/lib/neutron_plugins/ovs_base b/lib/neutron_plugins/ovs_base
index 616a236..8375bb6 100644
--- a/lib/neutron_plugins/ovs_base
+++ b/lib/neutron_plugins/ovs_base
@@ -71,7 +71,11 @@
 }
 
 function _neutron_ovs_base_configure_l3_agent {
-    iniset $Q_L3_CONF_FILE DEFAULT external_network_bridge $PUBLIC_BRIDGE
+    if [ "$Q_USE_PROVIDERNET_FOR_PUBLIC" = "True" ]; then
+        iniset $Q_L3_CONF_FILE DEFAULT external_network_bridge ""
+    else
+        iniset $Q_L3_CONF_FILE DEFAULT external_network_bridge $PUBLIC_BRIDGE
+    fi
 
     neutron-ovs-cleanup
     # --no-wait causes a race condition if $PUBLIC_BRIDGE is not up when ip addr flush is called
diff --git a/lib/neutron_plugins/services/vpn b/lib/neutron_plugins/services/vpn
index d920ba6..2478c47 100644
--- a/lib/neutron_plugins/services/vpn
+++ b/lib/neutron_plugins/services/vpn
@@ -7,7 +7,7 @@
 
 
 AGENT_VPN_BINARY="$NEUTRON_BIN_DIR/neutron-vpn-agent"
-VPN_PLUGIN="neutron.services.vpn.plugin.VPNDriverPlugin"
+VPN_PLUGIN=${VPN_PLUGIN:-"neutron.services.vpn.plugin.VPNDriverPlugin"}
 IPSEC_PACKAGE=${IPSEC_PACKAGE:-"openswan"}
 
 function neutron_vpn_install_agent_packages {
diff --git a/lib/neutron_plugins/vmware_nsx b/lib/neutron_plugins/vmware_nsx
index 5802ebf..f4eb82d 100644
--- a/lib/neutron_plugins/vmware_nsx
+++ b/lib/neutron_plugins/vmware_nsx
@@ -105,9 +105,6 @@
     if [[ "$NSX_PASSWORD" != "" ]]; then
         iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_password $NSX_PASSWORD
     fi
-    if [[ "$NSX_REQ_TIMEOUT" != "" ]]; then
-        iniset /$Q_PLUGIN_CONF_FILE DEFAULT req_timeout $NSX_REQ_TIMEOUT
-    fi
     if [[ "$NSX_HTTP_TIMEOUT" != "" ]]; then
         iniset /$Q_PLUGIN_CONF_FILE DEFAULT http_timeout $NSX_HTTP_TIMEOUT
     fi
diff --git a/lib/nova b/lib/nova
index 6b1afd9..b3a586c 100644
--- a/lib/nova
+++ b/lib/nova
@@ -59,10 +59,6 @@
 # Set the paths of certain binaries
 NOVA_ROOTWRAP=$(get_rootwrap_location nova)
 
-# Allow rate limiting to be turned off for testing, like for Tempest
-# NOTE: Set API_RATE_LIMIT="False" to turn OFF rate limiting
-API_RATE_LIMIT=${API_RATE_LIMIT:-"True"}
-
 # Option to enable/disable config drive
 # NOTE: Set FORCE_CONFIG_DRIVE="False" to turn OFF config drive
 FORCE_CONFIG_DRIVE=${FORCE_CONFIG_DRIVE:-"always"}
@@ -88,6 +84,10 @@
     | grep ^[ep] \
     | head -1)
 
+# $NOVA_VNC_ENABLED can be used to forcibly enable vnc configuration.
+# In multi-node setups allows compute hosts to not run n-novnc.
+NOVA_VNC_ENABLED=$(trueorfalse False $NOVA_VNC_ENABLED)
+
 # Get hypervisor configuration
 # ----------------------------
 
@@ -461,9 +461,6 @@
     if [ "$SYSLOG" != "False" ]; then
         iniset $NOVA_CONF DEFAULT use_syslog "True"
     fi
-    if [ "$API_RATE_LIMIT" != "True" ]; then
-        iniset $NOVA_CONF DEFAULT api_rate_limit "False"
-    fi
     if [ "$FORCE_CONFIG_DRIVE" != "False" ]; then
         iniset $NOVA_CONF DEFAULT force_config_drive "$FORCE_CONFIG_DRIVE"
     fi
@@ -492,7 +489,7 @@
         iniset $NOVA_CONF spice html5proxy_base_url "$SPICEHTML5PROXY_URL"
     fi
 
-    if is_service_enabled n-novnc || is_service_enabled n-xvnc; then
+    if is_service_enabled n-novnc || is_service_enabled n-xvnc || [ "$NOVA_VNC_ENABLED" != False ]; then
         # Address on which instance vncservers will listen on compute hosts.
         # For multi-host, this should be the management ip of the compute host.
         VNCSERVER_LISTEN=${VNCSERVER_LISTEN=127.0.0.1}
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index 6fb5c38..258e1a4 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -37,7 +37,7 @@
     # and HP images used in the gate; rackspace has firewalld but hp
     # cloud doesn't.  RHEL6 doesn't have firewalld either.  So we
     # don't care if it fails.
-    if is_fedora; then
+    if is_fedora && is_package_installed firewalld; then
         sudo service firewalld restart || true
     fi
 }
diff --git a/lib/nova_plugins/hypervisor-ironic b/lib/nova_plugins/hypervisor-ironic
index c068c74..344ef04 100644
--- a/lib/nova_plugins/hypervisor-ironic
+++ b/lib/nova_plugins/hypervisor-ironic
@@ -56,6 +56,9 @@
 
 # install_nova_hypervisor() - Install external components
 function install_nova_hypervisor {
+    if ! is_service_enabled neutron; then
+        die $LINENO "Neutron should be enabled for usage of the Ironic Nova driver."
+    fi
     install_libvirt
 }
 
diff --git a/lib/oslo b/lib/oslo
index 421fbce..025815c 100644
--- a/lib/oslo
+++ b/lib/oslo
@@ -39,10 +39,6 @@
 
 # install_oslo() - Collect source and prepare
 function install_oslo {
-    # TODO(sdague): remove this once we get to Icehouse, this just makes
-    # for a smoother transition of existing users.
-    cleanup_oslo
-
     git_clone $CLIFF_REPO $CLIFF_DIR $CLIFF_BRANCH
     setup_install $CLIFF_DIR
 
@@ -74,17 +70,6 @@
     setup_install $TASKFLOW_DIR
 }
 
-# cleanup_oslo() - purge possibly old versions of oslo
-function cleanup_oslo {
-    # this means we've got an old oslo installed, lets get rid of it
-    if ! python -c 'import oslo.config' 2>/dev/null; then
-        echo "Found old oslo.config... removing to ensure consistency"
-        local PIP_CMD=$(get_pip_command)
-        pip_install oslo.config
-        sudo $PIP_CMD uninstall -y oslo.config
-    fi
-}
-
 # Restore xtrace
 $XTRACE
 
diff --git a/lib/sahara b/lib/sahara
index 70feacd..70319d9 100644
--- a/lib/sahara
+++ b/lib/sahara
@@ -7,6 +7,7 @@
 # ``stack.sh`` calls the entry points in this order:
 #
 # install_sahara
+# install_python_saharaclient
 # configure_sahara
 # start_sahara
 # stop_sahara
@@ -24,8 +25,13 @@
 SAHARA_REPO=${SAHARA_REPO:-${GIT_BASE}/openstack/sahara.git}
 SAHARA_BRANCH=${SAHARA_BRANCH:-master}
 
+SAHARA_PYTHONCLIENT_REPO=${SAHARA_PYTHONCLIENT_REPO:-${GIT_BASE}/openstack/python-saharaclient.git}
+SAHARA_PYTHONCLIENT_BRANCH=${SAHARA_PYTHONCLIENT_BRANCH:-master}
+
 # Set up default directories
 SAHARA_DIR=$DEST/sahara
+SAHARA_PYTHONCLIENT_DIR=$DEST/python-saharaclient
+
 SAHARA_CONF_DIR=${SAHARA_CONF_DIR:-/etc/sahara}
 SAHARA_CONF_FILE=${SAHARA_CONF_DIR}/sahara.conf
 
@@ -57,18 +63,18 @@
 # service     sahara    admin
 function create_sahara_accounts {
 
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+    local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
 
-    SAHARA_USER=$(get_or_create_user "sahara" \
-        "$SERVICE_PASSWORD" $SERVICE_TENANT)
-    get_or_add_user_role $ADMIN_ROLE $SAHARA_USER $SERVICE_TENANT
+    local sahara_user=$(get_or_create_user "sahara" \
+        "$SERVICE_PASSWORD" $service_tenant)
+    get_or_add_user_role $admin_role $sahara_user $service_tenant
 
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
-        SAHARA_SERVICE=$(get_or_create_service "sahara" \
+        local sahara_service=$(get_or_create_service "sahara" \
             "data_processing" "Sahara Data Processing")
-        get_or_create_endpoint $SAHARA_SERVICE \
+        get_or_create_endpoint $sahara_service \
             "$REGION_NAME" \
             "$SAHARA_SERVICE_PROTOCOL://$SAHARA_SERVICE_HOST:$SAHARA_SERVICE_PORT/v1.1/\$(tenant_id)s" \
             "$SAHARA_SERVICE_PROTOCOL://$SAHARA_SERVICE_HOST:$SAHARA_SERVICE_PORT/v1.1/\$(tenant_id)s" \
@@ -111,6 +117,14 @@
     iniset $SAHARA_CONF_FILE keystone_authtoken signing_dir $SAHARA_AUTH_CACHE_DIR
     iniset $SAHARA_CONF_FILE keystone_authtoken cafile $KEYSTONE_SSL_CA
 
+    # Set configuration to send notifications
+
+    if is_service_enabled ceilometer; then
+        iniset $SAHARA_CONF_FILE DEFAULT enable_notifications "true"
+        iniset $SAHARA_CONF_FILE DEFAULT notification_driver "messaging"
+        iniset_rpc_backend sahara $SAHARA_CONF_FILE DEFAULT
+    fi
+
     iniset $SAHARA_CONF_FILE DEFAULT verbose True
     iniset $SAHARA_CONF_FILE DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
 
@@ -146,6 +160,12 @@
     setup_develop $SAHARA_DIR
 }
 
+# install_python_saharaclient() - Collect source and prepare
+function install_python_saharaclient {
+    git_clone $SAHARA_PYTHONCLIENT_REPO $SAHARA_PYTHONCLIENT_DIR $SAHARA_PYTHONCLIENT_BRANCH
+    setup_develop $SAHARA_PYTHONCLIENT_DIR
+}
+
 # start_sahara() - Start running processes, including screen
 function start_sahara {
     screen_it sahara "cd $SAHARA_DIR && $SAHARA_BIN_DIR/sahara-all --config-file $SAHARA_CONF_FILE"
diff --git a/lib/sahara-dashboard b/lib/sahara-dashboard
deleted file mode 100644
index a81df0f..0000000
--- a/lib/sahara-dashboard
+++ /dev/null
@@ -1,72 +0,0 @@
-# lib/sahara-dashboard
-
-# Dependencies:
-#
-# - ``functions`` file
-# - ``DEST``, ``DATA_DIR``, ``STACK_USER`` must be defined
-# - ``SERVICE_HOST``
-
-# ``stack.sh`` calls the entry points in this order:
-#
-# - install_sahara_dashboard
-# - configure_sahara_dashboard
-# - cleanup_sahara_dashboard
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-source $TOP_DIR/lib/horizon
-
-# Defaults
-# --------
-
-# Set up default repos
-SAHARA_DASHBOARD_REPO=${SAHARA_DASHBOARD_REPO:-${GIT_BASE}/openstack/sahara-dashboard.git}
-SAHARA_DASHBOARD_BRANCH=${SAHARA_DASHBOARD_BRANCH:-master}
-
-SAHARA_PYTHONCLIENT_REPO=${SAHARA_PYTHONCLIENT_REPO:-${GIT_BASE}/openstack/python-saharaclient.git}
-SAHARA_PYTHONCLIENT_BRANCH=${SAHARA_PYTHONCLIENT_BRANCH:-master}
-
-# Set up default directories
-SAHARA_DASHBOARD_DIR=$DEST/sahara-dashboard
-SAHARA_PYTHONCLIENT_DIR=$DEST/python-saharaclient
-
-# Functions
-# ---------
-
-function configure_sahara_dashboard {
-
-    echo -e "AUTO_ASSIGNMENT_ENABLED = False" >> $HORIZON_DIR/openstack_dashboard/local/local_settings.py
-    echo -e "HORIZON_CONFIG['dashboards'] += ('sahara',)" >> $HORIZON_DIR/openstack_dashboard/settings.py
-    echo -e "INSTALLED_APPS += ('saharadashboard',)" >> $HORIZON_DIR/openstack_dashboard/settings.py
-
-    if is_service_enabled neutron; then
-        echo -e "SAHARA_USE_NEUTRON = True" >> $HORIZON_DIR/openstack_dashboard/local/local_settings.py
-    fi
-}
-
-# install_sahara_dashboard() - Collect source and prepare
-function install_sahara_dashboard {
-    install_python_saharaclient
-    git_clone $SAHARA_DASHBOARD_REPO $SAHARA_DASHBOARD_DIR $SAHARA_DASHBOARD_BRANCH
-    setup_develop $SAHARA_DASHBOARD_DIR
-}
-
-function install_python_saharaclient {
-    git_clone $SAHARA_PYTHONCLIENT_REPO $SAHARA_PYTHONCLIENT_DIR $SAHARA_PYTHONCLIENT_BRANCH
-    setup_develop $SAHARA_PYTHONCLIENT_DIR
-}
-
-# Cleanup file settings.py from Sahara
-function cleanup_sahara_dashboard {
-    sed -i '/sahara/d' $HORIZON_DIR/openstack_dashboard/settings.py
-}
-
-# Restore xtrace
-$XTRACE
-
-# Local variables:
-# mode: shell-script
-# End:
-
diff --git a/lib/stackforge b/lib/stackforge
index e6528af..2d80dad 100644
--- a/lib/stackforge
+++ b/lib/stackforge
@@ -29,35 +29,21 @@
 # --------
 WSME_DIR=$DEST/wsme
 PECAN_DIR=$DEST/pecan
+SQLALCHEMY_MIGRATE_DIR=$DEST/sqlalchemy-migrate
 
 # Entry Points
 # ------------
 
 # install_stackforge() - Collect source and prepare
 function install_stackforge {
-    # TODO(sdague): remove this once we get to Icehouse, this just makes
-    # for a smoother transition of existing users.
-    cleanup_stackforge
-
     git_clone $WSME_REPO $WSME_DIR $WSME_BRANCH
     setup_package $WSME_DIR
 
     git_clone $PECAN_REPO $PECAN_DIR $PECAN_BRANCH
     setup_package $PECAN_DIR
-}
 
-# cleanup_stackforge() - purge possibly old versions of stackforge libraries
-function cleanup_stackforge {
-    # this means we've got an old version installed, lets get rid of it
-    # otherwise python hates itself
-    for lib in wsme pecan; do
-        if ! python -c "import $lib" 2>/dev/null; then
-            echo "Found old $lib... removing to ensure consistency"
-            local PIP_CMD=$(get_pip_command)
-            pip_install $lib
-            sudo $PIP_CMD uninstall -y $lib
-        fi
-    done
+    git_clone $SQLALCHEMY_MIGRATE_REPO $SQLALCHEMY_MIGRATE_DIR $SQLALCHEMY_MIGRATE_BRANCH
+    setup_package $SQLALCHEMY_MIGRATE_DIR
 }
 
 # Restore xtrace
diff --git a/lib/swift b/lib/swift
index 84304d3..6b96348 100644
--- a/lib/swift
+++ b/lib/swift
@@ -115,6 +115,10 @@
 CONTAINER_PORT_BASE=${CONTAINER_PORT_BASE:-6011}
 ACCOUNT_PORT_BASE=${ACCOUNT_PORT_BASE:-6012}
 
+# Enable tempurl feature
+SWIFT_ENABLE_TEMPURLS=${SWIFT_ENABLE_TEMPURLS:-False}
+SWIFT_TEMPURL_KEY=${SWIFT_TEMPURL_KEY}
+
 # Tell Tempest this project is present
 TEMPEST_SERVICES+=,swift
 
@@ -150,9 +154,10 @@
 function _cleanup_swift_apache_wsgi {
     sudo rm -f $SWIFT_APACHE_WSGI_DIR/*.wsgi
     disable_apache_site proxy-server
+    local node_number type
     for node_number in ${SWIFT_REPLICAS_SEQ}; do
         for type in object container account; do
-            site_name=${type}-server-${node_number}
+            local site_name=${type}-server-${node_number}
             disable_apache_site ${site_name}
             sudo rm -f $(apache_site_config_for ${site_name})
         done
@@ -182,10 +187,11 @@
     " -i ${SWIFT_APACHE_WSGI_DIR}/proxy-server.wsgi
 
     # copy apache vhost file and set name and port
+    local node_number
     for node_number in ${SWIFT_REPLICAS_SEQ}; do
-        object_port=$[OBJECT_PORT_BASE + 10 * ($node_number - 1)]
-        container_port=$[CONTAINER_PORT_BASE + 10 * ($node_number - 1)]
-        account_port=$[ACCOUNT_PORT_BASE + 10 * ($node_number - 1)]
+        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)]
 
         sudo cp ${SWIFT_DIR}/examples/apache2/object-server.template $(apache_site_config_for object-server-${node_number})
         sudo sed -e "
@@ -245,7 +251,7 @@
     local server_type=$4
 
     log_facility=$[ node_id - 1 ]
-    node_path=${SWIFT_DATA_DIR}/${node_number}
+    local node_path=${SWIFT_DATA_DIR}/${node_number}
 
     iniuncomment ${swift_node_config} DEFAULT user
     iniset ${swift_node_config} DEFAULT user ${STACK_USER}
@@ -416,8 +422,9 @@
     iniset ${SWIFT_CONF_DIR}/swift.conf swift-hash swift_hash_path_suffix ${SWIFT_HASH}
     iniset ${SWIFT_CONF_DIR}/swift.conf swift-constraints max_header_size ${SWIFT_MAX_HEADER_SIZE}
 
+    local node_number
     for node_number in ${SWIFT_REPLICAS_SEQ}; do
-        swift_node_config=${SWIFT_CONF_DIR}/object-server/${node_number}.conf
+        local swift_node_config=${SWIFT_CONF_DIR}/object-server/${node_number}.conf
         cp ${SWIFT_DIR}/etc/object-server.conf-sample ${swift_node_config}
         generate_swift_config ${swift_node_config} ${node_number} $[OBJECT_PORT_BASE + 10 * (node_number - 1)] object
         iniset ${swift_node_config} filter:recon recon_cache_path  ${SWIFT_DATA_DIR}/cache
@@ -460,7 +467,7 @@
         iniset ${testfile} func_test auth_prefix /v2.0/
     fi
 
-    swift_log_dir=${SWIFT_DATA_DIR}/logs
+    local swift_log_dir=${SWIFT_DATA_DIR}/logs
     rm -rf ${swift_log_dir}
     mkdir -p ${swift_log_dir}/hourly
     sudo chown -R ${STACK_USER}:adm ${swift_log_dir}
@@ -484,9 +491,9 @@
     # First do a bit of setup by creating the directories and
     # changing the permissions so we can run it as our user.
 
-    USER_GROUP=$(id -g ${STACK_USER})
+    local user_group=$(id -g ${STACK_USER})
     sudo mkdir -p ${SWIFT_DATA_DIR}/{drives,cache,run,logs}
-    sudo chown -R ${STACK_USER}:${USER_GROUP} ${SWIFT_DATA_DIR}
+    sudo chown -R ${STACK_USER}:${user_group} ${SWIFT_DATA_DIR}
 
     # Create a loopback disk and format it to XFS.
     if [[ -e ${SWIFT_DISK_IMAGE} ]]; then
@@ -514,15 +521,16 @@
 
     # Create a link to the above mount and
     # create all of the directories needed to emulate a few different servers
+    local node_number
     for node_number in ${SWIFT_REPLICAS_SEQ}; do
         sudo ln -sf ${SWIFT_DATA_DIR}/drives/sdb1/$node_number ${SWIFT_DATA_DIR}/$node_number;
-        drive=${SWIFT_DATA_DIR}/drives/sdb1/${node_number}
-        node=${SWIFT_DATA_DIR}/${node_number}/node
-        node_device=${node}/sdb1
+        local drive=${SWIFT_DATA_DIR}/drives/sdb1/${node_number}
+        local node=${SWIFT_DATA_DIR}/${node_number}/node
+        local node_device=${node}/sdb1
         [[ -d $node ]] && continue
         [[ -d $drive ]] && continue
-        sudo install -o ${STACK_USER} -g $USER_GROUP -d $drive
-        sudo install -o ${STACK_USER} -g $USER_GROUP -d $node_device
+        sudo install -o ${STACK_USER} -g $user_group -d $drive
+        sudo install -o ${STACK_USER} -g $user_group -d $node_device
         sudo chown -R ${STACK_USER}: ${node}
     done
 }
@@ -540,49 +548,49 @@
 
 function create_swift_accounts {
     # Defines specific passwords used by tools/create_userrc.sh
-    SWIFTUSERTEST1_PASSWORD=testing
-    SWIFTUSERTEST2_PASSWORD=testing2
-    SWIFTUSERTEST3_PASSWORD=testing3
+    local swiftusertest1_password=testing
+    local swiftusertest2_password=testing2
+    local swiftusertest3_password=testing3
 
     KEYSTONE_CATALOG_BACKEND=${KEYSTONE_CATALOG_BACKEND:-sql}
 
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+    local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
 
-    SWIFT_USER=$(get_or_create_user "swift" \
-        "$SERVICE_PASSWORD" $SERVICE_TENANT)
-    get_or_add_user_role $ADMIN_ROLE $SWIFT_USER $SERVICE_TENANT
+    local swift_user=$(get_or_create_user "swift" \
+        "$SERVICE_PASSWORD" $service_tenant)
+    get_or_add_user_role $admin_role $swift_user $service_tenant
 
     if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
-        SWIFT_SERVICE=$(get_or_create_service "swift" \
+        local swift_service=$(get_or_create_service "swift" \
             "object-store" "Swift Service")
-        get_or_create_endpoint $SWIFT_SERVICE \
+        get_or_create_endpoint $swift_service \
             "$REGION_NAME" \
             "http://$SERVICE_HOST:8080/v1/AUTH_\$(tenant_id)s" \
             "http://$SERVICE_HOST:8080" \
             "http://$SERVICE_HOST:8080/v1/AUTH_\$(tenant_id)s"
     fi
 
-    SWIFT_TENANT_TEST1=$(get_or_create_project swifttenanttest1)
-    die_if_not_set $LINENO SWIFT_TENANT_TEST1 "Failure creating SWIFT_TENANT_TEST1"
-    SWIFT_USER_TEST1=$(get_or_create_user swiftusertest1 $SWIFTUSERTEST1_PASSWORD \
-        "$SWIFT_TENANT_TEST1" "test@example.com")
+    local swift_tenant_test1=$(get_or_create_project swifttenanttest1)
+    die_if_not_set $LINENO swift_tenant_test1 "Failure creating swift_tenant_test1"
+    SWIFT_USER_TEST1=$(get_or_create_user swiftusertest1 $swiftusertest1_password \
+        "$swift_tenant_test1" "test@example.com")
     die_if_not_set $LINENO SWIFT_USER_TEST1 "Failure creating SWIFT_USER_TEST1"
-    get_or_add_user_role $ADMIN_ROLE $SWIFT_USER_TEST1 $SWIFT_TENANT_TEST1
+    get_or_add_user_role $admin_role $SWIFT_USER_TEST1 $swift_tenant_test1
 
-    SWIFT_USER_TEST3=$(get_or_create_user swiftusertest3 $SWIFTUSERTEST3_PASSWORD \
-        "$SWIFT_TENANT_TEST1" "test3@example.com")
-    die_if_not_set $LINENO SWIFT_USER_TEST3 "Failure creating SWIFT_USER_TEST3"
-    get_or_add_user_role $ANOTHER_ROLE $SWIFT_USER_TEST3 $SWIFT_TENANT_TEST1
+    local swift_user_test3=$(get_or_create_user swiftusertest3 $swiftusertest3_password \
+        "$swift_tenant_test1" "test3@example.com")
+    die_if_not_set $LINENO swift_user_test3 "Failure creating swift_user_test3"
+    get_or_add_user_role $ANOTHER_ROLE $swift_user_test3 $swift_tenant_test1
 
-    SWIFT_TENANT_TEST2=$(get_or_create_project swifttenanttest2)
-    die_if_not_set $LINENO SWIFT_TENANT_TEST2 "Failure creating SWIFT_TENANT_TEST2"
+    local swift_tenant_test2=$(get_or_create_project swifttenanttest2)
+    die_if_not_set $LINENO swift_tenant_test2 "Failure creating swift_tenant_test2"
 
-    SWIFT_USER_TEST2=$(get_or_create_user swiftusertest2 $SWIFTUSERTEST2_PASSWORD \
-        "$SWIFT_TENANT_TEST2" "test2@example.com")
-    die_if_not_set $LINENO SWIFT_USER_TEST2 "Failure creating SWIFT_USER_TEST2"
-    get_or_add_user_role $ADMIN_ROLE $SWIFT_USER_TEST2 $SWIFT_TENANT_TEST2
+    local swift_user_test2=$(get_or_create_user swiftusertest2 $swiftusertest2_password \
+        "$swift_tenant_test2" "test2@example.com")
+    die_if_not_set $LINENO swift_user_test2 "Failure creating swift_user_test2"
+    get_or_add_user_role $admin_role $swift_user_test2 $swift_tenant_test2
 }
 
 # init_swift() - Initialize rings
@@ -666,6 +674,7 @@
     # service so we can run it in foreground in screen.  ``swift-init ...
     # {stop|restart}`` exits with '1' if no servers are running, ignore it just
     # in case
+    local todo type
     swift-init --run-dir=${SWIFT_DATA_DIR}/run all restart || true
     if [[ ${SWIFT_REPLICAS} == 1 ]]; then
         todo="object container account"
@@ -679,10 +688,15 @@
             screen_it s-${type} "cd $SWIFT_DIR && $SWIFT_DIR/bin/swift-${type}-server ${SWIFT_CONF_DIR}/${type}-server/1.conf -v"
         done
     fi
+
+    if [[ "$SWIFT_ENABLE_TEMPURLS" == "True" ]]; then
+        swift_configure_tempurls
+    fi
 }
 
 # stop_swift() - Stop running processes (non-screen)
 function stop_swift {
+    local type
 
     if [ "$SWIFT_USE_MOD_WSGI" == "True" ]; then
         swift-init --run-dir=${SWIFT_DATA_DIR}/run rest stop && return 0
@@ -701,6 +715,13 @@
     pkill -f swift-
 }
 
+function swift_configure_tempurls {
+    OS_USERNAME=swift \
+        OS_TENANT_NAME=$SERVICE_TENANT_NAME \
+        OS_PASSWORD=$SERVICE_PASSWORD \
+        swift post -m "Temp-URL-Key: $SWIFT_TEMPURL_KEY"
+}
+
 # Restore xtrace
 $XTRACE
 
diff --git a/lib/tempest b/lib/tempest
index 91ede0d..2e8aa3e 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -52,7 +52,11 @@
 NOVA_SOURCE_DIR=$DEST/nova
 
 BUILD_INTERVAL=1
-BUILD_TIMEOUT=196
+
+# This is the timeout that tempest will wait for a VM to change state,
+# spawn, delete, etc.
+# The default is set to 196 seconds.
+BUILD_TIMEOUT=${BUILD_TIMEOUT:-196}
 
 
 BOTO_MATERIALS_PATH="$FILES/images/s3-materials/cirros-${CIRROS_VERSION}"
@@ -108,6 +112,8 @@
             image_uuid_alt="$IMAGE_UUID"
         fi
         images+=($IMAGE_UUID)
+    # TODO(stevemar): update this command to use openstackclient's `openstack image list`
+    # when it supports listing by status.
     done < <(glance image-list --status=active | awk -F'|' '!/^(+--)|ID|aki|ari/ { print $3,$2 }')
 
     case "${#images[*]}" in
@@ -371,6 +377,16 @@
     # Baremetal
     if [ "$VIRT_DRIVER" = "ironic" ] ; then
         iniset $TEMPEST_CONFIG baremetal driver_enabled True
+        iniset $TEMPEST_CONFIG compute-feature-enabled change_password False
+        iniset $TEMPEST_CONFIG compute-feature-enabled console_output False
+        iniset $TEMPEST_CONFIG compute-feature-enabled interface_attach False
+        iniset $TEMPEST_CONFIG compute-feature-enabled live_migration False
+        iniset $TEMPEST_CONFIG compute-feature-enabled pause False
+        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
@@ -406,7 +422,7 @@
 # install_tempest() - Collect source and prepare
 function install_tempest {
     git_clone $TEMPEST_REPO $TEMPEST_DIR $TEMPEST_BRANCH
-    pip_install "tox<1.7"
+    pip_install tox
 }
 
 # init_tempest() - Initialize ec2 images
diff --git a/lib/tls b/lib/tls
index e58e513..061c1ca 100644
--- a/lib/tls
+++ b/lib/tls
@@ -84,6 +84,7 @@
         return 0
     fi
 
+    local i
     for i in certs crl newcerts private; do
         mkdir -p $ca_dir/$i
     done
@@ -234,31 +235,34 @@
     local common_name=$3
     local alt_names=$4
 
-    # Generate a signing request
-    $OPENSSL req \
-        -sha1 \
-        -newkey rsa \
-        -nodes \
-        -keyout $ca_dir/private/$cert_name.key \
-        -out $ca_dir/$cert_name.csr \
-        -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}"
+    # Only generate the certificate if it doesn't exist yet on the disk
+    if [ ! -r "$ca_dir/$cert_name.crt" ]; then
+        # Generate a signing request
+        $OPENSSL req \
+            -sha1 \
+            -newkey rsa \
+            -nodes \
+            -keyout $ca_dir/private/$cert_name.key \
+            -out $ca_dir/$cert_name.csr \
+            -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}"
 
-    if [[ -z "$alt_names" ]]; then
-        alt_names="DNS:${common_name}"
-    else
-        alt_names="DNS:${common_name},${alt_names}"
+        if [[ -z "$alt_names" ]]; then
+            alt_names="DNS:${common_name}"
+        else
+            alt_names="DNS:${common_name},${alt_names}"
+        fi
+
+        # Sign the request valid for 1 year
+        SUBJECT_ALT_NAME="$alt_names" \
+        $OPENSSL ca -config $ca_dir/signing.conf \
+            -extensions req_extensions \
+            -days 365 \
+            -notext \
+            -in $ca_dir/$cert_name.csr \
+            -out $ca_dir/$cert_name.crt \
+            -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}" \
+            -batch
     fi
-
-    # Sign the request valid for 1 year
-    SUBJECT_ALT_NAME="$alt_names" \
-    $OPENSSL ca -config $ca_dir/signing.conf \
-        -extensions req_extensions \
-        -days 365 \
-        -notext \
-        -in $ca_dir/$cert_name.csr \
-        -out $ca_dir/$cert_name.crt \
-        -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}" \
-        -batch
 }
 
 
@@ -273,23 +277,25 @@
     create_CA_config $ca_dir 'Intermediate CA'
     create_signing_config $ca_dir
 
-    # Create a signing certificate request
-    $OPENSSL req -config $ca_dir/ca.conf \
-        -sha1 \
-        -newkey rsa \
-        -nodes \
-        -keyout $ca_dir/private/cacert.key \
-        -out $ca_dir/cacert.csr \
-        -outform PEM
+    if [ ! -r "$ca_dir/cacert.pem" ]; then
+        # Create a signing certificate request
+        $OPENSSL req -config $ca_dir/ca.conf \
+            -sha1 \
+            -newkey rsa \
+            -nodes \
+            -keyout $ca_dir/private/cacert.key \
+            -out $ca_dir/cacert.csr \
+            -outform PEM
 
-    # Sign the intermediate request valid for 1 year
-    $OPENSSL ca -config $signing_ca_dir/ca.conf \
-        -extensions ca_extensions \
-        -days 365 \
-        -notext \
-        -in $ca_dir/cacert.csr \
-        -out $ca_dir/cacert.pem \
-        -batch
+        # Sign the intermediate request valid for 1 year
+        $OPENSSL ca -config $signing_ca_dir/ca.conf \
+            -extensions ca_extensions \
+            -days 365 \
+            -notext \
+            -in $ca_dir/cacert.csr \
+            -out $ca_dir/cacert.pem \
+            -batch
+    fi
 }
 
 # Make a root CA to sign other CAs
diff --git a/lib/trove b/lib/trove
index 6877d0f..aa9442b 100644
--- a/lib/trove
+++ b/lib/trove
@@ -76,21 +76,20 @@
 # service              trove     admin        # if enabled
 
 function create_trove_accounts {
-    # Trove
-    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
-    SERVICE_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+    local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    local service_role=$(openstack role list | awk "/ admin / { print \$2 }")
 
     if [[ "$ENABLED_SERVICES" =~ "trove" ]]; then
 
-        TROVE_USER=$(get_or_create_user "trove" \
-            "$SERVICE_PASSWORD" $SERVICE_TENANT)
-        get_or_add_user_role $SERVICE_ROLE $TROVE_USER $SERVICE_TENANT
+        local trove_user=$(get_or_create_user "trove" \
+            "$SERVICE_PASSWORD" $service_tenant)
+        get_or_add_user_role $service_role $trove_user $service_tenant
 
         if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
 
-            TROVE_SERVICE=$(get_or_create_service "trove" \
+            local trove_service=$(get_or_create_service "trove" \
                 "database" "Trove Service")
-            get_or_create_endpoint $TROVE_SERVICE \
+            get_or_create_endpoint $trove_service \
                 "$REGION_NAME" \
                 "http://$SERVICE_HOST:8779/v1.0/\$(tenant_id)s" \
                 "http://$SERVICE_HOST:8779/v1.0/\$(tenant_id)s" \
@@ -211,7 +210,7 @@
     # The image is uploaded by stack.sh -- see $IMAGE_URLS handling
     GUEST_IMAGE_NAME=$(basename "$TROVE_GUEST_IMAGE_URL")
     GUEST_IMAGE_NAME=${GUEST_IMAGE_NAME%.*}
-    TROVE_GUEST_IMAGE_ID=$(glance --os-auth-token $TOKEN --os-image-url http://$GLANCE_HOSTPORT image-list | grep "${GUEST_IMAGE_NAME}" | get_field 1)
+    TROVE_GUEST_IMAGE_ID=$(openstack --os-token $TOKEN --os-url http://$GLANCE_HOSTPORT image list | grep "${GUEST_IMAGE_NAME}" | get_field 1)
     if [ -z "$TROVE_GUEST_IMAGE_ID" ]; then
         # If no glance id is found, skip remaining setup
         echo "Datastore ${TROVE_DATASTORE_TYPE} will not be created: guest image ${GUEST_IMAGE_NAME} not found."
@@ -237,6 +236,7 @@
 # stop_trove() - Stop running processes
 function stop_trove {
     # Kill the trove screen windows
+    local serv
     for serv in tr-api tr-tmgr tr-cond; do
         screen_stop $serv
     done
diff --git a/lib/zaqar b/lib/zaqar
new file mode 100644
index 0000000..0d33df2
--- /dev/null
+++ b/lib/zaqar
@@ -0,0 +1,211 @@
+# 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
+
+# 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_API_LOG_DIR=/var/log/zaqar
+ZAQAR_API_LOG_FILE=$ZAQAR_API_LOG_DIR/queues.log
+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() - Remove residual data files, anything left over from previous
+# runs that a clean run would need to clean up
+function cleanup_zaqar {
+    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
+
+    [ ! -d $ZAQAR_CONF_DIR ] && sudo mkdir -m 755 -p $ZAQAR_CONF_DIR
+    sudo chown $USER $ZAQAR_CONF_DIR
+
+    [ ! -d $ZAQAR_API_LOG_DIR ] &&  sudo mkdir -m 755 -p $ZAQAR_API_LOG_DIR
+    sudo chown $USER $ZAQAR_API_LOG_DIR
+
+    iniset $ZAQAR_CONF DEFAULT verbose True
+    iniset $ZAQAR_CONF DEFAULT use_syslog $SYSLOG
+    iniset $ZAQAR_CONF DEFAULT log_file $ZAQAR_API_LOG_FILE
+    iniset $ZAQAR_CONF 'drivers:transport:wsgi' bind $ZAQAR_SERVICE_HOST
+
+    iniset $ZAQAR_CONF keystone_authtoken auth_protocol http
+    iniset $ZAQAR_CONF keystone_authtoken admin_user zaqar
+    iniset $ZAQAR_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
+    iniset $ZAQAR_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
+    iniset $ZAQAR_CONF keystone_authtoken signing_dir $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
+        cleanup_zaqar
+    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 mkdir -p $ZAQAR_AUTH_CACHE_DIR
+    sudo chown $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
+        screen_it zaqar-server "zaqar-server --config-file $ZAQAR_CONF --daemon"
+    else
+        screen_it 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 {
+    # Kill the zaqar screen windows
+    for serv in zaqar-server; do
+        screen -S $SCREEN_NAME -p $serv -X kill
+    done
+}
+
+function create_zaqar_accounts {
+    SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+    ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+
+    ZAQAR_USER=$(get_or_create_user "zaqar" \
+        "$SERVICE_PASSWORD" $SERVICE_TENANT)
+    get_or_add_user_role $ADMIN_ROLE $ZAQAR_USER $SERVICE_TENANT
+
+    if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
+
+        ZAQAR_SERVICE=$(get_or_create_service "zaqar" \
+            "queuing" "Zaqar Service")
+        get_or_create_endpoint $ZAQAR_SERVICE \
+            "$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/stack.sh b/stack.sh
index 2aa03f7..1661b36 100755
--- a/stack.sh
+++ b/stack.sh
@@ -37,6 +37,56 @@
 # Keep track of the devstack directory
 TOP_DIR=$(cd $(dirname "$0") && pwd)
 
+
+# Sanity Checks
+# -------------
+
+# Clean up last environment var cache
+if [[ -r $TOP_DIR/.stackenv ]]; then
+    rm $TOP_DIR/.stackenv
+fi
+
+# ``stack.sh`` keeps the list of ``apt`` and ``rpm`` dependencies and config
+# templates and other useful files in the ``files`` subdirectory
+FILES=$TOP_DIR/files
+if [ ! -d $FILES ]; then
+    die $LINENO "missing devstack/files"
+fi
+
+# ``stack.sh`` keeps function libraries here
+# Make sure ``$TOP_DIR/lib`` directory is present
+if [ ! -d $TOP_DIR/lib ]; then
+    die $LINENO "missing devstack/lib"
+fi
+
+# Check if run as root
+# OpenStack is designed to be run as a non-root user; Horizon will fail to run
+# as **root** since Apache will not serve content from **root** user).
+# ``stack.sh`` must not be run as **root**.  It aborts and suggests one course of
+# action to create a suitable user account.
+
+if [[ $EUID -eq 0 ]]; then
+    echo "You are running this script as root."
+    echo "Cut it out."
+    echo "Really."
+    echo "If you need an account to run DevStack, do this (as root, heh) to create $STACK_USER:"
+    echo "$TOP_DIR/tools/create-stack-user.sh"
+    exit 1
+fi
+
+# Check to see if we are already running DevStack
+# Note that this may fail if USE_SCREEN=False
+if type -p screen >/dev/null && screen -ls | egrep -q "[0-9].$SCREEN_NAME"; then
+    echo "You are already running a stack.sh session."
+    echo "To rejoin this session type 'screen -x stack'."
+    echo "To destroy this session, type './unstack.sh'."
+    exit 1
+fi
+
+
+# Prepare the environment
+# -----------------------
+
 # Import common functions
 source $TOP_DIR/functions
 
@@ -48,9 +98,18 @@
 # and ``DISTRO``
 GetDistro
 
+# 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|7.0|wheezy|sid|testing|jessie|f19|f20|rhel6|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"
+    fi
+fi
+
 
 # Global Settings
-# ===============
+# ---------------
 
 # Check for a ``localrc`` section embedded in ``local.conf`` and extract if
 # ``localrc`` does not already exist
@@ -106,49 +165,11 @@
 # Make sure the proxy config is visible to sub-processes
 export_proxy_variables
 
-# Destination path for installation ``DEST``
-DEST=${DEST:-/opt/stack}
-
-
-# Sanity Check
-# ------------
-
-# Clean up last environment var cache
-if [[ -r $TOP_DIR/.stackenv ]]; then
-    rm $TOP_DIR/.stackenv
-fi
-
-# ``stack.sh`` keeps the list of ``apt`` and ``rpm`` dependencies and config
-# templates and other useful files in the ``files`` subdirectory
-FILES=$TOP_DIR/files
-if [ ! -d $FILES ]; then
-    die $LINENO "missing devstack/files"
-fi
-
-# ``stack.sh`` keeps function libraries here
-# Make sure ``$TOP_DIR/lib`` directory is present
-if [ ! -d $TOP_DIR/lib ]; then
-    die $LINENO "missing devstack/lib"
-fi
-
-# Import common services (database, message queue) configuration
-source $TOP_DIR/lib/database
-source $TOP_DIR/lib/rpc_backend
-
 # Remove services which were negated in ENABLED_SERVICES
 # using the "-" prefix (e.g., "-rabbit") instead of
 # calling disable_service().
 disable_negated_services
 
-# 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|7.0|wheezy|sid|testing|jessie|f19|f20|rhel6|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"
-    fi
-fi
-
 # Look for obsolete stuff
 if [[ ,${ENABLED_SERVICES}, =~ ,"swift", ]]; then
     echo "FATAL: 'swift' is not supported as a service name"
@@ -157,38 +178,11 @@
     exit 1
 fi
 
-# Make sure we only have one rpc backend enabled,
-# and the specified rpc backend is available on your platform.
-check_rpc_backend
-
-# Check to see if we are already running DevStack
-# Note that this may fail if USE_SCREEN=False
-if type -p screen >/dev/null && screen -ls | egrep -q "[0-9].$SCREEN_NAME"; then
-    echo "You are already running a stack.sh session."
-    echo "To rejoin this session type 'screen -x stack'."
-    echo "To destroy this session, type './unstack.sh'."
-    exit 1
-fi
-
 # Set up logging level
 VERBOSE=$(trueorfalse True $VERBOSE)
 
-# root Access
-# -----------
-
-# OpenStack is designed to be run as a non-root user; Horizon will fail to run
-# as **root** since Apache will not serve content from **root** user).
-# ``stack.sh`` must not be run as **root**.  It aborts and suggests one course of
-# action to create a suitable user account.
-
-if [[ $EUID -eq 0 ]]; then
-    echo "You are running this script as root."
-    echo "Cut it out."
-    echo "Really."
-    echo "If you need an account to run DevStack, do this (as root, heh) to create $STACK_USER:"
-    echo "$TOP_DIR/tools/create-stack-user.sh"
-    exit 1
-fi
+# Configure sudo
+# --------------
 
 # We're not **root**, make sure ``sudo`` is available
 is_package_installed sudo || install_package sudo
@@ -208,18 +202,27 @@
 sudo chown root:root $TEMPFILE
 sudo mv $TEMPFILE /etc/sudoers.d/50_stack_sh
 
-# Additional repos
-# ----------------
+
+# Configure Distro Repositories
+# -----------------------------
 
 # For debian/ubuntu make apt attempt to retry network ops on it's own
 if is_ubuntu; then
-    echo 'APT::Acquire::Retries "20";' | sudo tee /etc/apt/apt.conf.d/80retry
+    echo 'APT::Acquire::Retries "20";' | sudo tee /etc/apt/apt.conf.d/80retry  >/dev/null
+fi
+
+# upstream Rackspace centos7 images have an issue where cloud-init is
+# installed via pip because there were not official packages when the
+# image was created (fix in the works).  Remove all pip packages
+# before we do anything else
+if [[ $DISTRO = "rhel7" && is_rackspace ]]; then
+    (sudo pip freeze | xargs sudo pip uninstall -y) || true
 fi
 
 # Some distros need to add repos beyond the defaults provided by the vendor
 # to pick up required packages.
 
-if [[ is_fedora && $DISTRO =~ (rhel) ]]; then
+if [[ is_fedora && $DISTRO == "rhel6" ]]; then
     # Installing Open vSwitch on RHEL requires enabling the RDO repo.
     RHEL6_RDO_REPO_RPM=${RHEL6_RDO_REPO_RPM:-"http://rdo.fedorapeople.org/openstack-icehouse/rdo-release-icehouse.rpm"}
     RHEL6_RDO_REPO_ID=${RHEL6_RDO_REPO_ID:-"openstack-icehouse"}
@@ -228,10 +231,13 @@
         yum_install $RHEL6_RDO_REPO_RPM || \
             die $LINENO "Error installing RDO repo, cannot continue"
     fi
+fi
+
+if [[ is_fedora && ( $DISTRO == "rhel6" || $DISTRO == "rhel7" ) ]]; then
     # RHEL requires EPEL for many Open Stack dependencies
-    if [[ $DISTRO =~ (rhel7) ]]; then
+    if [[ $DISTRO == "rhel7" ]]; then
         EPEL_RPM=${RHEL7_EPEL_RPM:-"http://dl.fedoraproject.org/pub/epel/beta/7/x86_64/epel-release-7-0.2.noarch.rpm"}
-    else
+    elif [[ $DISTRO == "rhel6" ]]; then
         EPEL_RPM=${RHEL6_EPEL_RPM:-"http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm"}
     fi
     if ! sudo yum repolist enabled epel | grep -q 'epel'; then
@@ -242,17 +248,20 @@
 
     # ... and also optional to be enabled
     is_package_installed yum-utils || install_package yum-utils
-    if [[ $DISTRO =~ (rhel7) ]]; then
+    if [[ $DISTRO == "rhel7" ]]; then
         OPTIONAL_REPO=rhel-7-server-optional-rpms
-    else
+    elif [[ $DISTRO == "rhel6" ]]; then
         OPTIONAL_REPO=rhel-6-server-optional-rpms
     fi
     sudo yum-config-manager --enable ${OPTIONAL_REPO}
-
 fi
 
-# Filesystem setup
-# ----------------
+
+# Configure Target Directories
+# ----------------------------
+
+# Destination path for installation ``DEST``
+DEST=${DEST:-/opt/stack}
 
 # Create the destination directory and ensure it is writable by the user
 # and read/executable by everybody for daemons (e.g. apache run for horizon)
@@ -263,6 +272,12 @@
 # a 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
+safe_chown -R $STACK_USER $DATA_DIR
+
+# Configure proper hostname
 # Certain services such as rabbitmq require that the local hostname resolves
 # correctly.  Make sure it exists in /etc/hosts so that is always true.
 LOCAL_HOSTNAME=`hostname -s`
@@ -270,11 +285,6 @@
     sudo sed -i "s/\(^127.0.0.1.*\)/\1 $LOCAL_HOSTNAME/" /etc/hosts
 fi
 
-# Destination path for service data
-DATA_DIR=${DATA_DIR:-${DEST}/data}
-sudo mkdir -p $DATA_DIR
-safe_chown -R $STACK_USER $DATA_DIR
-
 
 # Common Configuration
 # --------------------
@@ -326,6 +336,14 @@
 SSL_BUNDLE_FILE="$DATA_DIR/ca-bundle.pem"
 rm -f $SSL_BUNDLE_FILE
 
+# Import common services (database, message queue) configuration
+source $TOP_DIR/lib/database
+source $TOP_DIR/lib/rpc_backend
+
+# Make sure we only have one rpc backend enabled,
+# and the specified rpc backend is available on your platform.
+check_rpc_backend
+
 
 # Configure Projects
 # ==================
@@ -472,6 +490,10 @@
     # ``SWIFT_HASH`` is a random unique string for a swift cluster that
     # can never change.
     read_password SWIFT_HASH "ENTER A RANDOM SWIFT HASH."
+
+    if [[ -z "$SWIFT_TEMPURL_KEY" ]] && [[ "$SWIFT_ENABLE_TEMPURLS" == "True" ]]; then
+        read_password SWIFT_TEMPURL_KEY "ENTER A KEY FOR SWIFT TEMPURLS."
+    fi
 fi
 
 
@@ -518,7 +540,7 @@
     echo $@ >&3
 }
 
-if [[ is_fedora && $DISTRO =~ (rhel) ]]; then
+if [[ is_fedora && $DISTRO == "rhel6" ]]; then
     # poor old python2.6 doesn't have argparse by default, which
     # outfilter.py uses
     is_package_installed python-argparse || install_package python-argparse
@@ -617,7 +639,11 @@
 
     if [[ $r -ne 0 ]]; then
         echo "Error on exit"
-        ./tools/worlddump.py -d $LOGDIR
+        if [[ -z $LOGDIR ]]; then
+            ./tools/worlddump.py
+        else
+            ./tools/worlddump.py -d $LOGDIR
+        fi
     fi
 
     exit $r
@@ -660,7 +686,7 @@
 fi
 
 # Do the ugly hacks for broken packages and distros
-$TOP_DIR/tools/fixup_stuff.sh
+source $TOP_DIR/tools/fixup_stuff.sh
 
 
 # Extras Pre-install
@@ -794,6 +820,7 @@
 
 if is_service_enabled heat; then
     install_heat
+    install_heat_other
     cleanup_heat
     configure_heat
 fi
@@ -1253,6 +1280,10 @@
     init_heat
     echo_summary "Starting Heat"
     start_heat
+    if [ "$HEAT_CREATE_TEST_IMAGE" = "True" ]; then
+        echo_summary "Building Heat functional test image"
+        build_heat_functional_test_image
+    fi
 fi
 
 
@@ -1392,51 +1423,55 @@
     echo_summary "WARNING: $DEPRECATED_TEXT"
 fi
 
-# TODO(dtroyer): Remove Q_AGENT_EXTRA_AGENT_OPTS after stable/juno branch is cut
-if [[ -n "$Q_AGENT_EXTRA_AGENT_OPTS" ]]; then
-    echo ""
-    echo_summary "WARNING: Q_AGENT_EXTRA_AGENT_OPTS is used"
-    echo "You are using Q_AGENT_EXTRA_AGENT_OPTS to pass configuration into $NEUTRON_CONF."
-    echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
-    echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
-    echo "
+if is_service_enabled neutron; then
+    # TODO(dtroyer): Remove Q_AGENT_EXTRA_AGENT_OPTS after stable/juno branch is cut
+    if [[ -n "$Q_AGENT_EXTRA_AGENT_OPTS" ]]; then
+        echo ""
+        echo_summary "WARNING: Q_AGENT_EXTRA_AGENT_OPTS is used"
+        echo "You are using Q_AGENT_EXTRA_AGENT_OPTS to pass configuration into $NEUTRON_CONF."
+        echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
+        echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
+        echo "
 [[post-config|/\$Q_PLUGIN_CONF_FILE]]
 [DEFAULT]
 "
-    for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        echo ${I}
-    done
-fi
+        for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
+            # Replace the first '=' with ' ' for iniset syntax
+            echo ${I}
+        done
+    fi
 
-# TODO(dtroyer): Remove Q_AGENT_EXTRA_SRV_OPTS after stable/juno branch is cut
-if [[ -n "$Q_AGENT_EXTRA_SRV_OPTS" ]]; then
-    echo ""
-    echo_summary "WARNING: Q_AGENT_EXTRA_SRV_OPTS is used"
-    echo "You are using Q_AGENT_EXTRA_SRV_OPTS to pass configuration into $NEUTRON_CONF."
-    echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
-    echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
-    echo "
+    # TODO(dtroyer): Remove Q_AGENT_EXTRA_SRV_OPTS after stable/juno branch is cut
+    if [[ -n "$Q_AGENT_EXTRA_SRV_OPTS" ]]; then
+        echo ""
+        echo_summary "WARNING: Q_AGENT_EXTRA_SRV_OPTS is used"
+        echo "You are using Q_AGENT_EXTRA_SRV_OPTS to pass configuration into $NEUTRON_CONF."
+        echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
+        echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
+        echo "
 [[post-config|/\$Q_PLUGIN_CONF_FILE]]
 [DEFAULT]
 "
-    for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
-        # Replace the first '=' with ' ' for iniset syntax
-        echo ${I}
-    done
+        for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
+            # Replace the first '=' with ' ' for iniset syntax
+            echo ${I}
+        done
+    fi
 fi
 
-# TODO(dtroyer): Remove CINDER_MULTI_LVM_BACKEND after stable/juno branch is cut
-if [[ "$CINDER_MULTI_LVM_BACKEND" = "True" ]]; then
-    echo ""
-    echo_summary "WARNING: CINDER_MULTI_LVM_BACKEND is used"
-    echo "You are using CINDER_MULTI_LVM_BACKEND to configure Cinder's multiple LVM backends"
-    echo "Please convert that configuration in local.conf to use CINDER_ENABLED_BACKENDS."
-    echo "CINDER_ENABLED_BACKENDS will be removed early in the 'K' development cycle"
-    echo "
+if is_service_enabled cinder; then
+    # TODO(dtroyer): Remove CINDER_MULTI_LVM_BACKEND after stable/juno branch is cut
+    if [[ "$CINDER_MULTI_LVM_BACKEND" = "True" ]]; then
+        echo ""
+        echo_summary "WARNING: CINDER_MULTI_LVM_BACKEND is used"
+        echo "You are using CINDER_MULTI_LVM_BACKEND to configure Cinder's multiple LVM backends"
+        echo "Please convert that configuration in local.conf to use CINDER_ENABLED_BACKENDS."
+        echo "CINDER_ENABLED_BACKENDS will be removed early in the 'K' development cycle"
+        echo "
 [[local|localrc]]
 CINDER_ENABLED_BACKENDS=lvm:lvmdriver-1,lvm:lvmdriver-2
 "
+    fi
 fi
 
 # Indicate how long this took to run (bash maintained variable ``SECONDS``)
diff --git a/stackrc b/stackrc
index fb84366..ad7da6c 100644
--- a/stackrc
+++ b/stackrc
@@ -52,6 +52,18 @@
     ENABLED_SERVICES+=,rabbit,tempest,mysql
 fi
 
+# SQLAlchemy supports multiple database drivers for each database server
+# type. For example, deployer may use MySQLdb, MySQLConnector, or oursql
+# to access MySQL database.
+#
+# When defined, the variable controls which database driver is used to
+# connect to database server. Otherwise using default driver defined for
+# each database type.
+#
+# You can find the list of currently supported drivers for each database
+# type at: http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html
+# SQLALCHEMY_DATABASE_DRIVER="mysqldb"
+
 # Global toggle for enabling services under mod_wsgi. If this is set to
 # ``True`` all services that use HTTPD + mod_wsgi as the preferred method of
 # deployment, will be deployed under Apache. If this is set to ``False`` all
@@ -124,6 +136,10 @@
 CINDERCLIENT_REPO=${CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-cinderclient.git}
 CINDERCLIENT_BRANCH=${CINDERCLIENT_BRANCH:-master}
 
+# diskimage-builder
+DIB_REPO=${DIB_REPO:-${GIT_BASE}/openstack/diskimage-builder.git}
+DIB_BRANCH=${DIB_BRANCH:-master}
+
 # image catalog service
 GLANCE_REPO=${GLANCE_REPO:-${GIT_BASE}/openstack/glance.git}
 GLANCE_BRANCH=${GLANCE_BRANCH:-master}
@@ -140,6 +156,14 @@
 HEATCLIENT_REPO=${HEATCLIENT_REPO:-${GIT_BASE}/openstack/python-heatclient.git}
 HEATCLIENT_BRANCH=${HEATCLIENT_BRANCH:-master}
 
+# heat-cfntools server agent
+HEAT_CFNTOOLS_REPO=${HEAT_CFNTOOLS_REPO:-${GIT_BASE}/openstack/heat-cfntools.git}
+HEAT_CFNTOOLS_BRANCH=${HEAT_CFNTOOLS_BRANCH:-master}
+
+# heat example templates and elements
+HEAT_TEMPLATES_REPO=${HEAT_TEMPLATES_REPO:-${GIT_BASE}/openstack/heat-templates.git}
+HEAT_TEMPLATES_BRANCH=${HEAT_TEMPLATES_BRANCH:-master}
+
 # django powered web control panel for openstack
 HORIZON_REPO=${HORIZON_REPO:-${GIT_BASE}/openstack/horizon.git}
 HORIZON_BRANCH=${HORIZON_BRANCH:-master}
@@ -176,10 +200,22 @@
 NOVACLIENT_REPO=${NOVACLIENT_REPO:-${GIT_BASE}/openstack/python-novaclient.git}
 NOVACLIENT_BRANCH=${NOVACLIENT_BRANCH:-master}
 
+# os-apply-config configuration template tool
+OAC_REPO=${OAC_REPO:-${GIT_BASE}/openstack/os-apply-config.git}
+OAC_BRANCH=${OAC_BRANCH:-master}
+
+# os-collect-config configuration agent
+OCC_REPO=${OCC_REPO:-${GIT_BASE}/openstack/os-collect-config.git}
+OCC_BRANCH=${OCC_BRANCH:-master}
+
 # consolidated openstack python client
 OPENSTACKCLIENT_REPO=${OPENSTACKCLIENT_REPO:-${GIT_BASE}/openstack/python-openstackclient.git}
 OPENSTACKCLIENT_BRANCH=${OPENSTACKCLIENT_BRANCH:-master}
 
+# os-refresh-config configuration run-parts tool
+ORC_REPO=${ORC_REPO:-${GIT_BASE}/openstack/os-refresh-config.git}
+ORC_BRANCH=${ORC_BRANCH:-master}
+
 # cliff command line framework
 CLIFF_REPO=${CLIFF_REPO:-${GIT_BASE}/openstack/cliff.git}
 CLIFF_BRANCH=${CLIFF_BRANCH:-master}
@@ -250,10 +286,9 @@
 TEMPEST_REPO=${TEMPEST_REPO:-${GIT_BASE}/openstack/tempest.git}
 TEMPEST_BRANCH=${TEMPEST_BRANCH:-master}
 
-
-# diskimage-builder
-DIB_REPO=${DIB_REPO:-${GIT_BASE}/openstack/diskimage-builder.git}
-DIB_BRANCH=${DIB_BRANCH:-master}
+# Tripleo elements for diskimage-builder images
+TIE_REPO=${TIE_REPO:-${GIT_BASE}/openstack/tripleo-image-elements.git}
+TIE_BRANCH=${TIE_BRANCH:-master}
 
 # a websockets/html5 or flash powered VNC console for vm instances
 NOVNC_REPO=${NOVNC_REPO:-https://github.com/kanaka/noVNC.git}
@@ -284,6 +319,10 @@
 PECAN_REPO=${PECAN_REPO:-${GIT_BASE}/stackforge/pecan.git}
 PECAN_BRANCH=${PECAN_BRANCH:-master}
 
+# sqlalchemy-migrate
+SQLALCHEMY_MIGRATE_REPO=${SQLALCHEMY_MIGRATE_REPO:-${GIT_BASE}/stackforge/sqlalchemy-migrate.git}
+SQLALCHEMY_MIGRATE_BRANCH=${SQLALCHEMY_MIGRATE_BRANCH:-master}
+
 
 # Nova hypervisor configuration.  We default to libvirt with **kvm** but will
 # drop back to **qemu** if we are unable to load the kvm module.  ``stack.sh`` can
@@ -366,6 +405,15 @@
         DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-0.3.0-x86_64-disk}
         IMAGE_URLS=${IMAGE_URLS:-"https://github.com/downloads/citrix-openstack/warehouse/cirros-0.3.0-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"};;
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 50fb31c..1732ecc 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -20,20 +20,24 @@
 #   - pre-install hgtools to work around a bug in RHEL6 distribute
 #   - install nose 1.1 from EPEL
 
-set -o errexit
-set -o xtrace
+# 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 xtrace
 
-# Keep track of the current directory
-TOOLS_DIR=$(cd $(dirname "$0") && pwd)
-TOP_DIR=$(cd $TOOLS_DIR/..; pwd)
+    # Keep track of the current directory
+    TOOLS_DIR=$(cd $(dirname "$0") && pwd)
+    TOP_DIR=$(cd $TOOLS_DIR/..; pwd)
 
-# Change dir to top of devstack
-cd $TOP_DIR
+    # Change dir to top of devstack
+    cd $TOP_DIR
 
-# Import common functions
-source $TOP_DIR/functions
+    # Import common functions
+    source $TOP_DIR/functions
 
-FILES=$TOP_DIR/files
+    FILES=$TOP_DIR/files
+fi
 
 # Keystone Port Reservation
 # -------------------------
@@ -99,6 +103,21 @@
     if selinuxenabled; then
         sudo setenforce 0
     fi
+
+    FORCE_FIREWALLD=$(trueorfalse False $FORCE_FIREWALLD)
+    if [[ ${DISTRO} =~ (f19|f20) && $FORCE_FIREWALLD == "False" ]]; then
+        # On Fedora 19 and 20 firewalld interacts badly with libvirt and
+        # slows things down significantly.  However, for those cases
+        # where that combination is desired, allow this fix to be skipped.
+
+        # There was also an additional issue with firewalld hanging
+        # after install of libvirt with polkit.  See
+        # https://bugzilla.redhat.com/show_bug.cgi?id=1099031
+        if is_package_installed firewalld; then
+            uninstall_package firewalld
+        fi
+    fi
+
 fi
 
 # RHEL6
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..c8a603b
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,13 @@
+[tox]
+minversion = 1.6
+skipsdist = True
+envlist = bashate
+
+[testenv]
+usedevelop = False
+install_command = pip install {opts} {packages}
+
+[testenv:bashate]
+deps = bashate
+whitelist_externals = bash
+commands = bash -c "find {toxinidir} -not -wholename \*.tox/\* -and \( -name \*.sh -or -name \*rc -or -name functions\* -or \( -wholename lib/\* -and -not -name \*.md \) \) -print0 | xargs -0 bashate -v"