Merge "Clean up Glance config files"
diff --git a/.gitignore b/.gitignore
index d2c127d..8553b3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 *.log
 *.log.[1-9]
 *.pem
+*.pyc
 .localrc.auto
 .localrc.password
 .prereqs
diff --git a/.zuul.yaml b/.zuul.yaml
index 710b229..a979fa4 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -9,6 +9,46 @@
           - controller
 
 - nodeset:
+    name: devstack-single-node-centos-7
+    nodes:
+      - name: controller
+        label: centos-7
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
+    name: devstack-single-node-opensuse-423
+    nodes:
+      - name: controller
+        label: opensuse-423
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
+    name: devstack-single-node-opensuse-tumbleweed
+    nodes:
+      - name: controller
+        label: opensuse-tumbleweed
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
+    name: devstack-single-node-fedora-27
+    nodes:
+      - name: controller
+        label: fedora-27
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
     name: openstack-two-node
     nodes:
       - name: controller
@@ -16,31 +56,47 @@
       - name: compute1
         label: ubuntu-xenial
     groups:
+      # Node where tests are executed and test results collected
       - name: tempest
         nodes:
           - controller
+      # Nodes running the compute service
       - name: compute
         nodes:
           - controller
           - compute1
+      # Nodes that are not the controller
+      - name: subnode
+        nodes:
+          - compute1
+      # Switch node for multinode networking setup
+      - name: switch
+        nodes:
+          - controller
+      # Peer nodes for multinode networking setup
+      - name: peers
+        nodes:
+          - compute1
 
 - job:
-    name: devstack
+    name: devstack-base
     parent: multinode
-    description: Base devstack job
-    nodeset: openstack-single-node
+    abstract: true
+    description: |
+      Base abstract Devstack job.
+
+      Defines plays and base variables, but it does not include any project
+      and it does not run any service by default. This is a common base for
+      all single Devstack jobs, single or multinode.
+      Variables are defined in job.vars, which is what is then used by single
+      node jobs and by multi node jobs for the controller, as well as in
+      job.group-vars.peers, which is what is used by multi node jobs for subnode
+      nodes (everything but the controller).
     required-projects:
       - openstack-dev/devstack
-      - openstack/cinder
-      - openstack/glance
-      - openstack/keystone
-      - openstack/neutron
-      - openstack/nova
-      - openstack/requirements
-      - openstack/swift
     roles:
+      - zuul: openstack-infra/devstack-gate
       - zuul: openstack-infra/openstack-zuul-jobs
-    timeout: 7200
     vars:
       devstack_localrc:
         DATABASE_PASSWORD: secretdatabase
@@ -48,36 +104,404 @@
         ADMIN_PASSWORD: secretadmin
         SERVICE_PASSWORD: secretservice
         NETWORK_GATEWAY: 10.1.0.1
-        Q_USE_DEBUG_COMMAND: true
         FIXED_RANGE: 10.1.0.0/20
         IPV4_ADDRS_SAFE_TO_USE: 10.1.0.0/20
         FLOATING_RANGE: 172.24.5.0/24
         PUBLIC_NETWORK_GATEWAY: 172.24.5.1
-        FLOATING_HOST_PREFIX: 172.24.4
-        FLOATING_HOST_MASK: 23
-        SWIFT_REPLICAS: 1
-        SWIFT_START_ALL_SERVICES: false
         LOGFILE: /opt/stack/logs/devstacklog.txt
         LOG_COLOR: false
         VERBOSE: true
+        VERBOSE_NO_TIMESTAMP: true
         NOVNC_FROM_PACKAGE: true
         ERROR_ON_CLONE: true
         # Gate jobs can't deal with nested virt. Disable it.
         LIBVIRT_TYPE: qemu
-        # NOTE(dims): etcd 3.x is not available in debian/ubuntu
-        # etc. As a stop gap measure, devstack uses wget to download
-        # from the location below for all the CI jobs.
-        ETCD_DOWNLOAD_URL: http://tarballs.openstack.org/etcd/
       devstack_services:
-        horizon: false
-        tempest: false
+        # Ignore any default set by devstack. Emit a "disable_all_services".
+        base: false
+      zuul_copy_output:
+        '{{ devstack_conf_dir }}/local.conf': 'logs'
+        '{{ devstack_conf_dir }}/localrc': 'logs'
+        '{{ devstack_conf_dir }}/.localrc.auto': 'logs'
+        '{{ devstack_conf_dir }}/.stackenv': 'logs'
+        '{{ devstack_log_dir }}/dstat-csv.log': 'logs'
+        '{{ devstack_log_dir }}/devstacklog.txt': 'logs'
+        '{{ devstack_log_dir }}/devstacklog.txt.summary': 'logs'
+        '{{ devstack_full_log}}': 'logs'
+        '{{ stage_dir }}/verify_tempest_conf.log': 'logs'
+        '{{ stage_dir }}/apache': 'logs'
+        '{{ stage_dir }}/apache_config': 'logs'
+        '{{ stage_dir }}/etc': 'logs'
+        '/var/log/rabbitmq': 'logs'
+        '/var/log/postgresql': 'logs'
+        '/var/log/mysql.err': 'logs'
+        '/var/log/mysql.log': 'logs'
+        '/var/log/libvirt': 'logs'
+        '/etc/sudoers': 'logs'
+        '/etc/sudoers.d': 'logs'
+        '{{ stage_dir }}/iptables.txt': 'logs'
+        '{{ stage_dir }}/df.txt': 'logs'
+        '{{ stage_dir }}/pip2-freeze.txt': 'logs'
+        '{{ stage_dir }}/pip3-freeze.txt': 'logs'
+        '{{ stage_dir }}/dpkg-l.txt': 'logs'
+        '{{ stage_dir }}/rpm-qa.txt': 'logs'
+        '{{ stage_dir }}/core': 'logs'
+        '{{ stage_dir }}/listen53.txt': 'logs'
+        '{{ stage_dir }}/deprecations.log': 'logs'
+        '/var/log/ceph': 'logs'
+        '/var/log/openvswitch': 'logs'
+        '/var/log/glusterfs': 'logs'
+        '/etc/glusterfs/glusterd.vol': 'logs'
+        '/etc/resolv.conf': 'logs'
+        '/var/log/unbound.log': 'logs'
+      extensions_to_txt:
+        conf: True
+        log: True
+        localrc: True
+        stackenv: True
+        auto: True
+    group-vars:
+      subnode:
+        devstack_localrc:
+          DATABASE_PASSWORD: secretdatabase
+          RABBIT_PASSWORD: secretrabbit
+          ADMIN_PASSWORD: secretadmin
+          SERVICE_PASSWORD: secretservice
+          NETWORK_GATEWAY: 10.1.0.1
+          FIXED_RANGE: 10.1.0.0/20
+          IPV4_ADDRS_SAFE_TO_USE: 10.1.0.0/20
+          FLOATING_RANGE: 172.24.5.0/24
+          PUBLIC_NETWORK_GATEWAY: 172.24.5.1
+          LOGFILE: /opt/stack/logs/devstacklog.txt
+          LOG_COLOR: false
+          VERBOSE: true
+          VERBOSE_NO_TIMESTAMP: true
+          NOVNC_FROM_PACKAGE: true
+          ERROR_ON_CLONE: true
+          LIBVIRT_TYPE: qemu
+        devstack_services:
+          base: false
     pre-run: playbooks/pre.yaml
     run: playbooks/devstack.yaml
     post-run: playbooks/post.yaml
+    irrelevant-files:
+      # Documentation related
+      - ^.*\.rst$
+      - ^api-ref/.*$
+      - ^doc/.*$
+      - ^releasenotes/.*$
+      # Translations
+      - ^.*/locale/.*po$
 
+- job:
+    name: devstack-minimal
+    parent: devstack-base
+    description: |
+      Minimal devstack base job, intended for use by jobs that need
+      less than the normal minimum set of required-projects.
+    nodeset: openstack-single-node
+    required-projects:
+      - openstack/requirements
+    vars:
+      devstack_localrc:
+        # Multinode specific settings
+        SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+        HOST_IP: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+        PUBLIC_BRIDGE_MTU: "{{ external_bridge_mtu }}"
+      devstack_services:
+        # Shared services
+        dstat: true
+        etcd3: true
+        mysql: true
+        peakmem_tracker: true
+        rabbit: true
+    group-vars:
+      subnode:
+        devstack_services:
+          # Shared services
+          dstat: true
+          peakmem_tracker: true
+        devstack_localrc:
+          # Multinode specific settings
+          HOST_IP: "{{ hostvars[inventory_hostname]['nodepool']['private_ipv4'] }}"
+          SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+          PUBLIC_BRIDGE_MTU: "{{ external_bridge_mtu }}"
+          # Subnode specific settings
+          DATABASE_TYPE: mysql
+          RABBIT_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+          DATABASE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+
+
+- job:
+    name: devstack
+    parent: devstack-minimal
+    description: |
+      Base devstack job for integration gate.
+
+      This base job can be used for single node and multinode devstack jobs.
+
+      With a single node nodeset, this job sets up an "all-in-one" (aio)
+      devstack with the six OpenStack services included in the devstack tree:
+      keystone, glance, cinder, neutron, nova and swift.
+
+      With a two node nodeset, this job sets up an aio + compute node.
+      The controller can be customised using host-vars.controller, the
+      sub-nodes can be customised using group-vars.subnode.
+
+      Descendent jobs can enable / disable services, add devstack configuration
+      options, enable devstack plugins, configure log files or directories to be
+      transferred to the log server.
+
+      The job assumes that there is only one controller node. The number of
+      subnodes can be scaled up seamlessly by setting a custom nodeset in
+      job.nodeset.
+
+      The run playbook consists of a single role, so it can be easily rewritten
+      and extended.
+    nodeset: openstack-single-node
+    required-projects:
+      - openstack/cinder
+      - openstack/glance
+      - openstack/keystone
+      - openstack/neutron
+      - openstack/nova
+      - openstack/swift
+    timeout: 7200
+    vars:
+      devstack_localrc:
+        # Common OpenStack services settings
+        SWIFT_REPLICAS: 1
+        SWIFT_START_ALL_SERVICES: false
+        SWIFT_HASH: 1234123412341234
+        CINDER_PERIODIC_INTERVAL: 10
+        DEBUG_LIBVIRT_COREDUMPS: True
+        NOVA_VNC_ENABLED: true
+        VNCSERVER_LISTEN: 0.0.0.0
+        VNCSERVER_PROXYCLIENT_ADDRESS: "{{ hostvars[inventory_hostname]['nodepool']['private_ipv4'] }}"
+      devstack_local_conf:
+        post-config:
+          $NEUTRON_CONF:
+            DEFAULT:
+              global_physnet_mtu: "{{ external_bridge_mtu }}"
+      devstack_services:
+        # Core services enabled for this branch.
+        # This list replaces the test-matrix.
+        # Shared services
+        dstat: true
+        etcd3: true
+        mysql: true
+        peakmem_tracker: true
+        rabbit: true
+        tls-proxy: true
+        # Keystone services
+        key: true
+        # Glance services
+        g-api: true
+        g-reg: true
+        # Nova services
+        n-api: true
+        n-api-meta: true
+        n-cauth: true
+        n-cond: true
+        n-cpu: true
+        n-novnc: true
+        n-obj: true
+        n-sch: true
+        placement-api: true
+        # Neutron services
+        # We need to keep using the neutron-legacy based services for
+        # now until all issues with the new lib/neutron code are solved
+        q-agt: true
+        q-dhcp: true
+        q-l3: true
+        q-meta: true
+        q-metering: true
+        q-svc: true
+        # neutron-api: true
+        # neutron-agent: true
+        # neutron-dhcp: true
+        # neutron-l3: true
+        # neutron-metadata-agent: true
+        # neutron-metering: true
+        # Swift services
+        s-account: true
+        s-container: true
+        s-object: true
+        s-proxy: true
+        # Cinder services
+        c-api: true
+        c-bak: true
+        c-sch: true
+        c-vol: true
+        cinder: true
+        # Services we don't need.
+        # This section is not really needed, it's for readability.
+        horizon: false
+        tempest: false
+        # Test matrix emits ceilometer but ceilomenter is not installed in the
+        # integrated gate, so specifying the services has not effect.
+        # ceilometer-*: false
+    group-vars:
+      subnode:
+        devstack_services:
+          # Core services enabled for this branch.
+          # This list replaces the test-matrix.
+          # Shared services
+          dstat: true
+          peakmem_tracker: true
+          tls-proxy: true
+          # Nova services
+          n-cpu: true
+          placement-client: true
+          # Neutron services
+          neutron-agent: true
+          # Cinder services
+          c-bak: true
+          c-vol: true
+          # Services we don't run at all on subnode.
+          # This section is not really needed, it's for readability.
+          # keystone: false
+          # s-*: false
+          horizon: false
+          tempest: false
+          # Test matrix emits ceilometer but ceilomenter is not installed in the
+          # integrated gate, so specifying the services has not effect.
+          # ceilometer-*: false
+        devstack_localrc:
+          # Subnode specific settings
+          GLANCE_HOSTPORT: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}:9292"
+          Q_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+
+- job:
+    name: devstack-multinode
+    parent: devstack
+    nodeset: openstack-two-node
+    description: |
+      Simple multinode test to verify multinode functionality on devstack side.
+      This is not meant to be used as a parent job.
+
+# NOTE(ianw) Platform tests have traditionally been non-voting because
+# we often have to rush things through devstack to stabilise the gate,
+# and these platforms don't have the round-the-clock support to avoid
+# becoming blockers in that situation.
+- job:
+    name: devstack-platform-centos-7
+    parent: tempest-full
+    description: Centos 7 platform test
+    nodeset: devstack-single-node-centos-7
+    voting: false
+
+- job:
+    name: devstack-platform-opensuse-423
+    parent: tempest-full
+    description: openSUSE 43.2 platform test
+    nodeset: devstack-single-node-opensuse-423
+    voting: false
+
+- job:
+    name: devstack-platform-opensuse-tumbleweed
+    parent: tempest-full
+    description: openSUSE Tumbleweed platform test
+    nodeset: devstack-single-node-opensuse-tumbleweed
+    voting: false
+
+- job:
+    name: devstack-platform-fedora-27
+    parent: tempest-full
+    description: Fedora 27 platform test
+    nodeset: devstack-single-node-fedora-27
+    voting: false
+
+- job:
+    name: devstack-tox-base
+    parent: devstack
+    description: |
+      Base job for devstack-based functional tests that use tox.
+
+      This job is not intended to be run directly. It's just here
+      for organizational purposes for devstack-tox-functional and
+      devstack-tox-functional-consumer.
+    post-run: playbooks/tox/post.yaml
+    vars:
+      tox_envlist: functional
+      tox_install_siblings: false
+
+- job:
+    name: devstack-tox-functional
+    parent: devstack-tox-base
+    description: |
+      Base job for devstack-based functional tests that use tox.
+
+      Runs devstack, then runs the tox ``functional`` environment,
+      then collects tox/testr build output like normal tox jobs.
+
+      Turns off tox sibling installation. Projects may be involved
+      in the devstack deployment and so may be in the required-projects
+      list, but may not want to test against master of the other
+      projects in their tox env. Child jobs can set tox_install_siblings
+      to True to re-enable sibling processing.
+    run: playbooks/tox/run-both.yaml
+
+- job:
+    name: devstack-tox-functional-consumer
+    parent: devstack
+    description: |
+      Base job for devstack-based functional tests for projects that
+      consume the devstack cloud.
+
+      This base job should only be used by projects that are not involved
+      in the devstack deployment step, but are instead projects that are using
+      devstack to get a cloud against which they can test things.
+
+      Runs devstack in pre-run, then runs the tox ``functional`` environment,
+      then collects tox/testr build output like normal tox jobs.
+
+      Turns off tox sibling installation. Projects may be involved
+      in the devstack deployment and so may be in the required-projects
+      list, but may not want to test against master of the other
+      projects in their tox env. Child jobs can set tox_install_siblings
+      to True to re-enable sibling processing.
+    pre-run:
+      - playbooks/devstack.yaml
+      - playbooks/tox/pre.yaml
+    run: playbooks/tox/run.yaml
+
+- job:
+    name: devstack-unit-tests
+    description: |
+      Runs unit tests on devstack project.
+
+      It runs  ``run_tests.sh``.
+    pre-run: playbooks/unit-tests/pre.yaml
+    run: playbooks/unit-tests/run.yaml
 
 - project:
-    name: openstack-dev/devstack
     check:
       jobs:
         - devstack
+        - devstack-platform-centos-7
+        - devstack-platform-opensuse-423
+        - devstack-platform-opensuse-tumbleweed
+        - devstack-platform-fedora-27
+        - devstack-multinode
+        - devstack-unit-tests
+    gate:
+      jobs:
+        - devstack
+        - devstack-unit-tests
+    # Please add a note on each job and conditions for the job not
+    # being experimental any more, so we can keep this list somewhat
+    # pruned.
+    #
+    # * nova-cells-v1: maintained by nova for cells v1 (nova-cells service);
+    #    nova gates on this job, it's in experimental for testing cells v1
+    #    changes to devstack w/o gating on it for all devstack changes.
+    # * nova-next: maintained by nova for unreleased/undefaulted
+    #    things like cellsv2 and placement-api
+    experimental:
+      jobs:
+        - nova-cells-v1:
+            irrelevant-files:
+              - ^.*\.rst$
+              - ^doc/.*$
+        - nova-next
diff --git a/clean.sh b/clean.sh
index 2333596..a29ebd9 100755
--- a/clean.sh
+++ b/clean.sh
@@ -122,9 +122,6 @@
 if [[ -n "$LOGDIR" ]] && [[ -d "$LOGDIR" ]]; then
     sudo rm -rf $LOGDIR
 fi
-if [[ -n "$SCREEN_LOGDIR" ]] && [[ -d "$SCREEN_LOGDIR" ]]; then
-    sudo rm -rf $SCREEN_LOGDIR
-fi
 
 # Clean out the sytemd user unit files if systemd was used.
 if [[ "$USE_SYSTEMD" = "True" ]]; then
diff --git a/doc/requirements.txt b/doc/requirements.txt
new file mode 100644
index 0000000..f65e9df
--- /dev/null
+++ b/doc/requirements.txt
@@ -0,0 +1,11 @@
+pbr>=2.0.0,!=2.1.0
+
+Pygments
+docutils
+sphinx>=1.6.2
+openstackdocstheme>=1.11.0
+nwdiag
+blockdiag
+sphinxcontrib-blockdiag
+sphinxcontrib-nwdiag
+zuul-sphinx>=0.2.0
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 780237f..e9708fa 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -26,7 +26,7 @@
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = [ 'openstackdocstheme', 'sphinxcontrib.blockdiag', 'sphinxcontrib.nwdiag' ]
+extensions = [ 'sphinx.ext.autodoc', 'zuul_sphinx', 'openstackdocstheme', 'sphinxcontrib.blockdiag', 'sphinxcontrib.nwdiag' ]
 
 # openstackdocstheme options
 repository_name = 'openstack-dev/devstack'
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index d932d8c..7efe4d6 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -41,6 +41,7 @@
 -  **extra** - runs after services are started and before any files in
    ``extra.d`` are executed
 -  **post-extra** - runs after files in ``extra.d`` are executed
+-  **test-config** - runs after tempest (and plugins) are configured
 
 The file is processed strictly in sequence; meta-sections may be
 specified more than once but if any settings are duplicated the last to
@@ -286,6 +287,18 @@
 
         LOG_COLOR=False
 
+When using the logfile, by default logs are sent to the console and
+the file.  You can set ``VERBOSE`` to ``false`` if you only wish the
+logs to be sent to the file (this may avoid having double-logging in
+some cases where you are capturing the script output and the log
+files).  If ``VERBOSE`` is ``true`` you can additionally set
+``VERBOSE_NO_TIMESTAMP`` to avoid timestamps being added to each
+output line sent to the console.  This can be useful in some
+situations where the console output is being captured by a runner or
+framework (e.g. Ansible) that adds its own timestamps.  Note that the
+log lines sent to the ``LOGFILE`` will still be prefixed with a
+timestamp.
+
 Logging the Service Output
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -643,7 +656,7 @@
 Cells
 ~~~~~
 
-`Cells <http://wiki.openstack.org/blueprint-nova-compute-cells>`__ is
+`Cells <https://wiki.openstack.org/wiki/Blueprint-nova-compute-cells>`__ is
 an alternative scaling option.  To setup a cells environment add the
 following to your ``localrc`` section:
 
@@ -667,7 +680,7 @@
 
         VOLUME_GROUP_NAME="stack-volumes"
         VOLUME_NAME_PREFIX="volume-"
-        VOLUME_BACKING_FILE_SIZE=10250M
+        VOLUME_BACKING_FILE_SIZE=24G
 
 
 Keystone
diff --git a/doc/source/faq.rst b/doc/source/faq.rst
index ed9b4da..efb315c 100644
--- a/doc/source/faq.rst
+++ b/doc/source/faq.rst
@@ -18,6 +18,57 @@
 Your best choice is probably to choose a `distribution of OpenStack
 <https://www.openstack.org/marketplace/distros/>`__.
 
+Can I use DevStack as a development environment?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sure, you can. That said, there are a couple of things you should note before
+doing so:
+
+- DevStack makes a lot of configuration changes to your system and should not
+  be run in your main development environment.
+
+- All the repositories that DevStack clones when deploying are considered
+  volatile by default and thus are subject to hard resets. This is necessary to
+  keep you in sync with the latest upstream, which is what you want in a CI
+  situation, but it can result in branches being overwritten and files being
+  removed.
+
+  The corollary of this is that if you are working on a specific project, using
+  the DevStack project repository (defaulted to ``/opt/stack/<project>``) as
+  the single master repository for storing all your work is not recommended.
+  This behavior can be overridden by setting the ``RECLONE`` config option to
+  ``no``.  Alternatively, you can avoid running ``stack.sh`` to redeploy by
+  restarting services manually. In any case, you should generally ensure work
+  in progress is pushed to Gerrit or otherwise backed up before running
+  ``stack.sh``.
+
+- If you use DevStack within a VM, you may wish to mount a local OpenStack
+  directory, such as ``~/src/openstack``, inside the VM and configure DevStack
+  to use this as the clone location using the ``{PROJECT}_REPO`` config
+  variables. For example, assuming you're using Vagrant and sharing your home
+  directory, you should place the following in ``local.conf``:
+
+  .. code-block:: shell
+
+     NEUTRON_REPO=/home/vagrant/src/neutron
+     NOVA_REPO=/home/vagrant/src/nova
+     KEYSTONE_REPO=/home/vagrant/src/keystone
+     GLANCE_REPO=/home/vagrant/src/glance
+     SWIFT_REPO=/home/vagrant/src/swift
+     HORIZON_REPO=/home/vagrant/src/horizon
+     CINDER_REPO=/home/vagrant/src/cinder
+     HEAT_REPO=/home/vagrant/src/heat
+     TEMPEST_REPO=/home/vagrant/src/tempest
+     HEATCLIENT_REPO=/home/vagrant/src/python-heatclient
+     GLANCECLIENT_REPO=/home/vagrant/src/python-glanceclient
+     NOVACLIENT_REPO=/home/vagrant/src/python-novaclient
+     NEUTRONCLIENT_REPO=/home/vagrant/src/python-neutronclient
+     OPENSTACKCLIENT_REPO=/home/vagrant/src/python-openstackclient
+     HEAT_CFNTOOLS_REPO=/home/vagrant/src/heat-cfntools
+     HEAT_TEMPLATES_REPO=/home/vagrant/src/heat-templates
+     NEUTRON_FWAAS_REPO=/home/vagrant/src/neutron-fwaas
+     # ...
+
 Why a shell script, why not chef/puppet/...
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/doc/source/guides.rst b/doc/source/guides.rst
index c2c7b91..82e0dd6 100644
--- a/doc/source/guides.rst
+++ b/doc/source/guides.rst
@@ -20,6 +20,7 @@
    guides/devstack-with-nested-kvm
    guides/nova
    guides/devstack-with-lbaas-v2
+   guides/devstack-with-ldap
 
 All-In-One Single VM
 --------------------
@@ -66,3 +67,8 @@
 --------------------------------
 
 Guide to working with nova features :doc:`Nova and devstack <guides/nova>`.
+
+Deploying DevStack with LDAP
+----------------------------
+
+Guide to setting up :doc:`DevStack with LDAP <guides/devstack-with-ldap>`.
diff --git a/doc/source/guides/devstack-with-lbaas-v2.rst b/doc/source/guides/devstack-with-lbaas-v2.rst
index 3592844..df3c7ce 100644
--- a/doc/source/guides/devstack-with-lbaas-v2.rst
+++ b/doc/source/guides/devstack-with-lbaas-v2.rst
@@ -2,7 +2,7 @@
 =================================
 
 Starting in the OpenStack Liberty release, the
-`neutron LBaaS v2 API <http://developer.openstack.org/api-ref-networking-v2-ext.html>`_
+`neutron LBaaS v2 API <https://developer.openstack.org/api-ref/network/v2/index.html>`_
 is now stable while the LBaaS v1 API has been deprecated.  The LBaaS v2 reference
 driver is based on Octavia.
 
@@ -15,7 +15,7 @@
 
 Install devstack
 
-  ::
+::
 
     git clone https://git.openstack.org/openstack-dev/devstack
     cd devstack
@@ -23,7 +23,7 @@
 
 Edit your ``local.conf`` to look like
 
-  ::
+::
 
     [[local|localrc]]
     # Load the external LBaaS plugin.
@@ -60,7 +60,7 @@
 
 Run stack.sh and do some sanity checks
 
-  ::
+::
 
     ./stack.sh
     . ./openrc
@@ -69,7 +69,7 @@
 
 Create two nova instances that we can use as test http servers:
 
-  ::
+::
 
     #create nova instances on private network
     nova boot --image $(nova image-list | awk '/ cirros-.*-x86_64-uec / {print $2}') --flavor 1 --nic net-id=$(openstack network list | awk '/ private / {print $2}') node1
@@ -83,7 +83,7 @@
 
 Set up a simple web server on each of these instances. ssh into each instance (username 'cirros', password 'cubswin:)') and run
 
- ::
+::
 
     MYIP=$(ifconfig eth0|grep 'inet addr'|awk -F: '{print $2}'| awk '{print $1}')
     while true; do echo -e "HTTP/1.0 200 OK\r\n\r\nWelcome to $MYIP" | sudo nc -l -p 80 ; done&
@@ -91,7 +91,7 @@
 Phase 2: Create your load balancers
 ------------------------------------
 
- ::
+::
 
     neutron lbaas-loadbalancer-create --name lb1 private-subnet
     neutron lbaas-loadbalancer-show lb1  # Wait for the provisioning_status to be ACTIVE.
diff --git a/doc/source/guides/devstack-with-ldap.rst b/doc/source/guides/devstack-with-ldap.rst
new file mode 100644
index 0000000..ec41141
--- /dev/null
+++ b/doc/source/guides/devstack-with-ldap.rst
@@ -0,0 +1,174 @@
+============================
+Deploying DevStack with LDAP
+============================
+
+The OpenStack Identity service has the ability to integrate with LDAP. The goal
+of this guide is to walk you through setting up an LDAP-backed OpenStack
+development environment.
+
+Introduction
+============
+
+LDAP support in keystone is read-only. You can use it to back an entire
+OpenStack deployment to a single LDAP server, or you can use it to back
+separate LDAP servers to specific keystone domains. Users within those domains
+will can authenticate against keystone, assume role assignments, and interact
+with other OpenStack services.
+
+Configuration
+=============
+
+To deploy an OpenLDAP server, make sure ``ldap`` is added to the list of
+``ENABLED_SERVICES``::
+
+    enable_service ldap
+
+Devstack will require a password to set up an LDAP administrator. This
+administrative user is also the bind user specified in keystone's configuration
+files, similar to a ``keystone`` user for MySQL databases.
+
+Devstack will prompt you for a password when running ``stack.sh`` if
+``LDAP_PASSWORD`` is not set. You can add the following to your
+``local.conf``::
+
+    LDAP_PASSWORD=super_secret_password
+
+At this point, devstack should have everything it needs to deploy OpenLDAP,
+bootstrap it with a minimal set of users, and configure it to back to a domain
+in keystone::
+
+    ./stack.sh
+
+Once ``stack.sh`` completes, you should have a running keystone deployment with
+a basic set of users. It is important to note that not all users will live
+within LDAP. Instead, keystone will back different domains to different
+identity sources. For example, the ``default`` domain will be backed by MySQL.
+This is usually where you'll find your administrative and services users. If
+you query keystone for a list of domains, you should see a domain called
+``Users``. This domain is set up by devstack and points to OpenLDAP.
+
+User Management
+===============
+
+Initially, there will only be two users in the LDAP server. The ``Manager``
+user is used by keystone to talk to OpenLDAP. The ``demo`` user is a generic
+user that you should be able to see if you query keystone for users within the
+``Users`` domain. Both of these users were added to LDAP using basic LDAP
+utilities installed by devstack (e.g. ``ldap-utils``) and LDIFs. The LDIFs used
+to create these users can be found in ``devstack/files/ldap/``.
+
+Listing Users
+-------------
+
+To list all users in LDAP directly, you can use ``ldapsearch`` with the LDAP
+user bootstrapped by devstack::
+
+    ldapsearch -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+        -H ldap://localhost -b dc=openstack,dc=org
+
+As you can see, devstack creates an OpenStack domain called ``openstack.org``
+as a container for the ``Manager`` and ``demo`` users.
+
+Creating Users
+--------------
+
+Since keystone's LDAP integration is read-only, users must be added directly to
+LDAP. Users added directly to OpenLDAP will automatically be placed into the
+``Users`` domain.
+
+LDIFs can be used to add users via the command line. The following is an
+example LDIF that can be used to create a new LDAP user, let's call it
+``peter.ldif.in``::
+
+    dn: cn=peter,ou=Users,dc=openstack,dc=org
+    cn: peter
+    displayName: Peter Quill
+    givenName: Peter Quill
+    mail: starlord@openstack.org
+    objectClass: inetOrgPerson
+    objectClass: top
+    sn: peter
+    uid: peter
+    userPassword: im-a-better-pilot-than-rocket
+
+Now, we use the ``Manager`` user to create a user for Peter in LDAP::
+
+    ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+        -H ldap://localhost -c -f peter.ldif.in
+
+We should be able to assign Peter roles on projects. After Peter has some level
+of authorization, he should be able to login to Horizon by specifying the
+``Users`` domain and using his ``peter`` username and password. Authorization
+can be given to Peter by creating a project within the ``Users`` domain and
+giving him a role assignment on that project::
+
+    $ openstack project create --domain Users awesome-mix-vol-1
+    +-------------+----------------------------------+
+    | Field       | Value                            |
+    +-------------+----------------------------------+
+    | description |                                  |
+    | domain_id   | 61a2de23107c46bea2d758167af707b9 |
+    | enabled     | True                             |
+    | id          | 7d422396d54945cdac8fe1e8e32baec4 |
+    | is_domain   | False                            |
+    | name        | awesome-mix-vol-1                |
+    | parent_id   | 61a2de23107c46bea2d758167af707b9 |
+    | tags        | []                               |
+    +-------------+----------------------------------+
+    $ openstack role add --user peter --user-domain Users \
+          --project awesome-mix-vol-1 --project-domain Users admin
+
+
+Deleting Users
+--------------
+
+We can use the same basic steps to remove users from LDAP, but instead of using
+LDIFs, we can just pass the ``dn`` of the user we want to delete::
+
+    ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+        -H ldap://localhost cn=peter,ou=Users,dc=openstack,dc=org
+
+Group Management
+================
+
+Like users, groups are considered specific identities. This means that groups
+also fall under the same read-only constraints as users and they can be managed
+directly with LDAP in the same way users are with LDIFs.
+
+Adding Groups
+-------------
+
+Let's define a specific group with the following LDIF::
+
+    dn: cn=guardians,ou=UserGroups,dc=openstack,dc=org
+    objectClass: groupOfNames
+    cn: guardians
+    description: Guardians of the Galaxy
+    member: cn=peter,dc=openstack,dc=org
+    member: cn=gamora,dc=openstack,dc=org
+    member: cn=drax,dc=openstack,dc=org
+    member: cn=rocket,dc=openstack,dc=org
+    member: cn=groot,dc=openstack,dc=org
+
+We can create the group using the same ``ldapadd`` command as we did with
+users::
+
+    ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+        -H ldap://localhost -c -f guardian-group.ldif.in
+
+If we check the group membership in Horizon, we'll see that only Peter is a
+member of the ``guardians`` group, despite the whole crew being specified in
+the LDIF. Once those accounts are created in LDAP, they will automatically be
+added to the ``guardians`` group. They will also assume any role assignments
+given to the ``guardians`` group.
+
+Deleting Groups
+---------------
+
+Just like users, groups can be deleted using the ``dn``::
+
+    ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+        -H ldap://localhost cn=guardians,ou=UserGroups,dc=openstack,dc=org
+
+Note that this operation will not remove users within that group. It will only
+remove the group itself and the memberships any users had with that group.
diff --git a/doc/source/guides/neutron.rst b/doc/source/guides/neutron.rst
index 092809a..1b8dccd 100644
--- a/doc/source/guides/neutron.rst
+++ b/doc/source/guides/neutron.rst
@@ -396,7 +396,7 @@
 
 In this configuration we are defining IPV4_ADDRS_SAFE_TO_USE to be a
 publicly routed IPv4 subnet. In this specific instance we are using
-the special TEST-NET-3 subnet defined in `RFC 5737 <http://tools.ietf.org/html/rfc5737>`_,
+the special TEST-NET-3 subnet defined in `RFC 5737 <https://tools.ietf.org/html/rfc5737>`_,
 which is used for documentation.  In your DevStack setup, IPV4_ADDRS_SAFE_TO_USE
 would be a public IP address range that you or your organization has
 allocated to you, so that you could access your instances from the
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 47087c5..2ff4ff0 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -139,6 +139,10 @@
 Enable :doc:`devstack plugins <plugins>` to support additional
 services, features, and configuration not present in base devstack.
 
+Use devstack in your CI with :doc:`Ansible roles <zuul_roles>` and
+:doc:`Jobs <zuul_jobs>` for Zuul V3. Migrate your devstack Zuul V2 jobs to Zuul
+V3 with this full migration :doc:`how-to <zuul_ci_jobs_migration>`.
+
 Get :doc:`the big picture <overview>` of what we are trying to do
 with devstack, and help us by :doc:`contributing to the project
 <hacking>`.
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
index c07a8e6..814a2b1 100644
--- a/doc/source/overview.rst
+++ b/doc/source/overview.rst
@@ -24,7 +24,7 @@
 
 -  Ubuntu: current LTS release plus current development release
 -  Fedora: current release plus previous release
--  RHEL/Centos: current major release
+-  RHEL/CentOS: current major release
 -  Other OS platforms may continue to be included but the maintenance of
    those platforms shall not be assumed simply due to their presence.
    Having a listed point-of-contact for each additional OS will greatly
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
index 43dd3c2..c21e0ef 100644
--- a/doc/source/plugin-registry.rst
+++ b/doc/source/plugin-registry.rst
@@ -26,19 +26,17 @@
 ====================================== ===
 almanach                               `git://git.openstack.org/openstack/almanach <https://git.openstack.org/cgit/openstack/almanach>`__
 aodh                                   `git://git.openstack.org/openstack/aodh <https://git.openstack.org/cgit/openstack/aodh>`__
-app-catalog-ui                         `git://git.openstack.org/openstack/app-catalog-ui <https://git.openstack.org/cgit/openstack/app-catalog-ui>`__
 astara                                 `git://git.openstack.org/openstack/astara <https://git.openstack.org/cgit/openstack/astara>`__
 barbican                               `git://git.openstack.org/openstack/barbican <https://git.openstack.org/cgit/openstack/barbican>`__
 bilean                                 `git://git.openstack.org/openstack/bilean <https://git.openstack.org/cgit/openstack/bilean>`__
 blazar                                 `git://git.openstack.org/openstack/blazar <https://git.openstack.org/cgit/openstack/blazar>`__
 broadview-collector                    `git://git.openstack.org/openstack/broadview-collector <https://git.openstack.org/cgit/openstack/broadview-collector>`__
+castellan-ui                           `git://git.openstack.org/openstack/castellan-ui <https://git.openstack.org/cgit/openstack/castellan-ui>`__
 ceilometer                             `git://git.openstack.org/openstack/ceilometer <https://git.openstack.org/cgit/openstack/ceilometer>`__
 ceilometer-powervm                     `git://git.openstack.org/openstack/ceilometer-powervm <https://git.openstack.org/cgit/openstack/ceilometer-powervm>`__
-cerberus                               `git://git.openstack.org/openstack/cerberus <https://git.openstack.org/cgit/openstack/cerberus>`__
 cloudkitty                             `git://git.openstack.org/openstack/cloudkitty <https://git.openstack.org/cgit/openstack/cloudkitty>`__
-collectd-ceilometer-plugin             `git://git.openstack.org/openstack/collectd-ceilometer-plugin <https://git.openstack.org/cgit/openstack/collectd-ceilometer-plugin>`__
+collectd-openstack-plugins             `git://git.openstack.org/openstack/collectd-openstack-plugins <https://git.openstack.org/cgit/openstack/collectd-openstack-plugins>`__
 congress                               `git://git.openstack.org/openstack/congress <https://git.openstack.org/cgit/openstack/congress>`__
-cue                                    `git://git.openstack.org/openstack/cue <https://git.openstack.org/cgit/openstack/cue>`__
 cyborg                                 `git://git.openstack.org/openstack/cyborg <https://git.openstack.org/cgit/openstack/cyborg>`__
 designate                              `git://git.openstack.org/openstack/designate <https://git.openstack.org/cgit/openstack/designate>`__
 devstack-plugin-additional-pkg-repos   `git://git.openstack.org/openstack/devstack-plugin-additional-pkg-repos <https://git.openstack.org/cgit/openstack/devstack-plugin-additional-pkg-repos>`__
@@ -73,13 +71,13 @@
 ironic-inspector                       `git://git.openstack.org/openstack/ironic-inspector <https://git.openstack.org/cgit/openstack/ironic-inspector>`__
 ironic-staging-drivers                 `git://git.openstack.org/openstack/ironic-staging-drivers <https://git.openstack.org/cgit/openstack/ironic-staging-drivers>`__
 ironic-ui                              `git://git.openstack.org/openstack/ironic-ui <https://git.openstack.org/cgit/openstack/ironic-ui>`__
-k8s-cloud-provider                     `git://git.openstack.org/openstack/k8s-cloud-provider <https://git.openstack.org/cgit/openstack/k8s-cloud-provider>`__
 karbor                                 `git://git.openstack.org/openstack/karbor <https://git.openstack.org/cgit/openstack/karbor>`__
 karbor-dashboard                       `git://git.openstack.org/openstack/karbor-dashboard <https://git.openstack.org/cgit/openstack/karbor-dashboard>`__
 keystone                               `git://git.openstack.org/openstack/keystone <https://git.openstack.org/cgit/openstack/keystone>`__
 kingbird                               `git://git.openstack.org/openstack/kingbird <https://git.openstack.org/cgit/openstack/kingbird>`__
 kuryr-kubernetes                       `git://git.openstack.org/openstack/kuryr-kubernetes <https://git.openstack.org/cgit/openstack/kuryr-kubernetes>`__
 kuryr-libnetwork                       `git://git.openstack.org/openstack/kuryr-libnetwork <https://git.openstack.org/cgit/openstack/kuryr-libnetwork>`__
+kuryr-tempest-plugin                   `git://git.openstack.org/openstack/kuryr-tempest-plugin <https://git.openstack.org/cgit/openstack/kuryr-tempest-plugin>`__
 magnum                                 `git://git.openstack.org/openstack/magnum <https://git.openstack.org/cgit/openstack/magnum>`__
 magnum-ui                              `git://git.openstack.org/openstack/magnum-ui <https://git.openstack.org/cgit/openstack/magnum-ui>`__
 manila                                 `git://git.openstack.org/openstack/manila <https://git.openstack.org/cgit/openstack/manila>`__
@@ -96,6 +94,7 @@
 monasca-ceilometer                     `git://git.openstack.org/openstack/monasca-ceilometer <https://git.openstack.org/cgit/openstack/monasca-ceilometer>`__
 monasca-events-api                     `git://git.openstack.org/openstack/monasca-events-api <https://git.openstack.org/cgit/openstack/monasca-events-api>`__
 monasca-log-api                        `git://git.openstack.org/openstack/monasca-log-api <https://git.openstack.org/cgit/openstack/monasca-log-api>`__
+monasca-tempest-plugin                 `git://git.openstack.org/openstack/monasca-tempest-plugin <https://git.openstack.org/cgit/openstack/monasca-tempest-plugin>`__
 monasca-transform                      `git://git.openstack.org/openstack/monasca-transform <https://git.openstack.org/cgit/openstack/monasca-transform>`__
 murano                                 `git://git.openstack.org/openstack/murano <https://git.openstack.org/cgit/openstack/murano>`__
 networking-6wind                       `git://git.openstack.org/openstack/networking-6wind <https://git.openstack.org/cgit/openstack/networking-6wind>`__
@@ -127,14 +126,17 @@
 networking-plumgrid                    `git://git.openstack.org/openstack/networking-plumgrid <https://git.openstack.org/cgit/openstack/networking-plumgrid>`__
 networking-powervm                     `git://git.openstack.org/openstack/networking-powervm <https://git.openstack.org/cgit/openstack/networking-powervm>`__
 networking-sfc                         `git://git.openstack.org/openstack/networking-sfc <https://git.openstack.org/cgit/openstack/networking-sfc>`__
+networking-spp                         `git://git.openstack.org/openstack/networking-spp <https://git.openstack.org/cgit/openstack/networking-spp>`__
 networking-vpp                         `git://git.openstack.org/openstack/networking-vpp <https://git.openstack.org/cgit/openstack/networking-vpp>`__
 networking-vsphere                     `git://git.openstack.org/openstack/networking-vsphere <https://git.openstack.org/cgit/openstack/networking-vsphere>`__
 neutron                                `git://git.openstack.org/openstack/neutron <https://git.openstack.org/cgit/openstack/neutron>`__
+neutron-classifier                     `git://git.openstack.org/openstack/neutron-classifier <https://git.openstack.org/cgit/openstack/neutron-classifier>`__
 neutron-dynamic-routing                `git://git.openstack.org/openstack/neutron-dynamic-routing <https://git.openstack.org/cgit/openstack/neutron-dynamic-routing>`__
 neutron-fwaas                          `git://git.openstack.org/openstack/neutron-fwaas <https://git.openstack.org/cgit/openstack/neutron-fwaas>`__
 neutron-fwaas-dashboard                `git://git.openstack.org/openstack/neutron-fwaas-dashboard <https://git.openstack.org/cgit/openstack/neutron-fwaas-dashboard>`__
 neutron-lbaas                          `git://git.openstack.org/openstack/neutron-lbaas <https://git.openstack.org/cgit/openstack/neutron-lbaas>`__
 neutron-lbaas-dashboard                `git://git.openstack.org/openstack/neutron-lbaas-dashboard <https://git.openstack.org/cgit/openstack/neutron-lbaas-dashboard>`__
+neutron-tempest-plugin                 `git://git.openstack.org/openstack/neutron-tempest-plugin <https://git.openstack.org/cgit/openstack/neutron-tempest-plugin>`__
 neutron-vpnaas                         `git://git.openstack.org/openstack/neutron-vpnaas <https://git.openstack.org/cgit/openstack/neutron-vpnaas>`__
 neutron-vpnaas-dashboard               `git://git.openstack.org/openstack/neutron-vpnaas-dashboard <https://git.openstack.org/cgit/openstack/neutron-vpnaas-dashboard>`__
 nova-dpm                               `git://git.openstack.org/openstack/nova-dpm <https://git.openstack.org/cgit/openstack/nova-dpm>`__
@@ -145,15 +147,16 @@
 octavia                                `git://git.openstack.org/openstack/octavia <https://git.openstack.org/cgit/openstack/octavia>`__
 octavia-dashboard                      `git://git.openstack.org/openstack/octavia-dashboard <https://git.openstack.org/cgit/openstack/octavia-dashboard>`__
 omni                                   `git://git.openstack.org/openstack/omni <https://git.openstack.org/cgit/openstack/omni>`__
+openstacksdk                           `git://git.openstack.org/openstack/openstacksdk <https://git.openstack.org/cgit/openstack/openstacksdk>`__
 os-xenapi                              `git://git.openstack.org/openstack/os-xenapi <https://git.openstack.org/cgit/openstack/os-xenapi>`__
 osprofiler                             `git://git.openstack.org/openstack/osprofiler <https://git.openstack.org/cgit/openstack/osprofiler>`__
 oswin-tempest-plugin                   `git://git.openstack.org/openstack/oswin-tempest-plugin <https://git.openstack.org/cgit/openstack/oswin-tempest-plugin>`__
 panko                                  `git://git.openstack.org/openstack/panko <https://git.openstack.org/cgit/openstack/panko>`__
 patrole                                `git://git.openstack.org/openstack/patrole <https://git.openstack.org/cgit/openstack/patrole>`__
 picasso                                `git://git.openstack.org/openstack/picasso <https://git.openstack.org/cgit/openstack/picasso>`__
-python-openstacksdk                    `git://git.openstack.org/openstack/python-openstacksdk <https://git.openstack.org/cgit/openstack/python-openstacksdk>`__
 qinling                                `git://git.openstack.org/openstack/qinling <https://git.openstack.org/cgit/openstack/qinling>`__
 rally                                  `git://git.openstack.org/openstack/rally <https://git.openstack.org/cgit/openstack/rally>`__
+rally-openstack                        `git://git.openstack.org/openstack/rally-openstack <https://git.openstack.org/cgit/openstack/rally-openstack>`__
 sahara                                 `git://git.openstack.org/openstack/sahara <https://git.openstack.org/cgit/openstack/sahara>`__
 sahara-dashboard                       `git://git.openstack.org/openstack/sahara-dashboard <https://git.openstack.org/cgit/openstack/sahara-dashboard>`__
 scalpels                               `git://git.openstack.org/openstack/scalpels <https://git.openstack.org/cgit/openstack/scalpels>`__
@@ -166,6 +169,8 @@
 tacker                                 `git://git.openstack.org/openstack/tacker <https://git.openstack.org/cgit/openstack/tacker>`__
 tap-as-a-service                       `git://git.openstack.org/openstack/tap-as-a-service <https://git.openstack.org/cgit/openstack/tap-as-a-service>`__
 tap-as-a-service-dashboard             `git://git.openstack.org/openstack/tap-as-a-service-dashboard <https://git.openstack.org/cgit/openstack/tap-as-a-service-dashboard>`__
+tatu                                   `git://git.openstack.org/openstack/tatu <https://git.openstack.org/cgit/openstack/tatu>`__
+telemetry-tempest-plugin               `git://git.openstack.org/openstack/telemetry-tempest-plugin <https://git.openstack.org/cgit/openstack/telemetry-tempest-plugin>`__
 tricircle                              `git://git.openstack.org/openstack/tricircle <https://git.openstack.org/cgit/openstack/tricircle>`__
 trio2o                                 `git://git.openstack.org/openstack/trio2o <https://git.openstack.org/cgit/openstack/trio2o>`__
 trove                                  `git://git.openstack.org/openstack/trove <https://git.openstack.org/cgit/openstack/trove>`__
@@ -173,6 +178,7 @@
 valet                                  `git://git.openstack.org/openstack/valet <https://git.openstack.org/cgit/openstack/valet>`__
 vitrage                                `git://git.openstack.org/openstack/vitrage <https://git.openstack.org/cgit/openstack/vitrage>`__
 vitrage-dashboard                      `git://git.openstack.org/openstack/vitrage-dashboard <https://git.openstack.org/cgit/openstack/vitrage-dashboard>`__
+vitrage-tempest-plugin                 `git://git.openstack.org/openstack/vitrage-tempest-plugin <https://git.openstack.org/cgit/openstack/vitrage-tempest-plugin>`__
 vmware-nsx                             `git://git.openstack.org/openstack/vmware-nsx <https://git.openstack.org/cgit/openstack/vmware-nsx>`__
 vmware-vspc                            `git://git.openstack.org/openstack/vmware-vspc <https://git.openstack.org/cgit/openstack/vmware-vspc>`__
 watcher                                `git://git.openstack.org/openstack/watcher <https://git.openstack.org/cgit/openstack/watcher>`__
diff --git a/doc/source/zuul_ci_jobs_migration.rst b/doc/source/zuul_ci_jobs_migration.rst
new file mode 100644
index 0000000..c00f06e
--- /dev/null
+++ b/doc/source/zuul_ci_jobs_migration.rst
@@ -0,0 +1,301 @@
+===============================
+Migrating Zuul V2 CI jobs to V3
+===============================
+
+The OpenStack CI system moved from Zuul v2 to Zuul v3, and all CI jobs moved to
+the new CI system. All jobs have been migrated automatically to a format
+compatible with Zuul v3; the jobs produced in this way however are suboptimal
+and do not use the capabilities introduced by Zuul v3, which allow for re-use of
+job parts, in the form of Ansible roles, as well as inheritance between jobs.
+
+DevStack hosts a set of roles, plays and jobs that can be used by other
+repositories to define their DevStack based jobs. To benefit from them, jobs
+must be migrated from the legacy v2 ones into v3 native format.
+
+This document provides guidance and examples to make the migration process as
+painless and smooth as possible.
+
+Where to host the job definitions.
+==================================
+
+In Zuul V3 jobs can be defined in the repository that contains the code they
+excercise. If you are writing CI jobs for an OpenStack service you can define
+your DevStack based CI jobs in one of the repositories that host the code for
+your service. If you have a branchless repo, like a Tempest plugin, that is
+a convenient choice to host the job definitions since job changes do not have
+to be backported. For example, see the beginning of the ``.zuul.yaml`` from the
+sahara Tempest plugin repo:
+
+.. code:: yaml
+
+  # In http://git.openstack.org/cgit/openstack/sahara-tests/tree/.zuul.yaml:
+  - job:
+      name: sahara-tests-tempest
+      description: |
+        Run Tempest tests from the Sahara plugin.
+      parent: devstack-tempest
+
+Which base job to start from
+============================
+
+If your job needs an OpenStack cloud deployed via DevStack, but you don't plan
+on running Tempest tests, you can start from one of the base
+:doc:`jobs <zuul_jobs>` defined in the DevStack repo.
+
+The ``devstack`` job can be used for both single-node jobs and multi-node jobs,
+and it includes the list of services used in the integrated gate (keystone,
+glance, nova, cinder, neutron and swift). Different topologies can be achieved
+by switching the nodeset used in the child job.
+
+The ``devstack-base`` job is similar to ``devstack`` but it does not specify any
+required repo or service to be run in DevStack. It can be useful to setup
+children jobs that use a very narrow DevStack setup.
+
+If your job needs an OpenStack cloud deployed via DevStack, and you do plan
+on running Tempest tests, you can start from one of the base jobs defined in the
+Tempest repo.
+
+The ``devstack-tempest`` job can be used for both single-node jobs and
+multi-node jobs. Different topologies can be achieved by switching the nodeset
+used in the child job.
+
+Jobs can be customized as follows without writing any Ansible code:
+
+- add and/or remove DevStack services
+- add or modify DevStack and services configuration
+- install DevStack plugins
+- extend the number of sub-nodes (multinode only)
+- define extra log files and/or directories to be uploaded on logs.o.o
+- define extra log file extensions to be rewritten to .txt for ease of access
+
+Tempest jobs can be further customized as follows:
+
+- define the Tempest tox environment to be used
+- define the test concurrency
+- define the test regular expression
+
+Writing Ansible code, or importing existing custom roles, jobs can be further
+extended by:
+
+- adding pre and/or post playbooks
+- overriding the run playbook, add custom roles
+
+The (partial) example below extends a Tempest single node base job
+"devstack-tempest" in the Kuryr repository. The parent job name is defined in
+job.parent.
+
+.. code:: yaml
+
+  # https://git.openstack.org/cgit/openstack/kuryr-kubernetes/tree/.zuul.yaml:
+  - job:
+      name: kuryr-kubernetes-tempest-base
+      parent: devstack-tempest
+      description: Base kuryr-kubernetes-job
+      required-projects:
+        - openstack/devstack-plugin-container
+        - openstack/kuryr
+        - openstack/kuryr-kubernetes
+        - openstack/kuryr-tempest-plugin
+        - openstack/neutron-lbaas
+      vars:
+        tempest_test_regex: '^(kuryr_tempest_plugin.tests.)'
+        tox_envlist: 'all'
+        devstack_localrc:
+          KURYR_K8S_API_PORT: 8080
+          TEMPEST_PLUGINS: '/opt/stack/kuryr-tempest-plugin'
+        devstack_services:
+          kubernetes-api: true
+          kubernetes-controller-manager: true
+          kubernetes-scheduler: true
+          kubelet: true
+          kuryr-kubernetes: true
+          (...)
+        devstack_plugins:
+          kuryr-kubernetes: https://git.openstack.org/openstack/kuryr
+          devstack-plugin-container: https://git.openstack.org/openstack/devstack-plugin-container
+          neutron-lbaas: https://git.openstack.org/openstack/neutron-lbaas
+        (...)
+
+Job variables
+=============
+
+Variables can be added to the job in three different places:
+
+- job.vars: these are global variables available to all node in the nodeset
+- job.host-vars.[HOST]: these are variables available only to the specified HOST
+- job.group-vars.[GROUP]: these are variables available only to the specified
+  GROUP
+
+Zuul merges dict variables through job inheritance. Host and group variables
+override variables with the same name defined as global variables.
+
+In the example below, for the sundaes job, hosts that are not part of the
+subnode group will run vanilla and chocolate. Hosts in the subnode group will
+run stracciatella and strawberry.
+
+.. code:: yaml
+
+  - job:
+      name: ice-creams
+      vars:
+        devstack_service:
+          vanilla: true
+          chocolate: false
+      group-vars:
+        subnode:
+          devstack_service:
+            pistacchio: true
+            stracciatella: true
+
+  - job:
+      name: sundaes
+      parent: ice-creams
+      vars:
+        devstack_service:
+          chocolate: true
+      group-vars:
+        subnode:
+          devstack_service:
+            strawberry: true
+            pistacchio: false
+
+
+DevStack Gate Flags
+===================
+
+The old CI system worked using a combination of DevStack, Tempest and
+devstack-gate to setup a test environment and run tests against it. With Zuul
+V3, the logic that used to live in devstack-gate is moved into different repos,
+including DevStack, Tempest and grenade.
+
+DevStack-gate exposes an interface for job definition based on a number of
+DEVSTACK_GATE_* environment variables, or flags. This guide shows how to map
+DEVSTACK_GATE flags into the new
+system.
+
+The repo column indicates in which repository is hosted the code that replaces
+the devstack-gate flag. The new implementation column explains how to reproduce
+the same or a similar behaviour in Zuul v3 jobs. For localrc settings,
+devstack-gate defined a default value. In ansible jobs the default is either the
+value defined in the parent job, or the default from DevStack, if any.
+
+==============================================  ============= ==================
+DevStack gate flag                              Repo          New implementation
+==============================================  ============= ==================
+OVERRIDE_ZUUL_BRANCH                            zuul          override-checkout:
+                                                              [branch]
+                                                              in the job definition.
+DEVSTACK_GATE_NET_OVERLAY                       zuul-jobs     A bridge called
+                                                              br-infra is set up for
+                                                              all jobs that inherit
+                                                              from multinode with
+                                                              a dedicated `bridge role <https://docs.openstack.org/infra/zuul-jobs/roles.html#role-multi-node-bridge>`_.
+DEVSTACK_GATE_FEATURE_MATRIX                    devstack-gate ``test_matrix_features``
+                                                              variable of the
+                                                              test-matrix role in
+                                                              devstack-gate. This
+                                                              is a temporary
+                                                              solution, feature
+                                                              matrix will go away.
+                                                              In the future services
+                                                              will be defined in
+                                                              jobs only.
+DEVSTACK_CINDER_VOLUME_CLEAR                    devstack      *CINDER_VOLUME_CLEAR: true/false*
+                                                              in devstack_localrc
+                                                              in the job vars.
+DEVSTACK_GATE_NEUTRON                           devstack      True by default. To
+                                                              disable, disable all
+                                                              neutron services in
+                                                              devstack_services in
+                                                              the job definition.
+DEVSTACK_GATE_CONFIGDRIVE                       devstack      *FORCE_CONFIG_DRIVE: true/false*
+                                                              in devstack_localrc
+                                                              in the job vars.
+DEVSTACK_GATE_INSTALL_TESTONLY                  devstack      *INSTALL_TESTONLY_PACKAGES: true/false*
+                                                              in devstack_localrc
+                                                              in the job vars.
+DEVSTACK_GATE_VIRT_DRIVER                       devstack      *VIRT_DRIVER: [virt driver]*
+                                                              in devstack_localrc
+                                                              in the job vars.
+DEVSTACK_GATE_LIBVIRT_TYPE                      devstack      *LIBVIRT_TYPE: [libvirt type]*
+                                                              in devstack_localrc
+                                                              in the job vars.
+DEVSTACK_GATE_TEMPEST                           devstack      Defined by the job
+                                                tempest       that is used. The
+                                                              ``devstack`` job only
+                                                              runs devstack.
+                                                              The ``devstack-tempest``
+                                                              one triggers a Tempest
+                                                              run as well.
+DEVSTACK_GATE_TEMPEST_FULL                      tempest       *tox_envlist: full*
+                                                              in the job vars.
+DEVSTACK_GATE_TEMPEST_ALL                       tempest       *tox_envlist: all*
+                                                              in the job vars.
+DEVSTACK_GATE_TEMPEST_ALL_PLUGINS               tempest       *tox_envlist: all-plugin*
+                                                              in the job vars.
+DEVSTACK_GATE_TEMPEST_SCENARIOS                 tempest       *tox_envlist: scenario*
+                                                              in the job vars.
+TEMPEST_CONCURRENCY                             tempest       *tempest_concurrency: [value]*
+                                                              in the job vars. This
+                                                              is available only on
+                                                              jobs that inherit from
+                                                              ``devstack-tempest``
+                                                              down.
+DEVSTACK_GATE_TEMPEST_NOTESTS                   tempest       *tox_envlist: venv-tempest*
+                                                              in the job vars. This
+                                                              will create Tempest
+                                                              virtual environment
+                                                              but run no tests.
+DEVSTACK_GATE_SMOKE_SERIAL                      tempest       *tox_envlist: smoke-serial*
+                                                              in the job vars.
+DEVSTACK_GATE_TEMPEST_DISABLE_TENANT_ISOLATION  tempest       *tox_envlist: full-serial*
+                                                              in the job vars.
+                                                              *TEMPEST_ALLOW_TENANT_ISOLATION: false*
+                                                              in devstack_localrc in
+                                                              the job vars.
+==============================================  ============= ==================
+
+The following flags have not been migrated yet or are legacy and won't be
+migrated at all.
+
+=====================================  ======  ==========================
+DevStack gate flag                     Status  Details
+=====================================  ======  ==========================
+DEVSTACK_GATE_TOPOLOGY                 WIP     The topology depends on the base
+                                               job that is used and more
+                                               specifically on the nodeset
+                                               attached to it. The new job
+                                               format allows project to define
+                                               the variables to be passed to
+                                               every node/node-group that exists
+                                               in the topology. Named topologies
+                                               that include the nodeset and the
+                                               matching variables can be defined
+                                               in the form of base jobs.
+DEVSTACK_GATE_GRENADE                  TBD     Grenade Zuul V3 jobs will be
+                                               hosted in the grenade repo.
+GRENADE_BASE_BRANCH                    TBD     Grenade Zuul V3 jobs will be
+                                               hosted in the grenade repo.
+DEVSTACK_GATE_NEUTRON_DVR              TBD     Depends on multinode support.
+DEVSTACK_GATE_EXERCISES                TBD     Can be done on request.
+DEVSTACK_GATE_IRONIC                   TBD     This will probably be implemented
+                                               on ironic side.
+DEVSTACK_GATE_IRONIC_DRIVER            TBD     This will probably be implemented
+                                               on ironic side.
+DEVSTACK_GATE_IRONIC_BUILD_RAMDISK     TBD     This will probably be implemented
+                                               on ironic side.
+DEVSTACK_GATE_POSTGRES                 Legacy  This flag exists in d-g but the
+                                               only thing that it does is
+                                               capture postgres logs. This is
+                                               already supported by the roles in
+                                               post, so the flag is useless in
+                                               the new jobs. postgres itself can
+                                               be enabled via the
+                                               devstack_service job variable.
+DEVSTACK_GATE_ZEROMQ                   Legacy  This has no effect in d-g.
+DEVSTACK_GATE_MQ_DRIVER                Legacy  This has no effect in d-g.
+DEVSTACK_GATE_TEMPEST_STRESS_ARGS      Legacy  Stress is not in Tempest anymore.
+DEVSTACK_GATE_TEMPEST_HEAT_SLOW        Legacy  This is not used anywhere.
+DEVSTACK_GATE_CELLS                    Legacy  This has no effect in d-g.
+DEVSTACK_GATE_NOVA_API_METADATA_SPLIT  Legacy  This has no effect in d-g.
+=====================================  ======  ==========================
diff --git a/doc/source/zuul_jobs.rst b/doc/source/zuul_jobs.rst
new file mode 100644
index 0000000..cf203a8
--- /dev/null
+++ b/doc/source/zuul_jobs.rst
@@ -0,0 +1,4 @@
+Zuul CI Jobs
+============
+
+.. zuul:autojobs::
diff --git a/doc/source/zuul_roles.rst b/doc/source/zuul_roles.rst
new file mode 100644
index 0000000..4939281
--- /dev/null
+++ b/doc/source/zuul_roles.rst
@@ -0,0 +1,4 @@
+Zuul CI Roles
+=============
+
+.. zuul:autoroles::
diff --git a/files/debs/general b/files/debs/general
index 8e0018d..df872a0 100644
--- a/files/debs/general
+++ b/files/debs/general
@@ -15,6 +15,7 @@
 libffi-dev # for pyOpenSSL
 libjpeg-dev # Pillow 3.0.0
 libmysqlclient-dev  # MySQL-python
+libpcre3-dev # for python-pcre
 libpq-dev  # psycopg2
 libssl-dev # for pyOpenSSL
 libsystemd-dev # for systemd-python
diff --git a/files/debs/horizon b/files/debs/horizon
index 1f45b54..4833289 100644
--- a/files/debs/horizon
+++ b/files/debs/horizon
@@ -1,3 +1,2 @@
 apache2  # NOPRIME
 libapache2-mod-wsgi  # NOPRIME
-libpcre3-dev  # pyScss
diff --git a/files/rpms-suse/general b/files/rpms-suse/general
index 0c1a281..b870d72 100644
--- a/files/rpms-suse/general
+++ b/files/rpms-suse/general
@@ -11,7 +11,6 @@
 iputils
 libffi-devel  # pyOpenSSL
 libjpeg8-devel # Pillow 3.0.0
-libmysqlclient-devel # MySQL-python
 libopenssl-devel # to rebuild pyOpenSSL if needed
 libxslt-devel  # lxml
 lsof # useful when debugging
@@ -19,6 +18,7 @@
 net-tools
 openssh
 openssl
+pcre-devel # python-pcre
 postgresql-devel  # psycopg2
 psmisc
 python-cmd2 # dist:opensuse-12.3
diff --git a/files/rpms-suse/n-cpu b/files/rpms-suse/n-cpu
index 9ece115..d0c572e 100644
--- a/files/rpms-suse/n-cpu
+++ b/files/rpms-suse/n-cpu
@@ -1,7 +1,7 @@
 cryptsetup
-genisoimage
 libosinfo
 lvm2
+mkisofs
 open-iscsi
 sg3_utils
 # Stuff for diablo volumes
diff --git a/files/rpms-suse/nova b/files/rpms-suse/nova
index ae115d2..4103a40 100644
--- a/files/rpms-suse/nova
+++ b/files/rpms-suse/nova
@@ -4,7 +4,6 @@
 dnsmasq-utils # dist:opensuse-12.3,opensuse-13.1
 ebtables
 gawk
-genisoimage # required for config_drive
 iptables
 iputils
 kpartx
@@ -12,6 +11,7 @@
 libvirt # NOPRIME
 libvirt-python # NOPRIME
 mariadb # NOPRIME
+mkisofs # required for config_drive
 parted
 polkit
 # qemu as fallback if kvm cannot be used
diff --git a/files/rpms/cinder b/files/rpms/cinder
index 3bc4e7a..e6addc6 100644
--- a/files/rpms/cinder
+++ b/files/rpms/cinder
@@ -1,5 +1,5 @@
 iscsi-initiator-utils
 lvm2
 qemu-img
-scsi-target-utils # not:rhel7,f24,f25,f26 NOPRIME
-targetcli # dist:rhel7,f24,f25,f26 NOPRIME
+scsi-target-utils # not:rhel7,f25,f26,f27 NOPRIME
+targetcli # dist:rhel7,f25,f26,f27 NOPRIME
diff --git a/files/rpms/general b/files/rpms/general
index f3f8708..5d9a4ad 100644
--- a/files/rpms/general
+++ b/files/rpms/general
@@ -9,9 +9,9 @@
 graphviz # needed only for docs
 httpd
 httpd-devel
-iptables-services  # NOPRIME f23,f24,f25,f26
+iptables-services  # NOPRIME f25,f26,f27
 java-1.7.0-openjdk-headless  # NOPRIME rhel7
-java-1.8.0-openjdk-headless  # NOPRIME f23,f24,f25,f26
+java-1.8.0-openjdk-headless  # NOPRIME f25,f26,f27
 libffi-devel
 libjpeg-turbo-devel # Pillow 3.0.0
 libxml2-devel # lxml
@@ -22,6 +22,7 @@
 openssh-server
 openssl
 openssl-devel # to rebuild pyOpenSSL if needed
+pcre-devel # for python-pcre
 pkgconfig
 postgresql-devel  # psycopg2
 psmisc
diff --git a/files/rpms/horizon b/files/rpms/horizon
index aeb2cb5..fa5601a 100644
--- a/files/rpms/horizon
+++ b/files/rpms/horizon
@@ -1,5 +1,4 @@
 Django
 httpd # NOPRIME
 mod_wsgi  # NOPRIME
-pcre-devel  # pyScss
 pyxattr
diff --git a/files/rpms/nova b/files/rpms/nova
index 64ed480..9fb7282 100644
--- a/files/rpms/nova
+++ b/files/rpms/nova
@@ -7,7 +7,7 @@
 genisoimage # required for config_drive
 iptables
 iputils
-kernel-modules # dist:f23,f24,f25,f26
+kernel-modules # dist:f25,f26,f27
 kpartx
 libxml2-python
 m2crypto
diff --git a/files/rpms/swift b/files/rpms/swift
index 2e09cec..be0db14 100644
--- a/files/rpms/swift
+++ b/files/rpms/swift
@@ -2,7 +2,7 @@
 liberasurecode-devel
 memcached
 pyxattr
-rsync-daemon # dist:f23,f24,f25,f26
+rsync-daemon # dist:f25,f26,f27
 sqlite
 xfsprogs
 xinetd
diff --git a/functions b/functions
index 20b83b3..24994c0 100644
--- a/functions
+++ b/functions
@@ -446,7 +446,12 @@
         ID=""
         while [[ "\$ID" == "" ]]; do
             sleep 1
-            ID=\$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" compute service list --host `hostname` --service nova-compute -c ID -f value)
+            if [[ "$VIRT_DRIVER" = 'fake' ]]; then
+                # When using the fake driver the compute hostnames have a suffix of 1 to NUMBER_FAKE_NOVA_COMPUTE
+                ID=\$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" compute service list --host `hostname`1 --service nova-compute -c ID -f value)
+            else
+                ID=\$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" compute service list --host `hostname` --service nova-compute -c ID -f value)
+            fi
         done
 EOF
     time_stop "wait_for_service"
@@ -503,13 +508,13 @@
 function get_instance_ip {
     local vm_id=$1
     local network_name=$2
-    local nova_result
+    local addresses
     local ip
 
-    nova_result="$(nova show $vm_id)"
-    ip=$(echo "$nova_result" | grep "$network_name" | get_field 2)
+    addresses=$(openstack server show -c addresses -f value "$vm_id")
+    ip=$(echo $addresses | sed -n "s/^.*$network_name=\([0-9\.]*\).*$/\1/p")
     if [[ $ip = "" ]];then
-        echo "$nova_result"
+        echo "addresses of server $vm_id : $addresses"
         die $LINENO "[Fail] Couldn't get ipaddress of VM"
     fi
     echo $ip
@@ -712,7 +717,7 @@
     # Create a loopback disk and format it to XFS.
     if [[ -e ${disk_image} ]]; then
         if egrep -q ${storage_data_dir} /proc/mounts; then
-            sudo umount ${storage_data_dir}/drives/sdb1
+            sudo umount ${storage_data_dir}
             sudo rm -f ${disk_image}
         fi
     fi
@@ -805,13 +810,11 @@
 #
 # Write out various useful state information to /etc/devstack-version
 function write_devstack_version {
-    cat - > /tmp/devstack-version <<EOF
+    cat - <<EOF | sudo tee /etc/devstack-version >/dev/null
 DevStack Version: ${DEVSTACK_SERIES}
 Change: $(git log --format="%H %s %ci" -1)
 OS Version: ${os_VENDOR} ${os_RELEASE} ${os_CODENAME}
 EOF
-    sudo install -m 644 /tmp/devstack-version /etc/devstack-version
-    rm /tmp/devstack-version
 }
 
 # Restore xtrace
diff --git a/functions-common b/functions-common
index df295a3..b1b0995 100644
--- a/functions-common
+++ b/functions-common
@@ -373,6 +373,9 @@
         DISTRO="f$os_RELEASE"
     elif [[ "$os_VENDOR" =~ (openSUSE) ]]; then
         DISTRO="opensuse-$os_RELEASE"
+        # Tumbleweed uses "n/a" as a codename, and the release is a datestring
+        # like 20180218, so not very useful.
+        [ "$os_CODENAME" = "n/a" ] && DISTRO="opensuse-tumbleweed"
     elif [[ "$os_VENDOR" =~ (SUSE LINUX) ]]; then
         # just use major release
         DISTRO="sle${os_RELEASE%.*}"
@@ -2301,12 +2304,7 @@
 
 function cleanup_oscwrap {
     local total=0
-    if python3_enabled ; then
-        local python=python3
-    else
-        local python=python
-    fi
-    total=$(cat $OSCWRAP_TIMER_FILE | $python -c "import sys; print(sum(int(l) for l in sys.stdin))")
+    total=$(cat $OSCWRAP_TIMER_FILE | $PYTHON -c "import sys; print(sum(int(l) for l in sys.stdin))")
     _TIME_TOTAL["osc"]=$total
     rm $OSCWRAP_TIMER_FILE
 }
diff --git a/inc/python b/inc/python
index 2e4eff0..96be107 100644
--- a/inc/python
+++ b/inc/python
@@ -49,7 +49,11 @@
     fi
     $xtrace
 
-    if is_fedora || is_suse; then
+    if python3_enabled && [ "$os_VENDOR" = "Fedora" -a $os_RELEASE -gt 26 ]; then
+        # Default Python 3 install prefix changed to /usr/local in Fedora 27:
+        # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
+        echo "/usr/local/bin"
+    elif is_fedora || is_suse; then
         echo "/usr/bin"
     else
         echo "/usr/local/bin"
@@ -407,12 +411,6 @@
 function lib_installed_from_git {
     local name=$1
     local safe_name
-    # TODO(mordred) This is a special case for python-openstacksdk, where the
-    # repo name and the pip name do not match. We should either add systemic
-    # support for providing aliases, or we should rename the git repo.
-    if [[ $name == 'python-openstacksdk' ]] ; then
-        name=openstacksdk
-    fi
     safe_name=$(python -c "from pkg_resources import safe_name; \
         print(safe_name('${name}'))")
     # Note "pip freeze" doesn't always work here, because it tries to
@@ -431,22 +429,6 @@
     [[ -n $(pip list --format=columns 2>/dev/null | awk "/^$safe_name/ {print \$3}") ]]
 }
 
-# check that everything that's in LIBS_FROM_GIT was actually installed
-# correctly, this helps double check issues with library fat fingering.
-function check_libs_from_git {
-    local lib=""
-    local not_installed=""
-    for lib in $(echo ${LIBS_FROM_GIT} | tr "," " "); do
-        if ! lib_installed_from_git "$lib"; then
-            not_installed+=" $lib"
-        fi
-    done
-    # if anything is not installed, say what it is.
-    if [[ -n "$not_installed" ]]; then
-        die $LINENO "The following LIBS_FROM_GIT were not installed correct: $not_installed"
-    fi
-}
-
 # setup a library by name. If we are trying to use the library from
 # git, we'll do a git based install, otherwise we'll punt and the
 # library should be installed by a requirements pull from another
@@ -557,6 +539,13 @@
 
     setup_package $project_dir "$flags" $extras
 
+    # If this project is in LIBS_FROM_GIT, verify it was actually installed
+    # correctly.  This helps catch errors caused by constraints mismatches.
+    if use_library_from_git "$project_dir"; then
+        if ! lib_installed_from_git "$project_dir"; then
+            die $LINENO "The following LIBS_FROM_GIT was not installed correctly: $project_dir"
+        fi
+    fi
 }
 
 # ``pip install -e`` the package, which processes the dependencies
diff --git a/lib/apache b/lib/apache
index 3af3411..84cec73 100644
--- a/lib/apache
+++ b/lib/apache
@@ -133,8 +133,9 @@
         sudo rm -f /etc/httpd/conf.d/000-*
         install_package httpd mod_wsgi
         # For consistency with Ubuntu, switch to the worker mpm, as
-        # the default is prefork
+        # the default is event
         sudo sed -i '/mod_mpm_prefork.so/s/^/#/g' /etc/httpd/conf.modules.d/00-mpm.conf
+        sudo sed -i '/mod_mpm_event.so/s/^/#/g' /etc/httpd/conf.modules.d/00-mpm.conf
         sudo sed -i '/mod_mpm_worker.so/s/^#//g' /etc/httpd/conf.modules.d/00-mpm.conf
     elif is_suse; then
         install_package apache2 apache2-mod_wsgi
diff --git a/lib/cinder b/lib/cinder
index a966a4b..3a8097f 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -101,7 +101,7 @@
 if is_fedora; then
     CINDER_ISCSI_HELPER=${CINDER_ISCSI_HELPER:-lioadm}
     if [[ ${CINDER_ISCSI_HELPER} != "lioadm" ]]; then
-        die "lioadm is the only valid Cinder iscsi_helper config on this platform"
+        die "lioadm is the only valid Cinder target_helper config on this platform"
     fi
 else
     CINDER_ISCSI_HELPER=${CINDER_ISCSI_HELPER:-tgtadm}
@@ -206,8 +206,6 @@
 function configure_cinder {
     sudo install -d -o $STACK_USER -m 755 $CINDER_CONF_DIR
 
-    cp -p $CINDER_DIR/etc/cinder/policy.json $CINDER_CONF_DIR
-
     rm -f $CINDER_CONF
 
     configure_rootwrap cinder
@@ -229,10 +227,9 @@
 
     configure_auth_token_middleware $CINDER_CONF cinder $CINDER_AUTH_CACHE_DIR
 
-    iniset $CINDER_CONF DEFAULT auth_strategy keystone
     iniset $CINDER_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
 
-    iniset $CINDER_CONF DEFAULT iscsi_helper "$CINDER_ISCSI_HELPER"
+    iniset $CINDER_CONF DEFAULT target_helper "$CINDER_ISCSI_HELPER"
     iniset $CINDER_CONF database connection `database_connection_url cinder`
     iniset $CINDER_CONF DEFAULT api_paste_config $CINDER_API_PASTE_INI
     iniset $CINDER_CONF DEFAULT rootwrap_config "$CINDER_CONF_DIR/rootwrap.conf"
@@ -328,7 +325,7 @@
     if [[ ! -z "$CINDER_COORDINATION_URL" ]]; then
         iniset $CINDER_CONF coordination backend_url "$CINDER_COORDINATION_URL"
     elif is_service_enabled etcd3; then
-        iniset $CINDER_CONF coordination backend_url "etcd3+http://${SERVICE_HOST}:2379"
+        iniset $CINDER_CONF coordination backend_url "etcd3+http://${SERVICE_HOST}:$ETCD_PORT"
     fi
 }
 
@@ -345,9 +342,16 @@
 
         create_service_user "cinder"
 
+        # block-storage is the official service type
+        get_or_create_service "cinder" "block-storage" "Cinder Volume Service"
         get_or_create_service "cinder" "volume" "Cinder Volume Service"
         if [ "$CINDER_USE_MOD_WSGI" == "False" ]; then
             get_or_create_endpoint \
+                "block-storage" \
+                "$REGION_NAME" \
+                "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/"
+
+            get_or_create_endpoint \
                 "volume" \
                 "$REGION_NAME" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/\$(project_id)s"
@@ -365,6 +369,11 @@
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v3/\$(project_id)s"
         else
             get_or_create_endpoint \
+                "block-storage" \
+                "$REGION_NAME" \
+                "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/volume/"
+
+            get_or_create_endpoint \
                 "volume" \
                 "$REGION_NAME" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/volume/v1/\$(project_id)s"
@@ -430,7 +439,7 @@
     setup_develop $CINDER_DIR
     if [[ "$CINDER_ISCSI_HELPER" == "tgtadm" ]]; then
         install_package tgt
-    elif [[ "$CINDER_ISCI_HELPER" == "lioadm" ]]; then
+    elif [[ "$CINDER_ISCSI_HELPER" == "lioadm" ]]; then
         install_package targetcli
     fi
 }
@@ -531,7 +540,17 @@
         local be be_name
         for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
             be_name=${be##*:}
-            openstack --os-region-name="$REGION_NAME" volume type create --property volume_backend_name="${be_name}" ${be_name}
+            # NOTE (e0ne): openstack client doesn't work with cinder in noauth mode
+            if is_service_enabled keystone; then
+                openstack --os-region-name="$REGION_NAME" volume type create --property volume_backend_name="${be_name}" ${be_name}
+            else
+                # TODO (e0ne): use openstack client once it will support cinder in noauth mode:
+                # https://bugs.launchpad.net/python-cinderclient/+bug/1755279
+                local cinder_url
+                cinder_url=$CINDER_SERVICE_PROTOCOL://$SERVICE_HOST:$CINDER_SERVICE_PORT/v3
+                OS_USER_ID=$OS_USERNAME OS_PROJECT_ID=$OS_PROJECT_NAME cinder --os-auth-type noauth --os-endpoint=$cinder_url type-create ${be_name}
+                OS_USER_ID=$OS_USERNAME OS_PROJECT_ID=$OS_PROJECT_NAME cinder --os-auth-type noauth --os-endpoint=$cinder_url type-key ${be_name} set volume_backend_name=${be_name}
+            fi
         done
     fi
 }
diff --git a/lib/cinder_backends/fake_gate b/lib/cinder_backends/fake_gate
index 6b1f848..3ffd9a6 100644
--- a/lib/cinder_backends/fake_gate
+++ b/lib/cinder_backends/fake_gate
@@ -50,7 +50,7 @@
     iniset $CINDER_CONF $be_name volume_backend_name $be_name
     iniset $CINDER_CONF $be_name volume_driver "cinder.tests.fake_driver.FakeGateDriver"
     iniset $CINDER_CONF $be_name volume_group $VOLUME_GROUP_NAME-$be_name
-    iniset $CINDER_CONF $be_name iscsi_helper "$CINDER_ISCSI_HELPER"
+    iniset $CINDER_CONF $be_name target_helper "$CINDER_ISCSI_HELPER"
     iniset $CINDER_CONF $be_name lvm_type "$CINDER_LVM_TYPE"
 
     if [[ "$CINDER_VOLUME_CLEAR" == "non" ]]; then
diff --git a/lib/cinder_backends/lvm b/lib/cinder_backends/lvm
index 03e1880..497081c 100644
--- a/lib/cinder_backends/lvm
+++ b/lib/cinder_backends/lvm
@@ -50,7 +50,7 @@
     iniset $CINDER_CONF $be_name volume_backend_name $be_name
     iniset $CINDER_CONF $be_name volume_driver "cinder.volume.drivers.lvm.LVMVolumeDriver"
     iniset $CINDER_CONF $be_name volume_group $VOLUME_GROUP_NAME-$be_name
-    iniset $CINDER_CONF $be_name iscsi_helper "$CINDER_ISCSI_HELPER"
+    iniset $CINDER_CONF $be_name target_helper "$CINDER_ISCSI_HELPER"
     iniset $CINDER_CONF $be_name lvm_type "$CINDER_LVM_TYPE"
 
 }
diff --git a/lib/etcd3 b/lib/etcd3
index d3f7226..26d07fd 100644
--- a/lib/etcd3
+++ b/lib/etcd3
@@ -27,7 +27,6 @@
 ETCD_DATA_DIR="$DATA_DIR/etcd"
 ETCD_SYSTEMD_SERVICE="devstack@etcd.service"
 ETCD_BIN_DIR="$DEST/bin"
-ETCD_PORT=2379
 
 if is_ubuntu ; then
     UBUNTU_RELEASE_BASE_NUM=`lsb_release -r | awk '{print $2}' | cut -d '.' -f 1`
@@ -38,13 +37,13 @@
     local cmd="$ETCD_BIN_DIR/etcd"
     cmd+=" --name $HOSTNAME --data-dir $ETCD_DATA_DIR"
     cmd+=" --initial-cluster-state new --initial-cluster-token etcd-cluster-01"
-    cmd+=" --initial-cluster $HOSTNAME=http://$SERVICE_HOST:2380"
-    cmd+=" --initial-advertise-peer-urls http://$SERVICE_HOST:2380"
+    cmd+=" --initial-cluster $HOSTNAME=http://$SERVICE_HOST:$ETCD_PEER_PORT"
+    cmd+=" --initial-advertise-peer-urls http://$SERVICE_HOST:$ETCD_PEER_PORT"
     cmd+=" --advertise-client-urls http://$SERVICE_HOST:$ETCD_PORT"
     if [ "$SERVICE_LISTEN_ADDRESS" == "::" ]; then
-        cmd+=" --listen-peer-urls http://[::]:2380 "
+        cmd+=" --listen-peer-urls http://[::]:$ETCD_PEER_PORT "
     else
-        cmd+=" --listen-peer-urls http://0.0.0.0:2380 "
+        cmd+=" --listen-peer-urls http://0.0.0.0:$ETCD_PEER_PORT "
     fi
     cmd+=" --listen-client-urls http://$SERVICE_HOST:$ETCD_PORT"
 
diff --git a/lib/glance b/lib/glance
index aad4726..6a0e719 100644
--- a/lib/glance
+++ b/lib/glance
@@ -56,6 +56,7 @@
 GLANCE_POLICY_JSON=$GLANCE_CONF_DIR/policy.json
 GLANCE_SCHEMA_JSON=$GLANCE_CONF_DIR/schema-image.json
 GLANCE_SWIFT_STORE_CONF=$GLANCE_CONF_DIR/glance-swift-store.conf
+GLANCE_IMAGE_IMPORT_CONF=$GLANCE_CONF_DIR/glance-image-import.conf
 GLANCE_V1_ENABLED=${GLANCE_V1_ENABLED:-False}
 
 if is_service_enabled tls-proxy; then
@@ -220,6 +221,11 @@
     # Store specific confs
     iniset $GLANCE_CACHE_CONF glance_store filesystem_store_datadir $GLANCE_IMAGE_DIR/
 
+    # Set default configuration options for the glance-image-import
+    iniset $GLANCE_IMAGE_IMPORT_CONF image_import_opts image_import_plugins []
+    iniset $GLANCE_IMAGE_IMPORT_CONF inject_metadata_properties ignore_user_roles admin
+    iniset $GLANCE_IMAGE_IMPORT_CONF inject_metadata_properties inject
+
     cp -p $GLANCE_DIR/etc/policy.json $GLANCE_POLICY_JSON
     cp -p $GLANCE_DIR/etc/schema-image.json $GLANCE_SCHEMA_JSON
 
diff --git a/lib/horizon b/lib/horizon
index 3d2f68d..fab41bb 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -26,9 +26,6 @@
 # Defaults
 # --------
 
-# Set up default directories
-GITDIR["django_openstack_auth"]=$DEST/django_openstack_auth
-
 HORIZON_DIR=$DEST/horizon
 
 # local_settings.py is used to customize Dashboard settings.
@@ -159,20 +156,6 @@
 
 }
 
-# install_django_openstack_auth() - Collect source and prepare
-function install_django_openstack_auth {
-    if use_library_from_git "django_openstack_auth"; then
-        local dir=${GITDIR["django_openstack_auth"]}
-        git_clone_by_name "django_openstack_auth"
-        # Compile message catalogs before installation
-        _prepare_message_catalog_compilation
-        (cd $dir; $PYTHON setup.py compile_catalog)
-        setup_dev_lib "django_openstack_auth"
-    fi
-    # if we aren't using this library from git, then we just let it
-    # get dragged in by the horizon setup.
-}
-
 # install_horizon() - Collect source and prepare
 function install_horizon {
     # Apache installation, because we mark it NOPRIME
@@ -191,13 +174,6 @@
     stop_apache_server
 }
 
-# NOTE: It can be moved to common functions, but it is only used by compilation
-# of django_openstack_auth catalogs at the moment.
-function _prepare_message_catalog_compilation {
-    pip_install_gr Babel
-}
-
-
 # Restore xtrace
 $_XTRACE_HORIZON
 
diff --git a/lib/keystone b/lib/keystone
index 714f089..696e351 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -202,7 +202,7 @@
     sudo install -d -o $STACK_USER $KEYSTONE_CONF_DIR
 
     if [[ "$KEYSTONE_CONF_DIR" != "$KEYSTONE_DIR/etc" ]]; then
-        install -m 600 $KEYSTONE_DIR/etc/keystone.conf.sample $KEYSTONE_CONF
+        install -m 600 /dev/null $KEYSTONE_CONF
         if [[ -f "$KEYSTONE_DIR/etc/keystone-paste.ini" ]]; then
             cp -p "$KEYSTONE_DIR/etc/keystone-paste.ini" "$KEYSTONE_PASTE_INI"
         fi
@@ -220,7 +220,7 @@
         inidelete $KEYSTONE_PASTE_INI composite:admin \\/v2.0
     fi
 
-    # Rewrite stock ``keystone.conf``
+    # Populate ``keystone.conf``
     if is_service_enabled ldap; then
         iniset $KEYSTONE_CONF identity domain_config_dir "$KEYSTONE_CONF_DIR/domains"
         iniset $KEYSTONE_CONF identity domain_specific_drivers_enabled "True"
diff --git a/lib/libraries b/lib/libraries
index 6d52f64..b4f3c31 100644
--- a/lib/libraries
+++ b/lib/libraries
@@ -28,6 +28,7 @@
 GITDIR["cursive"]=$DEST/cursive
 GITDIR["debtcollector"]=$DEST/debtcollector
 GITDIR["futurist"]=$DEST/futurist
+GITDIR["openstacksdk"]=$DEST/openstacksdk
 GITDIR["os-client-config"]=$DEST/os-client-config
 GITDIR["osc-lib"]=$DEST/osc-lib
 GITDIR["osc-placement"]=$DEST/osc-placement
@@ -51,7 +52,6 @@
 GITDIR["oslo.vmware"]=$DEST/oslo.vmware
 GITDIR["osprofiler"]=$DEST/osprofiler
 GITDIR["pycadf"]=$DEST/pycadf
-GITDIR["python-openstacksdk"]=$DEST/python-openstacksdk
 GITDIR["stevedore"]=$DEST/stevedore
 GITDIR["taskflow"]=$DEST/taskflow
 GITDIR["tooz"]=$DEST/tooz
@@ -91,6 +91,7 @@
     _install_lib_from_source "cursive"
     _install_lib_from_source "debtcollector"
     _install_lib_from_source "futurist"
+    _install_lib_from_source "openstacksdk"
     _install_lib_from_source "osc-lib"
     _install_lib_from_source "osc-placement"
     _install_lib_from_source "os-client-config"
@@ -114,7 +115,6 @@
     _install_lib_from_source "oslo.vmware"
     _install_lib_from_source "osprofiler"
     _install_lib_from_source "pycadf"
-    _install_lib_from_source "python-openstacksdk"
     _install_lib_from_source "stevedore"
     _install_lib_from_source "taskflow"
     _install_lib_from_source "tooz"
diff --git a/lib/neutron b/lib/neutron
index c5839f5..cef8d1f 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -32,6 +32,17 @@
 NEUTRON_DIR=$DEST/neutron
 NEUTRON_AUTH_CACHE_DIR=${NEUTRON_AUTH_CACHE_DIR:-/var/cache/neutron}
 
+NEUTRON_DISTRIBUTED_ROUTING=$(trueorfalse False NEUTRON_DISTRIBUTED_ROUTING)
+# Distributed Virtual Router (DVR) configuration
+# Can be:
+# - ``legacy``          - No DVR functionality
+# - ``dvr_snat``        - Controller or single node DVR
+# - ``dvr``             - Compute node in multi-node DVR
+# - ``dvr_no_external`` - Compute node in multi-node DVR, no external network
+#
+# Default is 'dvr_snat' since it can handle both DVR and legacy routers
+NEUTRON_DVR_MODE=${NEUTRON_DVR_MODE:-dvr_snat}
+
 NEUTRON_BIN_DIR=$(get_python_exec_prefix)
 NEUTRON_DHCP_BINARY="neutron-dhcp-agent"
 
@@ -42,6 +53,7 @@
 NEUTRON_DHCP_CONF=$NEUTRON_CONF_DIR/dhcp_agent.ini
 NEUTRON_L3_CONF=$NEUTRON_CONF_DIR/l3_agent.ini
 NEUTRON_AGENT_CONF=$NEUTRON_CONF_DIR/
+NEUTRON_CREATE_INITIAL_NETWORKS=${NEUTRON_CREATE_INITIAL_NETWORKS:-True}
 
 NEUTRON_STATE_PATH=${NEUTRON_STATE_PATH:=$DATA_DIR/neutron}
 NEUTRON_AUTH_CACHE_DIR=${NEUTRON_AUTH_CACHE_DIR:-/var/cache/neutron}
@@ -173,6 +185,7 @@
 
         iniset $NEUTRON_CONF DEFAULT policy_file $policy_file
         iniset $NEUTRON_CONF DEFAULT allow_overlapping_ips True
+        iniset $NEUTRON_CONF DEFAULT router_distributed $NEUTRON_DISTRIBUTED_ROUTING
 
         iniset $NEUTRON_CONF DEFAULT auth_strategy $NEUTRON_AUTH_STRATEGY
         configure_auth_token_middleware $NEUTRON_CONF neutron $NEUTRON_AUTH_CACHE_DIR keystone_authtoken
@@ -181,7 +194,15 @@
         # Configure VXLAN
         # TODO(sc68cal) not hardcode?
         iniset $NEUTRON_CORE_PLUGIN_CONF ml2 tenant_network_types vxlan
-        iniset $NEUTRON_CORE_PLUGIN_CONF ml2 mechanism_drivers openvswitch,linuxbridge
+
+        local mech_drivers="openvswitch"
+        if [[ "$NEUTRON_DISTRIBUTED_ROUTING" = "True" ]]; then
+            mech_drivers+=",l2population"
+        else
+            mech_drivers+=",linuxbridge"
+        fi
+        iniset $NEUTRON_CORE_PLUGIN_CONF ml2 mechanism_drivers $mech_drivers
+
         iniset $NEUTRON_CORE_PLUGIN_CONF ml2_type_vxlan vni_ranges 1001:2000
         iniset $NEUTRON_CORE_PLUGIN_CONF ml2_type_flat flat_networks public
         if [[ "$NEUTRON_PORT_SECURITY" = "True" ]]; then
@@ -202,6 +223,11 @@
         else
             iniset $NEUTRON_CORE_PLUGIN_CONF securitygroup firewall_driver iptables_hybrid
             iniset $NEUTRON_CORE_PLUGIN_CONF ovs local_ip $HOST_IP
+
+            if [[ "$NEUTRON_DISTRIBUTED_ROUTING" = "True" ]]; then
+                iniset $NEUTRON_CORE_PLUGIN_CONF agent l2_population True
+                iniset $NEUTRON_CORE_PLUGIN_CONF agent enable_distributed_routing True
+            fi
         fi
 
         if ! running_in_container; then
@@ -236,6 +262,10 @@
         else
             iniset $NEUTRON_CORE_PLUGIN_CONF ovs bridge_mappings "$PUBLIC_NETWORK_NAME:$PUBLIC_BRIDGE"
         fi
+
+        if [[ "$NEUTRON_DISTRIBUTED_ROUTING" = "True" ]]; then
+            iniset $NEUTRON_L3_CONF DEFAULT agent_mode $NEUTRON_DVR_MODE
+        fi
     fi
 
     # Metadata
@@ -306,7 +336,6 @@
     iniset $NOVA_CONF neutron project_domain_name "Default"
     iniset $NOVA_CONF neutron auth_strategy $NEUTRON_AUTH_STRATEGY
     iniset $NOVA_CONF neutron region_name "$REGION_NAME"
-    iniset $NOVA_CONF neutron url $NEUTRON_SERVICE_PROTOCOL://$NEUTRON_SERVICE_HOST:$NEUTRON_SERVICE_PORT
 
     iniset $NOVA_CONF DEFAULT firewall_driver nova.virt.firewall.NoopFirewallDriver
 
@@ -438,7 +467,7 @@
     if is_service_enabled neutron-l3; then
         run_process neutron-l3 "$NEUTRON_BIN_DIR/$NEUTRON_L3_BINARY --config-file $NEUTRON_CONF --config-file $NEUTRON_L3_CONF"
     fi
-    if is_service_enabled neutron-api; then
+    if is_service_enabled neutron-api && [[ "$NEUTRON_CREATE_INITIAL_NETWORKS" == "True" ]]; then
         # XXX(sc68cal) - Here's where plugins can wire up their own networks instead
         # of the code in lib/neutron_plugins/services/l3
         if type -p neutron_plugin_create_initial_networks > /dev/null; then
diff --git a/lib/neutron-legacy b/lib/neutron-legacy
index bb76c5f..0cd7e31 100644
--- a/lib/neutron-legacy
+++ b/lib/neutron-legacy
@@ -376,7 +376,6 @@
     iniset $NOVA_CONF neutron project_domain_name "$SERVICE_DOMAIN_NAME"
     iniset $NOVA_CONF neutron auth_strategy "$Q_AUTH_STRATEGY"
     iniset $NOVA_CONF neutron region_name "$REGION_NAME"
-    iniset $NOVA_CONF neutron url "${Q_PROTOCOL}://$Q_HOST:$Q_PORT"
 
     if [[ "$Q_USE_SECGROUP" == "True" ]]; then
         LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver
@@ -929,7 +928,7 @@
 }
 
 function _get_net_id {
-    neutron --os-cloud devstack-admin --os-region "$REGION_NAME" --os-tenant-name admin --os-username admin --os-password $ADMIN_PASSWORD net-list | grep $1 | awk '{print $2}'
+    openstack --os-cloud devstack-admin --os-region-name="$REGION_NAME" --os-project-name admin --os-username admin --os-password $ADMIN_PASSWORD network list | grep $1 | awk '{print $2}'
 }
 
 function _get_probe_cmd_prefix {
diff --git a/lib/neutron_plugins/ovs_base b/lib/neutron_plugins/ovs_base
index 50b9ae5..36e2ed2 100644
--- a/lib/neutron_plugins/ovs_base
+++ b/lib/neutron_plugins/ovs_base
@@ -72,7 +72,14 @@
         if [[ $DISTRO == "sle12" ]] && [[ $os_RELEASE -lt 12.2 ]]; then
             restart_service openvswitch-switch
         else
-            restart_service openvswitch
+            # workaround for https://bugzilla.suse.com/show_bug.cgi?id=1085971
+            if [[ $DISTRO =~ "tumbleweed" ]]; then
+                sudo sed -i -e "s,^OVS_USER_ID=.*,OVS_USER_ID='root:root'," /etc/sysconfig/openvswitch
+            fi
+            restart_service openvswitch || {
+                journalctl -xe || :
+                systemctl status openvswitch
+            }
         fi
     fi
 }
diff --git a/lib/neutron_plugins/services/l3 b/lib/neutron_plugins/services/l3
index 98315b7..9be32b7 100644
--- a/lib/neutron_plugins/services/l3
+++ b/lib/neutron_plugins/services/l3
@@ -39,9 +39,9 @@
 Q_L3_ROUTER_PER_TENANT=${Q_L3_ROUTER_PER_TENANT:-True}
 
 
-# Use flat providernet for public network
+# Use providernet for public network
 #
-# If Q_USE_PROVIDERNET_FOR_PUBLIC=True, use a flat provider network
+# If Q_USE_PROVIDERNET_FOR_PUBLIC=True, use a 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 ofagent, you should add the
@@ -59,6 +59,10 @@
 #    Q_USE_PROVIDERNET_FOR_PUBLIC=True
 #    PUBLIC_PHYSICAL_NETWORK=public
 #    OVS_BRIDGE_MAPPINGS=public:br-ex
+#
+# The provider-network-type defaults to flat, however, the values
+# PUBLIC_PROVIDERNET_TYPE and PUBLIC_PROVIDERNET_SEGMENTATION_ID could
+# be set to specify the parameters for an alternate network type.
 Q_USE_PROVIDERNET_FOR_PUBLIC=${Q_USE_PROVIDERNET_FOR_PUBLIC:-True}
 PUBLIC_PHYSICAL_NETWORK=${PUBLIC_PHYSICAL_NETWORK:-public}
 
@@ -188,7 +192,7 @@
             if [ -z $SUBNETPOOL_V4_ID ]; then
                 fixed_range_v4=$FIXED_RANGE
             fi
-            SUBNET_ID=$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" subnet create --project $project_id --ip-version 4 ${ALLOCATION_POOL:+--allocation-pool $ALLOCATION_POOL} $PROVIDER_SUBNET_NAME --gateway $NETWORK_GATEWAY ${SUBNETPOOL_V4_ID:+--subnet-pool $SUBNETPOOL_V4_ID} --network $NET_ID --subnet-range $fixed_range_v4 | grep ' id ' | get_field 2)
+            SUBNET_ID=$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" subnet create --project $project_id --ip-version 4 ${ALLOCATION_POOL:+--allocation-pool $ALLOCATION_POOL} $PROVIDER_SUBNET_NAME --gateway $NETWORK_GATEWAY ${SUBNETPOOL_V4_ID:+--subnet-pool $SUBNETPOOL_V4_ID} --network $NET_ID ${fixed_range_v4:+--subnet-range $fixed_range_v4} | grep ' id ' | get_field 2)
             die_if_not_set $LINENO SUBNET_ID "Failure creating SUBNET_ID for $PROVIDER_SUBNET_NAME $project_id"
         fi
 
@@ -198,7 +202,7 @@
             if [ -z $SUBNETPOOL_V6_ID ]; then
                 fixed_range_v6=$IPV6_PROVIDER_FIXED_RANGE
             fi
-            IPV6_SUBNET_ID=$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" subnet create --project $project_id --ip-version 6 --gateway $IPV6_PROVIDER_NETWORK_GATEWAY $IPV6_PROVIDER_SUBNET_NAME ${SUBNETPOOL_V6_ID:+--subnet-pool $SUBNETPOOL_V6_ID} --network $NET_ID --subnet-range $fixed_range_v6 | grep ' id ' | get_field 2)
+            IPV6_SUBNET_ID=$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" subnet create --project $project_id --ip-version 6 --gateway $IPV6_PROVIDER_NETWORK_GATEWAY $IPV6_PROVIDER_SUBNET_NAME ${SUBNETPOOL_V6_ID:+--subnet-pool $SUBNETPOOL_V6_ID} --network $NET_ID ${fixed_range_v6:+--subnet-range $fixed_range_v6} | grep ' id ' | get_field 2)
             die_if_not_set $LINENO IPV6_SUBNET_ID "Failure creating IPV6_SUBNET_ID for $IPV6_PROVIDER_SUBNET_NAME $project_id"
         fi
 
@@ -240,7 +244,7 @@
         fi
         # Create an external network, and a subnet. Configure the external network as router gw
         if [ "$Q_USE_PROVIDERNET_FOR_PUBLIC" = "True" ]; then
-            EXT_NET_ID=$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" network create "$PUBLIC_NETWORK_NAME" $EXTERNAL_NETWORK_FLAGS --provider-network-type flat --provider-physical-network ${PUBLIC_PHYSICAL_NETWORK} | grep ' id ' | get_field 2)
+            EXT_NET_ID=$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" network create "$PUBLIC_NETWORK_NAME" $EXTERNAL_NETWORK_FLAGS --provider-network-type ${PUBLIC_PROVIDERNET_TYPE:-flat} ${PUBLIC_PROVIDERNET_SEGMENTATION_ID:+--provider-segment $PUBLIC_PROVIDERNET_SEGMENTATION_ID} --provider-physical-network ${PUBLIC_PHYSICAL_NETWORK} | grep ' id ' | get_field 2)
         else
             EXT_NET_ID=$(openstack --os-cloud devstack-admin --os-region "$REGION_NAME" network create "$PUBLIC_NETWORK_NAME" $EXTERNAL_NETWORK_FLAGS | grep ' id ' | get_field 2)
         fi
diff --git a/lib/nova b/lib/nova
index 50deeeb..939806f 100644
--- a/lib/nova
+++ b/lib/nova
@@ -82,6 +82,10 @@
     NOVA_SERVICE_PROTOCOL="https"
 fi
 
+# Whether to use TLS for comms between the VNC/SPICE/serial proxy
+# services and the compute node
+NOVA_CONSOLE_PROXY_COMPUTE_TLS=${NOVA_CONSOLE_PROXY_COMPUTE_TLS:-False}
+
 # Public facing bits
 NOVA_SERVICE_HOST=${NOVA_SERVICE_HOST:-$SERVICE_HOST}
 NOVA_SERVICE_PORT=${NOVA_SERVICE_PORT:-8774}
@@ -197,6 +201,13 @@
     return 1
 }
 
+# is_nova_console_proxy_compute_tls_enabled() - Test if the Nova Console Proxy
+# service has TLS enabled
+function is_nova_console_proxy_compute_tls_enabled {
+    [[ ${NOVA_CONSOLE_PROXY_COMPUTE_TLS} = "True" ]] && return 0
+    return 1
+}
+
 # Helper to clean iptables rules
 function clean_iptables {
     # Delete rules
@@ -413,6 +424,9 @@
     iniset $NOVA_CONF DEFAULT rootwrap_config "$NOVA_CONF_DIR/rootwrap.conf"
     iniset $NOVA_CONF scheduler driver "$SCHEDULER"
     iniset $NOVA_CONF filter_scheduler enabled_filters "$FILTERS"
+    if [[ $SCHEDULER == "filter_scheduler" ]]; then
+        iniset $NOVA_CONF scheduler workers "$API_WORKERS"
+    fi
     iniset $NOVA_CONF DEFAULT default_floating_pool "$PUBLIC_NETWORK_NAME"
     if [[ $SERVICE_IP_VERSION == 6 ]]; then
         iniset $NOVA_CONF DEFAULT my_ip "$HOST_IPV6"
@@ -424,7 +438,7 @@
     iniset $NOVA_CONF DEFAULT osapi_compute_listen "$NOVA_SERVICE_LISTEN_ADDRESS"
     iniset $NOVA_CONF DEFAULT metadata_listen "$NOVA_SERVICE_LISTEN_ADDRESS"
 
-    iniset $NOVA_CONF key_manager api_class nova.keymgr.conf_key_mgr.ConfKeyManager
+    iniset $NOVA_CONF key_manager backend nova.keymgr.conf_key_mgr.ConfKeyManager
 
     if is_fedora || is_suse; then
         # nova defaults to /usr/local/bin, but fedora and suse pip like to
@@ -492,6 +506,12 @@
     if [ "$FORCE_CONFIG_DRIVE" != "False" ]; then
         iniset $NOVA_CONF DEFAULT force_config_drive "$FORCE_CONFIG_DRIVE"
     fi
+
+    # nova defaults to genisoimage but only mkisofs is available for 15.0+
+    if is_suse; then
+        iniset $NOVA_CONF DEFAULT mkisofs_cmd /usr/bin/mkisofs
+    fi
+
     # Format logging
     setup_logging $NOVA_CONF
 
@@ -524,6 +544,17 @@
         iniset $NOVA_CONF vnc server_proxyclient_address "$VNCSERVER_PROXYCLIENT_ADDRESS"
         iniset $NOVA_CONF vnc novncproxy_host "$NOVA_SERVICE_LISTEN_ADDRESS"
         iniset $NOVA_CONF vnc xvpvncproxy_host "$NOVA_SERVICE_LISTEN_ADDRESS"
+
+        if is_nova_console_proxy_compute_tls_enabled ; then
+            iniset $NOVA_CONF vnc auth_schemes "vencrypt"
+            iniset $NOVA_CONF vnc vencrypt_client_key "/etc/pki/nova-novnc/client-key.pem"
+            iniset $NOVA_CONF vnc vencrypt_client_cert "/etc/pki/nova-novnc/client-cert.pem"
+            iniset $NOVA_CONF vnc vencrypt_ca_certs "/etc/pki/nova-novnc/ca-cert.pem"
+
+            sudo mkdir -p /etc/pki/nova-novnc
+            deploy_int_CA /etc/pki/nova-novnc/ca-cert.pem
+            deploy_int_cert /etc/pki/nova-novnc/client-cert.pem /etc/pki/nova-novnc/client-key.pem
+        fi
     else
         iniset $NOVA_CONF vnc enabled false
     fi
@@ -544,7 +575,6 @@
     iniset $NOVA_CONF oslo_messaging_notifications driver "messagingv2"
     iniset $NOVA_CONF oslo_messaging_notifications transport_url $(get_notification_url)
     iniset_rpc_backend nova $NOVA_CONF
-    iniset $NOVA_CONF glance api_servers "$GLANCE_URL"
 
     iniset $NOVA_CONF DEFAULT osapi_compute_workers "$API_WORKERS"
     iniset $NOVA_CONF DEFAULT metadata_workers "$API_WORKERS"
@@ -600,6 +630,12 @@
             else
                 rpc_backend_add_vhost $vhost
                 iniset_rpc_backend nova $conf DEFAULT $vhost
+                # When running in superconductor mode, the cell conductor
+                # must be configured to talk to the placement service for
+                # reschedules to work.
+                if is_service_enabled placement placement-client; then
+                    configure_placement_nova_compute $conf
+                fi
             fi
             # Format logging
             setup_logging $conf
@@ -655,7 +691,7 @@
         $NOVA_BIN_DIR/nova-manage cell create --name=child --cell_type=child --username=$RABBIT_USERID --hostname=$RABBIT_HOST --port=5672 --password=$RABBIT_PASSWORD --virtual_host=child_cell --woffset=0 --wscale=1
 
         # Creates the single cells v2 cell for the child cell (v1) nova db.
-        nova-manage --config-file $NOVA_CELLS_CONF cell_v2 create_cell \
+        $NOVA_BIN_DIR/nova-manage --config-file $NOVA_CELLS_CONF cell_v2 create_cell \
             --transport-url $(get_transport_url child_cell) --name 'cell1'
     fi
 }
@@ -699,7 +735,7 @@
         # this needs to come after the api_db sync happens. We also want to run
         # this before the db sync below since that will migrate both the nova
         # and nova_cell0 databases.
-        nova-manage cell_v2 map_cell0 --database_connection `database_connection_url nova_cell0`
+        $NOVA_BIN_DIR/nova-manage cell_v2 map_cell0 --database_connection `database_connection_url nova_cell0`
 
         # (Re)create nova databases
         for i in $(seq 1 $NOVA_NUM_CELLS); do
@@ -720,7 +756,7 @@
 
         # create the cell1 cell for the main nova db where the hosts live
         for i in $(seq 1 $NOVA_NUM_CELLS); do
-            nova-manage --config-file $NOVA_CONF --config-file $(conductor_conf $i) cell_v2 create_cell --name "cell$i"
+            $NOVA_BIN_DIR/nova-manage --config-file $NOVA_CONF --config-file $(conductor_conf $i) cell_v2 create_cell --name "cell$i"
         done
     fi
 
@@ -872,7 +908,7 @@
             # creating or modifying real configurations. Each fake
             # gets its own configuration and own log file.
             local fake_conf="${NOVA_FAKE_CONF}-${i}"
-            iniset $fake_conf DEFAULT nhost "${HOSTNAME}${i}"
+            iniset $fake_conf DEFAULT host "${HOSTNAME}${i}"
             run_process "n-cpu-${i}" "$NOVA_BIN_DIR/nova-compute --config-file $NOVA_CPU_CONF --config-file $fake_conf"
         done
     else
@@ -973,7 +1009,7 @@
         # because of the dom0/domU split. Just ignore for now.
         return
     fi
-    wait_for_compute 60
+    wait_for_compute $NOVA_READY_TIMEOUT
 }
 
 function start_nova {
@@ -985,7 +1021,7 @@
     if is_service_enabled n-api; then
         # dump the cell mapping to ensure life is good
         echo "Dumping cells_v2 mapping"
-        nova-manage cell_v2 list_cells --verbose
+        $NOVA_BIN_DIR/nova-manage cell_v2 list_cells --verbose
     fi
 }
 
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index 147ed8b..fcb4777 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -149,6 +149,18 @@
         fi
     fi
 
+    if is_nova_console_proxy_compute_tls_enabled ; then
+        if is_service_enabled n-novnc ; then
+            echo "vnc_tls = 1" | sudo tee -a $QEMU_CONF
+            echo "vnc_tls_x509_verify = 1" | sudo tee -a $QEMU_CONF
+
+            sudo mkdir -p /etc/pki/libvirt-vnc
+            sudo chown libvirt-qemu:libvirt-qemu /etc/pki/libvirt-vnc
+            deploy_int_CA /etc/pki/libvirt-vnc/ca-cert.pem
+            deploy_int_cert /etc/pki/libvirt-vnc/server-cert.pem /etc/pki/libvirt-vnc/server-key.pem
+        fi
+    fi
+
     # Service needs to be started on redhat/fedora -- do a restart for
     # sanity after fiddling the config.
     restart_service $LIBVIRT_DAEMON
diff --git a/lib/nova_plugins/hypervisor-ironic b/lib/nova_plugins/hypervisor-ironic
index ee1a0e0..c91f70b 100644
--- a/lib/nova_plugins/hypervisor-ironic
+++ b/lib/nova_plugins/hypervisor-ironic
@@ -58,6 +58,9 @@
     iniset $NOVA_CONF ironic project_domain_id default
     iniset $NOVA_CONF ironic user_domain_id default
     iniset $NOVA_CONF ironic project_name demo
+
+    iniset $NOVA_CONF ironic api_max_retries 300
+    iniset $NOVA_CONF ironic api_retry_interval 5
 }
 
 # install_nova_hypervisor() - Install external components
diff --git a/lib/placement b/lib/placement
index 1875857..1d68f8a 100644
--- a/lib/placement
+++ b/lib/placement
@@ -103,14 +103,16 @@
 }
 
 function configure_placement_nova_compute {
-    iniset $NOVA_CONF placement auth_type "password"
-    iniset $NOVA_CONF placement auth_url "$KEYSTONE_SERVICE_URI"
-    iniset $NOVA_CONF placement username placement
-    iniset $NOVA_CONF placement password "$SERVICE_PASSWORD"
-    iniset $NOVA_CONF placement user_domain_name "$SERVICE_DOMAIN_NAME"
-    iniset $NOVA_CONF placement project_name "$SERVICE_TENANT_NAME"
-    iniset $NOVA_CONF placement project_domain_name "$SERVICE_DOMAIN_NAME"
-    iniset $NOVA_CONF placement os_region_name "$REGION_NAME"
+    # Use the provided config file path or default to $NOVA_CONF.
+    local conf=${1:-$NOVA_CONF}
+    iniset $conf placement auth_type "password"
+    iniset $conf placement auth_url "$KEYSTONE_SERVICE_URI"
+    iniset $conf placement username placement
+    iniset $conf placement password "$SERVICE_PASSWORD"
+    iniset $conf placement user_domain_name "$SERVICE_DOMAIN_NAME"
+    iniset $conf placement project_name "$SERVICE_TENANT_NAME"
+    iniset $conf placement project_domain_name "$SERVICE_DOMAIN_NAME"
+    iniset $conf placement os_region_name "$REGION_NAME"
     # TODO(cdent): auth_strategy, which is common to see in these
     # blocks is not currently used here. For the time being the
     # placement api uses the auth_strategy configuration setting
diff --git a/lib/rpc_backend b/lib/rpc_backend
index 44d0717..1c7c82f 100644
--- a/lib/rpc_backend
+++ b/lib/rpc_backend
@@ -52,7 +52,20 @@
     if is_service_enabled rabbit; then
         # Install rabbitmq-server
         install_package rabbitmq-server
-        if is_fedora; then
+        if is_suse; then
+            install_package rabbitmq-server-plugins
+            # the default systemd socket activation only listens on the loopback interface
+            # which causes rabbitmq to try to start its own epmd
+            sudo mkdir -p /etc/systemd/system/epmd.socket.d
+            cat <<EOF | sudo tee /etc/systemd/system/epmd.socket.d/ports.conf >/dev/null
+[Socket]
+ListenStream=
+ListenStream=[::]:4369
+EOF
+            sudo systemctl daemon-reload
+            sudo systemctl restart epmd.socket epmd.service
+        fi
+        if is_fedora || is_suse; then
             sudo systemctl enable rabbitmq-server
         fi
     fi
diff --git a/lib/swift b/lib/swift
index 1601e2b..933af10 100644
--- a/lib/swift
+++ b/lib/swift
@@ -37,6 +37,7 @@
 
 # Set up default directories
 GITDIR["python-swiftclient"]=$DEST/python-swiftclient
+SWIFT_DIR=$DEST/swift
 
 # Swift virtual environment
 if [[ ${USE_VENV} = True ]]; then
@@ -46,8 +47,6 @@
     SWIFT_BIN_DIR=$(get_python_exec_prefix)
 fi
 
-
-SWIFT_DIR=$DEST/swift
 SWIFT_AUTH_CACHE_DIR=${SWIFT_AUTH_CACHE_DIR:-/var/cache/swift}
 SWIFT_APACHE_WSGI_DIR=${SWIFT_APACHE_WSGI_DIR:-/var/www/swift}
 SWIFT3_DIR=$DEST/swift3
@@ -341,7 +340,7 @@
     local user_group
 
     # Make sure to kill all swift processes first
-    swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
+    $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
 
     sudo install -d -o ${STACK_USER} ${SWIFT_CONF_DIR}
     sudo install -d -o ${STACK_USER} ${SWIFT_CONF_DIR}/{object,container,account}-server
@@ -557,7 +556,11 @@
 
     local swift_log_dir=${SWIFT_DATA_DIR}/logs
     sudo rm -rf ${swift_log_dir}
-    sudo install -d -o ${STACK_USER} -g adm ${swift_log_dir}/hourly
+    local swift_log_group=adm
+    if is_suse; then
+        swift_log_group=root
+    fi
+    sudo install -d -o ${STACK_USER} -g ${swift_log_group} ${swift_log_dir}/hourly
 
     if [[ $SYSLOG != "False" ]]; then
         sed "s,%SWIFT_LOGDIR%,${swift_log_dir}," $FILES/swift/rsyslog.conf | sudo \
@@ -700,7 +703,7 @@
 function init_swift {
     local node_number
     # Make sure to kill all swift processes first
-    swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
+    $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
 
     # Forcibly re-create the backing filesystem
     create_swift_disk
@@ -711,9 +714,9 @@
 
         rm -f *.builder *.ring.gz backups/*.builder backups/*.ring.gz
 
-        swift-ring-builder object.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
-        swift-ring-builder container.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
-        swift-ring-builder account.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
+        $SWIFT_BIN_DIR/swift-ring-builder object.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
+        $SWIFT_BIN_DIR/swift-ring-builder container.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
+        $SWIFT_BIN_DIR/swift-ring-builder account.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
 
         # The ring will be created on each node, and because the order of
         # nodes is identical we can use a seed for rebalancing, making it
@@ -724,26 +727,26 @@
             node_number=1
 
             for node in ${SWIFT_STORAGE_IPS}; do
-                swift-ring-builder object.builder add z${node_number}-${node}:${OBJECT_PORT_BASE}/sdb1 1
-                swift-ring-builder container.builder add z${node_number}-${node}:${CONTAINER_PORT_BASE}/sdb1 1
-                swift-ring-builder account.builder add z${node_number}-${node}:${ACCOUNT_PORT_BASE}/sdb1 1
+                $SWIFT_BIN_DIR/swift-ring-builder object.builder add z${node_number}-${node}:${OBJECT_PORT_BASE}/sdb1 1
+                $SWIFT_BIN_DIR/swift-ring-builder container.builder add z${node_number}-${node}:${CONTAINER_PORT_BASE}/sdb1 1
+                $SWIFT_BIN_DIR/swift-ring-builder account.builder add z${node_number}-${node}:${ACCOUNT_PORT_BASE}/sdb1 1
                 let "node_number=node_number+1"
             done
 
         else
 
             for node_number in ${SWIFT_REPLICAS_SEQ}; do
-                swift-ring-builder object.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( OBJECT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
-                swift-ring-builder container.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( CONTAINER_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
-                swift-ring-builder account.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( ACCOUNT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
+                $SWIFT_BIN_DIR/swift-ring-builder object.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( OBJECT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
+                $SWIFT_BIN_DIR/swift-ring-builder container.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( CONTAINER_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
+                $SWIFT_BIN_DIR/swift-ring-builder account.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( ACCOUNT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
             done
         fi
 
         # We use a seed for rebalancing. Doing this allows us to create
         # identical rings on multiple nodes if SWIFT_STORAGE_IPS is the same
-        swift-ring-builder object.builder rebalance 42
-        swift-ring-builder container.builder rebalance 42
-        swift-ring-builder account.builder rebalance 42
+        $SWIFT_BIN_DIR/swift-ring-builder object.builder rebalance 42
+        $SWIFT_BIN_DIR/swift-ring-builder container.builder rebalance 42
+        $SWIFT_BIN_DIR/swift-ring-builder account.builder rebalance 42
     } && popd >/dev/null
 
     # Create cache dir
@@ -799,7 +802,7 @@
         # Apache should serve the "PACO" a.k.a "main" services
         restart_apache_server
         # The rest of the services should be started in backgroud
-        swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
+        $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
         return 0
     fi
 
@@ -823,15 +826,16 @@
         done
 
         if [[ "$SWIFT_START_ALL_SERVICES" == "True" ]]; then
-            swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
+            $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
         else
             # The container-sync daemon is strictly needed to pass the container
             # sync Tempest tests.
-            swift-init --run-dir=${SWIFT_DATA_DIR}/run container-sync start
+            enable_service s-container-sync
+            run_process s-container-sync "$SWIFT_BIN_DIR/swift-container-sync ${SWIFT_CONF_DIR}/container-server/1.conf"
         fi
     else
-        swift-init --run-dir=${SWIFT_DATA_DIR}/run all restart || true
-        swift-init --run-dir=${SWIFT_DATA_DIR}/run proxy stop || true
+        $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all restart || true
+        $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run proxy stop || true
     fi
 
     if is_service_enabled tls-proxy; then
@@ -858,12 +862,12 @@
     local type
 
     if [ "$SWIFT_USE_MOD_WSGI" == "True" ]; then
-        swift-init --run-dir=${SWIFT_DATA_DIR}/run rest stop && return 0
+        $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run rest stop && return 0
     fi
 
     # screen normally killed by ``unstack.sh``
-    if type -p swift-init >/dev/null; then
-        swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
+    if type -p $SWIFT_BIN_DIR/swift-init >/dev/null; then
+        $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
     fi
     # Dump all of the servers
     # Maintain the iteration as stop_process() has some desirable side-effects
diff --git a/lib/tempest b/lib/tempest
index bdbaaa5..e1c1f6c 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -299,6 +299,14 @@
         iniset $TEMPEST_CONFIG identity-feature-enabled domain_specific_drivers True
     fi
 
+    # TODO(felipemonteiro): Remove this once Tempest no longer supports Pike
+    # as this is supported in Queens and beyond.
+    iniset $TEMPEST_CONFIG identity-feature-enabled project_tags True
+
+    # In Queens and later, application credentials are enabled by default
+    # so remove this once Tempest no longer supports Pike.
+    iniset $TEMPEST_CONFIG identity-feature-enabled application_credentials True
+
     # Image
     # We want to be able to override this variable in the gate to avoid
     # doing an external HTTP fetch for this test.
@@ -310,7 +318,6 @@
     fi
 
     # Image Features
-    iniset $TEMPEST_CONFIG image-feature-enabled deactivate_image True
     if [ "$GLANCE_V1_ENABLED" != "True" ]; then
         iniset $TEMPEST_CONFIG image-feature-enabled api_v1 False
     fi
@@ -383,6 +390,10 @@
         fi
     fi
 
+    if [[ $ENABLE_VOLUME_MULTIATTACH == "True" ]]; then
+        iniset $TEMPEST_CONFIG compute-feature-enabled volume_multiattach True
+    fi
+
     if is_service_enabled n-novnc; then
         iniset $TEMPEST_CONFIG compute-feature-enabled vnc_console True
     fi
@@ -551,7 +562,10 @@
     if [[ "$OFFLINE" != "True" ]]; then
         tox -revenv-tempest --notest
     fi
-    tox -evenv-tempest -- pip install -c $REQUIREMENTS_DIR/upper-constraints.txt -r requirements.txt
+
+    # The requirements might be on a different branch, while tempest needs master requirements.
+    (cd $REQUIREMENTS_DIR && git show master:upper-constraints.txt) > u-c-m.txt
+    tox -evenv-tempest -- pip install -c u-c-m.txt -r requirements.txt
 
     # Auth:
     iniset $TEMPEST_CONFIG auth tempest_roles "Member"
@@ -636,7 +650,7 @@
 function install_tempest_plugins {
     pushd $TEMPEST_DIR
     if [[ $TEMPEST_PLUGINS != 0 ]] ; then
-        tox -evenv-tempest -- pip install $TEMPEST_PLUGINS
+        tox -evenv-tempest -- pip install -c $REQUIREMENTS_DIR/upper-constraints.txt $TEMPEST_PLUGINS
         echo "Checking installed Tempest plugins:"
         tox -evenv-tempest -- tempest list-plugins
     fi
diff --git a/lib/tls b/lib/tls
index 0bc389b..a72b708 100644
--- a/lib/tls
+++ b/lib/tls
@@ -340,6 +340,24 @@
     fi
 }
 
+# Deploy the service cert & key to a service specific
+# location
+function deploy_int_cert {
+    local cert_target_file=$1
+    local key_target_file=$2
+
+    sudo cp "$INT_CA_DIR/$DEVSTACK_CERT_NAME.crt" "$cert_target_file"
+    sudo cp "$INT_CA_DIR/private/$DEVSTACK_CERT_NAME.key" "$key_target_file"
+}
+
+# Deploy the intermediate CA cert bundle file to a service
+# specific location
+function deploy_int_CA {
+    local ca_target_file=$1
+
+    sudo cp "$INT_CA_DIR/ca-chain.pem" "$ca_target_file"
+}
+
 # If a non-system python-requests is installed then it will use the
 # built-in CA certificate store rather than the distro-specific
 # CA certificate store. Detect this and symlink to the correct
diff --git a/playbooks/devstack.yaml b/playbooks/devstack.yaml
index ede8382..d090638 100644
--- a/playbooks/devstack.yaml
+++ b/playbooks/devstack.yaml
@@ -1,3 +1,7 @@
 - hosts: all
+  # This is the default strategy, however since orchestrate-devstack requires
+  # "linear", it is safer to enforce it in case this is running in an
+  # environment configured with a different default strategy.
+  strategy: linear
   roles:
-    - run-devstack
+    - orchestrate-devstack
diff --git a/playbooks/post.yaml b/playbooks/post.yaml
index 6f5126f..9e66f20 100644
--- a/playbooks/post.yaml
+++ b/playbooks/post.yaml
@@ -1,4 +1,32 @@
 - hosts: all
+  become: True
+  vars:
+    devstack_log_dir: "{{ devstack_base_dir|default('/opt/stack') }}/logs/"
+    devstack_conf_dir: "{{ devstack_base_dir|default('/opt/stack') }}/devstack/"
+    devstack_full_log: "{{ devstack_early_log|default('/opt/stack/logs/devstack-early.txt') }}"
+  tasks:
+    # NOTE(andreaf) If the tempest service is enabled, a tempest.log is
+    # generated as part of lib/tempest, as a result of verify_tempest_config
+    - name: Check if a tempest log exits
+      stat:
+        path: "{{ devstack_conf_dir }}/tempest.log"
+      register: tempest_log
+    - name: Link post-devstack tempest.log
+      file:
+        src: "{{ devstack_conf_dir }}/tempest.log"
+        dest: "{{ stage_dir }}/verify_tempest_conf.log"
+        state: hard
+      when: tempest_log.stat.exists
   roles:
     - export-devstack-journal
-    - fetch-devstack-log-dir
+    - apache-logs-conf
+    - devstack-project-conf
+    # capture-system-logs should be the last role before stage-output
+    - capture-system-logs
+    - role: stage-output
+    # NOTE(andreaf) We need fetch-devstack-log-dir only as long as the base job
+    # starts pulling logs for us from {{ ansible_user_dir }}/logs.
+    # Meanwhile we already store things in ansible_user_dir and use
+    # fetch-devstack-log-dir setting devstack_base_dir
+    - role: fetch-devstack-log-dir
+      devstack_base_dir: "{{ ansible_user_dir }}"
diff --git a/playbooks/pre.yaml b/playbooks/pre.yaml
index 4d07960..4689a63 100644
--- a/playbooks/pre.yaml
+++ b/playbooks/pre.yaml
@@ -1,5 +1,25 @@
 - hosts: all
+  pre_tasks:
+    - name: Gather minimum local MTU
+      set_fact:
+        local_mtu: >
+          {% set mtus = [] -%}
+          {% for interface in ansible_interfaces -%}
+            {% set interface_variable = 'ansible_' + interface -%}
+            {% if interface_variable in hostvars[inventory_hostname] -%}
+              {% set _ = mtus.append(hostvars[inventory_hostname][interface_variable]['mtu']|int) -%}
+            {% endif -%}
+          {% endfor -%}
+          {{- mtus|min -}}
+    - name: Calculate external_bridge_mtu
+      # 50 bytes is overhead for vxlan (which is greater than GRE
+      # allowing us to use either overlay option with this MTU.
+      # TODO(andreaf) This should work, but it may have to be reconcilied with
+      # the MTU setting used by the multinode setup roles in multinode pre.yaml
+      set_fact:
+        external_bridge_mtu: "{{ local_mtu | int - 50 }}"
   roles:
+    - test-matrix
     - configure-swap
     - setup-stack-user
     - setup-tempest-user
@@ -8,15 +28,3 @@
     - setup-devstack-cache
     - start-fresh-logging
     - write-devstack-local-conf
-  # TODO(jeblair): remove when configure-mirrors is fixed  
-  tasks:
-    - name: Hack mirror_info
-      shell:
-        _raw_params: |
-          mkdir /etc/ci
-          cat << "EOF" > /etc/ci/mirror_info.sh
-          export NODEPOOL_UCA_MIRROR=http://mirror.dfw.rax.openstack.org/ubuntu-cloud-archive
-          EOF
-      args:
-        executable: /bin/bash
-      become: true
diff --git a/playbooks/tox/post.yaml b/playbooks/tox/post.yaml
new file mode 100644
index 0000000..7f0cb19
--- /dev/null
+++ b/playbooks/tox/post.yaml
@@ -0,0 +1,4 @@
+- hosts: all
+  roles:
+    - fetch-tox-output
+    - fetch-subunit-output
diff --git a/playbooks/tox/pre.yaml b/playbooks/tox/pre.yaml
new file mode 100644
index 0000000..d7e4670
--- /dev/null
+++ b/playbooks/tox/pre.yaml
@@ -0,0 +1,8 @@
+- hosts: all
+  roles:
+    # Run bindep and test-setup after devstack so that they won't interfere
+    - role: bindep
+      bindep_profile: test
+      bindep_dir: "{{ zuul_work_dir }}"
+    - test-setup
+    - ensure-tox
diff --git a/playbooks/tox/run-both.yaml b/playbooks/tox/run-both.yaml
new file mode 100644
index 0000000..e85c2ee
--- /dev/null
+++ b/playbooks/tox/run-both.yaml
@@ -0,0 +1,10 @@
+- hosts: all
+  roles:
+    - run-devstack
+    # Run bindep and test-setup after devstack so that they won't interfere
+    - role: bindep
+      bindep_profile: test
+      bindep_dir: "{{ zuul_work_dir }}"
+    - test-setup
+    - ensure-tox
+    - tox
diff --git a/playbooks/tox/run.yaml b/playbooks/tox/run.yaml
new file mode 100644
index 0000000..22f8209
--- /dev/null
+++ b/playbooks/tox/run.yaml
@@ -0,0 +1,3 @@
+- hosts: all
+  roles:
+    - tox
diff --git a/playbooks/unit-tests/pre.yaml b/playbooks/unit-tests/pre.yaml
new file mode 100644
index 0000000..cfa1676
--- /dev/null
+++ b/playbooks/unit-tests/pre.yaml
@@ -0,0 +1,13 @@
+- hosts: all
+
+  tasks:
+
+    - name: Install prerequisites
+      shell:
+        chdir: '{{ zuul.project.src_dir }}'
+        executable: /bin/bash
+        cmd: |
+          set -e
+          set -x
+          echo "IPV4_ADDRS_SAFE_TO_USE=10.1.0.0/20" >> localrc
+          ./tools/install_prereqs.sh
diff --git a/playbooks/unit-tests/run.yaml b/playbooks/unit-tests/run.yaml
new file mode 100644
index 0000000..181521f
--- /dev/null
+++ b/playbooks/unit-tests/run.yaml
@@ -0,0 +1,12 @@
+- hosts: all
+
+  tasks:
+
+    - name: Run run_tests.sh
+      shell:
+        chdir: '{{ zuul.project.src_dir }}'
+        executable: /bin/bash
+        cmd: |
+          set -e
+          set -x
+          ./run_tests.sh
diff --git a/roles/apache-logs-conf/README.rst b/roles/apache-logs-conf/README.rst
new file mode 100644
index 0000000..eccee40
--- /dev/null
+++ b/roles/apache-logs-conf/README.rst
@@ -0,0 +1,12 @@
+Prepare apache configs and logs for staging
+
+Make sure apache config files and log files are available in a linux flavor
+independent location. Note that this relies on hard links, to the staging
+directory must be in the same partition where the logs and configs are.
+
+**Role Variables**
+
+.. zuul:rolevar:: stage_dir
+   :default: {{ ansible_user_dir }}
+
+   The base stage directory.
diff --git a/roles/apache-logs-conf/defaults/main.yaml b/roles/apache-logs-conf/defaults/main.yaml
new file mode 100644
index 0000000..1fb04fe
--- /dev/null
+++ b/roles/apache-logs-conf/defaults/main.yaml
@@ -0,0 +1,2 @@
+devstack_base_dir: /opt/stack
+stage_dir: "{{ ansible_user_dir }}"
diff --git a/roles/apache-logs-conf/tasks/main.yaml b/roles/apache-logs-conf/tasks/main.yaml
new file mode 100644
index 0000000..bd64574
--- /dev/null
+++ b/roles/apache-logs-conf/tasks/main.yaml
@@ -0,0 +1,89 @@
+- name: Ensure {{ stage_dir }}/apache exists
+  file:
+    path: "{{ stage_dir }}/apache"
+    state: directory
+
+- name: Link apache logs on Debian/SuSE
+  block:
+  - name: Find logs
+    find:
+      path: "/var/log/apache2"
+      file_type: any
+    register: debian_suse_apache_logs
+
+  - name: Dereference files
+    stat:
+      path: "{{ item.path }}"
+    with_items: "{{ debian_suse_apache_logs.files }}"
+    register: debian_suse_apache_deref_logs
+
+  - name: Create hard links
+    file:
+      src: "{{ item.stat.lnk_source | default(item.stat.path) }}"
+      dest: "{{ stage_dir }}/apache/{{ item.stat.path | basename }}"
+      state: hard
+    with_items: "{{ debian_suse_apache_deref_logs.results }}"
+    when:
+      - item.stat.isreg or item.stat.islnk
+  when: ansible_os_family in ('Debian', 'Suse')
+  no_log: true
+
+- name: Link apache logs on RedHat
+  block:
+  - name: Find logs
+    find:
+      path: "/var/log/httpd"
+      file_type: any
+    register: redhat_apache_logs
+
+  - name: Dereference files
+    stat:
+      path: "{{ item.path }}"
+    with_items: "{{ redhat_apache_logs.files }}"
+    register: redhat_apache_deref_logs
+
+  - name: Create hard links
+    file:
+      src: "{{ item.stat.lnk_source | default(item.stat.path) }}"
+      dest: "{{ stage_dir }}/apache/{{ item.stat.path | basename }}"
+      state: hard
+    with_items: "{{ redhat_apache_deref_logs.results }}"
+    when:
+      - item.stat.isreg or item.stat.islnk
+  when: ansible_os_family == 'RedHat'
+  no_log: true
+
+- name: Ensure {{ stage_dir }}/apache_config apache_config exists
+  file:
+    path: "{{ stage_dir }}/apache_config"
+    state: directory
+
+- name: Define config paths
+  set_fact:
+    apache_config_paths:
+      'Debian': '/etc/apache2/sites-enabled/'
+      'Suse': '/etc/apache2/conf.d/'
+      'RedHat': '/etc/httpd/conf.d/'
+
+- name: Discover configurations
+  find:
+    path: "{{ apache_config_paths[ansible_os_family] }}"
+    file_type: any
+  register: apache_configs
+  no_log: true
+
+- name: Dereference configurations
+  stat:
+    path: "{{ item.path }}"
+  with_items: "{{ apache_configs.files }}"
+  register: apache_configs_deref
+  no_log: true
+
+- name: Link configurations
+  file:
+    src: "{{ item.stat.lnk_source | default(item.stat.path) }}"
+    dest: "{{ stage_dir }}/apache_config/{{ item.stat.path | basename }}"
+    state: hard
+  with_items: "{{ apache_configs_deref.results }}"
+  when: item.stat.isreg or item.stat.islnk
+  no_log: true
diff --git a/roles/capture-system-logs/README.rst b/roles/capture-system-logs/README.rst
new file mode 100644
index 0000000..c284124
--- /dev/null
+++ b/roles/capture-system-logs/README.rst
@@ -0,0 +1,20 @@
+Stage a number of system type logs
+
+Stage a number of different logs / reports:
+- snapshot of iptables
+- disk space available
+- pip[2|3] freeze
+- installed packages (dpkg/rpm)
+- ceph, openswitch, gluster
+- coredumps
+- dns resolver
+- listen53
+- unbound.log
+- deprecation messages
+
+**Role Variables**
+
+.. zuul:rolevar:: stage_dir
+   :default: {{ ansible_user_dir }}
+
+   The base stage directory.
diff --git a/roles/capture-system-logs/defaults/main.yaml b/roles/capture-system-logs/defaults/main.yaml
new file mode 100644
index 0000000..fea05c8
--- /dev/null
+++ b/roles/capture-system-logs/defaults/main.yaml
@@ -0,0 +1 @@
+devstack_base_dir: /opt/stack
diff --git a/roles/capture-system-logs/tasks/main.yaml b/roles/capture-system-logs/tasks/main.yaml
new file mode 100644
index 0000000..de4f8ed
--- /dev/null
+++ b/roles/capture-system-logs/tasks/main.yaml
@@ -0,0 +1,39 @@
+# TODO(andreaf) Make this into proper Ansible
+- name: Stage various logs and reports
+  shell:
+    executable: /bin/bash
+    cmd: |
+      sudo iptables-save > {{ stage_dir }}/iptables.txt
+      df -h > {{ stage_dir }}/df.txt
+
+      for py_ver in 2 3; do
+          if [[ `which python${py_ver}` ]]; then
+              python${py_ver} -m pip freeze > {{ stage_dir }}/pip${py_ver}-freeze.txt
+          fi
+      done
+
+      if [ `command -v dpkg` ]; then
+          dpkg -l> {{ stage_dir }}/dpkg-l.txt
+      fi
+      if [ `command -v rpm` ]; then
+          rpm -qa | sort > {{ stage_dir }}/rpm-qa.txt
+      fi
+
+      # gzip and save any coredumps in /var/core
+      if [ -d /var/core ]; then
+          sudo gzip -r /var/core
+          sudo cp -r /var/core {{ stage_dir }}/
+      fi
+
+      sudo ss -lntup | grep ':53' > {{ stage_dir }}/listen53.txt
+
+      # NOTE(andreaf) Service logs are already in logs/ thanks for the
+      # export-devstack-journal log. Apache logs are under apache/ thans to the
+      # apache-logs-conf role.
+      grep -i deprecat {{ stage_dir }}/logs/*.txt {{ stage_dir }}/apache/*.log | \
+          sed -r 's/[0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\.[0-9]{1,3}/ /g' | \
+          sed -r 's/[0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}/ /g' | \
+          sed -r 's/[0-9]{1,4}-[0-9]{1,2}-[0-9]{1,4}/ /g' |
+          sed -r 's/\[.*\]/ /g' | \
+          sed -r 's/\s[0-9]+\s/ /g' | \
+          awk '{if ($0 in seen) {seen[$0]++} else {out[++n]=$0;seen[$0]=1}} END { for (i=1; i<=n; i++) print seen[out[i]]" :: " out[i] }' > {{ stage_dir }}/deprecations.log
diff --git a/roles/devstack-project-conf/README.rst b/roles/devstack-project-conf/README.rst
new file mode 100644
index 0000000..3f2d4c9
--- /dev/null
+++ b/roles/devstack-project-conf/README.rst
@@ -0,0 +1,11 @@
+Prepare OpenStack project configurations for staging
+
+Prepare all relevant config files for staging.
+This is helpful to avoid staging the entire /etc.
+
+**Role Variables**
+
+.. zuul:rolevar:: stage_dir
+   :default: {{ ansible_user_dir }}
+
+   The base stage directory.
diff --git a/roles/devstack-project-conf/defaults/main.yaml b/roles/devstack-project-conf/defaults/main.yaml
new file mode 100644
index 0000000..f8fb8de
--- /dev/null
+++ b/roles/devstack-project-conf/defaults/main.yaml
@@ -0,0 +1 @@
+stage_dir: "{{ ansible_user_dir }}"
diff --git a/roles/devstack-project-conf/tasks/main.yaml b/roles/devstack-project-conf/tasks/main.yaml
new file mode 100644
index 0000000..917cdbc
--- /dev/null
+++ b/roles/devstack-project-conf/tasks/main.yaml
@@ -0,0 +1,25 @@
+- name: Ensure {{ stage_dir }}/etc exists
+  file:
+    path: "{{ stage_dir }}/etc"
+    state: directory
+
+- name: Check which projects have a config folder
+  stat:
+    path: "/etc/{{ item.value.short_name }}"
+  with_dict: "{{ zuul.projects }}"
+  register: project_configs
+  no_log: true
+
+- name: Copy configuration files
+  command: cp -pRL {{ item.stat.path }} {{ stage_dir }}/etc/{{ item.item.value.short_name }}
+  when: item.stat.exists
+  with_items: "{{ project_configs.results }}"
+
+- name: Check if openstack has a config folder
+  stat:
+    path: "/etc/openstack"
+  register: openstack_configs
+
+- name: Copy configuration files
+  command: cp -pRL /etc/openstack {{ stage_dir }}/etc/
+  when: openstack_configs.stat.exists
diff --git a/roles/export-devstack-journal/README.rst b/roles/export-devstack-journal/README.rst
index 5f00592..a34e070 100644
--- a/roles/export-devstack-journal/README.rst
+++ b/roles/export-devstack-journal/README.rst
@@ -5,11 +5,17 @@
 kernal and sudo messages.
 
 Writes the output to the ``logs/`` subdirectory of
-``devstack_base_dir``.
+``stage_dir``.
 
 **Role Variables**
 
 .. zuul:rolevar:: devstack_base_dir
    :default: /opt/stack
 
-   The devstack base directory.
+   The devstack base directory. This is used to obtain the
+   ``log-start-timestamp.txt``, used to filter the systemd journal.
+
+.. zuul:rolevar:: stage_dir
+   :default: {{ ansible_user_dir }}
+
+   The base stage directory.
diff --git a/roles/export-devstack-journal/defaults/main.yaml b/roles/export-devstack-journal/defaults/main.yaml
index fea05c8..1fb04fe 100644
--- a/roles/export-devstack-journal/defaults/main.yaml
+++ b/roles/export-devstack-journal/defaults/main.yaml
@@ -1 +1,2 @@
 devstack_base_dir: /opt/stack
+stage_dir: "{{ ansible_user_dir }}"
diff --git a/roles/export-devstack-journal/tasks/main.yaml b/roles/export-devstack-journal/tasks/main.yaml
index b9af02a..6e760c1 100644
--- a/roles/export-devstack-journal/tasks/main.yaml
+++ b/roles/export-devstack-journal/tasks/main.yaml
@@ -1,3 +1,11 @@
+# NOTE(andreaf) This bypasses the stage-output role
+- name: Ensure {{ stage_dir }}/logs exists
+  become: true
+  file:
+    path: "{{ stage_dir }}/logs"
+    state: directory
+    owner: "{{ ansible_user }}"
+
 # TODO: convert this to ansible
 - name: Export journal files
   become: true
@@ -7,7 +15,7 @@
       name=""
       for u in `systemctl list-unit-files | grep devstack | awk '{print $1}'`; do
         name=$(echo $u | sed 's/devstack@/screen-/' | sed 's/\.service//')
-        journalctl -o short-precise --unit $u | tee {{ devstack_base_dir }}/logs/$name.txt > /dev/null
+        journalctl -o short-precise --unit $u | gzip - > {{ stage_dir }}/logs/$name.txt.gz
       done
 
       # Export the journal in export format to make it downloadable
@@ -16,7 +24,7 @@
       # debugging much easier. We don't do the native conversion here as
       # some distros do not package that tooling.
       journalctl -u 'devstack@*' -o export | \
-          xz --threads=0 - > {{ devstack_base_dir }}/logs/devstack.journal.xz
+          xz --threads=0 - > {{ stage_dir }}/logs/devstack.journal.xz
 
       # The journal contains everything running under systemd, we'll
       # build an old school version of the syslog with just the
@@ -26,4 +34,4 @@
           -t sudo \
           --no-pager \
           --since="$(cat {{ devstack_base_dir }}/log-start-timestamp.txt)" \
-        | tee {{ devstack_base_dir }}/logs/syslog.txt > /dev/null
+        | gzip - > {{ stage_dir }}/logs/syslog.txt.gz
diff --git a/roles/orchestrate-devstack/README.rst b/roles/orchestrate-devstack/README.rst
new file mode 100644
index 0000000..097dcea
--- /dev/null
+++ b/roles/orchestrate-devstack/README.rst
@@ -0,0 +1,25 @@
+Orchestrate a devstack
+
+Runs devstack in a multinode scenario, with one controller node
+and a group of subnodes.
+
+The reason for this role is so that jobs in other repository may
+run devstack in their plays with no need for re-implementing the
+orchestration logic.
+
+The "run-devstack" role is available to run devstack with no
+orchestration.
+
+This role sets up the controller and CA first, it then pushes CA
+data to sub-nodes and run devstack there. The only requirement for
+this role is for the controller inventory_hostname to be "controller"
+and for all sub-nodes to be defined in a group called "subnode".
+
+This role needs to be invoked from a playbook that uses a "linear" strategy.
+
+**Role Variables**
+
+.. zuul:rolevar:: devstack_base_dir
+   :default: /opt/stack
+
+   The devstack base directory.
diff --git a/roles/orchestrate-devstack/defaults/main.yaml b/roles/orchestrate-devstack/defaults/main.yaml
new file mode 100644
index 0000000..fea05c8
--- /dev/null
+++ b/roles/orchestrate-devstack/defaults/main.yaml
@@ -0,0 +1 @@
+devstack_base_dir: /opt/stack
diff --git a/roles/orchestrate-devstack/tasks/main.yaml b/roles/orchestrate-devstack/tasks/main.yaml
new file mode 100644
index 0000000..12db58c
--- /dev/null
+++ b/roles/orchestrate-devstack/tasks/main.yaml
@@ -0,0 +1,38 @@
+- name: Run devstack on the controller
+  include_role:
+    name: run-devstack
+  when: inventory_hostname == 'controller'
+
+- name: Setup devstack on sub-nodes
+  block:
+
+  - name: Sync CA data to subnodes (when any)
+    # Only do this if the tls-proxy service is defined and enabled
+    include_role:
+      name: sync-devstack-data
+    when: devstack_services['tls-proxy']|default(false)
+
+  - name: Run devstack on the sub-nodes
+    include_role:
+      name: run-devstack
+    when: inventory_hostname in groups['subnode']
+
+  - name: Discover hosts
+    # Discovers compute nodes (subnodes) and maps them to cells. Only run
+    # on the controller node.
+    # NOTE(mriedem): We want to remove this if/when nova supports
+    # auto-registration of computes with cells, but that's not happening in
+    # Ocata.
+    # NOTE(andreaf) This is taken (NOTE included) from the discover_hosts
+    # function in devstack gate. Since this is now in devstack, which is
+    # branched, we know that the discover_hosts tool exists.
+    become: true
+    become_user: stack
+    shell: ./tools/discover_hosts.sh
+    args:
+      chdir: "{{ devstack_base_dir }}/devstack"
+    when: inventory_hostname == 'controller'
+
+  when:
+    - '"controller" in hostvars'
+    - '"subnode" in groups'
diff --git a/roles/run-devstack/tasks/main.yaml b/roles/run-devstack/tasks/main.yaml
index bafebaf..f58b31d 100644
--- a/roles/run-devstack/tasks/main.yaml
+++ b/roles/run-devstack/tasks/main.yaml
@@ -1,5 +1,10 @@
 - name: Run devstack
-  command: ./stack.sh
+  shell:
+    cmd: |
+      ./stack.sh 2>&1
+      rc=$?
+      echo "*** FINISHED ***"
+      exit $rc
   args:
     chdir: "{{devstack_base_dir}}/devstack"
   become: true
diff --git a/roles/setup-stack-user/tasks/main.yaml b/roles/setup-stack-user/tasks/main.yaml
index 8384515..0fc7c2d 100644
--- a/roles/setup-stack-user/tasks/main.yaml
+++ b/roles/setup-stack-user/tasks/main.yaml
@@ -21,10 +21,12 @@
     group: stack
   become: yes
 
-- name: Set stack user home directory permissions
+- name: Set stack user home directory permissions and ownership
   file:
     path: '{{ devstack_stack_home_dir }}'
     mode: 0755
+    owner: stack
+    group: stack
   become: yes
 
 - name: Copy 50_stack_sh file to /etc/sudoers.d
@@ -36,7 +38,7 @@
     group: root
   become: yes
 
-- name: Create new/.cache folder within BASE
+- name: Create .cache folder within BASE
   file:
     path: '{{ devstack_stack_home_dir }}/.cache'
     state: directory
diff --git a/roles/sync-devstack-data/README.rst b/roles/sync-devstack-data/README.rst
new file mode 100644
index 0000000..500e8cc
--- /dev/null
+++ b/roles/sync-devstack-data/README.rst
@@ -0,0 +1,12 @@
+Sync devstack data for multinode configurations
+
+Sync any data files which include certificates to be used if TLS is enabled.
+This role must be executed on the controller and it pushes data to all
+subnodes.
+
+**Role Variables**
+
+.. zuul:rolevar:: devstack_base_dir
+   :default: /opt/stack
+
+   The devstack base directory.
diff --git a/roles/sync-devstack-data/defaults/main.yaml b/roles/sync-devstack-data/defaults/main.yaml
new file mode 100644
index 0000000..fea05c8
--- /dev/null
+++ b/roles/sync-devstack-data/defaults/main.yaml
@@ -0,0 +1 @@
+devstack_base_dir: /opt/stack
diff --git a/roles/sync-devstack-data/tasks/main.yaml b/roles/sync-devstack-data/tasks/main.yaml
new file mode 100644
index 0000000..4600015
--- /dev/null
+++ b/roles/sync-devstack-data/tasks/main.yaml
@@ -0,0 +1,48 @@
+- name: Ensure the data folder exists
+  become: true
+  file:
+    path: "{{ devstack_base_dir }}/data"
+    state: directory
+    owner: stack
+    group: stack
+    mode: 0755
+  when: 'inventory_hostname in groups["subnode"]|default([])'
+
+- name: Ensure the CA folder exists
+  become: true
+  file:
+    path: "{{ devstack_base_dir }}/data/CA"
+    state: directory
+    owner: stack
+    group: stack
+    mode: 0755
+  when: 'inventory_hostname in groups["subnode"]|default([])'
+
+- name: Pull the CA certificate and folder
+  become: true
+  synchronize:
+    src: "{{ item }}"
+    dest: "{{ zuul.executor.work_root }}/{{ item | basename }}"
+    mode: pull
+  with_items:
+    - "{{ devstack_base_dir }}/data/ca-bundle.pem"
+    - "{{ devstack_base_dir }}/data/CA"
+  when: inventory_hostname == 'controller'
+
+- name: Push the CA certificate
+  become: true
+  become_user: stack
+  synchronize:
+    src: "{{ zuul.executor.work_root }}/ca-bundle.pem"
+    dest: "{{ devstack_base_dir }}/data/ca-bundle.pem"
+    mode: push
+  when: 'inventory_hostname in groups["subnode"]|default([])'
+
+- name: Push the CA folder
+  become: true
+  become_user: stack
+  synchronize:
+    src: "{{ zuul.executor.work_root }}/CA/"
+    dest: "{{ devstack_base_dir }}/data/"
+    mode: push
+  when: 'inventory_hostname in groups["subnode"]|default([])'
diff --git a/roles/write-devstack-local-conf/README.rst b/roles/write-devstack-local-conf/README.rst
index e30dfa1..bfce9c9 100644
--- a/roles/write-devstack-local-conf/README.rst
+++ b/roles/write-devstack-local-conf/README.rst
@@ -20,6 +20,14 @@
    bash shell variables, and will be ordered so that variables used by
    later entries appear first.
 
+   As a special case, the variable ``LIBS_FROM_GIT`` will be
+   constructed automatically from the projects which appear in the
+   ``required-projects`` list defined by the job.  To instruct
+   devstack to install a library from source rather than pypi, simply
+   add that library to the job's ``required-projects`` list.  To
+   override the automatically-generated value, set ``LIBS_FROM_GIT``
+   in ``devstack_localrc`` to the desired value.
+
 .. zuul:rolevar:: devstack_local_conf
    :type: dict
 
@@ -47,13 +55,27 @@
             This is a dictionary of key-value pairs which comprise
             this section of the INI file.
 
+.. zuul:rolevar:: devstack_base_services
+   :type: list
+   :default: {{ base_services | default(omit) }}
+
+   A list of base services which are enabled. Services can be added or removed
+   from this list via the ``devstack_services`` variable. This is ignored if
+   ``base`` is set to ``False`` in ``devstack_services``.
+
 .. zuul:rolevar:: devstack_services
    :type: dict
 
    A dictionary mapping service names to boolean values.  If the
    boolean value is ``false``, a ``disable_service`` line will be
    emitted for the service name.  If it is ``true``, then
-   ``enable_service`` will be emitted.  All other values are ignored.
+   ``enable_service`` will be emitted. All other values are ignored.
+
+   The special key ``base`` can be used to enable or disable the base set of
+   services enabled by default. If ``base`` is found, it will processed before
+   all other keys. If its value is ``False`` a ``disable_all_services`` will be
+   emitted; if its value is ``True`` services from ``devstack_base_services``
+   will be emitted via ``ENABLED_SERVICES``.
 
 .. zuul:rolevar:: devstack_plugins
    :type: dict
@@ -61,3 +83,7 @@
    A dictionary mapping a plugin name to a git repo location.  If the
    location is a non-empty string, then an ``enable_plugin`` line will
    be emmitted for the plugin name.
+
+   If a plugin declares a dependency on another plugin (via
+   ``plugin_requires`` in the plugin's settings file), this role will
+   automatically emit ``enable_plugin`` lines in the correct order.
diff --git a/roles/write-devstack-local-conf/defaults/main.yaml b/roles/write-devstack-local-conf/defaults/main.yaml
index 491fa0f..7bc1dec 100644
--- a/roles/write-devstack-local-conf/defaults/main.yaml
+++ b/roles/write-devstack-local-conf/defaults/main.yaml
@@ -1,2 +1,3 @@
 devstack_base_dir: /opt/stack
 devstack_local_conf_path: "{{ devstack_base_dir }}/devstack/local.conf"
+devstack_base_services: "{{ enabled_services | default(omit) }}"
diff --git a/roles/write-devstack-local-conf/library/devstack_local_conf.py b/roles/write-devstack-local-conf/library/devstack_local_conf.py
index 4134beb..9728fef 100644
--- a/roles/write-devstack-local-conf/library/devstack_local_conf.py
+++ b/roles/write-devstack-local-conf/library/devstack_local_conf.py
@@ -14,16 +14,69 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
 import re
 
 
-class VarGraph(object):
+class DependencyGraph(object):
     # This is based on the JobGraph from Zuul.
 
+    def __init__(self):
+        self._names = set()
+        self._dependencies = {}  # dependent_name -> set(parent_names)
+
+    def add(self, name, dependencies):
+        # Append the dependency information
+        self._dependencies.setdefault(name, set())
+        try:
+            for dependency in dependencies:
+                # Make sure a circular dependency is never created
+                ancestors = self._getParentNamesRecursively(
+                    dependency, soft=True)
+                ancestors.add(dependency)
+                if name in ancestors:
+                    raise Exception("Dependency cycle detected in {}".
+                                    format(name))
+                self._dependencies[name].add(dependency)
+        except Exception:
+            del self._dependencies[name]
+            raise
+
+    def getDependenciesRecursively(self, parent):
+        dependencies = []
+
+        current_dependencies = self._dependencies[parent]
+        for current in current_dependencies:
+            if current not in dependencies:
+                dependencies.append(current)
+            for dep in self.getDependenciesRecursively(current):
+                if dep not in dependencies:
+                    dependencies.append(dep)
+        return dependencies
+
+    def _getParentNamesRecursively(self, dependent, soft=False):
+        all_parent_items = set()
+        items_to_iterate = set([dependent])
+        while len(items_to_iterate) > 0:
+            current_item = items_to_iterate.pop()
+            current_parent_items = self._dependencies.get(current_item)
+            if current_parent_items is None:
+                if soft:
+                    current_parent_items = set()
+                else:
+                    raise Exception("Dependent item {} not found: ".format(
+                                    dependent))
+            new_parent_items = current_parent_items - all_parent_items
+            items_to_iterate |= new_parent_items
+            all_parent_items |= new_parent_items
+        return all_parent_items
+
+
+class VarGraph(DependencyGraph):
     def __init__(self, vars):
+        super(VarGraph, self).__init__()
         self.vars = {}
         self._varnames = set()
-        self._dependencies = {}  # dependent_var_name -> set(parent_var_names)
         for k, v in vars.items():
             self._varnames.add(k)
         for k, v in vars.items():
@@ -38,28 +91,21 @@
             raise Exception("Variable {} already added".format(key))
         self.vars[key] = value
         # Append the dependency information
-        self._dependencies.setdefault(key, set())
+        dependencies = set()
+        for dependency in self.getDependencies(value):
+            if dependency == key:
+                # A variable is allowed to reference itself; no
+                # dependency link needed in that case.
+                continue
+            if dependency not in self._varnames:
+                # It's not necessary to create a link for an
+                # external variable.
+                continue
+            dependencies.add(dependency)
         try:
-            for dependency in self.getDependencies(value):
-                if dependency == key:
-                    # A variable is allowed to reference itself; no
-                    # dependency link needed in that case.
-                    continue
-                if dependency not in self._varnames:
-                    # It's not necessary to create a link for an
-                    # external variable.
-                    continue
-                # Make sure a circular dependency is never created
-                ancestor_vars = self._getParentVarNamesRecursively(
-                    dependency, soft=True)
-                ancestor_vars.add(dependency)
-                if any((key == anc_var) for anc_var in ancestor_vars):
-                    raise Exception("Dependency cycle detected in var {}".
-                                    format(key))
-                self._dependencies[key].add(dependency)
+            self.add(key, dependencies)
         except Exception:
             del self.vars[key]
-            del self._dependencies[key]
             raise
 
     def getVars(self):
@@ -67,63 +113,127 @@
         keys = sorted(self.vars.keys())
         seen = set()
         for key in keys:
-            dependencies = self.getDependentVarsRecursively(key)
+            dependencies = self.getDependenciesRecursively(key)
             for var in dependencies + [key]:
                 if var not in seen:
                     ret.append((var, self.vars[var]))
                     seen.add(var)
         return ret
 
-    def getDependentVarsRecursively(self, parent_var):
-        dependent_vars = []
 
-        current_dependent_vars = self._dependencies[parent_var]
-        for current_var in current_dependent_vars:
-            if current_var not in dependent_vars:
-                dependent_vars.append(current_var)
-            for dep in self.getDependentVarsRecursively(current_var):
-                if dep not in dependent_vars:
-                    dependent_vars.append(dep)
-        return dependent_vars
+class PluginGraph(DependencyGraph):
+    def __init__(self, base_dir, plugins):
+        super(PluginGraph, self).__init__()
+        # The dependency trees expressed by all the plugins we found
+        # (which may be more than those the job is using).
+        self._plugin_dependencies = {}
+        self.loadPluginNames(base_dir)
 
-    def _getParentVarNamesRecursively(self, dependent_var, soft=False):
-        all_parent_vars = set()
-        vars_to_iterate = set([dependent_var])
-        while len(vars_to_iterate) > 0:
-            current_var = vars_to_iterate.pop()
-            current_parent_vars = self._dependencies.get(current_var)
-            if current_parent_vars is None:
-                if soft:
-                    current_parent_vars = set()
-                else:
-                    raise Exception("Dependent var {} not found: ".format(
-                                    dependent_var))
-            new_parent_vars = current_parent_vars - all_parent_vars
-            vars_to_iterate |= new_parent_vars
-            all_parent_vars |= new_parent_vars
-        return all_parent_vars
+        self.plugins = {}
+        self._pluginnames = set()
+        for k, v in plugins.items():
+            self._pluginnames.add(k)
+        for k, v in plugins.items():
+            self._addPlugin(k, str(v))
+
+    def loadPluginNames(self, base_dir):
+        if base_dir is None:
+            return
+        git_roots = []
+        for root, dirs, files in os.walk(base_dir):
+            if '.git' not in dirs:
+                continue
+            # Don't go deeper than git roots
+            dirs[:] = []
+            git_roots.append(root)
+        for root in git_roots:
+            devstack = os.path.join(root, 'devstack')
+            if not (os.path.exists(devstack) and os.path.isdir(devstack)):
+                continue
+            settings = os.path.join(devstack, 'settings')
+            if not (os.path.exists(settings) and os.path.isfile(settings)):
+                continue
+            self.loadDevstackPluginInfo(settings)
+
+    define_re = re.compile(r'^define_plugin\s+(\w+).*')
+    require_re = re.compile(r'^plugin_requires\s+(\w+)\s+(\w+).*')
+    def loadDevstackPluginInfo(self, fn):
+        name = None
+        reqs = set()
+        with open(fn) as f:
+            for line in f:
+                m = self.define_re.match(line)
+                if m:
+                    name = m.group(1)
+                m = self.require_re.match(line)
+                if m:
+                    if name == m.group(1):
+                        reqs.add(m.group(2))
+        if name and reqs:
+            self._plugin_dependencies[name] = reqs
+
+    def getDependencies(self, value):
+        return self._plugin_dependencies.get(value, [])
+
+    def _addPlugin(self, key, value):
+        if key in self.plugins:
+            raise Exception("Plugin {} already added".format(key))
+        self.plugins[key] = value
+        # Append the dependency information
+        dependencies = set()
+        for dependency in self.getDependencies(key):
+            if dependency == key:
+                continue
+            dependencies.add(dependency)
+        try:
+            self.add(key, dependencies)
+        except Exception:
+            del self.plugins[key]
+            raise
+
+    def getPlugins(self):
+        ret = []
+        keys = sorted(self.plugins.keys())
+        seen = set()
+        for key in keys:
+            dependencies = self.getDependenciesRecursively(key)
+            for plugin in dependencies + [key]:
+                if plugin not in seen:
+                    ret.append((plugin, self.plugins[plugin]))
+                    seen.add(plugin)
+        return ret
 
 
 class LocalConf(object):
 
-    def __init__(self, localrc, localconf, services, plugins):
+    def __init__(self, localrc, localconf, base_services, services, plugins,
+                 base_dir, projects):
         self.localrc = []
         self.meta_sections = {}
+        self.plugin_deps = {}
+        self.base_dir = base_dir
+        self.projects = projects
         if plugins:
             self.handle_plugins(plugins)
-        if services:
-            self.handle_services(services)
-        if localrc:
-            self.handle_localrc(localrc)
+        if services or base_services:
+            self.handle_services(base_services, services or {})
+        self.handle_localrc(localrc)
         if localconf:
             self.handle_localconf(localconf)
 
     def handle_plugins(self, plugins):
-        for k, v in plugins.items():
+        pg = PluginGraph(self.base_dir, plugins)
+        for k, v in pg.getPlugins():
             if v:
                 self.localrc.append('enable_plugin {} {}'.format(k, v))
 
-    def handle_services(self, services):
+    def handle_services(self, base_services, services):
+        enable_base_services = services.pop('base', True)
+        if enable_base_services and base_services:
+            self.localrc.append('ENABLED_SERVICES={}'.format(
+                ",".join(base_services)))
+        else:
+            self.localrc.append('disable_all_services')
         for k, v in services.items():
             if v is False:
                 self.localrc.append('disable_service {}'.format(k))
@@ -131,9 +241,22 @@
                 self.localrc.append('enable_service {}'.format(k))
 
     def handle_localrc(self, localrc):
-        vg = VarGraph(localrc)
-        for k, v in vg.getVars():
-            self.localrc.append('{}={}'.format(k, v))
+        lfg = False
+        if localrc:
+            vg = VarGraph(localrc)
+            for k, v in vg.getVars():
+                self.localrc.append('{}={}'.format(k, v))
+                if k == 'LIBS_FROM_GIT':
+                    lfg = True
+
+        if not lfg and self.projects:
+            required_projects = []
+            for project_name, project_info in self.projects.items():
+                if project_info.get('required'):
+                    required_projects.append(project_info['short_name'])
+            if required_projects:
+                self.localrc.append('LIBS_FROM_GIT={}'.format(
+                    ','.join(required_projects)))
 
     def handle_localconf(self, localconf):
         for phase, phase_data in localconf.items():
@@ -161,25 +284,34 @@
     module = AnsibleModule(
         argument_spec=dict(
             plugins=dict(type='dict'),
+            base_services=dict(type='list'),
             services=dict(type='dict'),
             localrc=dict(type='dict'),
             local_conf=dict(type='dict'),
+            base_dir=dict(type='path'),
             path=dict(type='str'),
+            projects=dict(type='dict'),
         )
     )
 
     p = module.params
     lc = LocalConf(p.get('localrc'),
                    p.get('local_conf'),
+                   p.get('base_services'),
                    p.get('services'),
-                   p.get('plugins'))
+                   p.get('plugins'),
+                   p.get('base_dir'),
+                   p.get('projects'))
     lc.write(p['path'])
 
     module.exit_json()
 
 
-from ansible.module_utils.basic import *  # noqa
-from ansible.module_utils.basic import AnsibleModule
+try:
+    from ansible.module_utils.basic import *  # noqa
+    from ansible.module_utils.basic import AnsibleModule
+except ImportError:
+    pass
 
 if __name__ == '__main__':
     main()
diff --git a/roles/write-devstack-local-conf/library/test.py b/roles/write-devstack-local-conf/library/test.py
new file mode 100644
index 0000000..7ccb68f
--- /dev/null
+++ b/roles/write-devstack-local-conf/library/test.py
@@ -0,0 +1,228 @@
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# 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.
+
+import os
+import shutil
+import tempfile
+import unittest
+
+from devstack_local_conf import LocalConf
+from collections import OrderedDict
+
+class TestDevstackLocalConf(unittest.TestCase):
+    def setUp(self):
+        self.tmpdir = tempfile.mkdtemp()
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir)
+
+    def test_plugins(self):
+        "Test that plugins without dependencies work"
+        localrc = {'test_localrc': '1'}
+        local_conf = {'install':
+                      {'nova.conf':
+                       {'main':
+                        {'test_conf': '2'}}}}
+        services = {'cinder': True}
+        # We use ordereddict here to make sure the plugins are in the
+        # *wrong* order for testing.
+        plugins = OrderedDict([
+            ('bar', 'git://git.openstack.org/openstack/bar-plugin'),
+            ('foo', 'git://git.openstack.org/openstack/foo-plugin'),
+            ('baz', 'git://git.openstack.org/openstack/baz-plugin'),
+            ])
+        p = dict(localrc=localrc,
+                 local_conf=local_conf,
+                 base_services=[],
+                 services=services,
+                 plugins=plugins,
+                 base_dir='./test',
+                 path=os.path.join(self.tmpdir, 'test.local.conf'))
+        lc = LocalConf(p.get('localrc'),
+                       p.get('local_conf'),
+                       p.get('base_services'),
+                       p.get('services'),
+                       p.get('plugins'),
+                       p.get('base_dir'),
+                       p.get('projects'))
+        lc.write(p['path'])
+
+        plugins = []
+        with open(p['path']) as f:
+            for line in f:
+                if line.startswith('enable_plugin'):
+                    plugins.append(line.split()[1])
+        self.assertEqual(['bar', 'baz', 'foo'], plugins)
+
+
+    def test_plugin_deps(self):
+        "Test that plugins with dependencies work"
+        os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', 'devstack'))
+        os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', '.git'))
+        os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', 'devstack'))
+        os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', '.git'))
+        with open(os.path.join(
+                self.tmpdir,
+                'foo-plugin', 'devstack', 'settings'), 'w') as f:
+            f.write('define_plugin foo\n')
+        with open(os.path.join(
+                self.tmpdir,
+                'bar-plugin', 'devstack', 'settings'), 'w') as f:
+            f.write('define_plugin bar\n')
+            f.write('plugin_requires bar foo\n')
+
+        localrc = {'test_localrc': '1'}
+        local_conf = {'install':
+                      {'nova.conf':
+                       {'main':
+                        {'test_conf': '2'}}}}
+        services = {'cinder': True}
+        # We use ordereddict here to make sure the plugins are in the
+        # *wrong* order for testing.
+        plugins = OrderedDict([
+            ('bar', 'git://git.openstack.org/openstack/bar-plugin'),
+            ('foo', 'git://git.openstack.org/openstack/foo-plugin'),
+            ])
+        p = dict(localrc=localrc,
+                 local_conf=local_conf,
+                 base_services=[],
+                 services=services,
+                 plugins=plugins,
+                 base_dir=self.tmpdir,
+                 path=os.path.join(self.tmpdir, 'test.local.conf'))
+
+    def test_libs_from_git(self):
+        "Test that LIBS_FROM_GIT is auto-generated"
+        projects = {
+            'git.openstack.org/openstack/nova': {
+                'required': True,
+                'short_name': 'nova',
+            },
+            'git.openstack.org/openstack/oslo.messaging': {
+                'required': True,
+                'short_name': 'oslo.messaging',
+            },
+            'git.openstack.org/openstack/devstack-plugin': {
+                'required': False,
+                'short_name': 'devstack-plugin',
+            },
+        }
+        p = dict(base_services=[],
+                 base_dir='./test',
+                 path=os.path.join(self.tmpdir, 'test.local.conf'),
+                 projects=projects)
+        lc = LocalConf(p.get('localrc'),
+                       p.get('local_conf'),
+                       p.get('base_services'),
+                       p.get('services'),
+                       p.get('plugins'),
+                       p.get('base_dir'),
+                       p.get('projects'))
+        lc.write(p['path'])
+
+        lfg = None
+        with open(p['path']) as f:
+            for line in f:
+                if line.startswith('LIBS_FROM_GIT'):
+                    lfg = line.strip().split('=')[1]
+        self.assertEqual('nova,oslo.messaging', lfg)
+
+    def test_overridelibs_from_git(self):
+        "Test that LIBS_FROM_GIT can be overridden"
+        localrc = {'LIBS_FROM_GIT': 'oslo.db'}
+        projects = {
+            'git.openstack.org/openstack/nova': {
+                'required': True,
+                'short_name': 'nova',
+            },
+            'git.openstack.org/openstack/oslo.messaging': {
+                'required': True,
+                'short_name': 'oslo.messaging',
+            },
+            'git.openstack.org/openstack/devstack-plugin': {
+                'required': False,
+                'short_name': 'devstack-plugin',
+            },
+        }
+        p = dict(localrc=localrc,
+                 base_services=[],
+                 base_dir='./test',
+                 path=os.path.join(self.tmpdir, 'test.local.conf'),
+                 projects=projects)
+        lc = LocalConf(p.get('localrc'),
+                       p.get('local_conf'),
+                       p.get('base_services'),
+                       p.get('services'),
+                       p.get('plugins'),
+                       p.get('base_dir'),
+                       p.get('projects'))
+        lc.write(p['path'])
+
+        lfg = None
+        with open(p['path']) as f:
+            for line in f:
+                if line.startswith('LIBS_FROM_GIT'):
+                    lfg = line.strip().split('=')[1]
+        self.assertEqual('oslo.db', lfg)
+
+    def test_plugin_circular_deps(self):
+        "Test that plugins with circular dependencies fail"
+        os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', 'devstack'))
+        os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', '.git'))
+        os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', 'devstack'))
+        os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', '.git'))
+        with open(os.path.join(
+                self.tmpdir,
+                'foo-plugin', 'devstack', 'settings'), 'w') as f:
+            f.write('define_plugin foo\n')
+            f.write('plugin_requires foo bar\n')
+        with open(os.path.join(
+                self.tmpdir,
+                'bar-plugin', 'devstack', 'settings'), 'w') as f:
+            f.write('define_plugin bar\n')
+            f.write('plugin_requires bar foo\n')
+
+        localrc = {'test_localrc': '1'}
+        local_conf = {'install':
+                      {'nova.conf':
+                       {'main':
+                        {'test_conf': '2'}}}}
+        services = {'cinder': True}
+        # We use ordereddict here to make sure the plugins are in the
+        # *wrong* order for testing.
+        plugins = OrderedDict([
+            ('bar', 'git://git.openstack.org/openstack/bar-plugin'),
+            ('foo', 'git://git.openstack.org/openstack/foo-plugin'),
+            ])
+        p = dict(localrc=localrc,
+                 local_conf=local_conf,
+                 base_services=[],
+                 services=services,
+                 plugins=plugins,
+                 base_dir=self.tmpdir,
+                 path=os.path.join(self.tmpdir, 'test.local.conf'))
+        with self.assertRaises(Exception):
+            lc = LocalConf(p.get('localrc'),
+                           p.get('local_conf'),
+                           p.get('base_services'),
+                           p.get('services'),
+                           p.get('plugins'),
+                           p.get('base_dir'))
+            lc.write(p['path'])
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/roles/write-devstack-local-conf/tasks/main.yaml b/roles/write-devstack-local-conf/tasks/main.yaml
index 1d67616..a294cae 100644
--- a/roles/write-devstack-local-conf/tasks/main.yaml
+++ b/roles/write-devstack-local-conf/tasks/main.yaml
@@ -4,6 +4,9 @@
   devstack_local_conf:
     path: "{{ devstack_local_conf_path }}"
     plugins: "{{ devstack_plugins|default(omit) }}"
+    base_services: "{{ devstack_base_services|default(omit) }}"
     services: "{{ devstack_services|default(omit) }}"
     localrc: "{{ devstack_localrc|default(omit) }}"
     local_conf: "{{ devstack_local_conf|default(omit) }}"
+    base_dir: "{{ devstack_base_dir|default(omit) }}"
+    projects: "{{ zuul.projects }}"
diff --git a/stack.sh b/stack.sh
index a125d4a..6899fa0 100755
--- a/stack.sh
+++ b/stack.sh
@@ -32,7 +32,7 @@
 # Devstack is written in bash, and many functions used throughout
 # devstack process text coming off a command (like the ip command)
 # and do transforms using grep, sed, cut, awk on the strings that are
-# returned. Many of these programs are interationalized, which is
+# returned. Many of these programs are internationalized, which is
 # great for end users, but means that the strings that devstack
 # functions depend upon might not be there in other locales. We thus
 # need to pin the world to an english basis during the runs.
@@ -221,7 +221,7 @@
 
 # Warn users who aren't on an explicitly supported distro, but allow them to
 # override check and attempt installation with ``FORCE=yes ./stack``
-if [[ ! ${DISTRO} =~ (xenial|yakkety|zesty|stretch|jessie|f24|f25|f26|opensuse-42.2|opensuse-42.3|rhel7) ]]; then
+if [[ ! ${DISTRO} =~ (xenial|artful|bionic|stretch|jessie|f25|f26|f27|opensuse-42.3|opensuse-tumbleweed|rhel7) ]]; then
     echo "WARNING: this script has not been tested on $DISTRO"
     if [[ "$FORCE" != "yes" ]]; then
         die $LINENO "If you wish to run this script anyway run with FORCE=yes"
@@ -282,7 +282,7 @@
 # Some distros need to add repos beyond the defaults provided by the vendor
 # to pick up required packages.
 
-function _install_epel_and_rdo {
+function _install_epel {
     # NOTE: We always remove and install latest -- some environments
     # use snapshot images, and if EPEL version updates they break
     # unless we update them to latest version.
@@ -313,13 +313,28 @@
     yum_install epel-release || \
         die $LINENO "Error installing EPEL repo, cannot continue"
     sudo rm -f /etc/yum.repos.d/epel-bootstrap.repo
+}
 
-    # ... and also optional to be enabled
+function _install_rdo {
+    # There are multiple options for this, including using CloudSIG
+    # repositories (centos-release-*), trunk versions, etc.  Since
+    # we're not interested in the actual openstack distributions
+    # (since we're using git to run!) but only peripherial packages
+    # like kvm or ovs, this has been reliable.
+
+    # TODO(ianw): figure out how to best mirror -- probably use infra
+    # mirror RDO reverse proxy.  We could either have test
+    # infrastructure set it up disabled like EPEL, or fiddle it here.
+    # Per the point above, it's a bunch of repos so starts getting a
+    # little messy...
+    if ! is_package_installed rdo-release ; then
+        yum_install https://rdoproject.org/repos/rdo-release.rpm
+    fi
+
+    # Also enable optional for RHEL7 proper.  Note this is a silent
+    # no-op on other platforms.
     sudo yum-config-manager --enable rhel-7-server-optional-rpms
 
-    # install the lastest RDO
-    is_package_installed rdo-release || yum_install https://rdoproject.org/repos/rdo-release.rpm
-
     if is_oraclelinux; then
         sudo yum-config-manager --enable ol7_optional_latest ol7_addons ol7_MySQL56
     fi
@@ -353,7 +368,7 @@
 # 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`
-if [ -z "`grep ^127.0.0.1 /etc/hosts | grep $LOCAL_HOSTNAME`" ]; then
+if ! fgrep -qwe "$LOCAL_HOSTNAME" /etc/hosts; then
     sudo sed -i "s/\(^127.0.0.1.*\)/\1 $LOCAL_HOSTNAME/" /etc/hosts
 fi
 
@@ -362,20 +377,22 @@
 # to speed things up
 SKIP_EPEL_INSTALL=$(trueorfalse False SKIP_EPEL_INSTALL)
 
-# If we have /etc/nodepool/provider assume we're on a OpenStack CI
-# node, where EPEL is already pointing at our internal mirror and RDO
-# is pre-installed.
-if [[ -f /etc/nodepool/provider ]]; then
-    SKIP_EPEL_INSTALL=True
-    if is_fedora; then
-        # However, EPEL is not enabled by default.
+if [[ $DISTRO == "rhel7" ]]; then
+    # If we have /etc/ci/mirror_info.sh assume we're on a OpenStack CI
+    # node, where EPEL is installed (but disabled) and already
+    # pointing at our internal mirror
+    if [[ -f /etc/ci/mirror_info.sh ]]; then
+        SKIP_EPEL_INSTALL=True
         sudo yum-config-manager --enable epel
     fi
-fi
 
-if is_fedora && [[ $DISTRO == "rhel7" ]] && \
-        [[ ${SKIP_EPEL_INSTALL} != True ]]; then
-    _install_epel_and_rdo
+    if [[ ${SKIP_EPEL_INSTALL} != True ]]; then
+        _install_epel
+    fi
+    # Along with EPEL, CentOS (and a-likes) require some packages only
+    # available in RDO repositories (e.g. OVS, or later versions of
+    # kvm) to run.
+    _install_rdo
 fi
 
 # Ensure python is installed
@@ -388,6 +405,7 @@
 
 # Set up logging level
 VERBOSE=$(trueorfalse True VERBOSE)
+VERBOSE_NO_TIMESTAMP=$(trueorfalse False VERBOSE)
 
 # Draw a spinner so the user knows something is happening
 function spinner {
@@ -453,8 +471,12 @@
     # stdout later.
     exec 3>&1
     if [[ "$VERBOSE" == "True" ]]; then
+        _of_args="-v"
+        if [[ "$VERBOSE_NO_TIMESTAMP" == "True" ]]; then
+            _of_args="$_of_args --no-timestamp"
+        fi
         # Set fd 1 and 2 to write the log file
-        exec 1> >( $TOP_DIR/tools/outfilter.py -v -o "${LOGFILE}" ) 2>&1
+        exec 1> >( $TOP_DIR/tools/outfilter.py $_of_args -o "${LOGFILE}" ) 2>&1
         # Set fd 6 to summary log file
         exec 6> >( $TOP_DIR/tools/outfilter.py -o "${SUMFILE}" )
     else
@@ -756,6 +778,7 @@
 
 # Do the ugly hacks for broken packages and distros
 source $TOP_DIR/tools/fixup_stuff.sh
+fixup_all
 
 if [[ "$USE_SYSTEMD" == "True" ]]; then
     pip_install_gr systemd-python
@@ -894,8 +917,6 @@
 fi
 
 if is_service_enabled horizon; then
-    # django openstack_auth
-    install_django_openstack_auth
     # dashboard
     stack_install_service horizon
 fi
@@ -942,17 +963,15 @@
 if [[ $SYSLOG != "False" ]]; then
     if [[ "$SYSLOG_HOST" = "$HOST_IP" ]]; then
         # Configure the master host to receive
-        cat <<EOF >/tmp/90-stack-m.conf
+        cat <<EOF | sudo tee /etc/rsyslog.d/90-stack-m.conf >/dev/null
 \$ModLoad imrelp
 \$InputRELPServerRun $SYSLOG_PORT
 EOF
-        sudo mv /tmp/90-stack-m.conf /etc/rsyslog.d
     else
         # Set rsyslog to send to remote host
-        cat <<EOF >/tmp/90-stack-s.conf
+        cat <<EOF | sudo tee /etc/rsyslog.d/90-stack-s.conf >/dev/null
 *.*		:omrelp:$SYSLOG_HOST:$SYSLOG_PORT
 EOF
-        sudo mv /tmp/90-stack-s.conf /etc/rsyslog.d
     fi
 
     RSYSLOGCONF="/etc/rsyslog.conf"
@@ -1241,16 +1260,17 @@
     done
 fi
 
-# Create a randomized default value for the key manager's fixed_key
-# NOTE(lyarwood): This is currently set to 36 as a workaround to the following
-# libvirt bug that incorrectly pads passphrases that are a multiple of 16 bytes
-# in length.
-# Unable to use LUKS passphrase that is exactly 16 bytes long
-# https://bugzilla.redhat.com/show_bug.cgi?id=1447297
+# NOTE(lyarwood): By default use a single hardcoded fixed_key across devstack
+# deployments.  This ensures the keys match across nova and cinder across all
+# hosts.
+FIXED_KEY=${FIXED_KEY:-bae3516cc1c0eb18b05440eba8012a4a880a2ee04d584a9c1579445e675b12defdc716ec}
 if is_service_enabled nova; then
-    key=$(generate_hex_string 36)
-    iniset $NOVA_CONF key_manager fixed_key "$key"
-    iniset $NOVA_CPU_CONF key_manager fixed_key "$key"
+    iniset $NOVA_CONF key_manager fixed_key "$FIXED_KEY"
+    iniset $NOVA_CPU_CONF key_manager fixed_key "$FIXED_KEY"
+fi
+
+if is_service_enabled cinder; then
+    iniset $CINDER_CONF key_manager fixed_key "$FIXED_KEY"
 fi
 
 # Launch the nova-api and wait for it to answer before continuing
@@ -1365,15 +1385,6 @@
 merge_config_group $TOP_DIR/local.conf post-extra
 
 
-# Run local script
-# ----------------
-
-# Run ``local.sh`` if it exists to perform user-managed tasks
-if [[ -x $TOP_DIR/local.sh ]]; then
-    echo "Running user script $TOP_DIR/local.sh"
-    $TOP_DIR/local.sh
-fi
-
 # Sanity checks
 # =============
 
@@ -1387,11 +1398,6 @@
 # Check the status of running services
 service_check
 
-# ensure that all the libraries we think we installed from git,
-# actually were.
-check_libs_from_git
-
-
 # Configure nova cellsv2
 # ----------------------
 
@@ -1408,6 +1414,15 @@
     fi
 fi
 
+# Run local script
+# ----------------
+
+# Run ``local.sh`` if it exists to perform user-managed tasks
+if [[ -x $TOP_DIR/local.sh ]]; then
+    echo "Running user script $TOP_DIR/local.sh"
+    $TOP_DIR/local.sh
+fi
+
 # Bash completion
 # ===============
 
diff --git a/stackrc b/stackrc
index 286a04d..3c4e437 100644
--- a/stackrc
+++ b/stackrc
@@ -13,6 +13,18 @@
 # Source required DevStack functions and globals
 source $RC_DIR/functions
 
+# Set the target branch. This is used so that stable branching
+# does not need to update each repo below.
+TARGET_BRANCH=master
+
+# Cycle trailing projects need to branch later than the others.
+TRAILING_TARGET_BRANCH=master
+
+# And some repos do not create stable branches, so this is used
+# to make it explicit and avoid accidentally setting to a stable
+# branch.
+BRANCHLESS_TARGET_BRANCH=master
+
 # Destination path for installation
 DEST=/opt/stack
 
@@ -121,7 +133,7 @@
 # base name of the directory from which they are installed. See
 # enable_python3_package to edit this variable and use_python3_for to
 # test membership.
-export ENABLED_PYTHON3_PACKAGES="nova,glance,cinder,uwsgi,python-openstackclient,python-openstacksdk"
+export ENABLED_PYTHON3_PACKAGES="nova,glance,cinder,uwsgi,python-openstackclient,openstacksdk"
 
 # Explicitly list services not to run under Python 3. See
 # disable_python3_package to edit this variable.
@@ -246,7 +258,7 @@
 # Setting the variable to 'ALL' will activate the download for all
 # libraries.
 
-DEVSTACK_SERIES="queens"
+DEVSTACK_SERIES="rocky"
 
 ##############
 #
@@ -256,35 +268,35 @@
 
 # block storage service
 CINDER_REPO=${CINDER_REPO:-${GIT_BASE}/openstack/cinder.git}
-CINDER_BRANCH=${CINDER_BRANCH:-master}
+CINDER_BRANCH=${CINDER_BRANCH:-$TARGET_BRANCH}
 
 # image catalog service
 GLANCE_REPO=${GLANCE_REPO:-${GIT_BASE}/openstack/glance.git}
-GLANCE_BRANCH=${GLANCE_BRANCH:-master}
+GLANCE_BRANCH=${GLANCE_BRANCH:-$TARGET_BRANCH}
 
 # django powered web control panel for openstack
 HORIZON_REPO=${HORIZON_REPO:-${GIT_BASE}/openstack/horizon.git}
-HORIZON_BRANCH=${HORIZON_BRANCH:-master}
+HORIZON_BRANCH=${HORIZON_BRANCH:-$TARGET_BRANCH}
 
 # unified auth system (manages accounts/tokens)
 KEYSTONE_REPO=${KEYSTONE_REPO:-${GIT_BASE}/openstack/keystone.git}
-KEYSTONE_BRANCH=${KEYSTONE_BRANCH:-master}
+KEYSTONE_BRANCH=${KEYSTONE_BRANCH:-$TARGET_BRANCH}
 
 # neutron service
 NEUTRON_REPO=${NEUTRON_REPO:-${GIT_BASE}/openstack/neutron.git}
-NEUTRON_BRANCH=${NEUTRON_BRANCH:-master}
+NEUTRON_BRANCH=${NEUTRON_BRANCH:-$TARGET_BRANCH}
 
 # neutron fwaas service
 NEUTRON_FWAAS_REPO=${NEUTRON_FWAAS_REPO:-${GIT_BASE}/openstack/neutron-fwaas.git}
-NEUTRON_FWAAS_BRANCH=${NEUTRON_FWAAS_BRANCH:-master}
+NEUTRON_FWAAS_BRANCH=${NEUTRON_FWAAS_BRANCH:-$TARGET_BRANCH}
 
 # compute service
 NOVA_REPO=${NOVA_REPO:-${GIT_BASE}/openstack/nova.git}
-NOVA_BRANCH=${NOVA_BRANCH:-master}
+NOVA_BRANCH=${NOVA_BRANCH:-$TARGET_BRANCH}
 
 # object storage service
 SWIFT_REPO=${SWIFT_REPO:-${GIT_BASE}/openstack/swift.git}
-SWIFT_BRANCH=${SWIFT_BRANCH:-master}
+SWIFT_BRANCH=${SWIFT_BRANCH:-$TARGET_BRANCH}
 
 ##############
 #
@@ -294,11 +306,11 @@
 
 # consolidated openstack requirements
 REQUIREMENTS_REPO=${REQUIREMENTS_REPO:-${GIT_BASE}/openstack/requirements.git}
-REQUIREMENTS_BRANCH=${REQUIREMENTS_BRANCH:-master}
+REQUIREMENTS_BRANCH=${REQUIREMENTS_BRANCH:-$TARGET_BRANCH}
 
 # Tempest test suite
 TEMPEST_REPO=${TEMPEST_REPO:-${GIT_BASE}/openstack/tempest.git}
-TEMPEST_BRANCH=${TEMPEST_BRANCH:-master}
+TEMPEST_BRANCH=${TEMPEST_BRANCH:-$BRANCHLESS_TARGET_BRANCH}
 
 
 ##############
@@ -310,56 +322,56 @@
 
 # volume client
 GITREPO["python-cinderclient"]=${CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-cinderclient.git}
-GITBRANCH["python-cinderclient"]=${CINDERCLIENT_BRANCH:-master}
+GITBRANCH["python-cinderclient"]=${CINDERCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # os-brick client for local volume attachement
 GITREPO["python-brick-cinderclient-ext"]=${BRICK_CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-brick-cinderclient-ext.git}
-GITBRANCH["python-brick-cinderclient-ext"]=${BRICK_CINDERCLIENT_BRANCH:-master}
+GITBRANCH["python-brick-cinderclient-ext"]=${BRICK_CINDERCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # python barbican client library
 GITREPO["python-barbicanclient"]=${BARBICANCLIENT_REPO:-${GIT_BASE}/openstack/python-barbicanclient.git}
-GITBRANCH["python-barbicanclient"]=${BARBICANCLIENT_BRANCH:-master}
+GITBRANCH["python-barbicanclient"]=${BARBICANCLIENT_BRANCH:-$TARGET_BRANCH}
 GITDIR["python-barbicanclient"]=$DEST/python-barbicanclient
 
 # python glance client library
 GITREPO["python-glanceclient"]=${GLANCECLIENT_REPO:-${GIT_BASE}/openstack/python-glanceclient.git}
-GITBRANCH["python-glanceclient"]=${GLANCECLIENT_BRANCH:-master}
+GITBRANCH["python-glanceclient"]=${GLANCECLIENT_BRANCH:-$TARGET_BRANCH}
 
 # ironic client
 GITREPO["python-ironicclient"]=${IRONICCLIENT_REPO:-${GIT_BASE}/openstack/python-ironicclient.git}
-GITBRANCH["python-ironicclient"]=${IRONICCLIENT_BRANCH:-master}
+GITBRANCH["python-ironicclient"]=${IRONICCLIENT_BRANCH:-$TARGET_BRANCH}
 # ironic plugin is out of tree, but nova uses it. set GITDIR here.
 GITDIR["python-ironicclient"]=$DEST/python-ironicclient
 
 # the base authentication plugins that clients use to authenticate
 GITREPO["keystoneauth"]=${KEYSTONEAUTH_REPO:-${GIT_BASE}/openstack/keystoneauth.git}
-GITBRANCH["keystoneauth"]=${KEYSTONEAUTH_BRANCH:-master}
+GITBRANCH["keystoneauth"]=${KEYSTONEAUTH_BRANCH:-$TARGET_BRANCH}
 
 # python keystone client library to nova that horizon uses
 GITREPO["python-keystoneclient"]=${KEYSTONECLIENT_REPO:-${GIT_BASE}/openstack/python-keystoneclient.git}
-GITBRANCH["python-keystoneclient"]=${KEYSTONECLIENT_BRANCH:-master}
+GITBRANCH["python-keystoneclient"]=${KEYSTONECLIENT_BRANCH:-$TARGET_BRANCH}
 
 # neutron client
 GITREPO["python-neutronclient"]=${NEUTRONCLIENT_REPO:-${GIT_BASE}/openstack/python-neutronclient.git}
-GITBRANCH["python-neutronclient"]=${NEUTRONCLIENT_BRANCH:-master}
+GITBRANCH["python-neutronclient"]=${NEUTRONCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # python client library to nova that horizon (and others) use
 GITREPO["python-novaclient"]=${NOVACLIENT_REPO:-${GIT_BASE}/openstack/python-novaclient.git}
-GITBRANCH["python-novaclient"]=${NOVACLIENT_BRANCH:-master}
+GITBRANCH["python-novaclient"]=${NOVACLIENT_BRANCH:-$TARGET_BRANCH}
 
 # python swift client library
 GITREPO["python-swiftclient"]=${SWIFTCLIENT_REPO:-${GIT_BASE}/openstack/python-swiftclient.git}
-GITBRANCH["python-swiftclient"]=${SWIFTCLIENT_BRANCH:-master}
+GITBRANCH["python-swiftclient"]=${SWIFTCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # consolidated openstack python client
 GITREPO["python-openstackclient"]=${OPENSTACKCLIENT_REPO:-${GIT_BASE}/openstack/python-openstackclient.git}
-GITBRANCH["python-openstackclient"]=${OPENSTACKCLIENT_BRANCH:-master}
+GITBRANCH["python-openstackclient"]=${OPENSTACKCLIENT_BRANCH:-$TARGET_BRANCH}
 # this doesn't exist in a lib file, so set it here
 GITDIR["python-openstackclient"]=$DEST/python-openstackclient
 
 # placement-api CLI
 GITREPO["osc-placement"]=${OSC_PLACEMENT_REPO:-${GIT_BASE}/openstack/osc-placement.git}
-GITBRANCH["osc-placement"]=${OSC_PLACEMENT_BRANCH:-master}
+GITBRANCH["osc-placement"]=${OSC_PLACEMENT_BRANCH:-$TARGET_BRANCH}
 
 
 ###################
@@ -371,119 +383,119 @@
 
 # castellan key manager interface
 GITREPO["castellan"]=${CASTELLAN_REPO:-${GIT_BASE}/openstack/castellan.git}
-GITBRANCH["castellan"]=${CASTELLAN_BRANCH:-master}
+GITBRANCH["castellan"]=${CASTELLAN_BRANCH:-$TARGET_BRANCH}
 
 # cliff command line framework
 GITREPO["cliff"]=${CLIFF_REPO:-${GIT_BASE}/openstack/cliff.git}
-GITBRANCH["cliff"]=${CLIFF_BRANCH:-master}
+GITBRANCH["cliff"]=${CLIFF_BRANCH:-$TARGET_BRANCH}
 
 # async framework/helpers
 GITREPO["futurist"]=${FUTURIST_REPO:-${GIT_BASE}/openstack/futurist.git}
-GITBRANCH["futurist"]=${FUTURIST_BRANCH:-master}
+GITBRANCH["futurist"]=${FUTURIST_BRANCH:-$TARGET_BRANCH}
 
 # debtcollector deprecation framework/helpers
 GITREPO["debtcollector"]=${DEBTCOLLECTOR_REPO:-${GIT_BASE}/openstack/debtcollector.git}
-GITBRANCH["debtcollector"]=${DEBTCOLLECTOR_BRANCH:-master}
+GITBRANCH["debtcollector"]=${DEBTCOLLECTOR_BRANCH:-$TARGET_BRANCH}
 
 # helpful state machines
 GITREPO["automaton"]=${AUTOMATON_REPO:-${GIT_BASE}/openstack/automaton.git}
-GITBRANCH["automaton"]=${AUTOMATON_BRANCH:-master}
+GITBRANCH["automaton"]=${AUTOMATON_BRANCH:-$TARGET_BRANCH}
 
 # oslo.cache
 GITREPO["oslo.cache"]=${OSLOCACHE_REPO:-${GIT_BASE}/openstack/oslo.cache.git}
-GITBRANCH["oslo.cache"]=${OSLOCACHE_BRANCH:-master}
+GITBRANCH["oslo.cache"]=${OSLOCACHE_BRANCH:-$TARGET_BRANCH}
 
 # oslo.concurrency
 GITREPO["oslo.concurrency"]=${OSLOCON_REPO:-${GIT_BASE}/openstack/oslo.concurrency.git}
-GITBRANCH["oslo.concurrency"]=${OSLOCON_BRANCH:-master}
+GITBRANCH["oslo.concurrency"]=${OSLOCON_BRANCH:-$TARGET_BRANCH}
 
 # oslo.config
 GITREPO["oslo.config"]=${OSLOCFG_REPO:-${GIT_BASE}/openstack/oslo.config.git}
-GITBRANCH["oslo.config"]=${OSLOCFG_BRANCH:-master}
+GITBRANCH["oslo.config"]=${OSLOCFG_BRANCH:-$TARGET_BRANCH}
 
 # oslo.context
 GITREPO["oslo.context"]=${OSLOCTX_REPO:-${GIT_BASE}/openstack/oslo.context.git}
-GITBRANCH["oslo.context"]=${OSLOCTX_BRANCH:-master}
+GITBRANCH["oslo.context"]=${OSLOCTX_BRANCH:-$TARGET_BRANCH}
 
 # oslo.db
 GITREPO["oslo.db"]=${OSLODB_REPO:-${GIT_BASE}/openstack/oslo.db.git}
-GITBRANCH["oslo.db"]=${OSLODB_BRANCH:-master}
+GITBRANCH["oslo.db"]=${OSLODB_BRANCH:-$TARGET_BRANCH}
 
 # oslo.i18n
 GITREPO["oslo.i18n"]=${OSLOI18N_REPO:-${GIT_BASE}/openstack/oslo.i18n.git}
-GITBRANCH["oslo.i18n"]=${OSLOI18N_BRANCH:-master}
+GITBRANCH["oslo.i18n"]=${OSLOI18N_BRANCH:-$TARGET_BRANCH}
 
 # oslo.log
 GITREPO["oslo.log"]=${OSLOLOG_REPO:-${GIT_BASE}/openstack/oslo.log.git}
-GITBRANCH["oslo.log"]=${OSLOLOG_BRANCH:-master}
+GITBRANCH["oslo.log"]=${OSLOLOG_BRANCH:-$TARGET_BRANCH}
 
 # oslo.messaging
 GITREPO["oslo.messaging"]=${OSLOMSG_REPO:-${GIT_BASE}/openstack/oslo.messaging.git}
-GITBRANCH["oslo.messaging"]=${OSLOMSG_BRANCH:-master}
+GITBRANCH["oslo.messaging"]=${OSLOMSG_BRANCH:-$TARGET_BRANCH}
 
 # oslo.middleware
 GITREPO["oslo.middleware"]=${OSLOMID_REPO:-${GIT_BASE}/openstack/oslo.middleware.git}
-GITBRANCH["oslo.middleware"]=${OSLOMID_BRANCH:-master}
+GITBRANCH["oslo.middleware"]=${OSLOMID_BRANCH:-$TARGET_BRANCH}
 
 # oslo.policy
 GITREPO["oslo.policy"]=${OSLOPOLICY_REPO:-${GIT_BASE}/openstack/oslo.policy.git}
-GITBRANCH["oslo.policy"]=${OSLOPOLICY_BRANCH:-master}
+GITBRANCH["oslo.policy"]=${OSLOPOLICY_BRANCH:-$TARGET_BRANCH}
 
 # oslo.privsep
 GITREPO["oslo.privsep"]=${OSLOPRIVSEP_REPO:-${GIT_BASE}/openstack/oslo.privsep.git}
-GITBRANCH["oslo.privsep"]=${OSLOPRIVSEP_BRANCH:-master}
+GITBRANCH["oslo.privsep"]=${OSLOPRIVSEP_BRANCH:-$TARGET_BRANCH}
 
 # oslo.reports
 GITREPO["oslo.reports"]=${OSLOREPORTS_REPO:-${GIT_BASE}/openstack/oslo.reports.git}
-GITBRANCH["oslo.reports"]=${OSLOREPORTS_BRANCH:-master}
+GITBRANCH["oslo.reports"]=${OSLOREPORTS_BRANCH:-$TARGET_BRANCH}
 
 # oslo.rootwrap
 GITREPO["oslo.rootwrap"]=${OSLORWRAP_REPO:-${GIT_BASE}/openstack/oslo.rootwrap.git}
-GITBRANCH["oslo.rootwrap"]=${OSLORWRAP_BRANCH:-master}
+GITBRANCH["oslo.rootwrap"]=${OSLORWRAP_BRANCH:-$TARGET_BRANCH}
 
 # oslo.serialization
 GITREPO["oslo.serialization"]=${OSLOSERIALIZATION_REPO:-${GIT_BASE}/openstack/oslo.serialization.git}
-GITBRANCH["oslo.serialization"]=${OSLOSERIALIZATION_BRANCH:-master}
+GITBRANCH["oslo.serialization"]=${OSLOSERIALIZATION_BRANCH:-$TARGET_BRANCH}
 
 # oslo.service
 GITREPO["oslo.service"]=${OSLOSERVICE_REPO:-${GIT_BASE}/openstack/oslo.service.git}
-GITBRANCH["oslo.service"]=${OSLOSERVICE_BRANCH:-master}
+GITBRANCH["oslo.service"]=${OSLOSERVICE_BRANCH:-$TARGET_BRANCH}
 
 # oslo.utils
 GITREPO["oslo.utils"]=${OSLOUTILS_REPO:-${GIT_BASE}/openstack/oslo.utils.git}
-GITBRANCH["oslo.utils"]=${OSLOUTILS_BRANCH:-master}
+GITBRANCH["oslo.utils"]=${OSLOUTILS_BRANCH:-$TARGET_BRANCH}
 
 # oslo.versionedobjects
 GITREPO["oslo.versionedobjects"]=${OSLOVERSIONEDOBJECTS_REPO:-${GIT_BASE}/openstack/oslo.versionedobjects.git}
-GITBRANCH["oslo.versionedobjects"]=${OSLOVERSIONEDOBJECTS_BRANCH:-master}
+GITBRANCH["oslo.versionedobjects"]=${OSLOVERSIONEDOBJECTS_BRANCH:-$TARGET_BRANCH}
 
 # oslo.vmware
 GITREPO["oslo.vmware"]=${OSLOVMWARE_REPO:-${GIT_BASE}/openstack/oslo.vmware.git}
-GITBRANCH["oslo.vmware"]=${OSLOVMWARE_BRANCH:-master}
+GITBRANCH["oslo.vmware"]=${OSLOVMWARE_BRANCH:-$TARGET_BRANCH}
 
 # osprofiler
 GITREPO["osprofiler"]=${OSPROFILER_REPO:-${GIT_BASE}/openstack/osprofiler.git}
-GITBRANCH["osprofiler"]=${OSPROFILER_BRANCH:-master}
+GITBRANCH["osprofiler"]=${OSPROFILER_BRANCH:-$TARGET_BRANCH}
 
 # pycadf auditing library
 GITREPO["pycadf"]=${PYCADF_REPO:-${GIT_BASE}/openstack/pycadf.git}
-GITBRANCH["pycadf"]=${PYCADF_BRANCH:-master}
+GITBRANCH["pycadf"]=${PYCADF_BRANCH:-$TARGET_BRANCH}
 
 # stevedore plugin manager
 GITREPO["stevedore"]=${STEVEDORE_REPO:-${GIT_BASE}/openstack/stevedore.git}
-GITBRANCH["stevedore"]=${STEVEDORE_BRANCH:-master}
+GITBRANCH["stevedore"]=${STEVEDORE_BRANCH:-$TARGET_BRANCH}
 
 # taskflow plugin manager
 GITREPO["taskflow"]=${TASKFLOW_REPO:-${GIT_BASE}/openstack/taskflow.git}
-GITBRANCH["taskflow"]=${TASKFLOW_BRANCH:-master}
+GITBRANCH["taskflow"]=${TASKFLOW_BRANCH:-$TARGET_BRANCH}
 
 # tooz plugin manager
 GITREPO["tooz"]=${TOOZ_REPO:-${GIT_BASE}/openstack/tooz.git}
-GITBRANCH["tooz"]=${TOOZ_BRANCH:-master}
+GITBRANCH["tooz"]=${TOOZ_BRANCH:-$TARGET_BRANCH}
 
 # pbr drives the setuptools configs
 GITREPO["pbr"]=${PBR_REPO:-${GIT_BASE}/openstack-dev/pbr.git}
-GITBRANCH["pbr"]=${PBR_BRANCH:-master}
+GITBRANCH["pbr"]=${PBR_BRANCH:-$TARGET_BRANCH}
 
 
 ##################
@@ -494,69 +506,65 @@
 
 # cursive library
 GITREPO["cursive"]=${CURSIVE_REPO:-${GIT_BASE}/openstack/cursive.git}
-GITBRANCH["cursive"]=${CURSIVE_BRANCH:-master}
+GITBRANCH["cursive"]=${CURSIVE_BRANCH:-$TARGET_BRANCH}
 
 # glance store library
 GITREPO["glance_store"]=${GLANCE_STORE_REPO:-${GIT_BASE}/openstack/glance_store.git}
-GITBRANCH["glance_store"]=${GLANCE_STORE_BRANCH:-master}
-
-# django openstack_auth library
-GITREPO["django_openstack_auth"]=${HORIZONAUTH_REPO:-${GIT_BASE}/openstack/django_openstack_auth.git}
-GITBRANCH["django_openstack_auth"]=${HORIZONAUTH_BRANCH:-master}
+GITBRANCH["glance_store"]=${GLANCE_STORE_BRANCH:-$TARGET_BRANCH}
 
 # keystone middleware
 GITREPO["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_REPO:-${GIT_BASE}/openstack/keystonemiddleware.git}
-GITBRANCH["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_BRANCH:-master}
+GITBRANCH["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_BRANCH:-$TARGET_BRANCH}
 
 # s3 support for swift
 SWIFT3_REPO=${SWIFT3_REPO:-${GIT_BASE}/openstack/swift3.git}
-SWIFT3_BRANCH=${SWIFT3_BRANCH:-master}
+SWIFT3_BRANCH=${SWIFT3_BRANCH:-$TARGET_BRANCH}
 
 # ceilometer middleware
 GITREPO["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_REPO:-${GIT_BASE}/openstack/ceilometermiddleware.git}
-GITBRANCH["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_BRANCH:-master}
+GITBRANCH["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_BRANCH:-$TARGET_BRANCH}
 GITDIR["ceilometermiddleware"]=$DEST/ceilometermiddleware
 
+# openstacksdk OpenStack Python SDK
+GITREPO["openstacksdk"]=${OPENSTACKSDK_REPO:-${GIT_BASE}/openstack/openstacksdk.git}
+GITBRANCH["openstacksdk"]=${OPENSTACKSDK_BRANCH:-$TARGET_BRANCH}
+
 # os-brick library to manage local volume attaches
 GITREPO["os-brick"]=${OS_BRICK_REPO:-${GIT_BASE}/openstack/os-brick.git}
-GITBRANCH["os-brick"]=${OS_BRICK_BRANCH:-master}
+GITBRANCH["os-brick"]=${OS_BRICK_BRANCH:-$TARGET_BRANCH}
 
 # os-client-config to manage clouds.yaml and friends
 GITREPO["os-client-config"]=${OS_CLIENT_CONFIG_REPO:-${GIT_BASE}/openstack/os-client-config.git}
-GITBRANCH["os-client-config"]=${OS_CLIENT_CONFIG_BRANCH:-master}
+GITBRANCH["os-client-config"]=${OS_CLIENT_CONFIG_BRANCH:-$TARGET_BRANCH}
 GITDIR["os-client-config"]=$DEST/os-client-config
 
 # os-vif library to communicate between Neutron to Nova
 GITREPO["os-vif"]=${OS_VIF_REPO:-${GIT_BASE}/openstack/os-vif.git}
-GITBRANCH["os-vif"]=${OS_VIF_BRANCH:-master}
+GITBRANCH["os-vif"]=${OS_VIF_BRANCH:-$TARGET_BRANCH}
 
 # osc-lib OpenStackClient common lib
 GITREPO["osc-lib"]=${OSC_LIB_REPO:-${GIT_BASE}/openstack/osc-lib.git}
-GITBRANCH["osc-lib"]=${OSC_LIB_BRANCH:-master}
-
-# python-openstacksdk OpenStack Python SDK
-GITREPO["python-openstacksdk"]=${OPENSTACKSDK_REPO:-${GIT_BASE}/openstack/python-openstacksdk.git}
-GITBRANCH["python-openstacksdk"]=${OPENSTACKSDK_BRANCH:-master}
+GITBRANCH["osc-lib"]=${OSC_LIB_BRANCH:-$TARGET_BRANCH}
 
 # ironic common lib
 GITREPO["ironic-lib"]=${IRONIC_LIB_REPO:-${GIT_BASE}/openstack/ironic-lib.git}
-GITBRANCH["ironic-lib"]=${IRONIC_LIB_BRANCH:-master}
+GITBRANCH["ironic-lib"]=${IRONIC_LIB_BRANCH:-$TARGET_BRANCH}
 # this doesn't exist in a lib file, so set it here
 GITDIR["ironic-lib"]=$DEST/ironic-lib
 
 # diskimage-builder tool
 GITREPO["diskimage-builder"]=${DIB_REPO:-${GIT_BASE}/openstack/diskimage-builder.git}
-GITBRANCH["diskimage-builder"]=${DIB_BRANCH:-master}
+GITBRANCH["diskimage-builder"]=${DIB_BRANCH:-$TARGET_BRANCH}
 GITDIR["diskimage-builder"]=$DEST/diskimage-builder
 
 # neutron-lib library containing neutron stable non-REST interfaces
 GITREPO["neutron-lib"]=${NEUTRON_LIB_REPO:-${GIT_BASE}/openstack/neutron-lib.git}
-GITBRANCH["neutron-lib"]=${NEUTRON_LIB_BRANCH:-master}
+GITBRANCH["neutron-lib"]=${NEUTRON_LIB_BRANCH:-$TARGET_BRANCH}
 GITDIR["neutron-lib"]=$DEST/neutron-lib
 
 # os-traits library for resource provider traits in the placement service
 GITREPO["os-traits"]=${OS_TRAITS_REPO:-${GIT_BASE}/openstack/os-traits.git}
-GITBRANCH["os-traits"]=${OS_TRAITS_BRANCH:-master}
+GITBRANCH["os-traits"]=${OS_TRAITS_BRANCH:-$TARGET_BRANCH}
 
 ##################
 #
@@ -566,19 +574,19 @@
 
 # run-parts script required by os-refresh-config
 DIB_UTILS_REPO=${DIB_UTILS_REPO:-${GIT_BASE}/openstack/dib-utils.git}
-DIB_UTILS_BRANCH=${DIB_UTILS_BRANCH:-master}
+DIB_UTILS_BRANCH=${DIB_UTILS_BRANCH:-$BRANCHLESS_TARGET_BRANCH}
 
 # os-apply-config configuration template tool
 OAC_REPO=${OAC_REPO:-${GIT_BASE}/openstack/os-apply-config.git}
-OAC_BRANCH=${OAC_BRANCH:-master}
+OAC_BRANCH=${OAC_BRANCH:-$TRAILING_TARGET_BRANCH}
 
 # os-collect-config configuration agent
 OCC_REPO=${OCC_REPO:-${GIT_BASE}/openstack/os-collect-config.git}
-OCC_BRANCH=${OCC_BRANCH:-master}
+OCC_BRANCH=${OCC_BRANCH:-$TRAILING_TARGET_BRANCH}
 
 # os-refresh-config configuration run-parts tool
 ORC_REPO=${ORC_REPO:-${GIT_BASE}/openstack/os-refresh-config.git}
-ORC_BRANCH=${ORC_BRANCH:-master}
+ORC_BRANCH=${ORC_BRANCH:-$TRAILING_TARGET_BRANCH}
 
 
 #################
@@ -591,7 +599,7 @@
 
 # ironic python agent
 IRONIC_PYTHON_AGENT_REPO=${IRONIC_PYTHON_AGENT_REPO:-${GIT_BASE}/openstack/ironic-python-agent.git}
-IRONIC_PYTHON_AGENT_BRANCH=${IRONIC_PYTHON_AGENT_BRANCH:-master}
+IRONIC_PYTHON_AGENT_BRANCH=${IRONIC_PYTHON_AGENT_BRANCH:-$TARGET_BRANCH}
 
 # a websockets/html5 or flash powered VNC console for vm instances
 NOVNC_REPO=${NOVNC_REPO:-https://github.com/novnc/noVNC.git}
@@ -599,8 +607,13 @@
 
 # a websockets/html5 or flash powered SPICE console for vm instances
 SPICE_REPO=${SPICE_REPO:-http://anongit.freedesktop.org/git/spice/spice-html5.git}
-SPICE_BRANCH=${SPICE_BRANCH:-master}
+SPICE_BRANCH=${SPICE_BRANCH:-$BRANCHLESS_TARGET_BRANCH}
 
+# Global flag used to configure Tempest and potentially other services if
+# volume multiattach is supported. In Queens, only the libvirt compute driver
+# and lvm volume driver support multiattach, and qemu must be less than 2.10
+# or libvirt must be greater than or equal to 3.10.
+ENABLE_VOLUME_MULTIATTACH=$(trueorfalse False ENABLE_VOLUME_MULTIATTACH)
 
 # 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
@@ -719,12 +732,12 @@
 EXTRA_CACHE_URLS=""
 
 # etcd3 defaults
-ETCD_VERSION=${ETCD_VERSION:-v3.1.10}
-ETCD_SHA256_AMD64="2d335f298619c6fb02b1124773a56966e448ad9952b26fea52909da4fe80d2be"
-# NOTE(sdague): etcd v3.1.10 doesn't have anything for these architectures, though 3.2.x does.
-ETCD_SHA256_ARM64=""
-ETCD_SHA256_PPC64=""
-ETCD_SHA256_S390X=""
+ETCD_VERSION=${ETCD_VERSION:-v3.2.17}
+ETCD_SHA256_AMD64=${ETCD_SHA256_AMD64:-"0a75e794502e2e76417b19da2807a9915fa58dcbf0985e397741d570f4f305cd"}
+ETCD_SHA256_ARM64=${ETCD_SHA256_ARM64:-"0ab4621c44c79d17d94e43bd184d0f23b763a3669056ce4ae2d0b2942410a98f"}
+ETCD_SHA256_PPC64=${ETCD_SHA256_PPC64:-"69e1279c4a2a52256b78d2a8dd23346ac46b836e678b971a459f2afaef3c275e"}
+# etcd v3.2.x doesn't have anything for s390x
+ETCD_SHA256_S390X=${ETCD_SHA256_S390X:-""}
 # Make sure etcd3 downloads the correct architecture
 if is_arch "x86_64"; then
     ETCD_ARCH="amd64"
@@ -748,6 +761,8 @@
 else
     exit_distro_not_supported "invalid hardware type - $ETCD_ARCH"
 fi
+ETCD_PORT=${ETCD_PORT:-2379}
+ETCD_PEER_PORT=${ETCD_PEER_PORT:-2380}
 ETCD_DOWNLOAD_URL=${ETCD_DOWNLOAD_URL:-https://github.com/coreos/etcd/releases/download}
 ETCD_NAME=etcd-$ETCD_VERSION-linux-$ETCD_ARCH
 ETCD_DOWNLOAD_FILE=$ETCD_NAME.tar.gz
@@ -762,8 +777,8 @@
     fi
 done
 
-# 10Gb default volume backing file size
-VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE:-10250M}
+# 24Gb default volume backing file size
+VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE:-24G}
 
 # Prefixes for volume and instance names
 VOLUME_NAME_PREFIX=${VOLUME_NAME_PREFIX:-volume-}
@@ -795,6 +810,9 @@
 # Service startup timeout
 SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}
 
+# Timeout for compute node registration in Nova
+NOVA_READY_TIMEOUT=${NOVA_READY_TIMEOUT:-$SERVICE_TIMEOUT}
+
 # Service graceful shutdown timeout
 SERVICE_GRACEFUL_SHUTDOWN_TIMEOUT=${SERVICE_GRACEFUL_SHUTDOWN_TIMEOUT:-5}
 
@@ -926,7 +944,6 @@
 fi
 
 # ``LOGDIR`` is always set at this point so it is not useful as a 'enable' for service logs
-# ``SCREEN_LOGDIR`` may be set, it is useful to enable the compat symlinks
 
 # System-wide ulimit file descriptors override
 ULIMIT_NOFILE=${ULIMIT_NOFILE:-2048}
diff --git a/tests/test_libs_from_pypi.sh b/tests/test_libs_from_pypi.sh
index 0bd8d49..c3b4457 100755
--- a/tests/test_libs_from_pypi.sh
+++ b/tests/test_libs_from_pypi.sh
@@ -35,10 +35,10 @@
 ALL_LIBS+=" oslo.messaging oslo.log cliff stevedore"
 ALL_LIBS+=" python-cinderclient glance_store oslo.concurrency oslo.db"
 ALL_LIBS+=" oslo.versionedobjects oslo.vmware keystonemiddleware"
-ALL_LIBS+=" oslo.serialization django_openstack_auth"
+ALL_LIBS+=" oslo.serialization"
 ALL_LIBS+=" python-openstackclient osc-lib osc-placement"
 ALL_LIBS+=" os-client-config oslo.rootwrap"
-ALL_LIBS+=" oslo.i18n oslo.utils python-openstacksdk python-swiftclient"
+ALL_LIBS+=" oslo.i18n oslo.utils openstacksdk python-swiftclient"
 ALL_LIBS+=" python-neutronclient tooz ceilometermiddleware oslo.policy"
 ALL_LIBS+=" debtcollector os-brick os-traits automaton futurist oslo.service"
 ALL_LIBS+=" oslo.cache oslo.reports osprofiler cursive"
diff --git a/tests/test_refs.sh b/tests/test_refs.sh
index 65848cd..0f9aa4a 100755
--- a/tests/test_refs.sh
+++ b/tests/test_refs.sh
@@ -15,10 +15,10 @@
 
 echo "Ensuring we don't have crazy refs"
 
-REFS=`grep BRANCH stackrc | grep -v -- '-master' | grep -v 'NOVNC_BRANCH'`
+REFS=`grep BRANCH stackrc | grep -v 'TARGET_BRANCH' | grep -v 'NOVNC_BRANCH'`
 rc=$?
 if [[ $rc -eq 0 ]]; then
-    echo "Branch defaults must be master. Found:"
+    echo "Branch defaults must be one of the *TARGET_BRANCH values. Found:"
     echo $REFS
     exit 1
 fi
diff --git a/tests/test_write_devstack_local_conf_role.sh b/tests/test_write_devstack_local_conf_role.sh
new file mode 100755
index 0000000..b2bc0a2
--- /dev/null
+++ b/tests/test_write_devstack_local_conf_role.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+TOP=$(cd $(dirname "$0")/.. && pwd)
+
+# Import common functions
+source $TOP/functions
+source $TOP/tests/unittest.sh
+
+python ./roles/write-devstack-local-conf/library/test.py
diff --git a/tools/cap-pip.txt b/tools/cap-pip.txt
index c280267..f5278d7 100644
--- a/tools/cap-pip.txt
+++ b/tools/cap-pip.txt
@@ -1 +1 @@
-pip!=8
+pip!=8,<10
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index efe0125..9147932 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -45,27 +45,29 @@
 # where Keystone will try and bind to the port and the port will already be
 # in use as an ephemeral port by another process. This places an explicit
 # exception into the Kernel for the Keystone AUTH ports.
-keystone_ports=${KEYSTONE_AUTH_PORT:-35357},${KEYSTONE_AUTH_PORT_INT:-35358}
+function fixup_keystone {
+    keystone_ports=${KEYSTONE_AUTH_PORT:-35357},${KEYSTONE_AUTH_PORT_INT:-35358}
 
-# Only do the reserved ports when available, on some system (like containers)
-# where it's not exposed we are almost pretty sure these ports would be
-# exclusive for our DevStack.
-if sysctl net.ipv4.ip_local_reserved_ports >/dev/null 2>&1; then
-    # Get any currently reserved ports, strip off leading whitespace
-    reserved_ports=$(sysctl net.ipv4.ip_local_reserved_ports | awk -F'=' '{print $2;}' | sed 's/^ //')
+    # Only do the reserved ports when available, on some system (like containers)
+    # where it's not exposed we are almost pretty sure these ports would be
+    # exclusive for our DevStack.
+    if sysctl net.ipv4.ip_local_reserved_ports >/dev/null 2>&1; then
+        # Get any currently reserved ports, strip off leading whitespace
+        reserved_ports=$(sysctl net.ipv4.ip_local_reserved_ports | awk -F'=' '{print $2;}' | sed 's/^ //')
 
-    if [[ -z "${reserved_ports}" ]]; then
-        # If there are no currently reserved ports, reserve the keystone ports
-        sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports}
+        if [[ -z "${reserved_ports}" ]]; then
+            # If there are no currently reserved ports, reserve the keystone ports
+            sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports}
+        else
+            # If there are currently reserved ports, keep those and also reserve the
+            # Keystone specific ports. Duplicate reservations are merged into a single
+            # reservation (or range) automatically by the kernel.
+            sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports},${reserved_ports}
+        fi
     else
-        # If there are currently reserved ports, keep those and also reserve the
-        # Keystone specific ports. Duplicate reservations are merged into a single
-        # reservation (or range) automatically by the kernel.
-        sudo sysctl -w net.ipv4.ip_local_reserved_ports=${keystone_ports},${reserved_ports}
+        echo_summary "WARNING: unable to reserve keystone ports"
     fi
-else
-    echo_summary "WARNING: unable to reserve keystone ports"
-fi
+}
 
 # Ubuntu Cloud Archive
 #---------------------
@@ -75,19 +77,23 @@
 # Make it possible to switch this based on an environment variable as
 # libvirt 2.5.0 doesn't handle nested virtualization quite well and this
 # is required for the trove development environment.
-if [[ "${ENABLE_UBUNTU_CLOUD_ARCHIVE}" == "True" && "$DISTRO" = "xenial" ]]; then
+function fixup_uca {
+    if [[ "${ENABLE_UBUNTU_CLOUD_ARCHIVE}" == "False" || "$DISTRO" != "xenial" ]]; then
+        return
+    fi
+
     # This pulls in apt-add-repository
     install_package "software-properties-common"
-    # Use UCA for newer libvirt. Should give us libvirt 2.5.0.
+    # Use UCA for newer libvirt.
     if [[ -f /etc/ci/mirror_info.sh ]] ; then
         # If we are on a nodepool provided host and it has told us about where
         # we can find local mirrors then use that mirror.
         source /etc/ci/mirror_info.sh
 
-        sudo apt-add-repository -y "deb $NODEPOOL_UCA_MIRROR xenial-updates/pike main"
+        sudo apt-add-repository -y "deb $NODEPOOL_UCA_MIRROR xenial-updates/queens main"
     else
         # Otherwise use upstream UCA
-        sudo add-apt-repository -y cloud-archive:pike
+        sudo add-apt-repository -y cloud-archive:queens
     fi
 
     # Disable use of libvirt wheel since a cached wheel build might be
@@ -99,8 +105,7 @@
     # Force update our APT repos, since we added UCA above.
     REPOS_UPDATED=False
     apt_get_update
-fi
-
+}
 
 # Python Packages
 # ---------------
@@ -115,27 +120,32 @@
 # Pre-install affected packages so we can fix the permissions
 # These can go away once we are confident that pip 1.4.1+ is available everywhere
 
-# Fix prettytable 0.7.2 permissions
-# Don't specify --upgrade so we use the existing package if present
-pip_install 'prettytable>=0.7'
-PACKAGE_DIR=$(get_package_path prettytable)
-# Only fix version 0.7.2
-dir=$(echo $PACKAGE_DIR/prettytable-0.7.2*)
-if [[ -d $dir ]]; then
-    sudo chmod +r $dir/*
-fi
+function fixup_python_packages {
+    # Fix prettytable 0.7.2 permissions
+    # Don't specify --upgrade so we use the existing package if present
+    pip_install 'prettytable>=0.7'
+    PACKAGE_DIR=$(get_package_path prettytable)
+    # Only fix version 0.7.2
+    dir=$(echo $PACKAGE_DIR/prettytable-0.7.2*)
+    if [[ -d $dir ]]; then
+        sudo chmod +r $dir/*
+    fi
 
-# Fix httplib2 0.8 permissions
-# Don't specify --upgrade so we use the existing package if present
-pip_install httplib2
-PACKAGE_DIR=$(get_package_path httplib2)
-# Only fix version 0.8
-dir=$(echo $PACKAGE_DIR-0.8*)
-if [[ -d $dir ]]; then
-    sudo chmod +r $dir/*
-fi
+    # Fix httplib2 0.8 permissions
+    # Don't specify --upgrade so we use the existing package if present
+    pip_install httplib2
+    PACKAGE_DIR=$(get_package_path httplib2)
+    # Only fix version 0.8
+    dir=$(echo $PACKAGE_DIR-0.8*)
+    if [[ -d $dir ]]; then
+        sudo chmod +r $dir/*
+    fi
+}
 
-if is_fedora; then
+function fixup_fedora {
+    if ! is_fedora; then
+        return
+    fi
     # Disable selinux to avoid configuring to allow Apache access
     # to Horizon files (LP#1175444)
     if selinuxenabled; then
@@ -193,7 +203,7 @@
             pip_install --upgrade --force-reinstall requests
         fi
     fi
-fi
+}
 
 # The version of pip(1.5.4) supported by python-virtualenv(1.11.4) has
 # connection issues under proxy so re-install the latest version using
@@ -217,7 +227,17 @@
 #            install.d/pip-and-virtualenv-source-install/04-install-pip
 # [2] https://bugzilla.redhat.com/show_bug.cgi?id=1477823
 
-if [[ ! -f /etc/ci/mirror_info.sh ]]; then
-    install_package python-virtualenv
-    pip_install -U --force-reinstall virtualenv
-fi
+function fixup_virtualenv {
+    if [[ ! -f /etc/ci/mirror_info.sh ]]; then
+        install_package python-virtualenv
+        pip_install -U --force-reinstall virtualenv
+    fi
+}
+
+function fixup_all {
+    fixup_keystone
+    fixup_uca
+    fixup_python_packages
+    fixup_fedora
+    fixup_virtualenv
+}
diff --git a/tools/install_pip.sh b/tools/install_pip.sh
index dbe5278..1bd7392 100755
--- a/tools/install_pip.sh
+++ b/tools/install_pip.sh
@@ -129,10 +129,10 @@
 
 # Eradicate any and all system packages
 
-# Python in fedora depends on the python-pip package so removing it
+# Python in fedora/suse depends on the python-pip package so removing it
 # results in a nonfunctional system. pip on fedora installs to /usr so pip
 # can safely override the system pip for all versions of fedora
-if ! is_fedora ; then
+if ! is_fedora  && ! is_suse; then
     uninstall_package python-pip
     uninstall_package python3-pip
 fi
diff --git a/tools/outfilter.py b/tools/outfilter.py
index f82939b..cf09124 100755
--- a/tools/outfilter.py
+++ b/tools/outfilter.py
@@ -36,6 +36,13 @@
     parser.add_argument('-o', '--outfile',
                         help='Output file for content',
                         default=None)
+    # NOTE(ianw): This is intended for the case where your stdout is
+    # being captured by something like ansible which independently
+    # logs timestamps on the lines it receives.  Note that if using a
+    # output file, those log lines are still timestamped.
+    parser.add_argument('-b', '--no-timestamp', action='store_true',
+                        help='Do not prefix stdout with timestamp (bare)',
+                        default=False)
     parser.add_argument('-v', '--verbose', action='store_true',
                         default=False)
     return parser.parse_args()
@@ -50,33 +57,45 @@
     opts = get_options()
     outfile = None
     if opts.outfile:
-        outfile = open(opts.outfile, 'a', 0)
+        # note, binary mode so we can do unbuffered output.
+        outfile = open(opts.outfile, 'ab', 0)
 
     # Otherwise fileinput reprocess args as files
     sys.argv = []
-    while True:
-        line = sys.stdin.readline()
-        if not line:
-            return 0
 
+    for line in iter(sys.stdin.readline, ''):
         # put skip lines here
         if skip_line(line):
             continue
 
-        # This prevents us from nesting date lines, because
-        # we'd like to pull this in directly in Grenade and not double
-        # up on DevStack lines
+        # This prevents us from nesting date lines, because we'd like
+        # to pull this in directly in Grenade and not double up on
+        # DevStack lines.
+        # NOTE(ianw): we could actually strip the extra ts in "bare"
+        # mode (which came after this)? ... as we get more experience
+        # with zuulv3 native jobs and ansible capture it may become
+        # clearer what to do
         if HAS_DATE.search(line) is None:
             now = datetime.datetime.utcnow()
-            line = ("%s | %s" % (
+            ts_line = ("%s | %s" % (
                 now.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
                 line))
+        else:
+            ts_line = line
 
         if opts.verbose:
-            sys.stdout.write(line)
+            sys.stdout.write(line if opts.no_timestamp else ts_line)
             sys.stdout.flush()
+
         if outfile:
-            outfile.write(line)
+            # We've opened outfile as a binary file to get the
+            # non-buffered behaviour.  on python3, sys.stdin was
+            # opened with the system encoding and made the line into
+            # utf-8, so write the logfile out in utf-8 bytes.
+            if sys.version_info < (3,):
+                outfile.write(ts_line)
+            else:
+                outfile.write(ts_line.encode('utf-8'))
             outfile.flush()
 
 
diff --git a/tools/worlddump.py b/tools/worlddump.py
index 6fff149..7506082 100755
--- a/tools/worlddump.py
+++ b/tools/worlddump.py
@@ -164,8 +164,7 @@
     _header("Network Dump")
 
     _dump_cmd("brctl show")
-    _dump_cmd("arp -n")
-    ip_cmds = ["addr", "link", "route"]
+    ip_cmds = ["neigh", "addr", "link", "route"]
     for cmd in ip_cmds + ['netns']:
         _dump_cmd("ip %s" % cmd)
     for netns_ in _netns_list():
diff --git a/tools/xen/README.md b/tools/xen/README.md
index 9559e77..22263bb 100644
--- a/tools/xen/README.md
+++ b/tools/xen/README.md
@@ -1,173 +1,3 @@
-# Getting Started With XenServer and Devstack
+Note: XenServer relative tools have been moved to `os-xenapi`_ and be maintained there.
 
-The purpose of the code in this directory it to help developers bootstrap a
-XenServer 6.2 (older versions may also work) + OpenStack development
-environment. This file gives some pointers on how to get started.
-
-Xenserver is a Type 1 hypervisor, so it is best installed on bare metal.  The
-OpenStack services are configured to run within a virtual machine (called OS
-domU) on the XenServer host. The VM uses the XAPI toolstack to communicate with
-the host over a network connection (see `MGT_BRIDGE_OR_NET_NAME`).
-
-The provided localrc helps to build a basic environment.
-
-## Introduction
-
-### Requirements
-
- - An internet-enabled network with a DHCP server on it
- - XenServer box plugged in to the same network
-This network will be used as the OpenStack management network. The VM Network
-and the Public Network will not be connected to any physical interfaces, only
-new virtual networks will be created by the `install_os_domU.sh` script.
-
-### Steps to follow
-
- - Install XenServer
- - Download Devstack to XenServer
- - Customise `localrc`
- - Start `install_os_domU.sh` script
-
-### Brief explanation
-
-The `install_os_domU.sh` script will:
- - Setup XenAPI plugins
- - Create the named networks, if they don't exist
- - Preseed-Netinstall an Ubuntu Virtual Machine (NOTE: you can save and reuse
-   it, see [Reuse the Ubuntu VM](#reuse-the-ubuntu-vm)), with 1 network
-   interface:
-   - `eth0` - Connected to `UBUNTU_INST_BRIDGE_OR_NET_NAME`, defaults to
-     `MGT_BRIDGE_OR_NET_NAME`
- - After the Ubuntu install process finished, the network configuration is
- modified to:
-   - `eth0` - Management interface, connected to `MGT_BRIDGE_OR_NET_NAME`. Xapi
-     must be accessible through this network.
-   - `eth1` - VM interface, connected to `VM_BRIDGE_OR_NET_NAME`
-   - `eth2` - Public interface, connected to `PUB_BRIDGE_OR_NET_NAME`
- - Start devstack inside the created OpenStack VM
-
-## Step 1: Install Xenserver
-Install XenServer on a clean box. You can download the latest XenServer for
-free from: http://www.xenserver.org/
-
-The XenServer IP configuration depends on your local network setup. If you are
-using dhcp, make a reservation for XenServer, so its IP address won't change
-over time. Make a note of the XenServer's IP address, as it has to be specified
-in `localrc`. The other option is to manually specify the IP setup for the
-XenServer box. Please make sure, that a gateway and a nameserver is configured,
-as `install_os_domU.sh` will connect to github.com to get source-code snapshots.
-
-## Step 2: Download devstack
-On your XenServer host, run the following commands as root:
-
-    wget --no-check-certificate https://github.com/openstack-dev/devstack/zipball/master
-    unzip -o master -d ./devstack
-    cd devstack/*/
-
-## Step 3: Configure your localrc inside the devstack directory
-Devstack uses a localrc for user-specific configuration.  Note that
-the `XENAPI_PASSWORD` must be your dom0 root password.
-Of course, use real passwords if this machine is exposed.
-
-    cat > ./localrc <<EOF
-    # At the moment, we depend on github's snapshot function.
-    GIT_BASE="http://github.com"
-
-    # Passwords
-    # NOTE: these need to be specified, otherwise devstack will try
-    # to prompt for these passwords, blocking the install process.
-
-    DATABASE_PASSWORD=my_super_secret
-    ADMIN_PASSWORD=my_super_secret
-    SERVICE_PASSWORD=my_super_secret
-    RABBIT_PASSWORD=my_super_secret
-    SWIFT_HASH="66a3d6b56c1f479c8b4e70ab5c2000f5"
-    # This will be the password for the OpenStack VM (both stack and root users)
-    GUEST_PASSWORD=my_super_secret
-
-    # XenAPI parameters
-    # NOTE: The following must be set to your XenServer root password!
-
-    XENAPI_PASSWORD=my_xenserver_root_password
-
-    XENAPI_CONNECTION_URL="http://address_of_your_xenserver"
-    VNCSERVER_PROXYCLIENT_ADDRESS=address_of_your_xenserver
-
-    # Explicitly set virt driver
-    VIRT_DRIVER=xenserver
-
-    # Explicitly enable multi-host for nova-network HA
-    MULTI_HOST=1
-
-    # Give extra time for boot
-    ACTIVE_TIMEOUT=45
-
-    EOF
-
-## Step 4: Run `./install_os_domU.sh` from the `tools/xen` directory
-
-    cd tools/xen
-    ./install_os_domU.sh
-
-Once this script finishes executing, log into the VM (openstack domU) that it
-installed and tail the run.sh.log file. You will need to wait until it run.sh
-has finished executing.
-
-# Appendix
-
-This section contains useful information for running devstack in CI
-environments / using ubuntu network mirrors.
-
-## Use a specific Ubuntu mirror for installation
-
-To speed up the Ubuntu installation, you can use a specific mirror. To specify
-a mirror explicitly, include the following settings in your `localrc` file:
-
-    UBUNTU_INST_HTTP_HOSTNAME="archive.ubuntu.com"
-    UBUNTU_INST_HTTP_DIRECTORY="/ubuntu"
-
-These variables set the `mirror/http/hostname` and `mirror/http/directory`
-settings in the ubuntu preseed file. The minimal ubuntu VM will use the
-specified parameters.
-
-## Use an http proxy to speed up Ubuntu installation
-
-To further speed up the Ubuntu VM and package installation, an internal http
-proxy could be used. `squid-deb-proxy` has prooven to be stable. To use an http
-proxy, specify:
-
-    UBUNTU_INST_HTTP_PROXY="http://ubuntu-proxy.somedomain.com:8000"
-
-in your `localrc` file.
-
-## Reuse the Ubuntu VM
-
-Performing a minimal ubuntu installation could take a lot of time, depending on
-your mirror/network speed. If you run `install_os_domU.sh` script on a clean
-hypervisor, you can speed up the installation, by re-using the ubuntu vm from
-a previous installation.
-
-### Export the Ubuntu VM to an XVA
-
-Given you have an nfs export `TEMPLATE_NFS_DIR`:
-
-    TEMPLATE_FILENAME=devstack-jeos.xva
-    TEMPLATE_NAME=jeos_template_for_devstack
-    mountdir=$(mktemp -d)
-    mount -t nfs "$TEMPLATE_NFS_DIR" "$mountdir"
-    VM="$(xe template-list name-label="$TEMPLATE_NAME" --minimal)"
-    xe template-export template-uuid=$VM filename="$mountdir/$TEMPLATE_FILENAME"
-    umount "$mountdir"
-    rm -rf "$mountdir"
-
-### Import the Ubuntu VM
-
-Given you have an nfs export `TEMPLATE_NFS_DIR` where you exported the Ubuntu
-VM as `TEMPLATE_FILENAME`:
-
-    mountdir=$(mktemp -d)
-    mount -t nfs "$TEMPLATE_NFS_DIR" "$mountdir"
-    xe vm-import filename="$mountdir/$TEMPLATE_FILENAME"
-    umount "$mountdir"
-    rm -rf "$mountdir"
-
+.. _os-xenapi: https://github.com/openstack/os-xenapi/
diff --git a/tools/xen/build_xva.sh b/tools/xen/build_xva.sh
deleted file mode 100755
index 34ef719..0000000
--- a/tools/xen/build_xva.sh
+++ /dev/null
@@ -1,191 +0,0 @@
-#!/bin/bash
-
-# This script is run by install_os_domU.sh
-#
-# It modifies the ubuntu image created by install_os_domU.sh
-# and previously moodified by prepare_guest_template.sh
-#
-# This script is responsible for:
-# - pushing in the DevStack code
-# - creating run.sh, to run the code on boot
-# It does this by mounting the disk image of the VM.
-#
-# The resultant image is then templated and started
-# by install_os_domU.sh
-
-# Exit on errors
-set -o errexit
-# Echo commands
-set -o xtrace
-
-# This directory
-TOP_DIR=$(cd $(dirname "$0") && pwd)
-
-# Include onexit commands
-. $TOP_DIR/scripts/on_exit.sh
-
-# xapi functions
-. $TOP_DIR/functions
-
-# Source params - override xenrc params in your localrc to suite your taste
-source xenrc
-
-#
-# Parameters
-#
-GUEST_NAME="$1"
-
-function _print_interface_config {
-    local device_nr
-    local ip_address
-    local netmask
-
-    device_nr="$1"
-    ip_address="$2"
-    netmask="$3"
-
-    local device
-
-    device="eth${device_nr}"
-
-    echo "auto $device"
-    if [ $ip_address == "dhcp" ]; then
-        echo "iface $device inet dhcp"
-    else
-        echo "iface $device inet static"
-        echo "  address $ip_address"
-        echo "  netmask $netmask"
-    fi
-
-    # Turn off tx checksumming for better performance
-    echo "  post-up ethtool -K $device tx off"
-}
-
-function print_interfaces_config {
-    echo "auto lo"
-    echo "iface lo inet loopback"
-
-    _print_interface_config $PUB_DEV_NR $PUB_IP $PUB_NETMASK
-    _print_interface_config $VM_DEV_NR $VM_IP $VM_NETMASK
-    _print_interface_config $MGT_DEV_NR $MGT_IP $MGT_NETMASK
-}
-
-#
-# Mount the VDI
-#
-STAGING_DIR=$($TOP_DIR/scripts/manage-vdi open $GUEST_NAME 0 1 | grep -o "/tmp/tmp.[[:alnum:]]*")
-add_on_exit "$TOP_DIR/scripts/manage-vdi close $GUEST_NAME 0 1"
-
-# Make sure we have a stage
-if [ ! -d $STAGING_DIR/etc ]; then
-    echo "Stage is not properly set up!"
-    exit 1
-fi
-
-# Only support DHCP for now - don't support how different versions of Ubuntu handle resolv.conf
-if [ "$MGT_IP" != "dhcp" ] && [ "$PUB_IP" != "dhcp" ]; then
-    echo "Configuration without DHCP not supported"
-    exit 1
-fi
-
-# Copy over devstack
-rm -f /tmp/devstack.tar
-cd $TOP_DIR/../../
-tar --exclude='stage' --exclude='xen/xvas' --exclude='xen/nova' -cvf /tmp/devstack.tar .
-mkdir -p $STAGING_DIR/opt/stack/devstack
-tar xf /tmp/devstack.tar -C $STAGING_DIR/opt/stack/devstack
-cd $TOP_DIR
-
-# Create an systemd task for devstack
-cat >$STAGING_DIR/etc/systemd/system/devstack.service << EOF
-[Unit]
-Description=Install OpenStack by DevStack
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStartPre=/bin/rm -f /opt/stack/runsh.succeeded
-ExecStart=/bin/su -c "/opt/stack/run.sh" stack
-StandardOutput=tty
-StandardError=tty
-
-[Install]
-WantedBy=multi-user.target
-
-EOF
-
-# enable this service
-ln -s $STAGING_DIR/etc/systemd/system/devstack.service $STAGING_DIR/etc/systemd/system/multi-user.target.wants/devstack.service
-
-# Configure the hostname
-echo $GUEST_NAME > $STAGING_DIR/etc/hostname
-
-# Hostname must resolve for rabbit
-HOSTS_FILE_IP=$PUB_IP
-if [ $MGT_IP != "dhcp" ]; then
-    HOSTS_FILE_IP=$MGT_IP
-fi
-cat <<EOF >$STAGING_DIR/etc/hosts
-$HOSTS_FILE_IP $GUEST_NAME
-127.0.0.1 localhost localhost.localdomain
-EOF
-
-# Configure the network
-print_interfaces_config > $STAGING_DIR/etc/network/interfaces
-
-# Gracefully cp only if source file/dir exists
-function cp_it {
-    if [ -e $1 ] || [ -d $1 ]; then
-        cp -pRL $1 $2
-    fi
-}
-
-# Copy over your ssh keys and env if desired
-COPYENV=${COPYENV:-1}
-if [ "$COPYENV" = "1" ]; then
-    cp_it ~/.ssh $STAGING_DIR/opt/stack/.ssh
-    cp_it ~/.ssh/id_rsa.pub $STAGING_DIR/opt/stack/.ssh/authorized_keys
-    cp_it ~/.gitconfig $STAGING_DIR/opt/stack/.gitconfig
-    cp_it ~/.vimrc $STAGING_DIR/opt/stack/.vimrc
-    cp_it ~/.bashrc $STAGING_DIR/opt/stack/.bashrc
-fi
-
-# Configure run.sh
-cat <<EOF >$STAGING_DIR/opt/stack/run.sh
-#!/bin/bash
-set -eux
-(
-  flock -n 9 || exit 1
-
-  sudo chown -R stack /opt/stack
-
-  [ -e /opt/stack/runsh.succeeded ] && rm /opt/stack/runsh.succeeded
-  echo \$\$ >> /opt/stack/run_sh.pid
-
-  cd /opt/stack/devstack
-  ./unstack.sh || true
-  ./stack.sh
-
-  # Got to the end - success
-  touch /opt/stack/runsh.succeeded
-
-  # Update /etc/issue
-  (
-      echo "OpenStack VM - Installed by DevStack"
-      IPADDR=$(ip -4 address show eth0 | sed -n 's/.*inet \([0-9\.]\+\).*/\1/p')
-      echo "  Management IP:   $IPADDR"
-      echo -n "  Devstack run:    "
-      if [ -e /opt/stack/runsh.succeeded ]; then
-          echo "SUCCEEDED"
-      else
-          echo "FAILED"
-      fi
-      echo ""
-  ) > /opt/stack/issue
-  sudo cp /opt/stack/issue /etc/issue
-
-  rm /opt/stack/run_sh.pid
-) 9> /opt/stack/.runsh_lock
-EOF
-
-chmod 755 $STAGING_DIR/opt/stack/run.sh
diff --git a/tools/xen/devstackubuntu_latecommand.sh b/tools/xen/devstackubuntu_latecommand.sh
deleted file mode 100644
index 2afbe2c..0000000
--- a/tools/xen/devstackubuntu_latecommand.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-set -eux
-
-# Need to set barrier=0 to avoid a Xen bug
-# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/824089
-sed -i -e 's/errors=/barrier=0,errors=/' /etc/fstab
-
-# Allow root to login with a password
-sed -i -e 's/.*PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config
-
-# Install the XenServer tools so IP addresses are reported
-wget --no-proxy @XS_TOOLS_URL@ -O /root/tools.deb
-dpkg -i /root/tools.deb
-rm /root/tools.deb
diff --git a/tools/xen/devstackubuntupreseed.cfg b/tools/xen/devstackubuntupreseed.cfg
deleted file mode 100644
index 80f334b..0000000
--- a/tools/xen/devstackubuntupreseed.cfg
+++ /dev/null
@@ -1,471 +0,0 @@
-### Contents of the preconfiguration file (for squeeze)
-### Localization
-# Preseeding only locale sets language, country and locale.
-d-i debian-installer/locale string en_US
-
-# The values can also be preseeded individually for greater flexibility.
-#d-i debian-installer/language string en
-#d-i debian-installer/country string NL
-#d-i debian-installer/locale string en_GB.UTF-8
-# Optionally specify additional locales to be generated.
-#d-i localechooser/supported-locales en_US.UTF-8, nl_NL.UTF-8
-
-# Keyboard selection.
-# Disable automatic (interactive) keymap detection.
-d-i console-setup/ask_detect boolean false
-#d-i keyboard-configuration/modelcode string pc105
-d-i keyboard-configuration/layoutcode string us
-# To select a variant of the selected layout (if you leave this out, the
-# basic form of the layout will be used):
-#d-i keyboard-configuration/variantcode string dvorak
-
-### Network configuration
-# Disable network configuration entirely. This is useful for cdrom
-# installations on non-networked devices where the network questions,
-# warning and long timeouts are a nuisance.
-#d-i netcfg/enable boolean false
-
-# netcfg will choose an interface that has link if possible. This makes it
-# skip displaying a list if there is more than one interface.
-d-i netcfg/choose_interface select auto
-
-# To pick a particular interface instead:
-#d-i netcfg/choose_interface select eth1
-
-# If you have a slow dhcp server and the installer times out waiting for
-# it, this might be useful.
-d-i netcfg/dhcp_timeout string 120
-
-# If you prefer to configure the network manually, uncomment this line and
-# the static network configuration below.
-#d-i netcfg/disable_autoconfig boolean true
-
-# If you want the preconfiguration file to work on systems both with and
-# without a dhcp server, uncomment these lines and the static network
-# configuration below.
-#d-i netcfg/dhcp_failed note
-#d-i netcfg/dhcp_options select Configure network manually
-
-# Static network configuration.
-#d-i netcfg/get_nameservers string 192.168.1.1
-#d-i netcfg/get_ipaddress string 192.168.1.42
-#d-i netcfg/get_netmask string 255.255.255.0
-#d-i netcfg/get_gateway string 192.168.1.1
-#d-i netcfg/confirm_static boolean true
-
-# Any hostname and domain names assigned from dhcp take precedence over
-# values set here. However, setting the values still prevents the questions
-# from being shown, even if values come from dhcp.
-d-i netcfg/get_hostname string stack
-d-i netcfg/get_domain string stackpass
-
-# Disable that annoying WEP key dialog.
-d-i netcfg/wireless_wep string
-# The wacky dhcp hostname that some ISPs use as a password of sorts.
-#d-i netcfg/dhcp_hostname string radish
-
-# If non-free firmware is needed for the network or other hardware, you can
-# configure the installer to always try to load it, without prompting. Or
-# change to false to disable asking.
-#d-i hw-detect/load_firmware boolean true
-
-### Network console
-# Use the following settings if you wish to make use of the network-console
-# component for remote installation over SSH. This only makes sense if you
-# intend to perform the remainder of the installation manually.
-#d-i anna/choose_modules string network-console
-#d-i network-console/password password r00tme
-#d-i network-console/password-again password r00tme
-
-### Mirror settings
-# If you select ftp, the mirror/country string does not need to be set.
-#d-i mirror/protocol string ftp
-d-i mirror/country string manual
-d-i mirror/http/hostname string archive.ubuntu.com
-d-i mirror/http/directory string /ubuntu
-d-i mirror/http/proxy string
-
-# Alternatively: by default, the installer uses CC.archive.ubuntu.com where
-# CC is the ISO-3166-2 code for the selected country. You can preseed this
-# so that it does so without asking.
-#d-i mirror/http/mirror select CC.archive.ubuntu.com
-
-# Suite to install.
-#d-i mirror/suite string squeeze
-# Suite to use for loading installer components (optional).
-#d-i mirror/udeb/suite string squeeze
-# Components to use for loading installer components (optional).
-#d-i mirror/udeb/components multiselect main, restricted
-
-### Clock and time zone setup
-# Controls whether or not the hardware clock is set to UTC.
-d-i clock-setup/utc boolean true
-
-# You may set this to any valid setting for $TZ; see the contents of
-# /usr/share/zoneinfo/ for valid values.
-d-i time/zone string US/Pacific
-
-# Controls whether to use NTP to set the clock during the install
-d-i clock-setup/ntp boolean true
-# NTP server to use. The default is almost always fine here.
-d-i clock-setup/ntp-server string 0.us.pool.ntp.org
-
-### Partitioning
-## Partitioning example
-# If the system has free space you can choose to only partition that space.
-# This is only honoured if partman-auto/method (below) is not set.
-# Alternatives: custom, some_device, some_device_crypto, some_device_lvm.
-#d-i partman-auto/init_automatically_partition select biggest_free
-
-# Alternatively, you may specify a disk to partition. If the system has only
-# one disk the installer will default to using that, but otherwise the device
-# name must be given in traditional, non-devfs format (so e.g. /dev/hda or
-# /dev/sda, and not e.g. /dev/discs/disc0/disc).
-# For example, to use the first SCSI/SATA hard disk:
-#d-i partman-auto/disk string /dev/sda
-# In addition, you'll need to specify the method to use.
-# The presently available methods are:
-# - regular: use the usual partition types for your architecture
-# - lvm:     use LVM to partition the disk
-# - crypto:  use LVM within an encrypted partition
-d-i partman-auto/method string regular
-
-# If one of the disks that are going to be automatically partitioned
-# contains an old LVM configuration, the user will normally receive a
-# warning. This can be preseeded away...
-d-i partman-lvm/device_remove_lvm boolean true
-# The same applies to pre-existing software RAID array:
-d-i partman-md/device_remove_md boolean true
-# And the same goes for the confirmation to write the lvm partitions.
-d-i partman-lvm/confirm boolean true
-
-# For LVM partitioning, you can select how much of the volume group to use
-# for logical volumes.
-#d-i partman-auto-lvm/guided_size string max
-#d-i partman-auto-lvm/guided_size string 10GB
-#d-i partman-auto-lvm/guided_size string 50%
-
-# You can choose one of the three predefined partitioning recipes:
-# - atomic: all files in one partition
-# - home:   separate /home partition
-# - multi:  separate /home, /usr, /var, and /tmp partitions
-d-i partman-auto/choose_recipe select atomic
-
-# Or provide a recipe of your own...
-# If you have a way to get a recipe file into the d-i environment, you can
-# just point at it.
-#d-i partman-auto/expert_recipe_file string /hd-media/recipe
-
-# If not, you can put an entire recipe into the preconfiguration file in one
-# (logical) line. This example creates a small /boot partition, suitable
-# swap, and uses the rest of the space for the root partition:
-#d-i partman-auto/expert_recipe string                         \
-#      boot-root ::                                            \
-#              40 50 100 ext3                                  \
-#                      $primary{ } $bootable{ }                \
-#                      method{ format } format{ }              \
-#                      use_filesystem{ } filesystem{ ext3 }    \
-#                      mountpoint{ /boot }                     \
-#              .                                               \
-#              500 10000 1000000000 ext3                       \
-#                      method{ format } format{ }              \
-#                      use_filesystem{ } filesystem{ ext3 }    \
-#                      mountpoint{ / }                         \
-#              .                                               \
-#              64 512 300% linux-swap                          \
-#                      method{ swap } format{ }                \
-#              .
-
-# If you just want to change the default filesystem from ext3 to something
-# else, you can do that without providing a full recipe.
-d-i partman/default_filesystem string ext3
-
-# The full recipe format is documented in the file partman-auto-recipe.txt
-# included in the 'debian-installer' package or available from D-I source
-# repository. This also documents how to specify settings such as file
-# system labels, volume group names and which physical devices to include
-# in a volume group.
-
-# This makes partman automatically partition without confirmation, provided
-# that you told it what to do using one of the methods above.
-d-i partman-partitioning/confirm_write_new_label boolean true
-d-i partman/choose_partition select finish
-d-i partman/confirm boolean true
-d-i partman/confirm_nooverwrite boolean true
-
-## Partitioning using RAID
-# The method should be set to "raid".
-#d-i partman-auto/method string raid
-# Specify the disks to be partitioned. They will all get the same layout,
-# so this will only work if the disks are the same size.
-#d-i partman-auto/disk string /dev/sda /dev/sdb
-
-# Next you need to specify the physical partitions that will be used. 
-#d-i partman-auto/expert_recipe string \
-#      multiraid ::                                         \
-#              1000 5000 4000 raid                          \
-#                      $primary{ } method{ raid }           \
-#              .                                            \
-#              64 512 300% raid                             \
-#                      method{ raid }                       \
-#              .                                            \
-#              500 10000 1000000000 raid                    \
-#                      method{ raid }                       \
-#              .
-
-# Last you need to specify how the previously defined partitions will be
-# used in the RAID setup. Remember to use the correct partition numbers
-# for logical partitions. RAID levels 0, 1, 5, 6 and 10 are supported;
-# devices are separated using "#".
-# Parameters are:
-# <raidtype> <devcount> <sparecount> <fstype> <mountpoint> \
-#          <devices> <sparedevices>
-
-#d-i partman-auto-raid/recipe string \
-#    1 2 0 ext3 /                    \
-#          /dev/sda1#/dev/sdb1       \
-#    .                               \
-#    1 2 0 swap -                    \
-#          /dev/sda5#/dev/sdb5       \
-#    .                               \
-#    0 2 0 ext3 /home                \
-#          /dev/sda6#/dev/sdb6       \
-#    .
-
-# For additional information see the file partman-auto-raid-recipe.txt
-# included in the 'debian-installer' package or available from D-I source
-# repository.
-
-# This makes partman automatically partition without confirmation.
-d-i partman-md/confirm boolean true
-d-i partman-partitioning/confirm_write_new_label boolean true
-d-i partman/choose_partition select finish
-d-i partman/confirm boolean true
-d-i partman/confirm_nooverwrite boolean true
-
-## Controlling how partitions are mounted
-# The default is to mount by UUID, but you can also choose "traditional" to
-# use traditional device names, or "label" to try filesystem labels before
-# falling back to UUIDs.
-#d-i partman/mount_style select uuid
-
-### Base system installation
-# Configure APT to not install recommended packages by default. Use of this
-# option can result in an incomplete system and should only be used by very
-# experienced users.
-#d-i base-installer/install-recommends boolean false
-
-# The kernel image (meta) package to be installed; "none" can be used if no
-# kernel is to be installed.
-d-i base-installer/kernel/image string linux-virtual
-
-### Account setup
-# Skip creation of a root account (normal user account will be able to
-# use sudo). The default is false; preseed this to true if you want to set
-# a root password.
-d-i passwd/root-login boolean true
-# Alternatively, to skip creation of a normal user account.
-d-i passwd/make-user boolean false
-
-# Root password, either in clear text
-d-i passwd/root-password password stackpass
-d-i passwd/root-password-again password stackpass
-# or encrypted using an MD5 hash.
-#d-i passwd/root-password-crypted password [MD5 hash]
-
-# To create a normal user account.
-#d-i passwd/user-fullname string Ubuntu User
-#d-i passwd/username string ubuntu
-# Normal user's password, either in clear text
-#d-i passwd/user-password password insecure
-#d-i passwd/user-password-again password insecure
-# or encrypted using an MD5 hash.
-#d-i passwd/user-password-crypted password [MD5 hash]
-# Create the first user with the specified UID instead of the default.
-#d-i passwd/user-uid string 1010
-# The installer will warn about weak passwords. If you are sure you know
-# what you're doing and want to override it, uncomment this.
-d-i user-setup/allow-password-weak boolean true
-
-# The user account will be added to some standard initial groups. To
-# override that, use this.
-#d-i passwd/user-default-groups string audio cdrom video
-
-# Set to true if you want to encrypt the first user's home directory.
-d-i user-setup/encrypt-home boolean false
-
-### Apt setup
-# You can choose to install restricted and universe software, or to install
-# software from the backports repository.
-d-i apt-setup/restricted boolean true
-d-i apt-setup/universe boolean true
-d-i apt-setup/backports boolean true
-# Uncomment this if you don't want to use a network mirror.
-#d-i apt-setup/use_mirror boolean false
-# Select which update services to use; define the mirrors to be used.
-# Values shown below are the normal defaults.
-#d-i apt-setup/services-select multiselect security
-#d-i apt-setup/security_host string security.ubuntu.com
-#d-i apt-setup/security_path string /ubuntu
-
-# Additional repositories, local[0-9] available
-#d-i apt-setup/local0/repository string \
-#       http://local.server/ubuntu squeeze main
-#d-i apt-setup/local0/comment string local server
-# Enable deb-src lines
-#d-i apt-setup/local0/source boolean true
-# URL to the public key of the local repository; you must provide a key or
-# apt will complain about the unauthenticated repository and so the
-# sources.list line will be left commented out
-#d-i apt-setup/local0/key string http://local.server/key
-
-# By default the installer requires that repositories be authenticated
-# using a known gpg key. This setting can be used to disable that
-# authentication. Warning: Insecure, not recommended.
-#d-i debian-installer/allow_unauthenticated boolean true
-
-### Package selection
-#tasksel tasksel/first multiselect ubuntu-desktop
-#tasksel tasksel/first multiselect lamp-server, print-server
-#tasksel tasksel/first multiselect kubuntu-desktop
-tasksel tasksel/first multiselect openssh-server
-
-# Individual additional packages to install
-d-i pkgsel/include string cracklib-runtime curl wget ssh openssh-server tcpdump ethtool git sudo python-netaddr coreutils
-
-# Whether to upgrade packages after debootstrap.
-# Allowed values: none, safe-upgrade, full-upgrade
-d-i pkgsel/upgrade select safe-upgrade
-
-# Language pack selection
-#d-i pkgsel/language-packs multiselect de, en, zh
-
-# Policy for applying updates. May be "none" (no automatic updates),
-# "unattended-upgrades" (install security updates automatically), or
-# "landscape" (manage system with Landscape).
-d-i pkgsel/update-policy select unattended-upgrades
-
-# Some versions of the installer can report back on what software you have
-# installed, and what software you use. The default is not to report back,
-# but sending reports helps the project determine what software is most
-# popular and include it on CDs.
-#popularity-contest popularity-contest/participate boolean false
-
-# By default, the system's locate database will be updated after the
-# installer has finished installing most packages. This may take a while, so
-# if you don't want it, you can set this to "false" to turn it off.
-d-i pkgsel/updatedb boolean false
-
-### Boot loader installation
-# Grub is the default boot loader (for x86). If you want lilo installed
-# instead, uncomment this:
-#d-i grub-installer/skip boolean true
-# To also skip installing lilo, and install no bootloader, uncomment this
-# too:
-#d-i lilo-installer/skip boolean true
-
-# With a few exceptions for unusual partitioning setups, GRUB 2 is now the
-# default. If you need GRUB Legacy for some particular reason, then
-# uncomment this:
-d-i grub-installer/grub2_instead_of_grub_legacy boolean false
-
-# This is fairly safe to set, it makes grub install automatically to the MBR
-# if no other operating system is detected on the machine.
-d-i grub-installer/only_debian boolean true
-
-# This one makes grub-installer install to the MBR if it also finds some other
-# OS, which is less safe as it might not be able to boot that other OS.
-d-i grub-installer/with_other_os boolean true
-
-# Alternatively, if you want to install to a location other than the mbr,
-# uncomment and edit these lines:
-#d-i grub-installer/only_debian boolean false
-#d-i grub-installer/with_other_os boolean false
-#d-i grub-installer/bootdev  string (hd0,0)
-# To install grub to multiple disks:
-#d-i grub-installer/bootdev  string (hd0,0) (hd1,0) (hd2,0)
-
-# Optional password for grub, either in clear text
-#d-i grub-installer/password password r00tme
-#d-i grub-installer/password-again password r00tme
-# or encrypted using an MD5 hash, see grub-md5-crypt(8).
-#d-i grub-installer/password-crypted password [MD5 hash]
-
-# Use the following option to add additional boot parameters for the
-# installed system (if supported by the bootloader installer).
-# Note: options passed to the installer will be added automatically.
-#d-i debian-installer/add-kernel-opts string nousb
-
-### Finishing up the installation
-# During installations from serial console, the regular virtual consoles
-# (VT1-VT6) are normally disabled in /etc/inittab. Uncomment the next
-# line to prevent this.
-d-i finish-install/keep-consoles boolean true
-
-# Avoid that last message about the install being complete.
-d-i finish-install/reboot_in_progress note
-
-# This will prevent the installer from ejecting the CD during the reboot,
-# which is useful in some situations.
-#d-i cdrom-detect/eject boolean false
-
-# This is how to make the installer shutdown when finished, but not
-# reboot into the installed system.
-#d-i debian-installer/exit/halt boolean true
-# This will power off the machine instead of just halting it.
-#d-i debian-installer/exit/poweroff boolean true
-
-### X configuration
-# X can detect the right driver for some cards, but if you're preseeding,
-# you override whatever it chooses. Still, vesa will work most places.
-#xserver-xorg xserver-xorg/config/device/driver select vesa
-
-# A caveat with mouse autodetection is that if it fails, X will retry it
-# over and over. So if it's preseeded to be done, there is a possibility of
-# an infinite loop if the mouse is not autodetected.
-#xserver-xorg xserver-xorg/autodetect_mouse boolean true
-
-# Monitor autodetection is recommended.
-xserver-xorg xserver-xorg/autodetect_monitor boolean true
-# Uncomment if you have an LCD display.
-#xserver-xorg xserver-xorg/config/monitor/lcd boolean true
-# X has three configuration paths for the monitor. Here's how to preseed
-# the "medium" path, which is always available. The "simple" path may not
-# be available, and the "advanced" path asks too many questions.
-xserver-xorg xserver-xorg/config/monitor/selection-method \
-       select medium
-xserver-xorg xserver-xorg/config/monitor/mode-list \
-       select 1024x768 @ 60 Hz
-
-### Preseeding other packages
-# Depending on what software you choose to install, or if things go wrong
-# during the installation process, it's possible that other questions may
-# be asked. You can preseed those too, of course. To get a list of every
-# possible question that could be asked during an install, do an
-# installation, and then run these commands:
-#   debconf-get-selections --installer > file
-#   debconf-get-selections >> file
-
-
-#### Advanced options
-### Running custom commands during the installation
-# d-i preseeding is inherently not secure. Nothing in the installer checks
-# for attempts at buffer overflows or other exploits of the values of a
-# preconfiguration file like this one. Only use preconfiguration files from
-# trusted locations! To drive that home, and because it's generally useful,
-# here's a way to run any shell command you'd like inside the installer,
-# automatically.
-
-# This first command is run as early as possible, just after
-# preseeding is read.
-#d-i preseed/early_command string anna-install some-udeb
-# This command is run immediately before the partitioner starts. It may be
-# useful to apply dynamic partitioner preseeding that depends on the state
-# of the disks (which may not be visible when preseed/early_command runs).
-#d-i partman/early_command \
-#       string debconf-set partman-auto/disk "$(list-devices disk | head -n1)"
-# This command is run just before the install finishes, but when there is
-# still a usable /target directory. You can chroot to /target and use it
-# directly, or use the apt-install and in-target commands to easily install
-# packages and run commands in the target system.
-d-i preseed/late_command string
diff --git a/tools/xen/functions b/tools/xen/functions
deleted file mode 100644
index bc0c515..0000000
--- a/tools/xen/functions
+++ /dev/null
@@ -1,341 +0,0 @@
-#!/bin/bash
-
-function die_with_error {
-    local err_msg
-
-    err_msg="$1"
-
-    echo "$err_msg" >&2
-    exit 1
-}
-
-function xapi_plugin_location {
-    for PLUGIN_DIR in "/etc/xapi.d/plugins/" "/usr/lib/xcp/plugins/" "/usr/lib/xapi/plugins" "/usr/lib64/xapi/plugins"; do
-        if [ -d $PLUGIN_DIR ]; then
-            echo $PLUGIN_DIR
-            return 0
-        fi
-    done
-    return 1
-}
-
-function create_directory_for_kernels {
-    if [ -d "/boot/guest" ]; then
-        echo "INFO: /boot/guest directory already exists, using that" >&2
-    else
-        local local_path
-        local_path="$(get_local_sr_path)/os-guest-kernels"
-        mkdir -p $local_path
-        ln -s $local_path /boot/guest
-    fi
-}
-
-function create_directory_for_images {
-    if [ -d "/images" ]; then
-        echo "INFO: /images directory already exists, using that" >&2
-    else
-        local local_path
-        local_path="$(get_local_sr_path)/os-images"
-        mkdir -p $local_path
-        ln -s $local_path /images
-    fi
-}
-
-function get_local_sr {
-    xe pool-list params=default-SR minimal=true
-}
-
-function get_local_sr_path {
-    pbd_path="/var/run/sr-mount/$(get_local_sr)"
-    pbd_device_config_path=`xe pbd-list sr-uuid=$(get_local_sr) params=device-config | grep " path: "`
-    if [ -n "$pbd_device_config_path" ]; then
-        pbd_uuid=`xe pbd-list sr-uuid=$(get_local_sr) minimal=true`
-        pbd_path=`xe pbd-param-get uuid=$pbd_uuid param-name=device-config param-key=path || echo ""`
-    fi
-    echo $pbd_path
-}
-
-function find_ip_by_name {
-    local guest_name="$1"
-    local interface="$2"
-
-    local period=10
-    local max_tries=10
-    local i=0
-
-    while true; do
-        if [ $i -ge $max_tries ]; then
-            echo "Timeout: ip address for interface $interface of $guest_name"
-            exit 11
-        fi
-
-        ipaddress=$(xe vm-list --minimal \
-                    name-label=$guest_name \
-                    params=networks | sed -ne "s,^.*${interface}/ip: \([0-9.]*\).*\$,\1,p")
-
-        if [ -z "$ipaddress" ]; then
-            sleep $period
-            i=$((i+1))
-        else
-            echo $ipaddress
-            break
-        fi
-    done
-}
-
-function _vm_uuid {
-    local vm_name_label
-
-    vm_name_label="$1"
-
-    xe vm-list name-label="$vm_name_label" --minimal
-}
-
-function _create_new_network {
-    local name_label
-    name_label=$1
-
-    xe network-create name-label="$name_label"
-}
-
-function _multiple_networks_with_name {
-    local name_label
-    name_label=$1
-
-    # A comma indicates multiple matches
-    xe network-list name-label="$name_label" --minimal | grep -q ","
-}
-
-function _network_exists {
-    local name_label
-    name_label=$1
-
-    ! [ -z "$(xe network-list name-label="$name_label" --minimal)" ]
-}
-
-function _bridge_exists {
-    local bridge
-    bridge=$1
-
-    ! [ -z "$(xe network-list bridge="$bridge" --minimal)" ]
-}
-
-function _network_uuid {
-    local bridge_or_net_name
-    bridge_or_net_name=$1
-
-    if _bridge_exists "$bridge_or_net_name"; then
-        xe network-list bridge="$bridge_or_net_name" --minimal
-    else
-        xe network-list name-label="$bridge_or_net_name" --minimal
-    fi
-}
-
-function add_interface {
-    local vm_name_label
-    local bridge_or_network_name
-
-    vm_name_label="$1"
-    bridge_or_network_name="$2"
-    device_number="$3"
-
-    local vm
-    local net
-
-    vm=$(_vm_uuid "$vm_name_label")
-    net=$(_network_uuid "$bridge_or_network_name")
-    xe vif-create network-uuid=$net vm-uuid=$vm device=$device_number
-}
-
-function setup_network {
-    local bridge_or_net_name
-    bridge_or_net_name=$1
-
-    if ! _bridge_exists "$bridge_or_net_name"; then
-        if _network_exists "$bridge_or_net_name"; then
-            if _multiple_networks_with_name "$bridge_or_net_name"; then
-                cat >&2 << EOF
-ERROR: Multiple networks found matching name-label to "$bridge_or_net_name"
-please review your XenServer network configuration / localrc file.
-EOF
-                exit 1
-            fi
-        else
-            _create_new_network "$bridge_or_net_name"
-        fi
-    fi
-}
-
-function bridge_for {
-    local bridge_or_net_name
-    bridge_or_net_name=$1
-
-    if _bridge_exists "$bridge_or_net_name"; then
-        echo "$bridge_or_net_name"
-    else
-        xe network-list name-label="$bridge_or_net_name" params=bridge --minimal
-    fi
-}
-
-function xenapi_ip_on {
-    local bridge_or_net_name
-    bridge_or_net_name=$1
-
-    ip -4 addr show $(bridge_for "$bridge_or_net_name") |\
-    awk '/inet/{split($2, ip, "/"); print ip[1];}'
-}
-
-function xenapi_is_listening_on {
-    local bridge_or_net_name
-    bridge_or_net_name=$1
-
-    ! [ -z $(xenapi_ip_on "$bridge_or_net_name") ]
-}
-
-function parameter_is_specified {
-    local parameter_name
-    parameter_name=$1
-
-    compgen -v | grep "$parameter_name"
-}
-
-function append_kernel_cmdline {
-    local vm_name_label
-    local kernel_args
-
-    vm_name_label="$1"
-    kernel_args="$2"
-
-    local vm
-    local pv_args
-
-    vm=$(_vm_uuid "$vm_name_label")
-    pv_args=$(xe vm-param-get param-name=PV-args uuid=$vm)
-    xe vm-param-set PV-args="$pv_args $kernel_args" uuid=$vm
-}
-
-function destroy_all_vifs_of {
-    local vm_name_label
-
-    vm_name_label="$1"
-
-    local vm
-
-    vm=$(_vm_uuid "$vm_name_label")
-    IFS=,
-    for vif in $(xe vif-list vm-uuid=$vm --minimal); do
-        xe vif-destroy uuid="$vif"
-    done
-    unset IFS
-}
-
-function have_multiple_hosts {
-    xe host-list --minimal | grep -q ","
-}
-
-function attach_network {
-    local bridge_or_net_name
-
-    bridge_or_net_name="$1"
-
-    local net
-    local host
-
-    net=$(_network_uuid "$bridge_or_net_name")
-    host=$(xe host-list --minimal)
-
-    xe network-attach uuid=$net host-uuid=$host
-}
-
-function set_vm_memory {
-    local vm_name_label
-    local memory
-
-    vm_name_label="$1"
-    memory="$2"
-
-    local vm
-
-    vm=$(_vm_uuid "$vm_name_label")
-
-    xe vm-memory-limits-set \
-        static-min=${memory}MiB \
-        static-max=${memory}MiB \
-        dynamic-min=${memory}MiB \
-        dynamic-max=${memory}MiB \
-        uuid=$vm
-}
-
-function max_vcpus {
-    local vm_name_label
-
-    vm_name_label="$1"
-
-    local vm
-    local host
-    local cpu_count
-
-    host=$(xe host-list --minimal)
-    vm=$(_vm_uuid "$vm_name_label")
-
-    cpu_count=$(xe host-param-get \
-        param-name=cpu_info \
-        uuid=$host |
-        sed -e 's/^.*cpu_count: \([0-9]*\);.*$/\1/g')
-
-    if [ -z "$cpu_count" ]; then
-        # get dom0's vcpu count
-        cpu_count=$(cat /proc/cpuinfo | grep processor | wc -l)
-    fi
-
-    # Assert cpu_count is not empty
-    [ -n "$cpu_count" ]
-
-    # Assert ithas a numeric nonzero value
-    expr "$cpu_count" + 0
-
-    # 8 VCPUs should be enough for devstack VM; avoid using too
-    # many VCPUs:
-    # 1. too many VCPUs may trigger a kernel bug which result VM
-    #    not able to boot:
-    #    https://kernel.googlesource.com/pub/scm/linux/kernel/git/wsa/linux/+/e2e004acc7cbe3c531e752a270a74e95cde3ea48
-    # 2. The remaining CPUs can be used for other purpose:
-    #    e.g. boot test VMs.
-    MAX_VCPUS=8
-    if [ $cpu_count -ge $MAX_VCPUS ]; then
-        cpu_count=$MAX_VCPUS
-    fi
-
-    xe vm-param-set uuid=$vm VCPUs-max=$cpu_count
-    xe vm-param-set uuid=$vm VCPUs-at-startup=$cpu_count
-}
-
-function get_domid {
-    local vm_name_label
-
-    vm_name_label="$1"
-
-    xe vm-list name-label="$vm_name_label" params=dom-id minimal=true
-}
-
-function install_conntrack_tools {
-    local xs_host
-    local xs_ver_major
-    local centos_ver
-    local conntrack_conf
-    xs_host=$(xe host-list --minimal)
-    xs_ver_major=$(xe host-param-get uuid=$xs_host param-name=software-version param-key=product_version_text_short | cut -d'.' -f 1)
-    if [ $xs_ver_major -gt 6 ]; then
-        # Only support conntrack-tools in Dom0 with XS7.0 and above
-        if [ ! -f /usr/sbin/conntrackd ]; then
-            sed -i s/#baseurl=/baseurl=/g /etc/yum.repos.d/CentOS-Base.repo
-            centos_ver=$(yum version nogroups |grep Installed | cut -d' ' -f 2 | cut -d'/' -f 1 | cut -d'-' -f 1)
-            yum install -y --enablerepo=base --releasever=$centos_ver conntrack-tools
-            # Backup conntrackd.conf after install conntrack-tools, use the one with statistic mode
-            mv /etc/conntrackd/conntrackd.conf /etc/conntrackd/conntrackd.conf.back
-            conntrack_conf=$(find /usr/share/doc -name conntrackd.conf |grep stats)
-            cp $conntrack_conf /etc/conntrackd/conntrackd.conf
-        fi
-        service conntrackd restart
-    fi
-}
diff --git a/tools/xen/install_os_domU.sh b/tools/xen/install_os_domU.sh
deleted file mode 100755
index f4ca71a..0000000
--- a/tools/xen/install_os_domU.sh
+++ /dev/null
@@ -1,418 +0,0 @@
-#!/bin/bash
-
-# This script must be run on a XenServer or XCP machine
-#
-# It creates a DomU VM that runs OpenStack services
-#
-# For more details see: README.md
-
-set -o errexit
-set -o nounset
-set -o xtrace
-
-export LC_ALL=C
-
-# This directory
-THIS_DIR=$(cd $(dirname "$0") && pwd)
-
-# Include onexit commands
-. $THIS_DIR/scripts/on_exit.sh
-
-# xapi functions
-. $THIS_DIR/functions
-
-#
-# Get Settings
-#
-TOP_DIR=$(cd $THIS_DIR/../../ && pwd)
-source $TOP_DIR/inc/meta-config
-rm -f $TOP_DIR/.localrc.auto
-extract_localrc_section $TOP_DIR/local.conf $TOP_DIR/localrc $TOP_DIR/.localrc.auto
-
-# Source params - override xenrc params in your localrc to suit your taste
-source $THIS_DIR/xenrc
-
-xe_min()
-{
-    local cmd="$1"
-    shift
-    xe "$cmd" --minimal "$@"
-}
-
-#
-# Prepare Dom0
-# including installing XenAPI plugins
-#
-
-cd $THIS_DIR
-
-# Die if multiple hosts listed
-if have_multiple_hosts; then
-    cat >&2 << EOF
-ERROR: multiple hosts found. This might mean that the XenServer is a member
-of a pool - Exiting.
-EOF
-    exit 1
-fi
-
-#
-# Configure Networking
-#
-
-MGT_NETWORK=`xe pif-list management=true params=network-uuid minimal=true`
-MGT_BRIDGE_OR_NET_NAME=`xe network-list uuid=$MGT_NETWORK params=bridge minimal=true`
-
-setup_network "$VM_BRIDGE_OR_NET_NAME"
-setup_network "$MGT_BRIDGE_OR_NET_NAME"
-setup_network "$PUB_BRIDGE_OR_NET_NAME"
-
-if parameter_is_specified "FLAT_NETWORK_BRIDGE"; then
-    if [ "$(bridge_for "$VM_BRIDGE_OR_NET_NAME")" != "$(bridge_for "$FLAT_NETWORK_BRIDGE")" ]; then
-        cat >&2 << EOF
-ERROR: FLAT_NETWORK_BRIDGE is specified in localrc file, and either no network
-found on XenServer by searching for networks by that value as name-label or
-bridge name or the network found does not match the network specified by
-VM_BRIDGE_OR_NET_NAME. Please check your localrc file.
-EOF
-        exit 1
-    fi
-fi
-
-if ! xenapi_is_listening_on "$MGT_BRIDGE_OR_NET_NAME"; then
-    cat >&2 << EOF
-ERROR: XenAPI does not have an assigned IP address on the management network.
-please review your XenServer network configuration / localrc file.
-EOF
-    exit 1
-fi
-
-HOST_IP=$(xenapi_ip_on "$MGT_BRIDGE_OR_NET_NAME")
-
-# Set up ip forwarding, but skip on xcp-xapi
-if [ -a /etc/sysconfig/network ]; then
-    if ! grep -q "FORWARD_IPV4=YES" /etc/sysconfig/network; then
-        # FIXME: This doesn't work on reboot!
-        echo "FORWARD_IPV4=YES" >> /etc/sysconfig/network
-    fi
-fi
-# Also, enable ip forwarding in rc.local, since the above trick isn't working
-if ! grep -q  "echo 1 >/proc/sys/net/ipv4/ip_forward" /etc/rc.local; then
-    echo "echo 1 >/proc/sys/net/ipv4/ip_forward" >> /etc/rc.local
-fi
-# Enable ip forwarding at runtime as well
-echo 1 > /proc/sys/net/ipv4/ip_forward
-
-
-#
-# Shutdown previous runs
-#
-
-DO_SHUTDOWN=${DO_SHUTDOWN:-1}
-CLEAN_TEMPLATES=${CLEAN_TEMPLATES:-false}
-if [ "$DO_SHUTDOWN" = "1" ]; then
-    # Shutdown all domU's that created previously
-    clean_templates_arg=""
-    if $CLEAN_TEMPLATES; then
-        clean_templates_arg="--remove-templates"
-    fi
-    ./scripts/uninstall-os-vpx.sh $clean_templates_arg
-
-    # Destroy any instances that were launched
-    for uuid in `xe vm-list | grep -1 instance | grep uuid | sed "s/.*\: //g"`; do
-        echo "Shutting down nova instance $uuid"
-        xe vm-uninstall uuid=$uuid force=true
-    done
-
-    # Destroy orphaned vdis
-    for uuid in `xe vdi-list | grep -1 Glance | grep uuid | sed "s/.*\: //g"`; do
-        xe vdi-destroy uuid=$uuid
-    done
-fi
-
-
-#
-# Create Ubuntu VM template
-# and/or create VM from template
-#
-
-GUEST_NAME=${GUEST_NAME:-"DevStackOSDomU"}
-TNAME="jeos_template_for_devstack"
-SNAME_TEMPLATE="jeos_snapshot_for_devstack"
-SNAME_FIRST_BOOT="before_first_boot"
-
-function wait_for_VM_to_halt {
-    set +x
-    echo "Waiting for the VM to halt.  Progress in-VM can be checked with XenCenter or xl console:"
-    mgmt_ip=$(echo $XENAPI_CONNECTION_URL | tr -d -c '1234567890.')
-    domid=$(get_domid "$GUEST_NAME")
-    echo "ssh root@$mgmt_ip \"xl console $domid\""
-    while true; do
-        state=$(xe_min vm-list name-label="$GUEST_NAME" power-state=halted)
-        if [ -n "$state" ]; then
-            break
-        else
-            echo -n "."
-            sleep 20
-        fi
-    done
-    set -x
-}
-
-templateuuid=$(xe template-list name-label="$TNAME")
-if [ -z "$templateuuid" ]; then
-    #
-    # Install Ubuntu over network
-    #
-    UBUNTU_INST_BRIDGE_OR_NET_NAME=${UBUNTU_INST_BRIDGE_OR_NET_NAME:-"$MGT_BRIDGE_OR_NET_NAME"}
-
-    # always update the preseed file, incase we have a newer one
-    PRESEED_URL=${PRESEED_URL:-""}
-    if [ -z "$PRESEED_URL" ]; then
-        PRESEED_URL="${HOST_IP}/devstackubuntupreseed.cfg"
-
-        HTTP_SERVER_LOCATION="/opt/xensource/www"
-        if [ ! -e $HTTP_SERVER_LOCATION ]; then
-            HTTP_SERVER_LOCATION="/var/www/html"
-            mkdir -p $HTTP_SERVER_LOCATION
-        fi
-
-        # Copy the tools DEB to the XS web server
-        XS_TOOLS_URL="https://github.com/downloads/citrix-openstack/warehouse/xe-guest-utilities_5.6.100-651_amd64.deb"
-        ISO_DIR="/opt/xensource/packages/iso"
-        if [ -e "$ISO_DIR" ]; then
-            TOOLS_ISO=$(ls -1 $ISO_DIR/*-tools-*.iso | head -1)
-            TMP_DIR=/tmp/temp.$RANDOM
-            mkdir -p $TMP_DIR
-            mount -o loop $TOOLS_ISO $TMP_DIR
-            # the target deb package maybe *amd64.deb or *all.deb,
-            # so use *amd64.deb by default. If it doesn't exist,
-            # then use *all.deb.
-            DEB_FILE=$(ls $TMP_DIR/Linux/*amd64.deb || ls $TMP_DIR/Linux/*all.deb)
-            cp $DEB_FILE $HTTP_SERVER_LOCATION
-            umount $TMP_DIR
-            rmdir $TMP_DIR
-            XS_TOOLS_URL=${HOST_IP}/$(basename $DEB_FILE)
-        fi
-
-        cp -f $THIS_DIR/devstackubuntupreseed.cfg $HTTP_SERVER_LOCATION
-        cp -f $THIS_DIR/devstackubuntu_latecommand.sh $HTTP_SERVER_LOCATION/latecommand.sh
-
-        sed \
-            -e "s,\(d-i mirror/http/hostname string\).*,\1 $UBUNTU_INST_HTTP_HOSTNAME,g" \
-            -e "s,\(d-i mirror/http/directory string\).*,\1 $UBUNTU_INST_HTTP_DIRECTORY,g" \
-            -e "s,\(d-i mirror/http/proxy string\).*,\1 $UBUNTU_INST_HTTP_PROXY,g" \
-            -e "s,\(d-i passwd/root-password password\).*,\1 $GUEST_PASSWORD,g" \
-            -e "s,\(d-i passwd/root-password-again password\).*,\1 $GUEST_PASSWORD,g" \
-            -e "s,\(d-i preseed/late_command string\).*,\1 in-target mkdir -p /tmp; in-target wget --no-proxy ${HOST_IP}/latecommand.sh -O /root/latecommand.sh; in-target bash /root/latecommand.sh,g" \
-            -i "${HTTP_SERVER_LOCATION}/devstackubuntupreseed.cfg"
-
-        sed \
-            -e "s,@XS_TOOLS_URL@,$XS_TOOLS_URL,g" \
-            -i "${HTTP_SERVER_LOCATION}/latecommand.sh"
-    fi
-
-    # Update the template
-    $THIS_DIR/scripts/install_ubuntu_template.sh $PRESEED_URL
-
-    # create a new VM from the given template with eth0 attached to the given
-    # network
-    $THIS_DIR/scripts/install-os-vpx.sh \
-        -t "$UBUNTU_INST_TEMPLATE_NAME" \
-        -n "$UBUNTU_INST_BRIDGE_OR_NET_NAME" \
-        -l "$GUEST_NAME"
-
-    set_vm_memory "$GUEST_NAME" "1024"
-
-    xe vm-start vm="$GUEST_NAME"
-
-    # wait for install to finish
-    wait_for_VM_to_halt
-
-    # set VM to restart after a reboot
-    vm_uuid=$(xe_min vm-list name-label="$GUEST_NAME")
-    xe vm-param-set actions-after-reboot=Restart uuid="$vm_uuid"
-
-    # Make template from VM
-    snuuid=$(xe vm-snapshot vm="$GUEST_NAME" new-name-label="$SNAME_TEMPLATE")
-    xe snapshot-clone uuid=$snuuid new-name-label="$TNAME"
-else
-    #
-    # Template already installed, create VM from template
-    #
-    vm_uuid=$(xe vm-install template="$TNAME" new-name-label="$GUEST_NAME")
-fi
-
-if [ -n "${EXIT_AFTER_JEOS_INSTALLATION:-}" ]; then
-    echo "User requested to quit after JEOS installation"
-    exit 0
-fi
-
-#
-# Prepare VM for DevStack
-#
-xe vm-param-set other-config:os-vpx=true uuid="$vm_uuid"
-
-# Install XenServer tools, and other such things
-$THIS_DIR/prepare_guest_template.sh "$GUEST_NAME"
-
-# Set virtual machine parameters
-set_vm_memory "$GUEST_NAME" "$OSDOMU_MEM_MB"
-
-# Max out VCPU count for better performance
-max_vcpus "$GUEST_NAME"
-
-# Wipe out all network cards
-destroy_all_vifs_of "$GUEST_NAME"
-
-# Add only one interface to prepare the guest template
-add_interface "$GUEST_NAME" "$MGT_BRIDGE_OR_NET_NAME" "0"
-
-# start the VM to run the prepare steps
-xe vm-start vm="$GUEST_NAME"
-
-# Wait for prep script to finish and shutdown system
-wait_for_VM_to_halt
-
-## Setup network cards
-# Wipe out all
-destroy_all_vifs_of "$GUEST_NAME"
-# Tenant network
-add_interface "$GUEST_NAME" "$VM_BRIDGE_OR_NET_NAME" "$VM_DEV_NR"
-# Management network
-add_interface "$GUEST_NAME" "$MGT_BRIDGE_OR_NET_NAME" "$MGT_DEV_NR"
-# Public network
-add_interface "$GUEST_NAME" "$PUB_BRIDGE_OR_NET_NAME" "$PUB_DEV_NR"
-
-#
-# Inject DevStack inside VM disk
-#
-$THIS_DIR/build_xva.sh "$GUEST_NAME"
-
-FLAT_NETWORK_BRIDGE="${FLAT_NETWORK_BRIDGE:-$(bridge_for "$VM_BRIDGE_OR_NET_NAME")}"
-append_kernel_cmdline "$GUEST_NAME" "flat_network_bridge=${FLAT_NETWORK_BRIDGE}"
-
-# Add a separate xvdb, if it was requested
-if [[ "0" != "$XEN_XVDB_SIZE_GB" ]]; then
-    vm=$(xe vm-list name-label="$GUEST_NAME" --minimal)
-
-    # Add a new disk
-    localsr=$(get_local_sr)
-    extra_vdi=$(xe vdi-create \
-        name-label=xvdb-added-by-devstack \
-        virtual-size="${XEN_XVDB_SIZE_GB}GiB" \
-        sr-uuid=$localsr type=user)
-    xe vbd-create vm-uuid=$vm vdi-uuid=$extra_vdi device=1
-fi
-
-# create a snapshot before the first boot
-# to allow a quick re-run with the same settings
-xe vm-snapshot vm="$GUEST_NAME" new-name-label="$SNAME_FIRST_BOOT"
-
-#
-# Run DevStack VM
-#
-xe vm-start vm="$GUEST_NAME"
-
-function ssh_no_check {
-    ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$@"
-}
-
-# Get hold of the Management IP of OpenStack VM
-OS_VM_MANAGEMENT_ADDRESS=$MGT_IP
-if [ $OS_VM_MANAGEMENT_ADDRESS == "dhcp" ]; then
-    OS_VM_MANAGEMENT_ADDRESS=$(find_ip_by_name $GUEST_NAME $MGT_DEV_NR)
-fi
-
-# Get hold of the Service IP of OpenStack VM
-if [ $HOST_IP_IFACE == "eth${MGT_DEV_NR}" ]; then
-    OS_VM_SERVICES_ADDRESS=$MGT_IP
-    if [ $MGT_IP == "dhcp" ]; then
-        OS_VM_SERVICES_ADDRESS=$(find_ip_by_name $GUEST_NAME $MGT_DEV_NR)
-    fi
-else
-    OS_VM_SERVICES_ADDRESS=$PUB_IP
-    if [ $PUB_IP == "dhcp" ]; then
-        OS_VM_SERVICES_ADDRESS=$(find_ip_by_name $GUEST_NAME $PUB_DEV_NR)
-    fi
-fi
-
-# Create an ssh-keypair, and set it up for dom0 user
-rm -f /root/dom0key /root/dom0key.pub
-ssh-keygen -f /root/dom0key -P "" -C "dom0"
-DOMID=$(get_domid "$GUEST_NAME")
-
-xenstore-write /local/domain/$DOMID/authorized_keys/$DOMZERO_USER "$(cat /root/dom0key.pub)"
-xenstore-chmod -u /local/domain/$DOMID/authorized_keys/$DOMZERO_USER r$DOMID
-
-function run_on_appliance {
-    ssh \
-        -i /root/dom0key \
-        -o UserKnownHostsFile=/dev/null \
-        -o StrictHostKeyChecking=no \
-        -o BatchMode=yes \
-        "$DOMZERO_USER@$OS_VM_MANAGEMENT_ADDRESS" "$@"
-}
-
-# Wait until we can log in to the appliance
-while ! run_on_appliance true; do
-    sleep 1
-done
-
-# Remove authenticated_keys updater cronjob
-echo "" | run_on_appliance crontab -
-
-# Generate a passwordless ssh key for domzero user
-echo "ssh-keygen -f /home/$DOMZERO_USER/.ssh/id_rsa -C $DOMZERO_USER@appliance -N \"\" -q" | run_on_appliance
-
-# Authenticate that user to dom0
-run_on_appliance cat /home/$DOMZERO_USER/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys
-
-# If we have copied our ssh credentials, use ssh to monitor while the installation runs
-WAIT_TILL_LAUNCH=${WAIT_TILL_LAUNCH:-1}
-COPYENV=${COPYENV:-1}
-if [ "$WAIT_TILL_LAUNCH" = "1" ]  && [ -e ~/.ssh/id_rsa.pub  ] && [ "$COPYENV" = "1" ]; then
-    set +x
-
-    echo "VM Launched - Waiting for run.sh"
-    while ! ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "test -e /opt/stack/run_sh.pid"; do
-        sleep 10
-    done
-    echo -n "devstack service is running, waiting for stack.sh to start logging..."
-
-    pid=`ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "cat /opt/stack/run_sh.pid"`
-    if [ -n "$SCREEN_LOGDIR" ]; then
-        while ! ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "test -e ${SCREEN_LOGDIR}/stack.log"; do
-            sleep 10
-        done
-
-        ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "tail --pid $pid -n +1 -f ${SCREEN_LOGDIR}/stack.log"
-    else
-        echo -n "SCREEN_LOGDIR not set; just waiting for process $pid to finish"
-        ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "wait $pid"
-    fi
-
-    set -x
-    # Fail if devstack did not succeed
-    ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS 'test -e /opt/stack/runsh.succeeded'
-
-    set +x
-    echo "################################################################################"
-    echo ""
-    echo "All Finished!"
-    echo "You can visit the OpenStack Dashboard"
-    echo "at http://$OS_VM_SERVICES_ADDRESS, and contact other services at the usual ports."
-else
-    set +x
-    echo "################################################################################"
-    echo ""
-    echo "All Finished!"
-    echo "Now, you can monitor the progress of the stack.sh installation by "
-    echo "looking at the console of your domU / checking the log files."
-    echo ""
-    echo "ssh into your domU now: 'ssh stack@$OS_VM_MANAGEMENT_ADDRESS' using your password"
-    echo "and then do: 'sudo systemctl status devstack' to check if devstack is still running."
-    echo "Check that /opt/stack/runsh.succeeded exists"
-    echo ""
-    echo "When devstack completes, you can visit the OpenStack Dashboard"
-    echo "at http://$OS_VM_SERVICES_ADDRESS, and contact other services at the usual ports."
-fi
diff --git a/tools/xen/mocks b/tools/xen/mocks
deleted file mode 100644
index 3b9b05c..0000000
--- a/tools/xen/mocks
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/bin/bash
-
-test ! -e "$LIST_OF_ACTIONS" && {
-    echo "Mocking is not set up properly."
-    echo "LIST_OF_ACTIONS should point to an existing file."
-    exit 1
-}
-
-test ! -e "$LIST_OF_DIRECTORIES" && {
-    echo "Mocking is not set up properly."
-    echo "LIST_OF_DIRECTORIES should point to an existing file."
-    exit 1
-}
-
-test ! -e "$XE_RESPONSE" && {
-    echo "Mocking is not set up properly."
-    echo "XE_RESPONSE should point to an existing file."
-    exit 1
-}
-
-test ! -e "$XE_CALLS" && {
-    echo "Mocking is not set up properly."
-    echo "XE_CALLS should point to an existing file."
-    exit 1
-}
-
-function mktemp {
-    if test "${1:-}" = "-d";
-    then
-        echo "tempdir"
-    else
-        echo "tempfile"
-    fi
-}
-
-function wget {
-    if [[ $@ =~ "failurl" ]]; then
-        return 1
-    fi
-    echo "wget $@" >> $LIST_OF_ACTIONS
-}
-
-function mkdir {
-    if test "${1:-}" = "-p";
-    then
-        echo "$2" >> $LIST_OF_DIRECTORIES
-    fi
-}
-
-function unzip {
-    echo "Random rubbish from unzip"
-    echo "unzip $@" >> $LIST_OF_ACTIONS
-}
-
-function rm {
-    echo "rm $@" >> $LIST_OF_ACTIONS
-}
-
-function ln {
-    echo "ln $@" >> $LIST_OF_ACTIONS
-}
-
-function [ {
-    if test "${1:-}" = "-d";
-    then
-        echo "[ $@" >> $LIST_OF_ACTIONS
-        for directory in $(cat $LIST_OF_DIRECTORIES)
-        do
-            if test "$directory" = "$2"
-            then
-                return 0
-            fi
-        done
-        return 1
-    fi
-    echo "Mock test does not implement the requested function: ${1:-}"
-    exit 1
-}
-
-function die_with_error {
-    echo "$1" >> $DEAD_MESSAGES
-}
-
-function xe {
-    cat $XE_RESPONSE
-    {
-    for i in $(seq "$#")
-    do
-        eval "echo \"\$$i\""
-    done
-    } >> $XE_CALLS
-}
diff --git a/tools/xen/prepare_guest.sh b/tools/xen/prepare_guest.sh
deleted file mode 100755
index 6de1afc..0000000
--- a/tools/xen/prepare_guest.sh
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/bin/bash
-
-# This script is run on an Ubuntu VM.
-# This script is inserted into the VM by prepare_guest_template.sh
-# and is run when that VM boots.
-# It customizes a fresh Ubuntu install, so it is ready
-# to run stack.sh
-#
-# This includes installing the XenServer tools,
-# creating the user called "stack",
-# and shuts down the VM to signal the script has completed
-
-set -o errexit
-set -o nounset
-set -o xtrace
-
-# Configurable nuggets
-GUEST_PASSWORD="$1"
-STACK_USER="$2"
-DOMZERO_USER="$3"
-
-
-function setup_domzero_user {
-    local username
-
-    username="$1"
-
-    local key_updater_script
-    local sudoers_file
-    key_updater_script="/home/$username/update_authorized_keys.sh"
-    sudoers_file="/etc/sudoers.d/allow_$username"
-
-    # Create user
-    adduser --disabled-password --quiet "$username" --gecos "$username"
-
-    # Give passwordless sudo
-    cat > $sudoers_file << EOF
-    $username ALL = NOPASSWD: ALL
-EOF
-    chmod 0440 $sudoers_file
-
-    # A script to populate this user's authenticated_keys from xenstore
-    cat > $key_updater_script << EOF
-#!/bin/bash
-set -eux
-
-DOMID=\$(sudo xenstore-read domid)
-sudo xenstore-exists /local/domain/\$DOMID/authorized_keys/$username
-sudo xenstore-read /local/domain/\$DOMID/authorized_keys/$username > /home/$username/xenstore_value
-cat /home/$username/xenstore_value > /home/$username/.ssh/authorized_keys
-EOF
-
-    # Give the key updater to the user
-    chown $username:$username $key_updater_script
-    chmod 0700 $key_updater_script
-
-    # Setup the .ssh folder
-    mkdir -p /home/$username/.ssh
-    chown $username:$username /home/$username/.ssh
-    chmod 0700 /home/$username/.ssh
-    touch /home/$username/.ssh/authorized_keys
-    chown $username:$username /home/$username/.ssh/authorized_keys
-    chmod 0600 /home/$username/.ssh/authorized_keys
-
-    # Setup the key updater as a cron job
-    crontab -u $username - << EOF
-* * * * * $key_updater_script
-EOF
-
-}
-
-# Make a small cracklib dictionary, so that passwd still works, but we don't
-# have the big dictionary.
-mkdir -p /usr/share/cracklib
-echo a | cracklib-packer
-
-# Make /etc/shadow, and set the root password
-pwconv
-echo "root:$GUEST_PASSWORD" | chpasswd
-
-# Put the VPX into UTC.
-rm -f /etc/localtime
-
-# Add stack user
-groupadd libvirtd
-useradd $STACK_USER -s /bin/bash -d /opt/stack -G libvirtd
-echo $STACK_USER:$GUEST_PASSWORD | chpasswd
-echo "$STACK_USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
-
-setup_domzero_user "$DOMZERO_USER"
-
-# Add an udev rule, so that new block devices could be written by stack user
-cat > /etc/udev/rules.d/50-openstack-blockdev.rules << EOF
-KERNEL=="xvd[b-z]", GROUP="$STACK_USER", MODE="0660"
-EOF
-
-# Give ownership of /opt/stack to stack user
-chown -R $STACK_USER /opt/stack
-
-function setup_vimrc {
-    if [ ! -e $1 ]; then
-        # Simple but usable vimrc
-        cat > $1 <<EOF
-se ts=4
-se expandtab
-se shiftwidth=4
-EOF
-    fi
-}
-
-# Setup simple .vimrcs
-setup_vimrc /root/.vimrc
-setup_vimrc /opt/stack/.vimrc
-
-# remove self from local.rc
-# so this script is not run again
-rm -rf /etc/rc.local
-
-# Restore rc.local file
-cp /etc/rc.local.preparebackup /etc/rc.local
-
-# shutdown to notify we are done
-shutdown -h now
diff --git a/tools/xen/prepare_guest_template.sh b/tools/xen/prepare_guest_template.sh
deleted file mode 100755
index 6cdddda..0000000
--- a/tools/xen/prepare_guest_template.sh
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/bin/bash
-
-# This script is run by install_os_domU.sh
-#
-# Parameters:
-# - $GUEST_NAME - hostname for the DomU VM
-#
-# It modifies the ubuntu image created by install_os_domU.sh
-#
-# This script is responsible for cusomtizing the fresh ubuntu
-# image so on boot it runs the prepare_guest.sh script
-# that modifies the VM so it is ready to run stack.sh.
-# It does this by mounting the disk image of the VM.
-#
-# The resultant image is started by install_os_domU.sh,
-# and once the VM has shutdown, build_xva.sh is run
-
-set -o errexit
-set -o nounset
-set -o xtrace
-
-# This directory
-TOP_DIR=$(cd $(dirname "$0") && pwd)
-
-# Include onexit commands
-. $TOP_DIR/scripts/on_exit.sh
-
-# xapi functions
-. $TOP_DIR/functions
-
-# Source params - override xenrc params in your localrc to suite your taste
-source xenrc
-
-#
-# Parameters
-#
-GUEST_NAME="$1"
-
-# Mount the VDI
-STAGING_DIR=$($TOP_DIR/scripts/manage-vdi open $GUEST_NAME 0 1 | grep -o "/tmp/tmp.[[:alnum:]]*")
-add_on_exit "$TOP_DIR/scripts/manage-vdi close $GUEST_NAME 0 1"
-
-# Make sure we have a stage
-if [ ! -d $STAGING_DIR/etc ]; then
-    echo "Stage is not properly set up!"
-    exit 1
-fi
-
-# Copy prepare_guest.sh to VM
-mkdir -p $STAGING_DIR/opt/stack/
-cp $TOP_DIR/prepare_guest.sh $STAGING_DIR/opt/stack/prepare_guest.sh
-
-# backup rc.local
-cp $STAGING_DIR/etc/rc.local $STAGING_DIR/etc/rc.local.preparebackup
-
-# run prepare_guest.sh on boot
-cat <<EOF >$STAGING_DIR/etc/rc.local
-#!/bin/sh -e
-bash /opt/stack/prepare_guest.sh \\
-    "$GUEST_PASSWORD" "$STACK_USER" "$DOMZERO_USER" \\
-    > /opt/stack/prepare_guest.log 2>&1
-EOF
-
-# Update ubuntu repositories
-cat > $STAGING_DIR/etc/apt/sources.list << EOF
-deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE} main restricted
-deb-src http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE} main restricted
-deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-updates main restricted
-deb-src http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-updates main restricted
-deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE} universe
-deb-src http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE} universe
-deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-updates universe
-deb-src http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-updates universe
-deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE} multiverse
-deb-src http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE} multiverse
-deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-updates multiverse
-deb-src http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-updates multiverse
-deb http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-backports main restricted universe multiverse
-deb-src http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY} ${UBUNTU_INST_RELEASE}-backports main restricted universe multiverse
-
-deb http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security main restricted
-deb-src http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security main restricted
-deb http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security universe
-deb-src http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security universe
-deb http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security multiverse
-deb-src http://security.ubuntu.com/ubuntu ${UBUNTU_INST_RELEASE}-security multiverse
-EOF
-
-rm -f $STAGING_DIR/etc/apt/apt.conf
-if [ -n "$UBUNTU_INST_HTTP_PROXY" ]; then
-    cat > $STAGING_DIR/etc/apt/apt.conf << EOF
-Acquire::http::Proxy "$UBUNTU_INST_HTTP_PROXY";
-EOF
-fi
diff --git a/tools/xen/scripts/install-os-vpx.sh b/tools/xen/scripts/install-os-vpx.sh
deleted file mode 100755
index 66f7ef4..0000000
--- a/tools/xen/scripts/install-os-vpx.sh
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2011 Citrix Systems, Inc.
-# Copyright 2011 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.
-#
-
-set -eux
-
-BRIDGE=
-NAME_LABEL=
-TEMPLATE_NAME=
-
-usage()
-{
-cat << EOF
-
-  Usage: $0 -t TEMPLATE_NW_INSTALL -l NAME_LABEL [-n BRIDGE]
-
-  Install a VM from a template
-
-  OPTIONS:
-
-     -h           Shows this message.
-     -t template  VM template to use
-     -l name      Specifies the name label for the VM.
-     -n bridge    The bridge/network to use for eth0. Defaults to xenbr0
-EOF
-}
-
-get_params()
-{
-    while getopts "hbn:r:l:t:" OPTION; do
-        case $OPTION in
-            h) usage
-                exit 1
-                ;;
-            n)
-                BRIDGE=$OPTARG
-                ;;
-            l)
-                NAME_LABEL=$OPTARG
-                ;;
-            t)
-                TEMPLATE_NAME=$OPTARG
-                ;;
-            ?)
-                usage
-                exit
-                ;;
-        esac
-    done
-    if [[ -z $BRIDGE ]]; then
-        BRIDGE=xenbr0
-    fi
-
-    if [[ -z $TEMPLATE_NAME ]]; then
-        echo "Please specify a template name" >&2
-        exit 1
-    fi
-
-    if [[ -z $NAME_LABEL ]]; then
-        echo "Please specify a name-label for the new VM" >&2
-        exit 1
-    fi
-}
-
-
-xe_min()
-{
-    local cmd="$1"
-    shift
-    xe "$cmd" --minimal "$@"
-}
-
-
-find_network()
-{
-    result=$(xe_min network-list bridge="$1")
-    if [ "$result" = "" ]; then
-        result=$(xe_min network-list name-label="$1")
-    fi
-    echo "$result"
-}
-
-
-create_vif()
-{
-    local v="$1"
-    echo "Installing VM interface on [$BRIDGE]"
-    local out_network_uuid
-    out_network_uuid=$(find_network "$BRIDGE")
-    xe vif-create vm-uuid="$v" network-uuid="$out_network_uuid" device="0"
-}
-
-
-
-# Make the VM auto-start on server boot.
-set_auto_start()
-{
-    local v="$1"
-    xe vm-param-set uuid="$v" other-config:auto_poweron=true
-}
-
-
-destroy_vifs()
-{
-    local v="$1"
-    IFS=,
-    for vif in $(xe_min vif-list vm-uuid="$v"); do
-        xe vif-destroy uuid="$vif"
-    done
-    unset IFS
-}
-
-
-get_params "$@"
-
-vm_uuid=$(xe_min vm-install template="$TEMPLATE_NAME" new-name-label="$NAME_LABEL")
-destroy_vifs "$vm_uuid"
-set_auto_start "$vm_uuid"
-create_vif "$vm_uuid"
-xe vm-param-set actions-after-reboot=Destroy uuid="$vm_uuid"
diff --git a/tools/xen/scripts/install_ubuntu_template.sh b/tools/xen/scripts/install_ubuntu_template.sh
deleted file mode 100755
index 6ea3642..0000000
--- a/tools/xen/scripts/install_ubuntu_template.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/bash
-#
-# This creates an Ubuntu Server 32bit or 64bit template
-# on Xenserver 5.6.x, 6.0.x and 6.1.x
-# The template does a net install only
-#
-# Based on a script by: David Markey <david.markey@citrix.com>
-#
-
-set -o errexit
-set -o nounset
-set -o xtrace
-
-# This directory
-BASE_DIR=$(cd $(dirname "$0") && pwd)
-
-# For default setings see xenrc
-source $BASE_DIR/../xenrc
-
-# Get the params
-preseed_url=$1
-
-# Delete template or skip template creation as required
-previous_template=$(xe template-list name-label="$UBUNTU_INST_TEMPLATE_NAME" \
-    params=uuid --minimal)
-if [ -n "$previous_template" ]; then
-    if $CLEAN_TEMPLATES; then
-        xe template-param-clear param-name=other-config uuid=$previous_template
-        xe template-uninstall template-uuid=$previous_template force=true
-    else
-        echo "Template $UBUNTU_INST_TEMPLATE_NAME already present"
-        exit 0
-    fi
-fi
-
-# Get built-in template
-builtin_name="Debian Squeeze 6.0 (32-bit)"
-builtin_uuid=$(xe template-list name-label="$builtin_name" --minimal)
-if [[ -z $builtin_uuid ]]; then
-    echo "Can't find the Debian Squeeze 32bit template on your XenServer."
-    exit 1
-fi
-
-# Clone built-in template to create new template
-new_uuid=$(xe vm-clone uuid=$builtin_uuid \
-    new-name-label="$UBUNTU_INST_TEMPLATE_NAME")
-disk_size=$(($OSDOMU_VDI_GB * 1024 * 1024 * 1024))
-
-# Some of these settings can be found in example preseed files
-# however these need to be answered before the netinstall
-# is ready to fetch the preseed file, and as such must be here
-# to get a fully automated install
-pvargs="quiet console=hvc0 partman/default_filesystem=ext3 \
-console-setup/ask_detect=false locale=${UBUNTU_INST_LOCALE} \
-keyboard-configuration/layoutcode=${UBUNTU_INST_KEYBOARD} \
-netcfg/choose_interface=eth0 \
-netcfg/get_hostname=os netcfg/get_domain=os auto \
-url=${preseed_url}"
-
-if [ "$UBUNTU_INST_IP" != "dhcp" ]; then
-    netcfgargs="netcfg/disable_autoconfig=true \
-netcfg/get_nameservers=${UBUNTU_INST_NAMESERVERS} \
-netcfg/get_ipaddress=${UBUNTU_INST_IP} \
-netcfg/get_netmask=${UBUNTU_INST_NETMASK} \
-netcfg/get_gateway=${UBUNTU_INST_GATEWAY} \
-netcfg/confirm_static=true"
-    pvargs="${pvargs} ${netcfgargs}"
-fi
-
-xe template-param-set uuid=$new_uuid \
-    other-config:install-methods=http \
-    other-config:install-repository="http://${UBUNTU_INST_HTTP_HOSTNAME}${UBUNTU_INST_HTTP_DIRECTORY}" \
-    PV-args="$pvargs" \
-    other-config:debian-release="$UBUNTU_INST_RELEASE" \
-    other-config:default_template=true \
-    other-config:disks='<provision><disk device="0" size="'$disk_size'" sr="" bootable="true" type="system"/></provision>' \
-    other-config:install-arch="$UBUNTU_INST_ARCH"
-
-if ! [ -z "$UBUNTU_INST_HTTP_PROXY" ]; then
-    xe template-param-set uuid=$new_uuid \
-        other-config:install-proxy="$UBUNTU_INST_HTTP_PROXY"
-fi
-
-echo "Ubuntu template installed uuid:$new_uuid"
diff --git a/tools/xen/scripts/manage-vdi b/tools/xen/scripts/manage-vdi
deleted file mode 100755
index 909ce32..0000000
--- a/tools/xen/scripts/manage-vdi
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/bin/bash
-
-set -eux
-
-action="$1"
-vm="$2"
-device="${3-0}"
-part="${4-}"
-
-function xe_min() {
-  local cmd="$1"
-  shift
-  xe "$cmd" --minimal "$@"
-}
-
-function run_udev_settle() {
-  which_udev=$(which udevsettle) || true
-  if [ -n "$which_udev" ]; then
-      udevsettle
-  else
-      udevadm settle
-  fi
-}
-
-vm_uuid=$(xe_min vm-list name-label="$vm")
-vdi_uuid=$(xe_min vbd-list params=vdi-uuid vm-uuid="$vm_uuid" \
-                           userdevice="$device")
-
-dom0_uuid=$(xe_min vm-list is-control-domain=true)
-
-function get_mount_device() {
-  vbd_uuid=$1
-
-  dev=$(xe_min vbd-list params=device uuid="$vbd_uuid")
-  if [[ "$dev" =~ "sm/" || "$dev" =~ "blktap-2/" ]]; then
-    DEBIAN_FRONTEND=noninteractive \
-        apt-get --option "Dpkg::Options::=--force-confold" --assume-yes \
-        install kpartx &> /dev/null || true
-    mapping=$(kpartx -av "/dev/$dev" | sed -ne 's,^add map \([a-z0-9\-]*\).*$,\1,p' | sed -ne "s,^\(.*${part}\)\$,\1,p")
-    if [ -z "$mapping" ]; then
-       echo "Failed to find mapping"
-       exit -1
-    fi
-
-    local device="/dev/mapper/${mapping}"
-    for (( i = 0; i < 5; i++ )) ; do
-        if [ -b $device ] ; then
-            echo $device
-            return
-        fi
-        sleep 1
-    done
-    echo "ERROR: timed out waiting for dev-mapper"
-    exit 1
-  else
-    echo "/dev/$dev$part"
-  fi
-}
-
-function clean_dev_mappings() {
-  dev=$(xe_min vbd-list params=device uuid="$vbd_uuid")
-  if [[ "$dev" =~ "sm/" || "$dev" =~ "blktap-2/" ]]; then
-    kpartx -dv "/dev/$dev"
-  fi
-}
-
-function open_vdi() {
-  vbd_uuid=$(xe vbd-create vm-uuid="$dom0_uuid" vdi-uuid="$vdi_uuid" \
-                         device=autodetect)
-  mp=$(mktemp -d)
-  xe vbd-plug uuid="$vbd_uuid"
-
-  run_udev_settle
-
-  mount_device=$(get_mount_device "$vbd_uuid")
-  mount "$mount_device" "$mp"
-  echo "Your vdi is mounted at $mp"
-}
-
-function close_vdi() {
-  vbd_uuid=$(xe_min vbd-list vm-uuid="$dom0_uuid" vdi-uuid="$vdi_uuid")
-  mount_device=$(get_mount_device "$vbd_uuid")
-  run_udev_settle
-  umount "$mount_device"
-
-  clean_dev_mappings
-
-  xe vbd-unplug uuid=$vbd_uuid
-  xe vbd-destroy uuid=$vbd_uuid
-}
-
-if [ "$action" == "open" ]; then
-  open_vdi
-elif [ "$action" == "close" ]; then
-  close_vdi
-fi
diff --git a/tools/xen/scripts/on_exit.sh b/tools/xen/scripts/on_exit.sh
deleted file mode 100755
index 2846dc4..0000000
--- a/tools/xen/scripts/on_exit.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-set -e
-set -o xtrace
-
-if [ -z "${on_exit_hooks:-}" ]; then
-    on_exit_hooks=()
-fi
-
-on_exit()
-{
-    for i in $(seq $((${#on_exit_hooks[*]} - 1)) -1 0); do
-        eval "${on_exit_hooks[$i]}"
-    done
-}
-
-add_on_exit()
-{
-    local n=${#on_exit_hooks[*]}
-    on_exit_hooks[$n]="$*"
-    if [[ $n -eq 0 ]]; then
-        trap on_exit EXIT
-    fi
-}
diff --git a/tools/xen/scripts/uninstall-os-vpx.sh b/tools/xen/scripts/uninstall-os-vpx.sh
deleted file mode 100755
index 96dad7e..0000000
--- a/tools/xen/scripts/uninstall-os-vpx.sh
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2011 Citrix Systems, Inc.
-# Copyright 2011 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.
-#
-
-set -ex
-
-# By default, don't remove the templates
-REMOVE_TEMPLATES=${REMOVE_TEMPLATES:-"false"}
-if [ "$1" = "--remove-templates" ]; then
-    REMOVE_TEMPLATES=true
-fi
-
-xe_min()
-{
-    local cmd="$1"
-    shift
-    xe "$cmd" --minimal "$@"
-}
-
-destroy_vdi()
-{
-    local vbd_uuid="$1"
-    local type
-    type=$(xe_min vbd-list uuid=$vbd_uuid params=type)
-    local dev
-    dev=$(xe_min vbd-list uuid=$vbd_uuid params=userdevice)
-    local vdi_uuid
-    vdi_uuid=$(xe_min vbd-list uuid=$vbd_uuid params=vdi-uuid)
-
-    if [ "$type" == 'Disk' ] && [ "$dev" != 'xvda' ] && [ "$dev" != '0' ]; then
-        xe vdi-destroy uuid=$vdi_uuid
-    fi
-}
-
-uninstall()
-{
-    local vm_uuid="$1"
-    local power_state
-    power_state=$(xe_min vm-list uuid=$vm_uuid params=power-state)
-
-    if [ "$power_state" != "halted" ]; then
-        xe vm-shutdown vm=$vm_uuid force=true
-    fi
-
-    for v in $(xe_min vbd-list vm-uuid=$vm_uuid | sed -e 's/,/ /g'); do
-        destroy_vdi "$v"
-    done
-
-    xe vm-uninstall vm=$vm_uuid force=true >/dev/null
-}
-
-uninstall_template()
-{
-    local vm_uuid="$1"
-
-    for v in $(xe_min vbd-list vm-uuid=$vm_uuid | sed -e 's/,/ /g'); do
-        destroy_vdi "$v"
-    done
-
-    xe template-uninstall template-uuid=$vm_uuid force=true >/dev/null
-}
-
-# remove the VMs and their disks
-for u in $(xe_min vm-list other-config:os-vpx=true | sed -e 's/,/ /g'); do
-    uninstall "$u"
-done
-
-# remove the templates
-if [ "$REMOVE_TEMPLATES" == "true" ]; then
-    for u in $(xe_min template-list other-config:os-vpx=true | sed -e 's/,/ /g'); do
-        uninstall_template "$u"
-    done
-fi
diff --git a/tools/xen/test_functions.sh b/tools/xen/test_functions.sh
deleted file mode 100755
index 324e6a1..0000000
--- a/tools/xen/test_functions.sh
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/bin/bash
-
-# Tests for functions.
-#
-# The tests are sourcing the mocks file to mock out various functions. The
-# mocking-out always happens in a sub-shell, thus it does not have impact on
-# the functions defined here.
-
-# To run the tests, please run:
-#
-# ./test_functions.sh run_tests
-#
-# To only print out the discovered test functions, run:
-#
-# ./test_functions.sh
-
-. functions
-
-# Setup
-function before_each_test {
-    LIST_OF_DIRECTORIES=$(mktemp)
-    truncate -s 0 $LIST_OF_DIRECTORIES
-
-    LIST_OF_ACTIONS=$(mktemp)
-    truncate -s 0 $LIST_OF_ACTIONS
-
-    XE_RESPONSE=$(mktemp)
-    truncate -s 0 $XE_RESPONSE
-
-    XE_CALLS=$(mktemp)
-    truncate -s 0 $XE_CALLS
-
-    DEAD_MESSAGES=$(mktemp)
-    truncate -s 0 $DEAD_MESSAGES
-}
-
-# Teardown
-function after_each_test {
-    rm -f $LIST_OF_DIRECTORIES
-    rm -f $LIST_OF_ACTIONS
-    rm -f $XE_RESPONSE
-    rm -f $XE_CALLS
-}
-
-# Helpers
-function setup_xe_response {
-    echo "$1" > $XE_RESPONSE
-}
-
-function given_directory_exists {
-    echo "$1" >> $LIST_OF_DIRECTORIES
-}
-
-function assert_directory_exists {
-    grep "$1" $LIST_OF_DIRECTORIES
-}
-
-function assert_previous_command_failed {
-    [ "$?" != "0" ] || exit 1
-}
-
-function assert_xe_min {
-    grep -qe "^--minimal\$" $XE_CALLS
-}
-
-function assert_xe_param {
-    grep -qe "^$1\$" $XE_CALLS
-}
-
-function assert_died_with {
-    diff -u <(echo "$1") $DEAD_MESSAGES
-}
-
-function mock_out {
-    local FNNAME="$1"
-    local OUTPUT="$2"
-
-    . <(cat << EOF
-function $FNNAME {
-    echo "$OUTPUT"
-}
-EOF
-)
-}
-
-function assert_symlink {
-    grep -qe "^ln -s $2 $1\$" $LIST_OF_ACTIONS
-}
-
-# Tests
-function test_plugin_directory_on_xenserver {
-    given_directory_exists "/etc/xapi.d/plugins/"
-
-    PLUGDIR=$(. mocks && xapi_plugin_location)
-
-    [ "/etc/xapi.d/plugins/" = "$PLUGDIR" ]
-}
-
-function test_plugin_directory_on_xcp {
-    given_directory_exists "/usr/lib/xcp/plugins/"
-
-    PLUGDIR=$(. mocks && xapi_plugin_location)
-
-    [ "/usr/lib/xcp/plugins/" = "$PLUGDIR" ]
-}
-
-function test_no_plugin_directory_found {
-    set +e
-
-    local IGNORE
-    IGNORE=$(. mocks && xapi_plugin_location)
-
-    assert_previous_command_failed
-
-    grep "[ -d /etc/xapi.d/plugins/ ]" $LIST_OF_ACTIONS
-    grep "[ -d /usr/lib/xcp/plugins/ ]" $LIST_OF_ACTIONS
-}
-
-function test_create_directory_for_kernels {
-    (
-        . mocks
-        mock_out get_local_sr_path /var/run/sr-mount/uuid1
-        create_directory_for_kernels
-    )
-
-    assert_directory_exists "/var/run/sr-mount/uuid1/os-guest-kernels"
-    assert_symlink "/boot/guest" "/var/run/sr-mount/uuid1/os-guest-kernels"
-}
-
-function test_create_directory_for_kernels_existing_dir {
-    (
-        . mocks
-        given_directory_exists "/boot/guest"
-        create_directory_for_kernels
-    )
-
-    diff -u $LIST_OF_ACTIONS - << EOF
-[ -d /boot/guest ]
-EOF
-}
-
-function test_create_directory_for_images {
-    (
-        . mocks
-        mock_out get_local_sr_path /var/run/sr-mount/uuid1
-        create_directory_for_images
-    )
-
-    assert_directory_exists "/var/run/sr-mount/uuid1/os-images"
-    assert_symlink "/images" "/var/run/sr-mount/uuid1/os-images"
-}
-
-function test_create_directory_for_images_existing_dir {
-    (
-        . mocks
-        given_directory_exists "/images"
-        create_directory_for_images
-    )
-
-    diff -u $LIST_OF_ACTIONS - << EOF
-[ -d /images ]
-EOF
-}
-
-function test_get_local_sr {
-    setup_xe_response "uuid123"
-
-    local RESULT
-    RESULT=$(. mocks && get_local_sr)
-
-    [ "$RESULT" == "uuid123" ]
-
-    assert_xe_param "pool-list" params=default-SR minimal=true
-}
-
-function test_get_local_sr_path {
-    local RESULT
-    RESULT=$(mock_out get_local_sr "uuid1" && get_local_sr_path)
-
-    [ "/var/run/sr-mount/uuid1" == "$RESULT" ]
-}
-
-# Test runner
-[ "$1" = "" ] && {
-    grep -e "^function *test_" $0 | cut -d" " -f2
-}
-
-[ "$1" = "run_tests" ] && {
-    for testname in $($0); do
-        echo "$testname"
-        before_each_test
-        (
-            set -eux
-            $testname
-        )
-        if [ "$?" != "0" ]; then
-            echo "FAIL"
-            exit 1
-        else
-            echo "PASS"
-        fi
-
-        after_each_test
-    done
-}
diff --git a/tools/xen/xenrc b/tools/xen/xenrc
deleted file mode 100644
index 169e042..0000000
--- a/tools/xen/xenrc
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/bin/bash
-
-#
-# XenServer specific defaults for the /tools/xen/ scripts
-# Similar to stackrc, you can override these in your localrc
-#
-
-# Name of this guest
-GUEST_NAME=${GUEST_NAME:-DevStackOSDomU}
-
-# Template cleanup
-CLEAN_TEMPLATES=${CLEAN_TEMPLATES:-false}
-
-# Size of image
-VDI_MB=${VDI_MB:-5000}
-
-# Devstack now contains many components.  4GB ram is not enough to prevent
-# swapping and memory fragmentation - the latter of which can cause failures
-# such as blkfront failing to plug a VBD and lead to random test fails.
-#
-# Set to 6GB so an 8GB XenServer VM can have a 1GB Dom0 and leave 1GB for VMs
-OSDOMU_MEM_MB=6144
-OSDOMU_VDI_GB=8
-
-# Network mapping. Specify bridge names or network names. Network names may
-# differ across localised versions of XenServer. If a given bridge/network
-# was not found, a new network will be created with the specified name.
-
-# Get the management network from the XS installation
-VM_BRIDGE_OR_NET_NAME="OpenStack VM Network"
-PUB_BRIDGE_OR_NET_NAME="OpenStack Public Network"
-
-# VM Password
-GUEST_PASSWORD=${GUEST_PASSWORD:-secret}
-
-# Extracted variables for OpenStack VM network device numbers.
-# Make sure they form a continuous sequence starting from 0
-MGT_DEV_NR=0
-VM_DEV_NR=1
-PUB_DEV_NR=2
-
-# Host Interface, i.e. the interface on the nova vm you want to expose the
-# services on. Usually the device connected to the management network or the
-# one connected to the public network is used.
-HOST_IP_IFACE=${HOST_IP_IFACE:-"eth${MGT_DEV_NR}"}
-
-#
-# Our nova host's network info
-#
-
-# Management network
-MGT_IP=${MGT_IP:-dhcp}
-MGT_NETMASK=${MGT_NETMASK:-ignored}
-
-# VM Network
-VM_IP=${VM_IP:-10.255.255.255}
-VM_NETMASK=${VM_NETMASK:-255.255.255.0}
-
-# Public network
-# Aligned with stack.sh - see FLOATING_RANGE
-PUB_IP=${PUB_IP:-172.24.4.10}
-PUB_NETMASK=${PUB_NETMASK:-255.255.255.0}
-
-# Ubuntu install settings
-UBUNTU_INST_RELEASE="xenial"
-UBUNTU_INST_TEMPLATE_NAME="Ubuntu 16.04 (64-bit) for DevStack"
-# For 12.04 use "precise" and update template name
-# However, for 12.04, you should be using
-# XenServer 6.1 and later or XCP 1.6 or later
-# 11.10 is only really supported with XenServer 6.0.2 and later
-UBUNTU_INST_ARCH="amd64"
-UBUNTU_INST_HTTP_HOSTNAME="archive.ubuntu.com"
-UBUNTU_INST_HTTP_DIRECTORY="/ubuntu"
-UBUNTU_INST_HTTP_PROXY=""
-UBUNTU_INST_LOCALE="en_US"
-UBUNTU_INST_KEYBOARD="us"
-# network configuration for ubuntu netinstall
-UBUNTU_INST_IP="dhcp"
-UBUNTU_INST_NAMESERVERS=""
-UBUNTU_INST_NETMASK=""
-UBUNTU_INST_GATEWAY=""
-
-# Create a separate xvdb. Tis could be used as a backing device for cinder
-# volumes. Specify
-#   XEN_XVDB_SIZE_GB=10
-#   VOLUME_BACKING_DEVICE=/dev/xvdb
-# in your localrc to avoid kernel lockups:
-#   https://bugs.launchpad.net/cinder/+bug/1023755
-#
-# Set the size to 0 to avoid creation of additional disk.
-XEN_XVDB_SIZE_GB=0
-
-STACK_USER=stack
-DOMZERO_USER=domzero
-
-RC_DIR="../.."
-
-restore_nounset=$(set +o | grep nounset)
-set +u
-
-## Note that the lines below are coming from stackrc to support
-## new-style config files
-source $RC_DIR/functions-common
-
-# allow local overrides of env variables, including repo config
-if [[ -f $RC_DIR/localrc ]]; then
-    # Old-style user-supplied config
-    source $RC_DIR/localrc
-elif [[ -f $RC_DIR/.localrc.auto ]]; then
-    # New-style user-supplied config extracted from local.conf
-    source $RC_DIR/.localrc.auto
-fi
-
-$restore_nounset
diff --git a/tox.ini b/tox.ini
index 46b15f4..74436b0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -34,16 +34,7 @@
          -print0 | xargs -0 bashate -v -iE006 -eE005,E042"
 
 [testenv:docs]
-deps =
-   Pygments
-   docutils
-   sphinx>=1.6.2
-   pbr>=2.0.0,!=2.1.0
-   openstackdocstheme>=1.11.0
-   nwdiag
-   blockdiag
-   sphinxcontrib-blockdiag
-   sphinxcontrib-nwdiag
+deps = -r{toxinidir}/doc/requirements.txt
 whitelist_externals = bash
 setenv =
   TOP_DIR={toxinidir}
@@ -51,11 +42,5 @@
   python setup.py build_sphinx
 
 [testenv:venv]
-deps =
-   pbr>=2.0.0,!=2.1.0
-   sphinx>=1.6.2
-   openstackdocstheme>=1.11.0
-   blockdiag
-   sphinxcontrib-blockdiag
-   sphinxcontrib-nwdiag
+deps = -r{toxinidir}/doc/requirements.txt
 commands = {posargs}
diff --git a/unstack.sh b/unstack.sh
index 5d3672e..ccea0ef 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -45,6 +45,10 @@
 # Configure Projects
 # ==================
 
+# Determine what system we are running on.  This provides ``os_VENDOR``,
+# ``os_RELEASE``, ``os_PACKAGE``, ``os_CODENAME`` and ``DISTRO``
+GetDistro
+
 # Plugin Phase 0: override_defaults - allow plugins to override
 # defaults before other services are run
 run_phase override_defaults
@@ -83,10 +87,6 @@
 
 load_plugin_settings
 
-# Determine what system we are running on.  This provides ``os_VENDOR``,
-# ``os_RELEASE``, ``os_PACKAGE``, ``os_CODENAME``
-GetOSVersion
-
 set -o xtrace
 
 # Run extras