Merge "Undeprecate neutron-legacy scripts"
diff --git a/.zuul.yaml b/.zuul.yaml
index 7801297..8c18e71 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -39,6 +39,16 @@
           - controller
 
 - nodeset:
+    name: devstack-single-node-centos-8
+    nodes:
+      - name: controller
+        label: centos-8
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
     name: devstack-single-node-opensuse-15
     nodes:
       - name: controller
@@ -52,7 +62,7 @@
     name: devstack-single-node-fedora-latest
     nodes:
       - name: controller
-        label: fedora-29
+        label: fedora-31
     groups:
       - name: tempest
         nodes:
@@ -490,6 +500,14 @@
 # and these platforms don't have the round-the-clock support to avoid
 # becoming blockers in that situation.
 - job:
+    name: devstack-platform-centos-8
+    parent: tempest-full-py3
+    description: Centos 8 platform test
+    nodeset: devstack-single-node-centos-8
+    voting: false
+    timeout: 9000
+
+- job:
     name: devstack-platform-opensuse-15
     parent: tempest-full-py3
     description: openSUSE 15.x platform test
@@ -504,6 +522,16 @@
     voting: false
 
 - job:
+    name: devstack-platform-fedora-latest-virt-preview
+    parent: tempest-full-py3
+    description: Fedora latest platform test using the virt-preview repo.
+    nodeset: devstack-single-node-fedora-latest
+    voting: false
+    vars:
+      devstack_localrc:
+        ENABLE_FEDORA_VIRT_PREVIEW_REPO: true
+
+- job:
     name: devstack-tox-base
     parent: devstack
     description: |
@@ -568,7 +596,6 @@
 
 - project:
     templates:
-      - integrated-gate
       - integrated-gate-py3
       - publish-openstack-docs-pti
     check:
@@ -577,6 +604,7 @@
         - devstack-ipv6
         - devstack-platform-opensuse-15
         - devstack-platform-fedora-latest
+        - devstack-platform-centos-8
         - devstack-multinode
         - devstack-unit-tests
         - openstack-tox-bashate
@@ -584,14 +612,19 @@
             voting: false
         - swift-dsvm-functional:
             voting: false
-            irrelevant-files:
+            irrelevant-files: &dsvm-irrelevant-files
               - ^.*\.rst$
               - ^doc/.*$
-        - grenade-py3:
+        - swift-dsvm-functional-py3:
+            voting: false
+            irrelevant-files: *dsvm-irrelevant-files
+        - grenade:
+            voting: false
             irrelevant-files:
               - ^.*\.rst$
               - ^doc/.*$
         - neutron-grenade-multinode:
+            voting: false
             irrelevant-files:
               - ^.*\.rst$
               - ^doc/.*$
@@ -599,7 +632,7 @@
             irrelevant-files:
               - ^.*\.rst$
               - ^doc/.*$
-        - tempest-multinode-full:
+        - tempest-multinode-full-py3:
             voting: false
             irrelevant-files:
               - ^.*\.rst$
@@ -628,18 +661,18 @@
         - devstack-multinode
         - devstack-unit-tests
         - openstack-tox-bashate
-        - neutron-grenade-multinode:
-            irrelevant-files:
-              - ^.*\.rst$
-              - ^doc/.*$
+        # - neutron-grenade-multinode:
+        #     irrelevant-files:
+        #       - ^.*\.rst$
+        #       - ^doc/.*$
         - neutron-tempest-linuxbridge:
             irrelevant-files:
               - ^.*\.rst$
               - ^doc/.*$
-        - grenade-py3:
-            irrelevant-files:
-              - ^.*\.rst$
-              - ^doc/.*$
+        # - grenade:
+        #     irrelevant-files:
+        #       - ^.*\.rst$
+        #       - ^doc/.*$
         - openstacksdk-functional-devstack:
             irrelevant-files:
               - ^.*\.rst$
@@ -665,6 +698,10 @@
     #    Next cycle we can remove this if everything run out stable enough.
     # * nova-multi-cell: maintained by nova and currently non-voting in the
     #    check queue for nova changes but relies on devstack configuration
+    # * devstack-platform-fedora-latest-virt-preview: Maintained by lyarwood
+    #    for Nova to allow early testing of the latest versions of Libvirt and
+    #    QEMU. Should only graduate out of experimental if it ever moves into
+    #    the check queue for Nova.
 
     experimental:
       jobs:
@@ -673,10 +710,6 @@
         - neutron-fullstack-with-uwsgi
         - neutron-functional-with-uwsgi
         - neutron-tempest-with-uwsgi
-        - devstack-plugin-ceph-tempest:
-            irrelevant-files:
-              - ^.*\.rst$
-              - ^doc/.*$
         - devstack-plugin-ceph-tempest-py3:
             irrelevant-files:
               - ^.*\.rst$
@@ -701,3 +734,4 @@
             irrelevant-files:
               - ^.*\.rst$
               - ^doc/.*$
+        - devstack-platform-fedora-latest-virt-preview
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 0000000..bb51165
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,19 @@
+The source repository for this project can be found at:
+
+   https://opendev.org/openstack/devstack
+
+Pull requests submitted through GitHub are not monitored.
+
+To start contributing to OpenStack, follow the steps in the contribution guide
+to set up and use Gerrit:
+
+   https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
+
+Bugs should be filed on Launchpad:
+
+   https://bugs.launchpad.net/devstack
+
+For more specific information about contributing to this repository, see the
+Devstack contributor guide:
+
+   https://docs.openstack.org/devstack/latest/contributor/contributing.html
diff --git a/clean.sh b/clean.sh
index d6c6b40..685a719 100755
--- a/clean.sh
+++ b/clean.sh
@@ -123,12 +123,10 @@
     sudo rm -rf $LOGDIR
 fi
 
-# Clean out the systemd user unit files if systemd was used.
-if [[ "$USE_SYSTEMD" = "True" ]]; then
-    sudo find $SYSTEMD_DIR -type f -name '*devstack@*service' -delete
-    # Make systemd aware of the deletion.
-    $SYSTEMCTL daemon-reload
-fi
+# Clean out the systemd unit files.
+sudo find $SYSTEMD_DIR -type f -name '*devstack@*service' -delete
+# Make systemd aware of the deletion.
+$SYSTEMCTL daemon-reload
 
 # Clean up venvs
 DIRS_TO_CLEAN="$WHEELHOUSE ${PROJECT_VENV[@]} .config/openstack"
diff --git a/doc/requirements.txt b/doc/requirements.txt
index fffb83d..ffce3ff 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -2,8 +2,8 @@
 
 Pygments
 docutils
-sphinx>=1.6.2
-openstackdocstheme>=1.20.0
+sphinx>=2.0.0,!=2.1.0 # BSD
+openstackdocstheme>=2.2.1 # Apache-2.0
 nwdiag
 blockdiag
 sphinxcontrib-blockdiag
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 56043ba..2e17da1 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -30,9 +30,15 @@
                'sphinxcontrib.nwdiag' ]
 
 # openstackdocstheme options
-repository_name = 'openstack-dev/devstack'
-bug_project = 'devstack'
-bug_tag = ''
+openstackdocs_repo_name = 'openstack-dev/devstack'
+openstackdocs_pdf_link = True
+openstackdocs_bug_project = 'devstack'
+openstackdocs_bug_tag = ''
+openstackdocs_auto_name = False
+# This repo is not tagged, so don't set versions
+openstackdocs_auto_version = False
+version = ''
+release = ''
 
 todo_include_todos = True
 
@@ -81,7 +87,7 @@
 show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = 'native'
 
 # A list of ignored prefixes for module index sorting.
 modindex_common_prefix = ['DevStack-doc.']
diff --git a/doc/source/contributor/contributing.rst b/doc/source/contributor/contributing.rst
new file mode 100644
index 0000000..5e0df56
--- /dev/null
+++ b/doc/source/contributor/contributing.rst
@@ -0,0 +1,56 @@
+============================
+So You Want to Contribute...
+============================
+
+For general information on contributing to OpenStack, please check out the
+`contributor guide <https://docs.openstack.org/contributors/>`_ to get started.
+It covers all the basics that are common to all OpenStack projects: the accounts
+you need, the basics of interacting with our Gerrit review system, how we
+communicate as a community, etc.
+
+Below will cover the more project specific information you need to get started
+with Devstack.
+
+Communication
+~~~~~~~~~~~~~
+* IRC channel ``#openstack-qa`` at FreeNode
+* Mailing list (prefix subjects with ``[qa][devstack]`` for faster responses)
+  http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
+
+Contacting the Core Team
+~~~~~~~~~~~~~~~~~~~~~~~~
+Please refer to the `Devstack Core Team
+<https://review.opendev.org/#/admin/groups/50,members>`_ contacts.
+
+New Feature Planning
+~~~~~~~~~~~~~~~~~~~~
+If you want to propose a new feature please read `Feature Proposal Process`_
+Devstack features are tracked on `Launchpad BP <https://blueprints.launchpad.net/devstack>`_.
+
+Task Tracking
+~~~~~~~~~~~~~
+We track our tasks in `Launchpad <https://bugs.launchpad.net/devstack>`_.
+
+Reporting a Bug
+~~~~~~~~~~~~~~~
+You found an issue and want to make sure we are aware of it? You can do so on
+`Launchpad <https://bugs.launchpad.net/devstack/+filebug>`__.
+More info about Launchpad usage can be found on `OpenStack docs page
+<https://docs.openstack.org/contributors/common/task-tracking.html#launchpad>`_
+
+Getting Your Patch Merged
+~~~~~~~~~~~~~~~~~~~~~~~~~
+All changes proposed to the Devstack require two ``Code-Review +2`` votes from
+Devstack core reviewers before one of the core reviewers can approve the patch
+by giving ``Workflow +1`` vote. One exception is for patches to unblock the gate
+which can be approved by single core reviewers.
+
+Project Team Lead Duties
+~~~~~~~~~~~~~~~~~~~~~~~~
+All common PTL duties are enumerated in the `PTL guide
+<https://docs.openstack.org/project-team-guide/ptl.html>`_.
+
+The Release Process for QA is documented in `QA Release Process
+<https://wiki.openstack.org/wiki/QA/releases>`_.
+
+.. _Feature Proposal Process: https://wiki.openstack.org/wiki/QA#Feature_Proposal_.26_Design_discussions
diff --git a/doc/source/guides.rst b/doc/source/guides.rst
index 82e0dd6..e7ec629 100644
--- a/doc/source/guides.rst
+++ b/doc/source/guides.rst
@@ -10,6 +10,7 @@
 
 .. toctree::
    :glob:
+   :hidden:
    :maxdepth: 1
 
    guides/single-vm
@@ -68,6 +69,11 @@
 
 Guide to working with nova features :doc:`Nova and devstack <guides/nova>`.
 
+Configure Load-Balancer Version 2
+-----------------------------------
+
+Guide on :doc:`Configure Load-Balancer Version 2 <guides/devstack-with-lbaas-v2>`.
+
 Deploying DevStack with LDAP
 ----------------------------
 
diff --git a/doc/source/guides/devstack-with-lbaas-v2.rst b/doc/source/guides/devstack-with-lbaas-v2.rst
index 669a70d..7fde6f1 100644
--- a/doc/source/guides/devstack-with-lbaas-v2.rst
+++ b/doc/source/guides/devstack-with-lbaas-v2.rst
@@ -62,7 +62,7 @@
     ENABLED_SERVICES+=,n-api,n-crt,n-cpu,n-cond,n-sch,n-api-meta,n-sproxy
     ENABLED_SERVICES+=,placement-api,placement-client
     # Glance
-    ENABLED_SERVICES+=,g-api,g-reg
+    ENABLED_SERVICES+=,g-api
     # Neutron
     ENABLED_SERVICES+=,q-svc,q-agt,q-dhcp,q-l3,q-meta,neutron
     ENABLED_SERVICES+=,octavia,o-cw,o-hk,o-hm,o-api
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 6694022..7923cb8 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -151,6 +151,13 @@
 with devstack, and help us by :doc:`contributing to the project
 <hacking>`.
 
+If you are a new contributor to devstack please refer: :doc:`contributor/contributing`
+
+.. toctree::
+   :hidden:
+
+   contributor/contributing
+
 Contents
 ++++++++
 
diff --git a/doc/source/networking.rst b/doc/source/networking.rst
index 74010cd..e65c7ef 100644
--- a/doc/source/networking.rst
+++ b/doc/source/networking.rst
@@ -40,7 +40,7 @@
 Locally Accessible Guests
 =========================
 
-If you want to make you guests accessible from other machines on your
+If you want to make your guests accessible from other machines on your
 network, we have to connect ``br-ex`` to a physical interface.
 
 Dedicated Guest Interface
@@ -81,7 +81,7 @@
    [[local|localrc]]
    PUBLIC_INTERFACE=eth0
    HOST_IP=10.42.0.52
-   FLOATING_RANGE=10.42.0.52/24
+   FLOATING_RANGE=10.42.0.0/24
    PUBLIC_NETWORK_GATEWAY=10.42.0.1
    Q_FLOATING_ALLOCATION_POOL=start=10.42.0.250,end=10.42.0.254
 
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
index 904400e..05a19ac 100644
--- a/doc/source/plugin-registry.rst
+++ b/doc/source/plugin-registry.rst
@@ -31,13 +31,14 @@
 openstack/ceilometer-powervm             `https://opendev.org/openstack/ceilometer-powervm <https://opendev.org/openstack/ceilometer-powervm>`__
 openstack/cinderlib                      `https://opendev.org/openstack/cinderlib <https://opendev.org/openstack/cinderlib>`__
 openstack/cloudkitty                     `https://opendev.org/openstack/cloudkitty <https://opendev.org/openstack/cloudkitty>`__
-openstack/congress                       `https://opendev.org/openstack/congress <https://opendev.org/openstack/congress>`__
 openstack/cyborg                         `https://opendev.org/openstack/cyborg <https://opendev.org/openstack/cyborg>`__
 openstack/designate                      `https://opendev.org/openstack/designate <https://opendev.org/openstack/designate>`__
 openstack/devstack-plugin-amqp1          `https://opendev.org/openstack/devstack-plugin-amqp1 <https://opendev.org/openstack/devstack-plugin-amqp1>`__
 openstack/devstack-plugin-ceph           `https://opendev.org/openstack/devstack-plugin-ceph <https://opendev.org/openstack/devstack-plugin-ceph>`__
 openstack/devstack-plugin-container      `https://opendev.org/openstack/devstack-plugin-container <https://opendev.org/openstack/devstack-plugin-container>`__
 openstack/devstack-plugin-kafka          `https://opendev.org/openstack/devstack-plugin-kafka <https://opendev.org/openstack/devstack-plugin-kafka>`__
+openstack/devstack-plugin-nfs            `https://opendev.org/openstack/devstack-plugin-nfs <https://opendev.org/openstack/devstack-plugin-nfs>`__
+openstack/devstack-plugin-open-cas       `https://opendev.org/openstack/devstack-plugin-open-cas <https://opendev.org/openstack/devstack-plugin-open-cas>`__
 openstack/devstack-plugin-pika           `https://opendev.org/openstack/devstack-plugin-pika <https://opendev.org/openstack/devstack-plugin-pika>`__
 openstack/devstack-plugin-zmq            `https://opendev.org/openstack/devstack-plugin-zmq <https://opendev.org/openstack/devstack-plugin-zmq>`__
 openstack/dragonflow                     `https://opendev.org/openstack/dragonflow <https://opendev.org/openstack/dragonflow>`__
@@ -50,6 +51,7 @@
 openstack/heat-dashboard                 `https://opendev.org/openstack/heat-dashboard <https://opendev.org/openstack/heat-dashboard>`__
 openstack/ironic                         `https://opendev.org/openstack/ironic <https://opendev.org/openstack/ironic>`__
 openstack/ironic-inspector               `https://opendev.org/openstack/ironic-inspector <https://opendev.org/openstack/ironic-inspector>`__
+openstack/ironic-prometheus-exporter     `https://opendev.org/openstack/ironic-prometheus-exporter <https://opendev.org/openstack/ironic-prometheus-exporter>`__
 openstack/ironic-ui                      `https://opendev.org/openstack/ironic-ui <https://opendev.org/openstack/ironic-ui>`__
 openstack/karbor                         `https://opendev.org/openstack/karbor <https://opendev.org/openstack/karbor>`__
 openstack/karbor-dashboard               `https://opendev.org/openstack/karbor-dashboard <https://opendev.org/openstack/karbor-dashboard>`__
@@ -75,14 +77,12 @@
 openstack/networking-bagpipe             `https://opendev.org/openstack/networking-bagpipe <https://opendev.org/openstack/networking-bagpipe>`__
 openstack/networking-baremetal           `https://opendev.org/openstack/networking-baremetal <https://opendev.org/openstack/networking-baremetal>`__
 openstack/networking-bgpvpn              `https://opendev.org/openstack/networking-bgpvpn <https://opendev.org/openstack/networking-bgpvpn>`__
-openstack/networking-calico              `https://opendev.org/openstack/networking-calico <https://opendev.org/openstack/networking-calico>`__
 openstack/networking-generic-switch      `https://opendev.org/openstack/networking-generic-switch <https://opendev.org/openstack/networking-generic-switch>`__
 openstack/networking-hyperv              `https://opendev.org/openstack/networking-hyperv <https://opendev.org/openstack/networking-hyperv>`__
 openstack/networking-l2gw                `https://opendev.org/openstack/networking-l2gw <https://opendev.org/openstack/networking-l2gw>`__
 openstack/networking-midonet             `https://opendev.org/openstack/networking-midonet <https://opendev.org/openstack/networking-midonet>`__
 openstack/networking-odl                 `https://opendev.org/openstack/networking-odl <https://opendev.org/openstack/networking-odl>`__
 openstack/networking-onos                `https://opendev.org/openstack/networking-onos <https://opendev.org/openstack/networking-onos>`__
-openstack/networking-ovn                 `https://opendev.org/openstack/networking-ovn <https://opendev.org/openstack/networking-ovn>`__
 openstack/networking-powervm             `https://opendev.org/openstack/networking-powervm <https://opendev.org/openstack/networking-powervm>`__
 openstack/networking-sfc                 `https://opendev.org/openstack/networking-sfc <https://opendev.org/openstack/networking-sfc>`__
 openstack/neutron                        `https://opendev.org/openstack/neutron <https://opendev.org/openstack/neutron>`__
@@ -95,15 +95,16 @@
 openstack/nova-powervm                   `https://opendev.org/openstack/nova-powervm <https://opendev.org/openstack/nova-powervm>`__
 openstack/octavia                        `https://opendev.org/openstack/octavia <https://opendev.org/openstack/octavia>`__
 openstack/octavia-dashboard              `https://opendev.org/openstack/octavia-dashboard <https://opendev.org/openstack/octavia-dashboard>`__
+openstack/octavia-tempest-plugin         `https://opendev.org/openstack/octavia-tempest-plugin <https://opendev.org/openstack/octavia-tempest-plugin>`__
 openstack/openstacksdk                   `https://opendev.org/openstack/openstacksdk <https://opendev.org/openstack/openstacksdk>`__
 openstack/os-loganalyze                  `https://opendev.org/openstack/os-loganalyze <https://opendev.org/openstack/os-loganalyze>`__
 openstack/osprofiler                     `https://opendev.org/openstack/osprofiler <https://opendev.org/openstack/osprofiler>`__
 openstack/oswin-tempest-plugin           `https://opendev.org/openstack/oswin-tempest-plugin <https://opendev.org/openstack/oswin-tempest-plugin>`__
+openstack/ovn-octavia-provider           `https://opendev.org/openstack/ovn-octavia-provider <https://opendev.org/openstack/ovn-octavia-provider>`__
 openstack/panko                          `https://opendev.org/openstack/panko <https://opendev.org/openstack/panko>`__
 openstack/patrole                        `https://opendev.org/openstack/patrole <https://opendev.org/openstack/patrole>`__
 openstack/qinling                        `https://opendev.org/openstack/qinling <https://opendev.org/openstack/qinling>`__
 openstack/qinling-dashboard              `https://opendev.org/openstack/qinling-dashboard <https://opendev.org/openstack/qinling-dashboard>`__
-openstack/rally                          `https://opendev.org/openstack/rally <https://opendev.org/openstack/rally>`__
 openstack/rally-openstack                `https://opendev.org/openstack/rally-openstack <https://opendev.org/openstack/rally-openstack>`__
 openstack/sahara                         `https://opendev.org/openstack/sahara <https://opendev.org/openstack/sahara>`__
 openstack/sahara-dashboard               `https://opendev.org/openstack/sahara-dashboard <https://opendev.org/openstack/sahara-dashboard>`__
@@ -135,18 +136,17 @@
 starlingx/metal                          `https://opendev.org/starlingx/metal <https://opendev.org/starlingx/metal>`__
 starlingx/nfv                            `https://opendev.org/starlingx/nfv <https://opendev.org/starlingx/nfv>`__
 starlingx/update                         `https://opendev.org/starlingx/update <https://opendev.org/starlingx/update>`__
+vexxhost/openstack-operator              `https://opendev.org/vexxhost/openstack-operator <https://opendev.org/vexxhost/openstack-operator>`__
 x/almanach                               `https://opendev.org/x/almanach <https://opendev.org/x/almanach>`__
 x/apmec                                  `https://opendev.org/x/apmec <https://opendev.org/x/apmec>`__
 x/bilean                                 `https://opendev.org/x/bilean <https://opendev.org/x/bilean>`__
 x/broadview-collector                    `https://opendev.org/x/broadview-collector <https://opendev.org/x/broadview-collector>`__
 x/collectd-openstack-plugins             `https://opendev.org/x/collectd-openstack-plugins <https://opendev.org/x/collectd-openstack-plugins>`__
 x/devstack-plugin-additional-pkg-repos   `https://opendev.org/x/devstack-plugin-additional-pkg-repos <https://opendev.org/x/devstack-plugin-additional-pkg-repos>`__
-x/devstack-plugin-bdd                    `https://opendev.org/x/devstack-plugin-bdd <https://opendev.org/x/devstack-plugin-bdd>`__
 x/devstack-plugin-glusterfs              `https://opendev.org/x/devstack-plugin-glusterfs <https://opendev.org/x/devstack-plugin-glusterfs>`__
 x/devstack-plugin-hdfs                   `https://opendev.org/x/devstack-plugin-hdfs <https://opendev.org/x/devstack-plugin-hdfs>`__
 x/devstack-plugin-libvirt-qemu           `https://opendev.org/x/devstack-plugin-libvirt-qemu <https://opendev.org/x/devstack-plugin-libvirt-qemu>`__
 x/devstack-plugin-mariadb                `https://opendev.org/x/devstack-plugin-mariadb <https://opendev.org/x/devstack-plugin-mariadb>`__
-x/devstack-plugin-nfs                    `https://opendev.org/x/devstack-plugin-nfs <https://opendev.org/x/devstack-plugin-nfs>`__
 x/devstack-plugin-vmax                   `https://opendev.org/x/devstack-plugin-vmax <https://opendev.org/x/devstack-plugin-vmax>`__
 x/drbd-devstack                          `https://opendev.org/x/drbd-devstack <https://opendev.org/x/drbd-devstack>`__
 x/fenix                                  `https://opendev.org/x/fenix <https://opendev.org/x/fenix>`__
diff --git a/doc/source/systemd.rst b/doc/source/systemd.rst
index 15b3f75..4f83b36 100644
--- a/doc/source/systemd.rst
+++ b/doc/source/systemd.rst
@@ -208,7 +208,8 @@
   the one you want.
 - ``systemd`` - a python 3 only library, not what you want.
 - ``python-systemd`` - another library you don't want. Installing it
-  on a system will break ansible's ability to run.
+  on a system will break ansible's ability to run. The package has now
+  been renamed to ``cysystemd``, which avoids the namespace collision.
 
 
 If we were using user units, the ``[Service]`` - ``Group=`` parameter
diff --git a/doc/source/zuul_ci_jobs_migration.rst b/doc/source/zuul_ci_jobs_migration.rst
index 17e7e16..c43603e 100644
--- a/doc/source/zuul_ci_jobs_migration.rst
+++ b/doc/source/zuul_ci_jobs_migration.rst
@@ -195,12 +195,6 @@
      - A bridge called br-infra is set up for all jobs that inherit
        from multinode with a dedicated `bridge role
        <https://zuul-ci.org/docs/zuul-jobs/general-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
diff --git a/files/debs/general b/files/debs/general
index df872a0..2d8cd80 100644
--- a/files/debs/general
+++ b/files/debs/general
@@ -1,7 +1,6 @@
 apache2
 apache2-dev
 bc
-bridge-utils
 bsdmainutils
 curl
 default-jre-headless  # NOPRIME
@@ -27,9 +26,8 @@
 openssl
 pkg-config
 psmisc
-python2.7
-python-dev
-python-gdbm # needed for testr
+python3-dev
+python3-venv
 tar
 tcpdump
 unzip
diff --git a/files/debs/keystone b/files/debs/keystone
index fd0317b..1cfa6ff 100644
--- a/files/debs/keystone
+++ b/files/debs/keystone
@@ -2,5 +2,5 @@
 libldap2-dev
 libsasl2-dev
 memcached
-python-mysqldb
+python3-mysqldb
 sqlite3
diff --git a/files/debs/ldap b/files/debs/ldap
index aa3a934..54896bb 100644
--- a/files/debs/ldap
+++ b/files/debs/ldap
@@ -1,3 +1,3 @@
 ldap-utils
-python-ldap
+python3-ldap
 slapd
diff --git a/files/debs/n-cpu b/files/debs/n-cpu
index 636644f..54d6fa3 100644
--- a/files/debs/n-cpu
+++ b/files/debs/n-cpu
@@ -5,7 +5,7 @@
 lvm2 # NOPRIME
 netcat-openbsd
 open-iscsi
-python-guestfs # NOPRIME
+python3-guestfs # NOPRIME
 qemu-utils
 sg3-utils
 sysfsutils
diff --git a/files/debs/neutron-common b/files/debs/neutron-common
index b269f63..e548396 100644
--- a/files/debs/neutron-common
+++ b/files/debs/neutron-common
@@ -9,7 +9,7 @@
 libmysqlclient-dev
 mysql-server #NOPRIME
 postgresql-server-dev-all
-python-mysqldb
+python3-mysqldb
 rabbitmq-server # NOPRIME
 radvd # NOPRIME
 sqlite3
diff --git a/files/debs/nova b/files/debs/nova
index e5110e9..dce8f6a 100644
--- a/files/debs/nova
+++ b/files/debs/nova
@@ -16,7 +16,7 @@
 mysql-server # NOPRIME
 parted
 pm-utils
-python-mysqldb
+python3-mysqldb
 qemu # dist:wheezy,jessie NOPRIME
 qemu-kvm # NOPRIME
 rabbitmq-server # NOPRIME
diff --git a/files/rpms-suse/general b/files/rpms-suse/general
index b870d72..0af2b5b 100644
--- a/files/rpms-suse/general
+++ b/files/rpms-suse/general
@@ -1,7 +1,6 @@
 apache2
 apache2-devel
 bc
-bridge-utils
 ca-certificates-mozilla
 curl
 gcc
diff --git a/files/rpms-suse/neutron-common b/files/rpms-suse/neutron-common
index d1cc73f..e3799a9 100644
--- a/files/rpms-suse/neutron-common
+++ b/files/rpms-suse/neutron-common
@@ -5,7 +5,6 @@
 haproxy # to serve as metadata proxy inside router/dhcp namespaces
 iptables
 iputils
-mariadb # NOPRIME
 rabbitmq-server # NOPRIME
 radvd # NOPRIME
 sqlite3
diff --git a/files/rpms-suse/nova b/files/rpms-suse/nova
index 1d58121..9923760 100644
--- a/files/rpms-suse/nova
+++ b/files/rpms-suse/nova
@@ -11,7 +11,6 @@
 kvm # NOPRIME
 libvirt # NOPRIME
 libvirt-python # NOPRIME
-mariadb # NOPRIME
 # mkisofs is required for config_drive
 mkisofs # not:sle12
 parted
diff --git a/files/rpms/cinder b/files/rpms/cinder
index e6b33dc..c21ea08 100644
--- a/files/rpms/cinder
+++ b/files/rpms/cinder
@@ -1,5 +1,4 @@
 iscsi-initiator-utils
 lvm2
 qemu-img
-scsi-target-utils # not:rhel7,f25,f26,f27,f28,f29 NOPRIME
-targetcli # dist:rhel7,f25,f26,f27,f28,f29 NOPRIME
+targetcli
diff --git a/files/rpms/dstat b/files/rpms/dstat
index d7b272a..6524bed 100644
--- a/files/rpms/dstat
+++ b/files/rpms/dstat
@@ -1,2 +1 @@
-dstat # not:f29
-pcp-system-tools # dist:f29
+pcp-system-tools
diff --git a/files/rpms/general b/files/rpms/general
index 5bf1e9a..303510c 100644
--- a/files/rpms/general
+++ b/files/rpms/general
@@ -1,5 +1,4 @@
 bc
-bridge-utils
 curl
 dbus
 gcc
@@ -9,15 +8,13 @@
 graphviz # needed only for docs
 httpd
 httpd-devel
-iptables-services  # NOPRIME f25,f26,f27,f28,f29
-java-1.7.0-openjdk-headless  # NOPRIME rhel7
-java-1.8.0-openjdk-headless  # NOPRIME f25,f26,f27,f28,f29
+iptables-services
+java-1.8.0-openjdk-headless
 libffi-devel
 libjpeg-turbo-devel # Pillow 3.0.0
 libxml2-devel # lxml
 libxslt-devel # lxml
 libyaml-devel
-mariadb-devel  # MySQL-python
 net-tools
 openssh-server
 openssl
@@ -26,8 +23,7 @@
 pkgconfig
 postgresql-devel  # psycopg2
 psmisc
-pyOpenSSL # version in pip uses too much memory
-python-devel
+python3-devel
 redhat-rpm-config # missing dep for gcc hardening flags, see rhbz#1217376
 systemd-devel # for systemd-python
 tar
diff --git a/files/rpms/neutron-common b/files/rpms/neutron-common
index 0cc8d11..fe25f57 100644
--- a/files/rpms/neutron-common
+++ b/files/rpms/neutron-common
@@ -5,8 +5,6 @@
 haproxy # to serve as metadata proxy inside router/dhcp namespaces
 iptables
 iputils
-mysql-devel
-mysql-server # NOPRIME
 openvswitch # NOPRIME
 rabbitmq-server # NOPRIME
 radvd # NOPRIME
diff --git a/files/rpms/nova b/files/rpms/nova
index 639d793..2218330 100644
--- a/files/rpms/nova
+++ b/files/rpms/nova
@@ -7,12 +7,8 @@
 genisoimage # required for config_drive
 iptables
 iputils
-kernel-modules # dist:f25,f26,f27,f28,f29
+kernel-modules
 kpartx
-libxml2-python
-m2crypto
-mysql-devel
-mysql-server # NOPRIME
 parted
 polkit
 rabbitmq-server # NOPRIME
diff --git a/files/rpms/swift b/files/rpms/swift
index be524d1..376c6f3 100644
--- a/files/rpms/swift
+++ b/files/rpms/swift
@@ -1,8 +1,7 @@
 curl
 liberasurecode-devel
 memcached
-pyxattr
-rsync-daemon # dist:f25,f26,f27,f28,f29
+rsync-daemon
 sqlite
 xfsprogs
 xinetd
diff --git a/functions b/functions
index 8ea634e..2470015 100644
--- a/functions
+++ b/functions
@@ -341,6 +341,12 @@
             disk_format=qcow2
             container_format=bare
             ;;
+        *.qcow2.xz)
+            image_name=$(basename "$image" ".qcow2.xz")
+            disk_format=qcow2
+            container_format=bare
+            unpack=unxz
+            ;;
         *.raw)
             image_name=$(basename "$image" ".raw")
             disk_format=raw
@@ -376,6 +382,16 @@
             openstack --os-cloud=devstack-admin --os-region-name="$REGION_NAME" image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < <(zcat --force "${image}")
         elif [ "$unpack" = "bunzip2" ]; then
             openstack --os-cloud=devstack-admin --os-region-name="$REGION_NAME" image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < <(bunzip2 -cdk "${image}")
+        elif [ "$unpack" = "unxz" ]; then
+            # NOTE(brtknr): unxz the file first and cleanup afterwards to
+            # prevent timeout while Glance tries to upload image (e.g. to Swift).
+            local tmp_dir
+            local image_path
+            tmp_dir=$(mktemp -d)
+            image_path="$tmp_dir/$image_name"
+            unxz -cv "${image}" > "$image_path"
+            openstack --os-cloud=devstack-admin --os-region-name="$REGION_NAME" image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format --file "$image_path"
+            rm -rf $tmp_dir
         else
             openstack --os-cloud=devstack-admin --os-region-name="$REGION_NAME" image create "$image_name" $img_property --public --container-format=$container_format --disk-format $disk_format < "${image}"
         fi
@@ -635,40 +651,29 @@
 # This sets up defaults we like in devstack for logging for tracking
 # down issues, and makes sure everything is done the same between
 # projects.
+# NOTE(jh): Historically this function switched between three different
+# functions: setup_systemd_logging, setup_colorized_logging and
+# setup_standard_logging_identity. Since we always run with systemd now,
+# this could be cleaned up, but the other functions may still be in use
+# by plugins. Since deprecations haven't worked in the past, we'll just
+# leave them in place.
 function setup_logging {
-    local conf_file=$1
-    local other_cond=${2:-"False"}
-    if [[ "$USE_SYSTEMD" == "True" ]]; then
-        setup_systemd_logging $conf_file
-    elif [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ] && [ "$other_cond" == "False" ]; then
-        setup_colorized_logging $conf_file
-    else
-        setup_standard_logging_identity $conf_file
-    fi
+    setup_systemd_logging $1
 }
 
 # This function sets log formatting options for colorizing log
 # output to stdout. It is meant to be called by lib modules.
-# The last two parameters are optional and can be used to specify
-# non-default value for project and user format variables.
-# Defaults are respectively 'project_name' and 'user_name'
-#
-# setup_colorized_logging something.conf SOMESECTION
 function setup_colorized_logging {
     local conf_file=$1
-    local conf_section="DEFAULT"
-    local project_var="project_name"
-    local user_var="user_name"
     # Add color to logging output
-    iniset $conf_file $conf_section logging_context_format_string "%(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [%(request_id)s %("$project_var")s %("$user_var")s%(color)s] %(instance)s%(color)s%(message)s"
-    iniset $conf_file $conf_section logging_default_format_string "%(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [-%(color)s] %(instance)s%(color)s%(message)s"
-    iniset $conf_file $conf_section logging_debug_format_suffix "from (pid=%(process)d) %(funcName)s %(pathname)s:%(lineno)d"
-    iniset $conf_file $conf_section logging_exception_prefix "%(color)s%(asctime)s.%(msecs)03d TRACE %(name)s %(instance)s"
+    iniset $conf_file DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [%(request_id)s %(project_name)s %(user_name)s%(color)s] %(instance)s%(color)s%(message)s"
+    iniset $conf_file DEFAULT logging_default_format_string "%(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [-%(color)s] %(instance)s%(color)s%(message)s"
+    iniset $conf_file DEFAULT logging_debug_format_suffix "from (pid=%(process)d) %(funcName)s %(pathname)s:%(lineno)d"
+    iniset $conf_file DEFAULT logging_exception_prefix "%(color)s%(asctime)s.%(msecs)03d TRACE %(name)s %(instance)s"
 }
 
 function setup_systemd_logging {
     local conf_file=$1
-    local conf_section="DEFAULT"
     # NOTE(sdague): this is a nice to have, and means we're using the
     # native systemd path, which provides for things like search on
     # request-id. However, there may be an eventlet interaction here,
@@ -676,16 +681,16 @@
     USE_JOURNAL=$(trueorfalse False USE_JOURNAL)
     local pidstr=""
     if [[ "$USE_JOURNAL" == "True" ]]; then
-        iniset $conf_file $conf_section use_journal "True"
+        iniset $conf_file DEFAULT use_journal "True"
         # if we are using the journal directly, our process id is already correct
     else
         pidstr="(pid=%(process)d) "
     fi
-    iniset $conf_file $conf_section logging_debug_format_suffix "{{${pidstr}%(funcName)s %(pathname)s:%(lineno)d}}"
+    iniset $conf_file DEFAULT logging_debug_format_suffix "{{${pidstr}%(funcName)s %(pathname)s:%(lineno)d}}"
 
-    iniset $conf_file $conf_section logging_context_format_string "%(color)s%(levelname)s %(name)s [%(global_request_id)s %(request_id)s %(project_name)s %(user_name)s%(color)s] %(instance)s%(color)s%(message)s"
-    iniset $conf_file $conf_section logging_default_format_string "%(color)s%(levelname)s %(name)s [-%(color)s] %(instance)s%(color)s%(message)s"
-    iniset $conf_file $conf_section logging_exception_prefix "ERROR %(name)s %(instance)s"
+    iniset $conf_file DEFAULT logging_context_format_string "%(color)s%(levelname)s %(name)s [%(global_request_id)s %(request_id)s %(project_name)s %(user_name)s%(color)s] %(instance)s%(color)s%(message)s"
+    iniset $conf_file DEFAULT logging_default_format_string "%(color)s%(levelname)s %(name)s [-%(color)s] %(instance)s%(color)s%(message)s"
+    iniset $conf_file DEFAULT logging_exception_prefix "ERROR %(name)s %(instance)s"
 }
 
 function setup_standard_logging_identity {
diff --git a/functions-common b/functions-common
index 6be07b4..dea5aa9 100644
--- a/functions-common
+++ b/functions-common
@@ -129,6 +129,11 @@
         --os-password $ADMIN_PASSWORD \
         --os-system-scope all
 
+    cat >> $CLOUDS_YAML <<EOF
+functional:
+  image_name: $DEFAULT_IMAGE_NAME
+EOF
+
     # CLean up any old clouds.yaml files we had laying around
     rm -f $(eval echo ~"$STACK_USER")/.config/openstack/clouds.yaml
 }
@@ -329,9 +334,6 @@
         sudo zypper -n install lsb-release
     elif [[ -x $(command -v dnf 2>/dev/null) ]]; then
         sudo dnf install -y redhat-lsb-core
-    elif [[ -x $(command -v yum 2>/dev/null) ]]; then
-        # all rh patforms (fedora, centos, rhel) have this pkg
-        sudo yum install -y redhat-lsb-core
     else
         die $LINENO "Unable to find or auto-install lsb_release"
     fi
@@ -1361,7 +1363,7 @@
     if is_ubuntu; then
         apt_get purge "$@"
     elif is_fedora; then
-        sudo ${YUM:-yum} remove -y "$@" ||:
+        sudo dnf remove -y "$@" ||:
     elif is_suse; then
         sudo zypper remove -y "$@" ||:
     else
@@ -1369,8 +1371,11 @@
     fi
 }
 
-# Wrapper for ``yum`` to set proxy environment variables
-# Uses globals ``OFFLINE``, ``*_proxy``, ``YUM``
+# Wrapper for ``dnf`` to set proxy environment variables
+# Uses globals ``OFFLINE``, ``*_proxy``
+# The name is kept for backwards compatability with external
+# callers, despite none of our supported platforms using yum
+# any more.
 # yum_install package [package ...]
 function yum_install {
     local result parse_yum_result
@@ -1378,44 +1383,8 @@
     [[ "$OFFLINE" = "True" ]] && return
 
     time_start "yum_install"
-
-    # This is a bit tricky, because yum -y assumes missing or failed
-    # packages are OK (see [1]).  We want devstack to stop if we are
-    # installing missing packages.
-    #
-    # Thus we manually match on the output (stack.sh runs in a fixed
-    # locale, so lang shouldn't change).
-    #
-    # If yum returns !0, we echo the result as "YUM_FAILED" and return
-    # that from the awk (we're subverting -e with this trick).
-    # Otherwise we use awk to look for failure strings and return "2"
-    # to indicate a terminal failure.
-    #
-    # [1] https://bugzilla.redhat.com/show_bug.cgi?id=965567
-    parse_yum_result='              \
-        BEGIN { result=0 }          \
-        /^YUM_FAILED/ { result=$2 } \
-        /^No package/ { result=2 }  \
-        /^Failed:/    { result=2 }  \
-        //{ print }                 \
-        END { exit result }'
-    (sudo_with_proxies "${YUM:-yum}" install -y "$@" 2>&1 || echo YUM_FAILED $?) \
-        | awk "$parse_yum_result" && result=$? || result=$?
-
+    sudo_with_proxies dnf install -y "$@"
     time_stop "yum_install"
-
-    # if we return 1, then the wrapper functions will run an update
-    # and try installing the package again as a defense against bad
-    # mirrors.  This can hide failures, especially when we have
-    # packages that are in the "Failed:" section because their rpm
-    # install scripts failed to run correctly (in this case, the
-    # package looks installed, so when the retry happens we just think
-    # the package is OK, and incorrectly continue on).
-    if [ "$result" == 2 ]; then
-        die "Detected fatal package install failure"
-    fi
-
-    return "$result"
 }
 
 # zypper wrapper to set arguments correctly
diff --git a/inc/python b/inc/python
index 37e8399..dd77296 100644
--- a/inc/python
+++ b/inc/python
@@ -21,6 +21,14 @@
 # project.  A null value installs to the system Python directories.
 declare -A -g PROJECT_VENV
 
+# Utility Functions
+# =================
+
+# Joins bash array of extras with commas as expected by other functions
+function join_extras {
+    local IFS=","
+    echo "$*"
+}
 
 # Python Functions
 # ================
@@ -80,9 +88,9 @@
 function pip_install_gr_extras {
     local name=$1
     local extras=$2
-    local clean_name
-    clean_name=$(get_from_global_requirements $name)
-    pip_install $clean_name[$extras]
+    local version_constraints
+    version_constraints=$(get_version_constraints_from_global_requirements $name)
+    pip_install $name[$extras]$version_constraints
 }
 
 # enable_python3_package() -- no-op for backwards compatibility
@@ -179,13 +187,6 @@
 
     $xtrace
 
-    # Also install test requirements
-    local install_test_reqs=""
-    local test_req="${package_dir}/test-requirements.txt"
-    if [[ -e "$test_req" ]]; then
-        install_test_reqs="-r $test_req"
-    fi
-
     # adding SETUPTOOLS_SYS_PATH_TECHNIQUE is a workaround to keep
     # the same behaviour of setuptools before version 25.0.0.
     # related issue: https://github.com/pypa/pip/issues/3874
@@ -195,7 +196,7 @@
         no_proxy="${no_proxy:-}" \
         PIP_FIND_LINKS=$PIP_FIND_LINKS \
         SETUPTOOLS_SYS_PATH_TECHNIQUE=rewrite \
-        $cmd_pip $upgrade $install_test_reqs \
+        $cmd_pip $upgrade \
         $@
     result=$?
 
@@ -237,6 +238,19 @@
     echo $required_pkg
 }
 
+# get only version constraints of a package from global requirements file
+# get_version_constraints_from_global_requirements <package>
+function get_version_constraints_from_global_requirements {
+    local package=$1
+    local required_pkg_version_constraint
+    # drop the package name from output (\K)
+    required_pkg_version_constraint=$(grep -i -h -o -P "^${package}\K.*" $REQUIREMENTS_DIR/global-requirements.txt | cut -d\# -f1)
+    if [[ $required_pkg_version_constraint == ""  ]]; then
+        die $LINENO "Can't find package $package in requirements"
+    fi
+    echo $required_pkg_version_constraint
+}
+
 # should we use this library from their git repo, or should we let it
 # get pulled in via pip dependencies.
 function use_library_from_git {
@@ -285,7 +299,7 @@
 #
 # use this for non namespaced libraries
 #
-# setup_dev_lib [-bindep] <name>
+# setup_dev_lib [-bindep] <name> [<extras>]
 function setup_dev_lib {
     local bindep
     if [[ $1 == -bindep* ]]; then
@@ -294,7 +308,8 @@
     fi
     local name=$1
     local dir=${GITDIR[$name]}
-    setup_develop $bindep $dir
+    local extras=$2
+    setup_develop $bindep $dir $extras
 }
 
 # this should be used if you want to install globally, all libraries should
@@ -450,12 +465,43 @@
     fi
 }
 
+# Provide requested python version and sets PYTHON variable
+function install_python {
+    # NOTE: install_python function should finally just do what install_python3
+    # does as soon Python 2 support has been dropped
+    if python3_enabled; then
+        install_python3
+        export PYTHON=$(which python${PYTHON3_VERSION} 2>/dev/null ||
+                        which python3 2>/dev/null)
+        if [[ "${DISTRO}" =~ (rhel8) ]]; then
+            # Use Python 3 as default python command so that we have only one
+            # python alternative to use on the system for either python and
+            # python3
+            sudo alternatives --set python "${PYTHON}"
+        else
+            # Install anyway Python 2 for legacy scripts that still requires
+            # python instead of python3 command
+            install_package python
+        fi
+    else
+        echo "WARNING - Python 2 support has been deprecated in favor of Python 3"
+        install_package python
+        export PYTHON=$(which python 2>/dev/null)
+    fi
+}
+
 # Install python3 packages
 function install_python3 {
     if is_ubuntu; then
         apt_get install python${PYTHON3_VERSION} python${PYTHON3_VERSION}-dev
     elif is_suse; then
         install_package python3-devel python3-dbm
+    elif is_fedora; then
+        if [ "$os_VENDOR" = "Fedora" ]; then
+            install_package python${PYTHON3_VERSION//.}
+        else
+            install_package python${PYTHON3_VERSION//.} python${PYTHON3_VERSION//.}-devel
+        fi
     fi
 }
 
diff --git a/lib/apache b/lib/apache
index 84cec73..41c2e3d 100644
--- a/lib/apache
+++ b/lib/apache
@@ -82,26 +82,56 @@
         apxs="apxs"
     fi
 
-    # Ubuntu xenial is back level on uwsgi so the proxy doesn't
-    # actually work. Hence we have to build from source for now.
+    # This varies based on packaged/installed.  If we've
+    # pip_installed, then the pip setup will only build a "python"
+    # module that will be either python2 or python3 depending on what
+    # it was built with.
     #
-    # Centos 7 actually has the module in epel, but there was a big
-    # push to disable epel by default. As such, compile from source
-    # there as well.
+    # For package installs, the distro ships both plugins and you need
+    # to select the right one ... it will not be autodetected.
+    if python3_enabled; then
+        UWSGI_PYTHON_PLUGIN=python3
+    else
+        UWSGI_PYTHON_PLUGIN=python
+    fi
 
-    local dir
-    dir=$(mktemp -d)
-    pushd $dir
-    pip_install uwsgi
-    pip download uwsgi -c $REQUIREMENTS_DIR/upper-constraints.txt
-    local uwsgi
-    uwsgi=$(ls uwsgi*)
-    tar xvf $uwsgi
-    cd uwsgi*/apache2
-    sudo $apxs -i -c mod_proxy_uwsgi.c
-    popd
-    # delete the temp directory
-    sudo rm -rf $dir
+    if is_ubuntu; then
+        install_package uwsgi \
+                        uwsgi-plugin-python \
+                        uwsgi-plugin-python3 \
+                        libapache2-mod-proxy-uwsgi
+    elif [[ $os_VENDOR == "Fedora" ]]; then
+        # Note httpd comes with mod_proxy_uwsgi and it is loaded by
+        # default; the mod_proxy_uwsgi package actually conflicts now.
+        # See:
+        #  https://bugzilla.redhat.com/show_bug.cgi?id=1574335
+        #
+        # Thus there is nothing else to do after this install
+        install_package uwsgi \
+                        uwsgi-plugin-python3
+    elif [[ $os_VENDOR =~ openSUSE ]]; then
+        install_package uwsgi \
+                        uwsgi-python3 \
+                        apache2-mod_uwsgi
+    else
+        # Centos actually has the module in epel, but there was a big
+        # push to disable epel by default. As such, compile from source
+        # there.
+        local dir
+        dir=$(mktemp -d)
+        pushd $dir
+        pip_install uwsgi
+        pip download uwsgi -c $REQUIREMENTS_DIR/upper-constraints.txt
+        local uwsgi
+        uwsgi=$(ls uwsgi*)
+        tar xvf $uwsgi
+        cd uwsgi*/apache2
+        sudo $apxs -i -c mod_proxy_uwsgi.c
+        popd
+        # delete the temp directory
+        sudo rm -rf $dir
+        UWSGI_PYTHON_PLUGIN=python
+    fi
 
     if is_ubuntu || is_suse ; then
         # we've got to enable proxy and proxy_uwsgi for this to work
@@ -265,7 +295,7 @@
     # configured after graceful shutdown
     iniset "$file" uwsgi worker-reload-mercy $WORKER_TIMEOUT
     iniset "$file" uwsgi enable-threads true
-    iniset "$file" uwsgi plugins python
+    iniset "$file" uwsgi plugins http,${UWSGI_PYTHON_PLUGIN}
     # uwsgi recommends this to prevent thundering herd on accept.
     iniset "$file" uwsgi thunder-lock true
     # Set hook to trigger graceful shutdown on SIGTERM
@@ -318,7 +348,7 @@
     iniset "$file" uwsgi die-on-term true
     iniset "$file" uwsgi exit-on-reload false
     iniset "$file" uwsgi enable-threads true
-    iniset "$file" uwsgi plugins python
+    iniset "$file" uwsgi plugins http,${UWSGI_PYTHON_PLUGIN}
     # uwsgi recommends this to prevent thundering herd on accept.
     iniset "$file" uwsgi thunder-lock true
     # Set hook to trigger graceful shutdown on SIGTERM
diff --git a/lib/cinder b/lib/cinder
index fd96053..c2e55f9 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -492,7 +492,7 @@
                 start_tls_proxy cinder '*' $CINDER_SERVICE_PORT $CINDER_SERVICE_HOST $CINDER_SERVICE_PORT_INT
             fi
         else
-            run_process "c-api" "$CINDER_BIN_DIR/uwsgi --procname-prefix cinder-api --ini $CINDER_UWSGI_CONF"
+            run_process "c-api" "$(which uwsgi) --procname-prefix cinder-api --ini $CINDER_UWSGI_CONF"
             cinder_url=$service_protocol://$SERVICE_HOST/volume/v3
         fi
     fi
diff --git a/lib/databases/mysql b/lib/databases/mysql
index 420a86e..d4969d7 100644
--- a/lib/databases/mysql
+++ b/lib/databases/mysql
@@ -109,8 +109,10 @@
         sudo mysql $cmd_args -e "UPDATE mysql.user SET plugin='' WHERE user='$DATABASE_USER' AND host='localhost';"
         sudo mysql $cmd_args -e "FLUSH PRIVILEGES;"
     fi
+    # Create DB user if it does not already exist
+    sudo mysql $cmd_args -e "CREATE USER IF NOT EXISTS '$DATABASE_USER'@'%' identified by '$DATABASE_PASSWORD';"
     # Update the DB to give user '$DATABASE_USER'@'%' full control of the all databases:
-    sudo mysql $cmd_args -e "GRANT ALL PRIVILEGES ON *.* TO '$DATABASE_USER'@'%' identified by '$DATABASE_PASSWORD';"
+    sudo mysql $cmd_args -e "GRANT ALL PRIVILEGES ON *.* TO '$DATABASE_USER'@'%';"
 
     # Now update ``my.cnf`` for some local needs and restart the mysql service
 
@@ -120,8 +122,6 @@
     iniset -sudo $my_conf mysqld sql_mode TRADITIONAL
     iniset -sudo $my_conf mysqld default-storage-engine InnoDB
     iniset -sudo $my_conf mysqld max_connections 1024
-    iniset -sudo $my_conf mysqld query_cache_type OFF
-    iniset -sudo $my_conf mysqld query_cache_size 0
 
     if [[ "$DATABASE_QUERY_LOGGING" == "True" ]]; then
         echo_summary "Enabling MySQL query logging"
@@ -175,7 +175,10 @@
     # Install mysql-server
     if is_oraclelinux; then
         install_package mysql-community-server
-    elif is_fedora || is_suse; then
+    elif is_fedora; then
+        install_package mariadb-server mariadb-devel
+        sudo systemctl enable $MYSQL_SERVICE_NAME
+    elif is_suse; then
         install_package mariadb-server
         sudo systemctl enable $MYSQL_SERVICE_NAME
     elif is_ubuntu; then
diff --git a/lib/dstat b/lib/dstat
index fe38d75..f5bd2bb 100644
--- a/lib/dstat
+++ b/lib/dstat
@@ -9,6 +9,7 @@
 
 # ``stack.sh`` calls the entry points in this order:
 #
+# - install_dstat
 # - start_dstat
 # - stop_dstat
 
@@ -16,6 +17,14 @@
 _XTRACE_DSTAT=$(set +o | grep xtrace)
 set +o xtrace
 
+# install_dstat() - Install prerequisites for dstat services
+function install_dstat {
+    if is_service_enabled memory_tracker; then
+        # Install python libraries required by tools/mlock_report.py
+        pip_install_gr psutil
+    fi
+}
+
 # start_dstat() - Start running processes
 function start_dstat {
     # A better kind of sysstat, with the top process per time slice
diff --git a/lib/glance b/lib/glance
index 740bcab..8ee422d 100644
--- a/lib/glance
+++ b/lib/glance
@@ -41,9 +41,29 @@
     GLANCE_BIN_DIR=$(get_python_exec_prefix)
 fi
 
+# Glance multi-store configuration
+# Boolean flag to enable multiple store configuration for glance
+GLANCE_ENABLE_MULTIPLE_STORES=$(trueorfalse False GLANCE_ENABLE_MULTIPLE_STORES)
+
+# Comma separated list for configuring multiple file stores of glance,
+# for example; GLANCE_MULTIPLE_FILE_STORES = fast,cheap,slow
+GLANCE_MULTIPLE_FILE_STORES=${GLANCE_MULTIPLE_FILE_STORES:-fast}
+
+# Default store/backend for glance, must be one of the store specified
+# in GLANCE_MULTIPLE_FILE_STORES option.
+GLANCE_DEFAULT_BACKEND=${GLANCE_DEFAULT_BACKEND:-fast}
+
 GLANCE_CACHE_DIR=${GLANCE_CACHE_DIR:=$DATA_DIR/glance/cache}
+
+# File path for each store specified in GLANCE_MULTIPLE_FILE_STORES, the store
+# identifier will be appended to this path at runtime. If GLANCE_MULTIPLE_FILE_STORES
+# has fast,cheap specified then filepath will be generated like $DATA_DIR/glance/fast
+# and $DATA_DIR/glance/cheap.
+GLANCE_MULTISTORE_FILE_IMAGE_DIR=${GLANCE_MULTISTORE_FILE_IMAGE_DIR:=$DATA_DIR/glance}
 GLANCE_IMAGE_DIR=${GLANCE_IMAGE_DIR:=$DATA_DIR/glance/images}
 GLANCE_LOCK_DIR=${GLANCE_LOCK_DIR:=$DATA_DIR/glance/locks}
+GLANCE_STAGING_DIR=${GLANCE_MULTISTORE_FILE_IMAGE_DIR:=$DATA_DIR/os_glance_staging_store}
+GLANCE_TASKS_DIR=${GLANCE_MULTISTORE_FILE_IMAGE_DIR:=$DATA_DIR/os_glance_tasks_store}
 
 GLANCE_CONF_DIR=${GLANCE_CONF_DIR:-/etc/glance}
 GLANCE_METADEF_DIR=$GLANCE_CONF_DIR/metadefs
@@ -97,6 +117,18 @@
 function cleanup_glance {
     # delete image files (glance)
     sudo rm -rf $GLANCE_CACHE_DIR $GLANCE_IMAGE_DIR
+
+    # Cleanup multiple stores directories
+    if [[ "$GLANCE_ENABLE_MULTIPLE_STORES" == "True" ]]; then
+        local store file_dir
+        for store in $(echo $GLANCE_MULTIPLE_FILE_STORES | tr "," "\n"); do
+            file_dir="${GLANCE_MULTISTORE_FILE_IMAGE_DIR}/${store}/"
+            sudo rm -rf $file_dir
+        done
+
+        # Cleanup reserved stores directories
+        sudo rm -rf $GLANCE_STAGING_DIR $GLANCE_TASKS_DIR
+    fi
 }
 
 # configure_glance() - Set config files, create data dirs, etc
@@ -117,6 +149,16 @@
     iniset_rpc_backend glance $GLANCE_REGISTRY_CONF
     iniset $GLANCE_REGISTRY_CONF DEFAULT graceful_shutdown_timeout "$SERVICE_GRACEFUL_SHUTDOWN_TIMEOUT"
 
+    # Configure multiple stores
+    if [[ "$GLANCE_ENABLE_MULTIPLE_STORES" == "True" ]]; then
+        local store enabled_backends
+        enabled_backends=""
+        for store in $(echo $GLANCE_MULTIPLE_FILE_STORES | tr "," "\n"); do
+            enabled_backends+="${store}:file,"
+        done
+        iniset $GLANCE_API_CONF DEFAULT enabled_backends ${enabled_backends::-1}
+    fi
+
     # Set non-default configuration options for the API server
     iniset $GLANCE_API_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
     iniset $GLANCE_API_CONF database connection $dburl
@@ -141,8 +183,21 @@
         iniset $GLANCE_API_CONF DEFAULT enable_v1_api False
     fi
 
-    # Store specific configs
-    iniset $GLANCE_API_CONF glance_store filesystem_store_datadir $GLANCE_IMAGE_DIR/
+    # Glance multiple store Store specific configs
+    if [[ "$GLANCE_ENABLE_MULTIPLE_STORES" == "True" ]]; then
+        iniset $GLANCE_API_CONF glance_store default_backend $GLANCE_DEFAULT_BACKEND
+        local store
+        for store in $(echo $GLANCE_MULTIPLE_FILE_STORES | tr "," "\n"); do
+            iniset $GLANCE_API_CONF $store filesystem_store_datadir "${GLANCE_MULTISTORE_FILE_IMAGE_DIR}/${store}/"
+        done
+
+        # Glance configure reserved stores
+        iniset $GLANCE_API_CONF os_glance_staging_store filesystem_store_datadir "${GLANCE_MULTISTORE_FILE_IMAGE_DIR}/os_glance_staging_store/"
+        iniset $GLANCE_API_CONF os_glance_tasks_store filesystem_store_datadir "${GLANCE_MULTISTORE_FILE_IMAGE_DIR}/os_glance_tasks_store/"
+    else
+        # Store specific configs
+        iniset $GLANCE_API_CONF glance_store filesystem_store_datadir $GLANCE_IMAGE_DIR/
+    fi
     iniset $GLANCE_API_CONF DEFAULT registry_host $(ipv6_unquote $GLANCE_SERVICE_HOST)
 
     # CORS feature support - to allow calls from Horizon by default
@@ -152,6 +207,7 @@
         iniset $GLANCE_API_CONF cors allowed_origin "http://$SERVICE_HOST"
     fi
 
+    # No multiple stores for swift yet
     # Store the images in swift if enabled.
     if is_service_enabled s-proxy; then
         iniset $GLANCE_API_CONF glance_store default_store swift
@@ -299,11 +355,24 @@
 
 # install_glance() - Collect source and prepare
 function install_glance {
+    local glance_store_extras=()
+
+    if is_service_enabled cinder; then
+        glance_store_extras=("cinder" "${glance_store_extras[@]}")
+    fi
+
+    if is_service_enabled swift; then
+        glance_store_extras=("swift" "${glance_store_extras[@]}")
+    fi
+
     # Install glance_store from git so we make sure we're testing
     # the latest code.
     if use_library_from_git "glance_store"; then
         git_clone_by_name "glance_store"
-        setup_dev_lib "glance_store"
+        setup_dev_lib "glance_store" $(join_extras "${glance_store_extras[@]}")
+    else
+        # we still need to pass extras
+        pip_install_gr_extras glance-store $(join_extras "${glance_store_extras[@]}")
     fi
 
     git_clone $GLANCE_REPO $GLANCE_DIR $GLANCE_BRANCH
@@ -323,7 +392,7 @@
 
     run_process g-reg "$GLANCE_BIN_DIR/glance-registry --config-file=$GLANCE_CONF_DIR/glance-registry.conf"
     if [[ "$WSGI_MODE" == "uwsgi" ]]; then
-        run_process g-api "$GLANCE_BIN_DIR/uwsgi --procname-prefix glance-api --ini $GLANCE_UWSGI_CONF"
+        run_process g-api "$(which uwsgi) --procname-prefix glance-api --ini $GLANCE_UWSGI_CONF"
     else
         run_process g-api "$GLANCE_BIN_DIR/glance-api --config-dir=$GLANCE_CONF_DIR"
     fi
diff --git a/lib/keystone b/lib/keystone
index 9ceb829..1910f34 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -421,7 +421,7 @@
     iniset $conf_file $section project_domain_name "$SERVICE_DOMAIN_NAME"
 
     iniset $conf_file $section cafile $SSL_BUNDLE_FILE
-    iniset $conf_file $section memcached_servers localhost:11211
+    iniset $conf_file $section memcached_servers $MEMCACHE_SERVERS
 }
 
 # configure_auth_token_middleware conf_file admin_user IGNORED [section]
@@ -504,8 +504,6 @@
 
     if [ "$KEYSTONE_DEPLOY" == "mod_wsgi" ]; then
         install_apache_wsgi
-    elif [ "$KEYSTONE_DEPLOY" == "uwsgi" ]; then
-        pip_install uwsgi
     fi
 }
 
@@ -523,7 +521,7 @@
         enable_apache_site keystone
         restart_apache_server
     else # uwsgi
-        run_process keystone "$KEYSTONE_BIN_DIR/uwsgi --procname-prefix keystone --ini $KEYSTONE_PUBLIC_UWSGI_CONF" ""
+        run_process keystone "$(which uwsgi) --procname-prefix keystone --ini $KEYSTONE_PUBLIC_UWSGI_CONF" ""
     fi
 
     echo "Waiting for keystone to start..."
diff --git a/lib/lvm b/lib/lvm
index d9e78a0..92265f2 100644
--- a/lib/lvm
+++ b/lib/lvm
@@ -124,13 +124,15 @@
     local vg=$1
     local size=$2
 
-    # Start the lvmetad and tgtd services
-    if is_fedora || is_suse; then
+    # Start the lvmetad on f30 (dropped from f31) or SUSE
+    if [[ $DISTRO =~ f30 ]] || is_suse; then
         # services is not started by default
         start_service lvm2-lvmetad
-        if [ "$CINDER_ISCSI_HELPER" = "tgtadm" ]; then
-            start_service tgtd
-        fi
+    fi
+
+    # Start the tgtd service on Fedora and SUSE if tgtadm is used
+    if  is_fedora || is_suse  && [[ "$CINDER_ISCSI_HELPER" = "tgtadm" ]]; then
+        start_service tgtd
     fi
 
     # Start with a clean volume group
diff --git a/lib/neutron b/lib/neutron
index 9e6a80c..885df97 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -463,7 +463,7 @@
     done
 
     if [ "$NEUTRON_DEPLOY_MOD_WSGI" == "True" ]; then
-        run_process neutron-api "$NEUTRON_BIN_DIR/uwsgi --procname-prefix neutron-api --ini $NEUTRON_UWSGI_CONF"
+        run_process neutron-api "$(which uwsgi) --procname-prefix neutron-api --ini $NEUTRON_UWSGI_CONF"
         neutron_url=$service_protocol://$NEUTRON_SERVICE_HOST/networking/
         enable_service neutron-rpc-server
         run_process neutron-rpc-server "$NEUTRON_BIN_DIR/neutron-rpc-server $opts"
diff --git a/lib/neutron-legacy b/lib/neutron-legacy
index 8fb8858..bb1536a 100644
--- a/lib/neutron-legacy
+++ b/lib/neutron-legacy
@@ -475,7 +475,7 @@
     # Start the Neutron service
     if [ "$NEUTRON_DEPLOY_MOD_WSGI" == "True" ]; then
         enable_service neutron-api
-        run_process neutron-api "$NEUTRON_BIN_DIR/uwsgi --procname-prefix neutron-api --ini $NEUTRON_UWSGI_CONF"
+        run_process neutron-api "$(which uwsgi) --procname-prefix neutron-api --ini $NEUTRON_UWSGI_CONF"
         neutron_url=$Q_PROTOCOL://$Q_HOST/networking/
         enable_service neutron-rpc-server
         run_process neutron-rpc-server "$NEUTRON_BIN_DIR/neutron-rpc-server $cfg_file_options"
diff --git a/lib/neutron_plugins/linuxbridge_agent b/lib/neutron_plugins/linuxbridge_agent
index 1f1b0e8..bdeaf0f 100644
--- a/lib/neutron_plugins/linuxbridge_agent
+++ b/lib/neutron_plugins/linuxbridge_agent
@@ -38,7 +38,7 @@
 }
 
 function neutron_plugin_install_agent_packages {
-    install_package bridge-utils
+    :
 }
 
 function neutron_plugin_configure_dhcp_agent {
diff --git a/lib/nova b/lib/nova
index 7557a51..c1354e7 100644
--- a/lib/nova
+++ b/lib/nova
@@ -96,10 +96,6 @@
 # NOTE: Set ``FORCE_CONFIG_DRIVE="False"`` to turn OFF config drive
 FORCE_CONFIG_DRIVE=${FORCE_CONFIG_DRIVE:-"False"}
 
-# Nova supports pluggable schedulers.  The default ``FilterScheduler``
-# should work in most cases.
-SCHEDULER=${SCHEDULER:-filter_scheduler}
-
 # The following NOVA_FILTERS contains SameHostFilter and DifferentHostFilter with
 # the default filters.
 NOVA_FILTERS="AvailabilityZoneFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,ServerGroupAffinityFilter,SameHostFilter,DifferentHostFilter"
@@ -259,6 +255,7 @@
                 if [ ! -e /dev/kvm ]; then
                     echo "WARNING: Switching to QEMU"
                     LIBVIRT_TYPE=qemu
+                    LIBVIRT_CPU_MODE=none
                     if which selinuxenabled >/dev/null 2>&1 && selinuxenabled; then
                         # https://bugzilla.redhat.com/show_bug.cgi?id=753589
                         sudo setsebool virt_use_execmem on
@@ -296,13 +293,9 @@
             fi
         fi
 
-        if is_fedora && [[ $DISTRO =~ f[0-9][0-9] ]]; then
-            # There is an iscsi-initiator bug where it inserts
-            # different whitespace that causes a bunch of output
-            # matching to fail.  We have not been able to get
-            # fixed, yet :/  Exists in fedora 29 & 30 at least
-            #  https://bugzilla.redhat.com/show_bug.cgi?id=1676365
-            sudo dnf copr enable -y iwienand/iscsi-initiator-utils
+        if is_fedora && [[ $DISTRO =~ f3[0-1] ]]; then
+            # For f30 and f31 use the rebased 2.1.0 version of the package.
+            sudo dnf copr enable -y lyarwood/iscsi-initiator-utils
             sudo dnf update -y
         fi
 
@@ -401,11 +394,8 @@
     fi
     iniset $NOVA_CONF wsgi api_paste_config "$NOVA_API_PASTE_INI"
     iniset $NOVA_CONF DEFAULT rootwrap_config "$NOVA_CONF_DIR/rootwrap.conf"
-    iniset $NOVA_CONF scheduler driver "$SCHEDULER"
     iniset $NOVA_CONF filter_scheduler enabled_filters "$NOVA_FILTERS"
-    if [[ $SCHEDULER == "filter_scheduler" ]]; then
-        iniset $NOVA_CONF scheduler workers "$API_WORKERS"
-    fi
+    iniset $NOVA_CONF scheduler workers "$API_WORKERS"
     iniset $NOVA_CONF neutron default_floating_pool "$PUBLIC_NETWORK_NAME"
     if [[ $SERVICE_IP_VERSION == 6 ]]; then
         iniset $NOVA_CONF DEFAULT my_ip "$HOST_IPV6"
@@ -867,7 +857,7 @@
             start_tls_proxy nova '*' $NOVA_SERVICE_PORT $NOVA_SERVICE_HOST $NOVA_SERVICE_PORT_INT
         fi
     else
-        run_process "n-api" "$NOVA_BIN_DIR/uwsgi --procname-prefix nova-api --ini $NOVA_UWSGI_CONF"
+        run_process "n-api" "$(which uwsgi) --procname-prefix nova-api --ini $NOVA_UWSGI_CONF"
         nova_url=$service_protocol://$SERVICE_HOST/compute/v2.1/
     fi
 
@@ -958,7 +948,7 @@
     if [ "$NOVA_USE_MOD_WSGI" == "False" ]; then
         run_process n-api-meta "$NOVA_BIN_DIR/nova-api-metadata --config-file $compute_cell_conf"
     else
-        run_process n-api-meta "$NOVA_BIN_DIR/uwsgi --procname-prefix nova-api-meta --ini $NOVA_METADATA_UWSGI_CONF"
+        run_process n-api-meta "$(which uwsgi) --procname-prefix nova-api-meta --ini $NOVA_METADATA_UWSGI_CONF"
     fi
 
     export PATH=$old_path
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index 3566639..d3827c3 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -24,6 +24,10 @@
 # Currently fairly specific to OpenStackCI hosts
 DEBUG_LIBVIRT_COREDUMPS=$(trueorfalse False DEBUG_LIBVIRT_COREDUMPS)
 
+# Enable the Fedora Virtualization Preview Copr repo that provides the latest
+# rawhide builds of QEMU, Libvirt and other virt tools.
+ENABLE_FEDORA_VIRT_PREVIEW_REPO=$(trueorfalse False ENABLE_FEDORA_VIRT_PREVIEW_REPO)
+
 # Enable coredumps for libvirt
 #  Bug: https://bugs.launchpad.net/nova/+bug/1643911
 function _enable_coredump {
@@ -55,12 +59,21 @@
 
     if is_ubuntu; then
         install_package qemu-system libvirt-clients libvirt-daemon-system libvirt-dev
+        if is_arch "aarch64"; then
+            install_package qemu-efi
+        fi
         # uninstall in case the libvirt version changed
         pip_uninstall libvirt-python
         pip_install_gr libvirt-python
         #pip_install_gr <there-si-no-guestfs-in-pypi>
     elif is_fedora || is_suse; then
 
+        # Optionally enable the virt-preview repo when on Fedora
+        if [[ $DISTRO =~ f[0-9][0-9] ]] && [[ ${ENABLE_FEDORA_VIRT_PREVIEW_REPO} == "True" ]]; then
+            # https://copr.fedorainfracloud.org/coprs/g/virtmaint-sig/virt-preview/
+            sudo dnf copr enable -y @virtmaint-sig/virt-preview
+        fi
+
         # Note that in CentOS/RHEL this needs to come from the RDO
         # repositories (qemu-kvm-ev ... which provides this package)
         # as the base system version is too old.  We should have
@@ -68,6 +81,10 @@
         install_package qemu-kvm
 
         install_package libvirt libvirt-devel
+        if is_arch "aarch64"; then
+            install_package edk2.git-aarch64
+        fi
+
         pip_uninstall libvirt-python
         pip_install_gr libvirt-python
     fi
diff --git a/lib/nova_plugins/hypervisor-ironic b/lib/nova_plugins/hypervisor-ironic
index 113e2a7..b147c43 100644
--- a/lib/nova_plugins/hypervisor-ironic
+++ b/lib/nova_plugins/hypervisor-ironic
@@ -68,13 +68,6 @@
         return
     fi
     install_libvirt
-    if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] && is_ubuntu; then
-        # Ubuntu packaging+apparmor issue prevents libvirt from loading
-        # the ROM from /usr/share/misc.  Workaround by installing it directly
-        # to a directory that it can read from. (LP: #1393548)
-        sudo rm -rf /usr/share/qemu/sgabios.bin
-        sudo cp /usr/share/misc/sgabios.bin /usr/share/qemu/sgabios.bin
-    fi
 }
 
 # start_nova_hypervisor - Start any required external services
diff --git a/lib/nova_plugins/hypervisor-libvirt b/lib/nova_plugins/hypervisor-libvirt
index 7d3ace8..b25bc0c 100644
--- a/lib/nova_plugins/hypervisor-libvirt
+++ b/lib/nova_plugins/hypervisor-libvirt
@@ -39,7 +39,7 @@
 function configure_nova_hypervisor {
     configure_libvirt
     iniset $NOVA_CONF libvirt virt_type "$LIBVIRT_TYPE"
-    iniset $NOVA_CONF libvirt cpu_mode "none"
+    iniset $NOVA_CONF libvirt cpu_mode "$LIBVIRT_CPU_MODE"
     # Do not enable USB tablet input devices to avoid QEMU CPU overhead.
     iniset $NOVA_CONF DEFAULT pointer_model "ps2mouse"
     iniset $NOVA_CONF libvirt live_migration_uri "qemu+ssh://$STACK_USER@%s/system"
@@ -52,8 +52,6 @@
 
     # arm64-specific configuration
     if is_arch "aarch64"; then
-        # arm64 architecture currently does not support graphical consoles.
-        iniset $NOVA_CONF vnc enabled "false"
         iniset $NOVA_CONF libvirt cpu_mode "host-passthrough"
     fi
 
@@ -117,7 +115,7 @@
             # Workaround for missing dependencies in python-libguestfs
             install_package python-libguestfs guestfs-data augeas augeas-lenses
         elif is_fedora; then
-            install_package python-libguestfs
+            install_package python3-libguestfs
         fi
     fi
 }
diff --git a/lib/placement b/lib/placement
index 785b0dd..2a449bf 100644
--- a/lib/placement
+++ b/lib/placement
@@ -144,7 +144,7 @@
 # start_placement_api() - Start the API processes ahead of other things
 function start_placement_api {
     if [[ "$WSGI_MODE" == "uwsgi" ]]; then
-        run_process "placement-api" "$PLACEMENT_BIN_DIR/uwsgi --procname-prefix placement --ini $PLACEMENT_UWSGI_CONF"
+        run_process "placement-api" "$(which uwsgi) --procname-prefix placement --ini $PLACEMENT_UWSGI_CONF"
     else
         enable_apache_site placement-api
         restart_apache_server
diff --git a/lib/swift b/lib/swift
index 5be9e35..3c121ca 100644
--- a/lib/swift
+++ b/lib/swift
@@ -428,10 +428,13 @@
         swift_pipeline+=" s3api"
     fi
     if is_service_enabled keystone; then
+        swift_pipeline+=" authtoken"
         if is_service_enabled s3api;then
             swift_pipeline+=" s3token"
+            iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:s3token auth_uri ${KEYSTONE_AUTH_URI_V3}
+            iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:s3token delay_auth_decision true
         fi
-        swift_pipeline+=" authtoken keystoneauth"
+        swift_pipeline+=" keystoneauth"
     fi
 
     swift_pipeline+=" tempauth "
diff --git a/lib/tempest b/lib/tempest
index 1066cd4..05fcb1f 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -203,13 +203,13 @@
             if [[ ! ( $available_flavors =~ 'm1.nano' ) ]]; then
                 # Determine the flavor disk size based on the image size.
                 disk=$(image_size_in_gib $image_uuid)
-                openstack flavor create --id 42 --ram 64 --disk $disk --vcpus 1 --property hw_rng:allowed=True m1.nano
+                openstack flavor create --id 42 --ram 128 --disk $disk --vcpus 1 --property hw_rng:allowed=True m1.nano
             fi
             flavor_ref=42
             if [[ ! ( $available_flavors =~ 'm1.micro' ) ]]; then
                 # Determine the alt flavor disk size based on the alt image size.
                 disk=$(image_size_in_gib $image_uuid_alt)
-                openstack flavor create --id 84 --ram 128 --disk $disk --vcpus 1 --property hw_rng:allowed=True m1.micro
+                openstack flavor create --id 84 --ram 192 --disk $disk --vcpus 1 --property hw_rng:allowed=True m1.micro
             fi
             flavor_ref_alt=84
         else
@@ -472,6 +472,11 @@
         TEMPEST_EXTEND_ATTACHED_VOLUME=${TEMPEST_EXTEND_ATTACHED_VOLUME:-True}
     fi
     iniset $TEMPEST_CONFIG volume-feature-enabled extend_attached_volume $(trueorfalse False TEMPEST_EXTEND_ATTACHED_VOLUME)
+    # Only turn on TEMPEST_VOLUME_REVERT_TO_SNAPSHOT by default for "lvm" backends
+    if [[ "$CINDER_ENABLED_BACKENDS" == *"lvm"* ]]; then
+        TEMPEST_VOLUME_REVERT_TO_SNAPSHOT=${TEMPEST_VOLUME_REVERT_TO_SNAPSHOT:-True}
+    fi
+    iniset $TEMPEST_CONFIG volume-feature-enabled volume_revert $(trueorfalse False TEMPEST_VOLUME_REVERT_TO_SNAPSHOT)
     local tempest_volume_min_microversion=${TEMPEST_VOLUME_MIN_MICROVERSION:-None}
     local tempest_volume_max_microversion=${TEMPEST_VOLUME_MAX_MICROVERSION:-"latest"}
     # Reset microversions to None where v2 is running which does not support microversion.
@@ -570,6 +575,7 @@
             iniset $TEMPEST_CONFIG compute-feature-enabled snapshot False
             iniset $TEMPEST_CONFIG compute-feature-enabled suspend False
         else
+            iniset $TEMPEST_CONFIG compute-feature-enabled stable_rescue True
             iniset $TEMPEST_CONFIG compute-feature-enabled swap_volume True
         fi
     fi
@@ -690,6 +696,11 @@
     git_clone $TEMPEST_REPO $TEMPEST_DIR $TEMPEST_BRANCH
     pip_install 'tox!=2.8.0'
     pushd $TEMPEST_DIR
+    # NOTE(gmann): checkout the TEMPEST_BRANCH in case TEMPEST_BRANCH
+    # is tag name not master. git_clone would not checkout tag because
+    # TEMPEST_DIR already exist until RECLONE is true.
+    git checkout $TEMPEST_BRANCH
+
     tox -r --notest -efull
     # NOTE(mtreinish) Respect constraints in the tempest full venv, things that
     # are using a tox job other than full will not be respecting constraints but
diff --git a/lib/tls b/lib/tls
index 65ffeb9..d05536b 100644
--- a/lib/tls
+++ b/lib/tls
@@ -369,8 +369,7 @@
 function fix_system_ca_bundle_path {
     if is_service_enabled tls-proxy; then
         local capath
-        local python_cmd=${1:-python}
-        capath=$($python_cmd -c $'try:\n from requests import certs\n print (certs.where())\nexcept ImportError: pass')
+        capath=$(python3 -c $'try:\n from requests import certs\n print (certs.where())\nexcept ImportError: pass')
 
         if [[ ! $capath == "" && ! $capath =~ ^/etc/.* && ! -L $capath ]]; then
             if is_fedora; then
diff --git a/playbooks/pre.yaml b/playbooks/pre.yaml
index 60f365a..ff97a1f 100644
--- a/playbooks/pre.yaml
+++ b/playbooks/pre.yaml
@@ -26,7 +26,6 @@
       set_fact:
         external_bridge_mtu: "{{ local_mtu | int - 50 }}"
   roles:
-    - test-matrix
     - configure-swap
     - setup-stack-user
     - setup-tempest-user
diff --git a/roles/setup-devstack-source-dirs/defaults/main.yaml b/roles/setup-devstack-source-dirs/defaults/main.yaml
index fea05c8..77a74d7 100644
--- a/roles/setup-devstack-source-dirs/defaults/main.yaml
+++ b/roles/setup-devstack-source-dirs/defaults/main.yaml
@@ -1 +1,9 @@
 devstack_base_dir: /opt/stack
+devstack_source_dirs:
+  - src/opendev.org/opendev
+  - src/opendev.org/openstack
+  - src/opendev.org/openstack-dev
+  - src/opendev.org/openstack-infra
+  - src/opendev.org/starlingx
+  - src/opendev.org/x
+  - src/opendev.org/zuul
diff --git a/roles/setup-devstack-source-dirs/tasks/main.yaml b/roles/setup-devstack-source-dirs/tasks/main.yaml
index 160757e..294c29c 100644
--- a/roles/setup-devstack-source-dirs/tasks/main.yaml
+++ b/roles/setup-devstack-source-dirs/tasks/main.yaml
@@ -1,13 +1,6 @@
 - name: Find all OpenStack source repos used by this job
   find:
-    paths:
-      - src/opendev.org/opendev
-      - src/opendev.org/openstack
-      - src/opendev.org/openstack-dev
-      - src/opendev.org/openstack-infra
-      - src/opendev.org/starlingx
-      - src/opendev.org/x
-      - src/opendev.org/zuul
+    paths: "{{ devstack_source_dirs }}"
     file_type: directory
   register: found_repos
 
diff --git a/setup.cfg b/setup.cfg
index 4e27ad8..146f010 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -10,6 +10,3 @@
     Intended Audience :: Developers
     License :: OSI Approved :: Apache Software License
     Operating System :: POSIX :: Linux
-
-[wheel]
-universal = 1
diff --git a/stack.sh b/stack.sh
index fe71eae..80c6d4d 100755
--- a/stack.sh
+++ b/stack.sh
@@ -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} =~ (bionic|stretch|jessie|f29|opensuse-15.0|opensuse-15.1|opensuse-tumbleweed|rhel7) ]]; then
+if [[ ! ${DISTRO} =~ (bionic|stretch|jessie|f30|f31|opensuse-15.0|opensuse-15.1|opensuse-tumbleweed|rhel8) ]]; 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"
@@ -283,74 +283,20 @@
 # to pick up required packages.
 
 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.
-    if sudo yum repolist enabled epel | grep -q 'epel'; then
-        uninstall_package epel-release || true
-    fi
+    # epel-release is in extras repo which is enabled by default
+    install_package epel-release
 
-    # This trick installs the latest epel-release from a bootstrap
-    # repo, then removes itself (as epel-release installed the
-    # "real" repo).
-    #
-    # You would think that rather than this, you could use
-    # $releasever directly in .repo file we create below.  However
-    # RHEL gives a $releasever of "6Server" which breaks the path;
-    # see https://bugzilla.redhat.com/show_bug.cgi?id=1150759
-    cat <<EOF | sudo tee /etc/yum.repos.d/epel-bootstrap.repo
-[epel-bootstrap]
-name=Bootstrap EPEL
-mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-7&arch=\$basearch
-failovermethod=priority
-enabled=0
-gpgcheck=0
-EOF
-    # Enable a bootstrap repo.  It is removed after finishing
-    # the epel-release installation.
-    is_package_installed yum-utils || install_package yum-utils
-    sudo yum-config-manager --enable epel-bootstrap
-    yum_install epel-release || \
-        die $LINENO "Error installing EPEL repo, cannot continue"
-    sudo rm -f /etc/yum.repos.d/epel-bootstrap.repo
+    # RDO repos are not tested with epel and may have incompatibilities so
+    # let's limit the packages fetched from epel to the ones not in RDO repos.
+    sudo dnf config-manager --save --setopt=includepkgs=debootstrap,dpkg epel
 }
 
 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
-        if [[ "$TARGET_BRANCH" == "master" ]]; then
-            yum_install https://rdoproject.org/repos/rdo-release.rpm
-        else
-            # Get latest rdo-release-$rdo_release RPM package version
-            rdo_release=$(echo $TARGET_BRANCH | sed "s|stable/||g")
-            yum_install https://rdoproject.org/repos/openstack-$rdo_release/rdo-release-$rdo_release.rpm
-        fi
-    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
-
-    # Enable the Software Collections (SCL) repository for CentOS.
-    # This repository includes useful software (e.g. the Go Toolset)
-    # which is not present in the main repository.
-    if [[ "$os_VENDOR" =~ (CentOS) ]]; then
-        yum_install centos-release-scl
-    fi
-
-    if is_oraclelinux; then
-        sudo yum-config-manager --enable ol7_optional_latest ol7_addons ol7_MySQL56
-    fi
+    # NOTE(ianw) 2020-04-30 : when we have future branches, we
+    # probably want to install the relevant branch RDO release as
+    # well.  But for now it's all master.
+    sudo dnf -y install https://rdoproject.org/repos/rdo-release.el8.rpm
+    sudo dnf -y update
 }
 
 
@@ -395,15 +341,19 @@
 # to speed things up
 SKIP_EPEL_INSTALL=$(trueorfalse False SKIP_EPEL_INSTALL)
 
-if [[ $DISTRO == "rhel7" ]]; then
+if [[ $DISTRO == "rhel8" ]]; 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
+        sudo dnf config-manager --set-enabled epel
     fi
 
+    # PowerTools repo provides libyaml-devel required by devstack itself and
+    # EPEL packages assume that the PowerTools repository is enable.
+    sudo dnf config-manager --set-enabled PowerTools
+
     if [[ ${SKIP_EPEL_INSTALL} != True ]]; then
         _install_epel
     fi
@@ -411,11 +361,17 @@
     # available in RDO repositories (e.g. OVS, or later versions of
     # kvm) to run.
     _install_rdo
+
+    # NOTE(cgoncalves): workaround RHBZ#1154272
+    # dnf fails for non-privileged users when expired_repos.json doesn't exist.
+    # RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1154272
+    # Patch: https://github.com/rpm-software-management/dnf/pull/1448
+    echo "[]" | sudo tee /var/cache/dnf/expired_repos.json
 fi
 
 # Ensure python is installed
 # --------------------------
-is_package_installed python || install_package python
+install_python
 
 
 # Configure Logging
@@ -494,14 +450,14 @@
             _of_args="$_of_args --no-timestamp"
         fi
         # Set fd 1 and 2 to write the log file
-        exec 1> >( $TOP_DIR/tools/outfilter.py $_of_args -o "${LOGFILE}" ) 2>&1
+        exec 1> >( $PYTHON $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}" )
+        exec 6> >( $PYTHON $TOP_DIR/tools/outfilter.py -o "${SUMFILE}" )
     else
         # Set fd 1 and 2 to primary logfile
-        exec 1> >( $TOP_DIR/tools/outfilter.py -o "${LOGFILE}" ) 2>&1
+        exec 1> >( $PYTHON $TOP_DIR/tools/outfilter.py -o "${LOGFILE}" ) 2>&1
         # Set fd 6 to summary logfile and stdout
-        exec 6> >( $TOP_DIR/tools/outfilter.py -v -o "${SUMFILE}" >&3 )
+        exec 6> >( $PYTHON $TOP_DIR/tools/outfilter.py -v -o "${SUMFILE}" >&3 )
     fi
 
     echo_summary "stack.sh log $LOGFILE"
@@ -518,7 +474,7 @@
         exec 1>/dev/null 2>&1
     fi
     # Always send summary fd to original stdout
-    exec 6> >( $TOP_DIR/tools/outfilter.py -v >&3 )
+    exec 6> >( $PYTHON $TOP_DIR/tools/outfilter.py -v >&3 )
 fi
 
 # Basic test for ``$DEST`` path permissions (fatal on error unless skipped)
@@ -554,9 +510,9 @@
             generate-subunit $DEVSTACK_START_TIME $SECONDS 'fail' >> ${SUBUNIT_OUTPUT}
         fi
         if [[ -z $LOGDIR ]]; then
-            $TOP_DIR/tools/worlddump.py
+            ${PYTHON} $TOP_DIR/tools/worlddump.py
         else
-            $TOP_DIR/tools/worlddump.py -d $LOGDIR
+            ${PYTHON} $TOP_DIR/tools/worlddump.py -d $LOGDIR
         fi
     else
         # If we error before we've installed os-testr, this will fail.
@@ -803,13 +759,11 @@
 # Install subunit for the subunit output stream
 pip_install -U os-testr
 
-if [[ "$USE_SYSTEMD" == "True" ]]; then
-    pip_install_gr systemd-python
-    # the default rate limit of 1000 messages / 30 seconds is not
-    # sufficient given how verbose our logging is.
-    iniset -sudo /etc/systemd/journald.conf "Journal" "RateLimitBurst" "0"
-    sudo systemctl restart systemd-journald
-fi
+pip_install_gr systemd-python
+# the default rate limit of 1000 messages / 30 seconds is not
+# sufficient given how verbose our logging is.
+iniset -sudo /etc/systemd/journald.conf "Journal" "RateLimitBurst" "0"
+sudo systemctl restart systemd-journald
 
 # Virtual Environment
 # -------------------
@@ -867,6 +821,13 @@
     init_cert
 fi
 
+# Dstat
+# -----
+
+# Install dstat services prerequisites
+install_dstat
+
+
 # Check Out and Install Source
 # ----------------------------
 
@@ -961,9 +922,6 @@
 
 if is_service_enabled tls-proxy; then
     fix_system_ca_bundle_path
-    if python3_enabled ; then
-        fix_system_ca_bundle_path python3
-    fi
 fi
 
 # Extras Install
@@ -1522,14 +1480,11 @@
     echo
 fi
 
-# If USE_SYSTEMD is enabled, tell the user about using it.
-if [[ "$USE_SYSTEMD" == "True" ]]; then
-    echo
-    echo "Services are running under systemd unit files."
-    echo "For more information see: "
-    echo "https://docs.openstack.org/devstack/latest/systemd.html"
-    echo
-fi
+echo
+echo "Services are running under systemd unit files."
+echo "For more information see: "
+echo "https://docs.openstack.org/devstack/latest/systemd.html"
+echo
 
 # Useful info on current state
 cat /etc/devstack-version
diff --git a/stackrc b/stackrc
index b31fb39..07c4c4b 100644
--- a/stackrc
+++ b/stackrc
@@ -109,9 +109,7 @@
 # Set the root URL for Horizon
 HORIZON_APACHE_ROOT="/dashboard"
 
-# Whether to use SYSTEMD to manage services, we only do this from
-# Queens forward.
-USE_SYSTEMD="True"
+# Whether to use user specific units for running services or global ones.
 USER_UNITS=$(trueorfalse False USER_UNITS)
 if [[ "$USER_UNITS" == "True" ]]; then
     SYSTEMD_DIR="$HOME/.local/share/systemd/user"
@@ -142,7 +140,7 @@
 # version of Python 3 to this variable will install the app using that
 # version of the interpreter instead of 2.7.
 _DEFAULT_PYTHON3_VERSION="$(_get_python_version python3)"
-export PYTHON3_VERSION=${PYTHON3_VERSION:-${_DEFAULT_PYTHON3_VERSION:-3.5}}
+export PYTHON3_VERSION=${PYTHON3_VERSION:-${_DEFAULT_PYTHON3_VERSION:-3}}
 
 # Just to be more explicit on the Python 2 version to use.
 _DEFAULT_PYTHON2_VERSION="$(_get_python_version python2)"
@@ -150,7 +148,8 @@
 
 # Create a virtualenv with this
 if [[ ${USE_PYTHON3} == True ]]; then
-    export VIRTUALENV_CMD="virtualenv -p python3"
+    # Use the built-in venv to avoid more dependencies
+    export VIRTUALENV_CMD="python3 -m venv"
 else
     export VIRTUALENV_CMD="virtualenv "
 fi
@@ -254,7 +253,7 @@
 # Setting the variable to 'ALL' will activate the download for all
 # libraries.
 
-DEVSTACK_SERIES="ussuri"
+DEVSTACK_SERIES="victoria"
 
 ##############
 #
@@ -599,7 +598,7 @@
 
 # a websockets/html5 or flash powered VNC console for vm instances
 NOVNC_REPO=${NOVNC_REPO:-https://github.com/novnc/noVNC.git}
-NOVNC_BRANCH=${NOVNC_BRANCH:-v1.0.0}
+NOVNC_BRANCH=${NOVNC_BRANCH:-v1.1.0}
 
 # a websockets/html5 or flash powered SPICE console for vm instances
 SPICE_REPO=${SPICE_REPO:-http://anongit.freedesktop.org/git/spice/spice-html5.git}
@@ -621,6 +620,7 @@
 case "$VIRT_DRIVER" in
     ironic|libvirt)
         LIBVIRT_TYPE=${LIBVIRT_TYPE:-kvm}
+        LIBVIRT_CPU_MODE=${LIBVIRT_CPU_MODE:-none}
         if [[ "$os_VENDOR" =~ (Debian|Ubuntu) ]]; then
             # The groups change with newer libvirt. Older Ubuntu used
             # 'libvirtd', but now uses libvirt like Debian. Do a quick check
@@ -674,7 +674,7 @@
 #IMAGE_URLS="http://smoser.brickies.net/ubuntu/ttylinux-uec/ttylinux-uec-amd64-11.2_2.6.35-15_1.tar.gz" # old ttylinux-uec image
 #IMAGE_URLS="http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-disk.img" # cirros full disk image
 
-CIRROS_VERSION=${CIRROS_VERSION:-"0.4.0"}
+CIRROS_VERSION=${CIRROS_VERSION:-"0.5.1"}
 CIRROS_ARCH=${CIRROS_ARCH:-"x86_64"}
 
 # Set default image based on ``VIRT_DRIVER`` and ``LIBVIRT_TYPE``, either of
@@ -811,15 +811,6 @@
 # Service graceful shutdown timeout
 WORKER_TIMEOUT=${WORKER_TIMEOUT:-90}
 
-# Choose DNF on RedHat/Fedora platforms with it, or otherwise default
-# to YUM.  Can remove this when only dnf is supported (i.e. centos7
-# disappears)
-if [[ -e /usr/bin/dnf ]]; then
-    YUM=${YUM:-dnf}
-else
-    YUM=${YUM:-yum}
-fi
-
 # Common Configuration
 # --------------------
 
diff --git a/tests/test_worlddump.sh b/tests/test_worlddump.sh
index f407d40..9196525 100755
--- a/tests/test_worlddump.sh
+++ b/tests/test_worlddump.sh
@@ -8,7 +8,7 @@
 
 OUT_DIR=$(mktemp -d)
 
-$TOP/tools/worlddump.py -d $OUT_DIR
+${PYTHON} $TOP/tools/worlddump.py -d $OUT_DIR
 
 if [[ $? -ne 0 ]]; then
     fail "worlddump failed"
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index a5c1667..e140929 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -79,7 +79,9 @@
     # manifest of what to remove.  However, in most cases, simply
     # overwriting works.  So this hacks around those packages that
     # have been dragged in by some other system dependency
-    sudo rm -f /usr/lib/python3/dist-packages/PyYAML-*.egg-info
+    sudo rm -rf /usr/lib/python3/dist-packages/httplib2-*.egg-info
+    sudo rm -rf /usr/lib/python3/dist-packages/pyasn1_modules-*.egg-info
+    sudo rm -rf /usr/lib/python3/dist-packages/PyYAML-*.egg-info
 }
 
 # Python Packages
@@ -198,35 +200,11 @@
     # have been dragged in by some other system dependency
     sudo rm -rf /usr/lib/python3.6/site-packages/ply-*.egg-info
     sudo rm -rf /usr/lib/python3.6/site-packages/six-*.egg-info
-}
 
-# 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
-# pip. To avoid having pip's virtualenv overwritten by the distro's
-# package (e.g. due to installing a distro package with a dependency
-# on python-virtualenv), first install the distro python-virtualenv
-# to satisfy any dependencies then use pip to overwrite it.
-
-# ... but, for infra builds, the pip-and-virtualenv [1] element has
-# already done this to ensure the latest pip, virtualenv and
-# setuptools on the base image for all platforms.  It has also added
-# the packages to the yum/dnf ignore list to prevent them being
-# overwritten with old versions.  F26 and dnf 2.0 has changed
-# behaviour that means re-installing python-virtualenv fails [2].
-# Thus we do a quick check if we're in the infra environment by
-# looking for the mirror config script before doing this, and just
-# skip it if so.
-
-# [1] https://opendev.org/openstack/diskimage-builder/src/branch/master/ \
-#        diskimage_builder/elements/pip-and-virtualenv/ \
-#            install.d/pip-and-virtualenv-source-install/04-install-pip
-# [2] https://bugzilla.redhat.com/show_bug.cgi?id=1477823
-
-function fixup_virtualenv {
-    if [[ ! -f /etc/ci/mirror_info.sh ]]; then
-        install_package python-virtualenv
-        pip_install -U --force-reinstall virtualenv
-    fi
+    # Ensure trusted CA certificates are up to date
+    # See https://bugzilla.suse.com/show_bug.cgi?id=1154871
+    # May be removed once a new opensuse-15 image is available in nodepool
+    sudo zypper up -y p11-kit ca-certificates-mozilla
 }
 
 function fixup_all {
@@ -234,5 +212,4 @@
     fixup_ubuntu
     fixup_fedora
     fixup_suse
-    fixup_virtualenv
 }
diff --git a/tools/generate-devstack-plugins-list.py b/tools/generate-devstack-plugins-list.py
index 3ac9c4d..1cacd06 100644
--- a/tools/generate-devstack-plugins-list.py
+++ b/tools/generate-devstack-plugins-list.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
 # Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
 #
diff --git a/tools/install_pip.sh b/tools/install_pip.sh
index dcd5466..5eb538c 100755
--- a/tools/install_pip.sh
+++ b/tools/install_pip.sh
@@ -133,8 +133,12 @@
 # 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  && ! is_suse; then
-    uninstall_package python-pip
-    uninstall_package python3-pip
+    if is_package_installed python-pip ; then
+        uninstall_package python-pip
+    fi
+    if is_package_installed python3-pip ; then
+        uninstall_package python3-pip
+    fi
 fi
 
 install_get_pip
diff --git a/tools/install_prereqs.sh b/tools/install_prereqs.sh
index da59093..a7c03d2 100755
--- a/tools/install_prereqs.sh
+++ b/tools/install_prereqs.sh
@@ -81,12 +81,6 @@
     fi
 fi
 
-if python3_enabled; then
-    install_python3
-    export PYTHON=$(which python${PYTHON3_VERSION} 2>/dev/null || which python3 2>/dev/null)
-else
-    export PYTHON=$(which python 2>/dev/null)
-fi
 
 # Mark end of run
 # ---------------
diff --git a/tools/memory_tracker.sh b/tools/memory_tracker.sh
index 63f25ca..6c36534 100755
--- a/tools/memory_tracker.sh
+++ b/tools/memory_tracker.sh
@@ -14,7 +14,7 @@
 
 set -o errexit
 
-PYTHON=${PYTHON:-python}
+PYTHON=${PYTHON:-python3}
 
 # time to sleep between checks
 SLEEP_TIME=20
diff --git a/tools/mlock_report.py b/tools/mlock_report.py
old mode 100755
new mode 100644
index 07716b0..b15a0bf
--- a/tools/mlock_report.py
+++ b/tools/mlock_report.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-
 # This tool lists processes that lock memory pages from swapping to disk.
 
 import re
diff --git a/tools/outfilter.py b/tools/outfilter.py
old mode 100755
new mode 100644
index cf09124..e910f79
--- a/tools/outfilter.py
+++ b/tools/outfilter.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-#
+#!/usr/bin/env python3
+
 # Copyright 2014 Hewlett-Packard Development Company, L.P.
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may
diff --git a/tools/update_clouds_yaml.py b/tools/update_clouds_yaml.py
index 9187c66..7be995e 100755
--- a/tools/update_clouds_yaml.py
+++ b/tools/update_clouds_yaml.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # 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
diff --git a/tools/worlddump.py b/tools/worlddump.py
index d5ff5d1..6a618f5 100755
--- a/tools/worlddump.py
+++ b/tools/worlddump.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2014 Hewlett-Packard Development Company, L.P.
 #
@@ -17,14 +17,12 @@
 
 """Dump the state of the world for post mortem."""
 
-from __future__ import print_function
-
 import argparse
 import datetime
 from distutils import spawn
 import fnmatch
+import io
 import os
-import os.path
 import shutil
 import subprocess
 import sys
@@ -109,9 +107,10 @@
 # This method gets max version searching 'OpenFlow versions 0x1:0x'.
 # And return a version value converted to an integer type.
 def _get_ofp_version():
-    process = subprocess.Popen(['ovs-ofctl', '--version'], stdout=subprocess.PIPE)
+    process = subprocess.Popen(['ovs-ofctl', '--version'],
+                               stdout=subprocess.PIPE)
     stdout, _ = process.communicate()
-    find_str = 'OpenFlow versions 0x1:0x'
+    find_str = b'OpenFlow versions 0x1:0x'
     offset = stdout.find(find_str)
     return int(stdout[offset + len(find_str):-1]) - 1
 
@@ -165,15 +164,13 @@
     _header("Network Dump")
 
     _dump_cmd("bridge link")
-    if _find_cmd("brctl"):
-        _dump_cmd("brctl show")
     _dump_cmd("ip link show type bridge")
     ip_cmds = ["neigh", "addr", "link", "route"]
     for cmd in ip_cmds + ['netns']:
         _dump_cmd("ip %s" % cmd)
     for netns_ in _netns_list():
         for cmd in ip_cmds:
-            args = {'netns': netns_, 'cmd': cmd}
+            args = {'netns': bytes.decode(netns_), 'cmd': cmd}
             _dump_cmd('sudo ip netns exec %(netns)s ip %(cmd)s' % args)
 
 
@@ -194,7 +191,7 @@
     _dump_cmd("sudo ovs-vsctl show")
     for ofctl_cmd in ofctl_cmds:
         for bridge in bridges:
-            args = {'vers': vers, 'cmd': ofctl_cmd, 'bridge': bridge}
+            args = {'vers': vers, 'cmd': ofctl_cmd, 'bridge': bytes.decode(bridge)}
             _dump_cmd("sudo ovs-ofctl --protocols=%(vers)s %(cmd)s %(bridge)s" % args)
 
 
@@ -206,7 +203,7 @@
 
 def compute_consoles():
     _header("Compute consoles")
-    for root, dirnames, filenames in os.walk('/opt/stack'):
+    for root, _, filenames in os.walk('/opt/stack'):
         for filename in fnmatch.filter(filenames, 'console.log'):
             fullpath = os.path.join(root, filename)
             _dump_cmd("sudo cat %s" % fullpath)
@@ -234,12 +231,22 @@
         # tools out there that can do that sort of thing though.
         _dump_cmd("ls -ltrah /var/core")
 
+
+def disable_stdio_buffering():
+    # re-open STDOUT as binary, then wrap it in a
+    # TextIOWrapper, and write through everything.
+    binary_stdout = io.open(sys.stdout.fileno(), 'wb', 0)
+    sys.stdout = io.TextIOWrapper(binary_stdout, write_through=True)
+
+
 def main():
     opts = get_options()
     fname = filename(opts.dir, opts.name)
     print("World dumping... see %s for details" % fname)
-    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
-    with open(fname, 'w') as f:
+
+    disable_stdio_buffering()
+
+    with io.open(fname, 'w') as f:
         os.dup2(f.fileno(), sys.stdout.fileno())
         disk_space()
         process_list()
diff --git a/tox.ini b/tox.ini
index 26baa2a..ed28636 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,15 +5,14 @@
 
 [testenv]
 usedevelop = False
-install_command = pip install {opts} {packages}
+basepython = python3
 
 [testenv:bashate]
-basepython = python3
 # if you want to test out some changes you have made to bashate
 # against devstack, just set BASHATE_INSTALL_PATH=/path/... to your
 # modified bashate tree
 deps =
-   {env:BASHATE_INSTALL_PATH:bashate==0.5.1}
+   {env:BASHATE_INSTALL_PATH:bashate==2.0.0}
 whitelist_externals = bash
 commands = bash -c "find {toxinidir}             \
          -not \( -type d -name .?\* -prune \)    \
@@ -35,7 +34,6 @@
          -print0 | xargs -0 bashate -v -iE006 -eE005,E042"
 
 [testenv:docs]
-basepython = python3
 deps = -r{toxinidir}/doc/requirements.txt
 whitelist_externals = bash
 setenv =
@@ -44,7 +42,6 @@
   sphinx-build -W -b html -d doc/build/doctrees doc/source doc/build/html
 
 [testenv:pdf-docs]
-basepython = python3
 deps = {[testenv:docs]deps}
 whitelist_externals =
    make
@@ -53,6 +50,5 @@
    make -C doc/build/pdf
 
 [testenv:venv]
-basepython = python3
 deps = -r{toxinidir}/doc/requirements.txt
 commands = {posargs}