Merge "[TrivialFix] Delete symlink apts-debs"
diff --git a/.zuul.yaml b/.zuul.yaml
index 67d4c24..4ca0257 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -468,6 +468,8 @@
         SWIFT_HASH: 1234123412341234
         DEBUG_LIBVIRT_COREDUMPS: true
         NOVA_VNC_ENABLED: true
+        OVN_L3_CREATE_PUBLIC_NETWORK: true
+        OVN_DBS_LOG_LEVEL: dbg
       devstack_local_conf:
         post-config:
           $NEUTRON_CONF:
@@ -496,13 +498,14 @@
         n-sch: true
         # Placement service
         placement-api: true
+        # OVN services
+        ovn-controller: true
+        ovn-northd: true
+        ovs-vswitchd: true
+        ovsdb-server: true
         # Neutron services
-        q-agt: true
-        q-dhcp: true
-        q-l3: true
-        q-meta: true
-        q-metering: true
         q-svc: true
+        q-ovn-metadata-agent: true
         # Swift services
         s-account: true
         s-container: true
@@ -534,8 +537,12 @@
           n-cpu: true
           # Placement services
           placement-client: true
+          # OVN services
+          ovn-controller: true
+          ovs-vswitchd: true
+          ovsdb-server: true
           # Neutron services
-          q-agt: true
+          q-ovn-metadata-agent: true
           # Cinder services
           c-bak: true
           c-vol: true
@@ -553,6 +560,7 @@
           GLANCE_HOSTPORT: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}:9292"
           Q_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
           NOVA_VNC_ENABLED: true
+          ENABLE_CHASSIS_AS_GW: false
 
 - job:
     name: devstack-ipv6
@@ -585,13 +593,6 @@
     timeout: 9000
 
 - job:
-    name: devstack-platform-bionic
-    parent: tempest-full-py3
-    description: Ubuntu Bionic platform test
-    nodeset: openstack-single-node-bionic
-    voting: false
-
-- job:
     name: devstack-async
     parent: tempest-full-py3
     description: Async mode enabled
@@ -692,7 +693,6 @@
         - devstack-ipv6
         - devstack-platform-fedora-latest
         - devstack-platform-centos-8
-        - devstack-platform-bionic
         - devstack-async
         - devstack-multinode
         - devstack-unit-tests
diff --git a/files/debs/dstat b/files/debs/dstat
index 2b643b8..40d00f4 100644
--- a/files/debs/dstat
+++ b/files/debs/dstat
@@ -1 +1,2 @@
-dstat
+dstat # dist:bionic
+pcp
diff --git a/lib/apache b/lib/apache
index 870a65a..04259ba 100644
--- a/lib/apache
+++ b/lib/apache
@@ -93,9 +93,6 @@
 
     if is_ubuntu; then
         local pkg_list="uwsgi uwsgi-plugin-python3 libapache2-mod-proxy-uwsgi"
-        if [[ "$DISTRO" == 'bionic' ]]; then
-            pkg_list="${pkg_list} uwsgi-plugin-python"
-        fi
         install_package ${pkg_list}
     elif is_fedora; then
         # Note httpd comes with mod_proxy_uwsgi and it is loaded by
diff --git a/lib/cinder b/lib/cinder
index f20631b..f6fd095 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -88,15 +88,14 @@
 CINDER_VOLUME_CLEAR=${CINDER_VOLUME_CLEAR:-${CINDER_VOLUME_CLEAR_DEFAULT:-zero}}
 CINDER_VOLUME_CLEAR=$(echo ${CINDER_VOLUME_CLEAR} | tr '[:upper:]' '[:lower:]')
 
-# Centos7 and OpenSUSE switched to using LIO and that's all that's supported,
-# although the tgt bits are in EPEL and OpenSUSE we don't want that for CI
+# Default to lioadm
+CINDER_ISCSI_HELPER=${CINDER_ISCSI_HELPER:-lioadm}
+
+# EL and SUSE should only use lioadm
 if is_fedora || is_suse; then
-    CINDER_ISCSI_HELPER=${CINDER_ISCSI_HELPER:-lioadm}
     if [[ ${CINDER_ISCSI_HELPER} != "lioadm" ]]; then
         die "lioadm is the only valid Cinder target_helper config on this platform"
     fi
-else
-    CINDER_ISCSI_HELPER=${CINDER_ISCSI_HELPER:-tgtadm}
 fi
 
 # For backward compatibility
@@ -281,9 +280,6 @@
                 default_name=$be_name
             fi
             enabled_backends+=$be_name,
-
-            iniset $CINDER_CONF $be_name volume_clear $CINDER_VOLUME_CLEAR
-
         done
         iniset $CINDER_CONF DEFAULT enabled_backends ${enabled_backends%,*}
         if [[ -n "$default_name" ]]; then
@@ -374,12 +370,6 @@
                 "$REGION_NAME" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v3/\$(project_id)s"
 
-            get_or_create_service "cinderv2" "volumev2" "Cinder Volume Service V2"
-            get_or_create_endpoint \
-                "volumev2" \
-                "$REGION_NAME" \
-                "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v2/\$(project_id)s"
-
             get_or_create_service "cinderv3" "volumev3" "Cinder Volume Service V3"
             get_or_create_endpoint \
                 "volumev3" \
@@ -391,12 +381,6 @@
                 "$REGION_NAME" \
                 "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/volume/v3/\$(project_id)s"
 
-            get_or_create_service "cinderv2" "volumev2" "Cinder Volume Service V2"
-            get_or_create_endpoint \
-                "volumev2" \
-                "$REGION_NAME" \
-                "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/volume/v2/\$(project_id)s"
-
             get_or_create_service "cinderv3" "volumev3" "Cinder Volume Service V3"
             get_or_create_endpoint \
                 "volumev3" \
diff --git a/lib/cinder_backends/ceph_iscsi b/lib/cinder_backends/ceph_iscsi
new file mode 100644
index 0000000..94412e0
--- /dev/null
+++ b/lib/cinder_backends/ceph_iscsi
@@ -0,0 +1,56 @@
+#!/bin/bash
+#
+# lib/cinder_backends/ceph_iscsi
+# Configure the ceph_iscsi backend
+
+# Enable with:
+#
+#   CINDER_ENABLED_BACKENDS+=,ceph_iscsi:ceph_iscsi
+#
+# Optional paramteters:
+#   CEPH_ISCSI_API_URL=<url to the rbd-target-api service>
+#
+# Dependencies:
+#
+# - ``functions`` file
+# - ``cinder`` configurations
+
+# configure_ceph_backend_ceph_iscsi - called from configure_cinder()
+
+
+# Save trace setting
+_XTRACE_CINDER_CEPH_ISCSI=$(set +o | grep xtrace)
+set +o xtrace
+
+# Entry Points
+# ------------
+
+# configure_cinder_backend_ceph_iscsi - Set config files, create data dirs, etc
+# configure_cinder_backend_ceph_iscsi $name
+function configure_cinder_backend_ceph_iscsi {
+    local be_name=$1
+
+    CEPH_ISCSI_API_URL=${CEPH_ISCSI_API_URL:-http://$CEPH_ISCSI_API_HOST:$CEPH_ISCSI_API_PORT}
+
+    iniset $CINDER_CONF $be_name volume_backend_name $be_name
+    iniset $CINDER_CONF $be_name volume_driver "cinder.volume.drivers.ceph.rbd_iscsi.RBDISCSIDriver"
+    iniset $CINDER_CONF $be_name rbd_ceph_conf "$CEPH_CONF_FILE"
+    iniset $CINDER_CONF $be_name rbd_pool "$CINDER_CEPH_POOL"
+    iniset $CINDER_CONF $be_name rbd_user "$CINDER_CEPH_USER"
+    iniset $CINDER_CONF $be_name rbd_iscsi_api_user "$CEPH_ISCSI_API_USER"
+    iniset $CINDER_CONF $be_name rbd_iscsi_api_password "$CEPH_ISCSI_API_PASSWORD"
+    iniset $CINDER_CONF $be_name rbd_iscsi_api_url "$CEPH_ISCSI_API_URL"
+    iniset $CINDER_CONF $be_name rbd_iscsi_target_iqn "$CEPH_ISCSI_TARGET_IQN"
+    iniset $CINDER_CONF $be_name rbd_flatten_volume_from_snapshot False
+    iniset $CINDER_CONF $be_name rbd_max_clone_depth 5
+    iniset $CINDER_CONF DEFAULT glance_api_version 2
+
+    pip_install rbd-iscsi-client
+}
+
+# Restore xtrace
+$_XTRACE_CINDER_CEPH_ISCSI
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/lib/cinder_backends/lvm b/lib/cinder_backends/lvm
index 497081c..e03ef14 100644
--- a/lib/cinder_backends/lvm
+++ b/lib/cinder_backends/lvm
@@ -52,7 +52,7 @@
     iniset $CINDER_CONF $be_name volume_group $VOLUME_GROUP_NAME-$be_name
     iniset $CINDER_CONF $be_name target_helper "$CINDER_ISCSI_HELPER"
     iniset $CINDER_CONF $be_name lvm_type "$CINDER_LVM_TYPE"
-
+    iniset $CINDER_CONF $be_name volume_clear "$CINDER_VOLUME_CLEAR"
 }
 
 # init_cinder_backend_lvm - Initialize volume group
diff --git a/lib/neutron_plugins/ml2 b/lib/neutron_plugins/ml2
index ae4b251..e1f868f 100644
--- a/lib/neutron_plugins/ml2
+++ b/lib/neutron_plugins/ml2
@@ -7,15 +7,16 @@
 _XTRACE_NEUTRON_ML2=$(set +o | grep xtrace)
 set +o xtrace
 
-# Default openvswitch L2 agent
-Q_AGENT=${Q_AGENT:-openvswitch}
+# Default OVN L2 agent
+Q_AGENT=${Q_AGENT:-ovn}
 if [ -f $TOP_DIR/lib/neutron_plugins/${Q_AGENT}_agent ]; then
     source $TOP_DIR/lib/neutron_plugins/${Q_AGENT}_agent
 fi
 
 # Enable this to simply and quickly enable tunneling with ML2.
-# Select either 'gre', 'vxlan', or 'gre,vxlan'
-Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-"vxlan"}
+# For ML2/OVS select either 'gre', 'vxlan', or 'gre,vxlan'.
+# For ML2/OVN use 'geneve'.
+Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-"geneve"}
 # This has to be set here since the agent will set this in the config file
 if [[ "$Q_ML2_TENANT_NETWORK_TYPE" == "gre" || "$Q_ML2_TENANT_NETWORK_TYPE" == "vxlan" ]]; then
     Q_TUNNEL_TYPES=$Q_ML2_TENANT_NETWORK_TYPE
@@ -24,7 +25,7 @@
 fi
 
 # List of MechanismDrivers to load
-Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_ML2_PLUGIN_MECHANISM_DRIVERS:-openvswitch,linuxbridge}
+Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_ML2_PLUGIN_MECHANISM_DRIVERS:-ovn}
 # Default GRE TypeDriver options
 Q_ML2_PLUGIN_GRE_TYPE_OPTIONS=${Q_ML2_PLUGIN_GRE_TYPE_OPTIONS:-tunnel_id_ranges=$TENANT_TUNNEL_RANGES}
 # Default VXLAN TypeDriver options
diff --git a/lib/neutron_plugins/ovn_agent b/lib/neutron_plugins/ovn_agent
index 2f6d1ab..4af1340 100644
--- a/lib/neutron_plugins/ovn_agent
+++ b/lib/neutron_plugins/ovn_agent
@@ -21,10 +21,6 @@
 source ${TOP_DIR}/lib/neutron_plugins/ovs_base
 source ${TOP_DIR}/lib/neutron_plugins/openvswitch_agent
 
-# Load devstack ovs base functions
-source $NEUTRON_DIR/devstack/lib/ovs
-
-
 # Defaults
 # --------
 
@@ -88,12 +84,18 @@
 # configure the MTU DHCP option.
 OVN_GENEVE_OVERHEAD=${OVN_GENEVE_OVERHEAD:-38}
 
-# The log level of the OVN databases (north and south)
+# The log level of the OVN databases (north and south).
+# Supported log levels are: off, emer, err, warn, info or dbg.
+# More information about log levels can be found at
+# http://www.openvswitch.org/support/dist-docs/ovs-appctl.8.txt
 OVN_DBS_LOG_LEVEL=${OVN_DBS_LOG_LEVEL:-info}
 
 OVN_META_CONF=$NEUTRON_CONF_DIR/neutron_ovn_metadata_agent.ini
 OVN_META_DATA_HOST=${OVN_META_DATA_HOST:-$(ipv6_unquote $SERVICE_HOST)}
 
+# If True (default) the node will be considered a gateway node.
+ENABLE_CHASSIS_AS_GW=$(trueorfalse True ENABLE_CHASSIS_AS_GW)
+
 export OVSDB_SERVER_LOCAL_HOST=$SERVICE_LOCAL_HOST
 if [[ "$SERVICE_IP_VERSION" == 6 ]]; then
     OVSDB_SERVER_LOCAL_HOST=[$OVSDB_SERVER_LOCAL_HOST]
@@ -171,6 +173,9 @@
 }
 
 function use_new_ovn_repository {
+    if [[ "$OVN_BUILD_FROM_SOURCE" == "False" ]]; then
+        return 0
+    fi
     if [ -z "$is_new_ovn" ]; then
         local ovs_repo_dir=$DEST/$OVS_REPO_NAME
         if [ ! -d $ovs_repo_dir ]; then
@@ -390,6 +395,9 @@
     sudo ln -s $OVS_RUNDIR $OVN_RUNDIR
 
     if [[ "$OVN_BUILD_FROM_SOURCE" == "True" ]]; then
+        # Load devstack ovs base functions
+        source $NEUTRON_DIR/devstack/lib/ovs
+
         # If OVS is already installed, remove it, because we're about to
         # re-install it from source.
         for package in openvswitch openvswitch-switch openvswitch-common; do
@@ -545,6 +553,14 @@
         fi
     fi
 
+    # Erase the pre-set configurations from packages. DevStack will
+    # configure OVS and OVN accordingly for its use.
+    if [[ "$OVN_BUILD_FROM_SOURCE" == "False" ]] && is_fedora; then
+        sudo truncate -s 0 /etc/openvswitch/default.conf
+        sudo truncate -s 0 /etc/sysconfig/openvswitch
+        sudo truncate -s 0 /etc/sysconfig/ovn
+    fi
+
     # Metadata
     if is_service_enabled q-ovn-metadata-agent && is_service_enabled ovn-controller; then
         sudo install -d -o $STACK_USER $NEUTRON_CONF_DIR
diff --git a/lib/tempest b/lib/tempest
index 29a6229..d835c68 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -459,13 +459,6 @@
     iniset $TEMPEST_CONFIG validation network_for_ssh $TEMPEST_SSH_NETWORK_NAME
 
     # Volume
-    # Set the service catalog entry for Tempest to run on. Typically
-    # used to try different Volume API version targets. The tempest
-    # default it to 'volumev3'(v3 APIs endpoint) , so only set this
-    # if you want to change it.
-    if [[ -n "$TEMPEST_VOLUME_TYPE" ]]; then
-        iniset $TEMPEST_CONFIG volume catalog_type $TEMPEST_VOLUME_TYPE
-    fi
     # Only turn on TEMPEST_VOLUME_MANAGE_SNAPSHOT by default for "lvm" backends
     if [[ "$CINDER_ENABLED_BACKENDS" == *"lvm"* ]]; then
         TEMPEST_VOLUME_MANAGE_SNAPSHOT=${TEMPEST_VOLUME_MANAGE_SNAPSHOT:-True}
@@ -489,12 +482,6 @@
     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.
-    # Both "None" means no microversion testing.
-    if [[ "$TEMPEST_VOLUME_TYPE" == "volumev2" ]]; then
-        tempest_volume_min_microversion=None
-        tempest_volume_max_microversion=None
-    fi
     if [ "$tempest_volume_min_microversion" == "None" ]; then
         inicomment $TEMPEST_CONFIG volume min_microversion
     else
diff --git a/stack.sh b/stack.sh
index ca9ecfa..6858ab8 100755
--- a/stack.sh
+++ b/stack.sh
@@ -227,7 +227,7 @@
 
 # Warn users who aren't on an explicitly supported distro, but allow them to
 # override check and attempt installation with ``FORCE=yes ./stack``
-SUPPORTED_DISTROS="bionic|focal|f31|f32|opensuse-15.2|opensuse-tumbleweed|rhel8"
+SUPPORTED_DISTROS="focal|f31|f32|opensuse-15.2|opensuse-tumbleweed|rhel8"
 
 if [[ ! ${DISTRO} =~ $SUPPORTED_DISTROS ]]; then
     echo "WARNING: this script has not been tested on $DISTRO"
@@ -1238,17 +1238,21 @@
 # deployments.  This ensures the keys match across nova and cinder across all
 # hosts.
 FIXED_KEY=${FIXED_KEY:-bae3516cc1c0eb18b05440eba8012a4a880a2ee04d584a9c1579445e675b12defdc716ec}
-if is_service_enabled nova; then
-    iniset $NOVA_CONF key_manager fixed_key "$FIXED_KEY"
-    iniset $NOVA_CPU_CONF key_manager fixed_key "$FIXED_KEY"
-fi
-
 if is_service_enabled cinder; then
     iniset $CINDER_CONF key_manager fixed_key "$FIXED_KEY"
 fi
 
 async_wait configure_neutron_nova
 
+# NOTE(clarkb): This must come after async_wait configure_neutron_nova because
+# configure_neutron_nova modifies $NOVA_CONF and $NOVA_CPU_CONF as well. If
+# we don't wait then these two ini updates race either other and can result
+# in unexpected configs.
+if is_service_enabled nova; then
+    iniset $NOVA_CONF key_manager fixed_key "$FIXED_KEY"
+    iniset $NOVA_CPU_CONF key_manager fixed_key "$FIXED_KEY"
+fi
+
 # Launch the nova-api and wait for it to answer before continuing
 if is_service_enabled n-api; then
     echo_summary "Starting Nova API"
diff --git a/stackrc b/stackrc
index 9630221..0501659 100644
--- a/stackrc
+++ b/stackrc
@@ -72,8 +72,10 @@
     ENABLED_SERVICES+=,g-api
     # Cinder
     ENABLED_SERVICES+=,c-sch,c-api,c-vol
+    # OVN
+    ENABLED_SERVICES+=,ovn-controller,ovn-northd,ovs-vswitchd,ovsdb-server
     # Neutron
-    ENABLED_SERVICES+=,q-svc,q-dhcp,q-meta,q-agt,q-l3
+    ENABLED_SERVICES+=,q-svc,q-ovn-metadata-agent
     # Dashboard
     ENABLED_SERVICES+=,horizon
     # Additional services
@@ -758,8 +760,8 @@
     fi
 done
 
-# 24Gb default volume backing file size
-VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE:-24G}
+# 30Gb default volume backing file size
+VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE:-30G}
 
 # Prefixes for volume and instance names
 VOLUME_NAME_PREFIX=${VOLUME_NAME_PREFIX:-volume-}
diff --git a/tests/test_write_devstack_local_conf_role.sh b/tests/test_write_devstack_local_conf_role.sh
index b2bc0a2..71d8d51 100755
--- a/tests/test_write_devstack_local_conf_role.sh
+++ b/tests/test_write_devstack_local_conf_role.sh
@@ -6,4 +6,4 @@
 source $TOP/functions
 source $TOP/tests/unittest.sh
 
-python ./roles/write-devstack-local-conf/library/test.py
+${PYTHON} $TOP/roles/write-devstack-local-conf/library/test.py
diff --git a/tests/unittest.sh b/tests/unittest.sh
index 3703ece..fced2ab 100644
--- a/tests/unittest.sh
+++ b/tests/unittest.sh
@@ -17,6 +17,8 @@
 PASS=0
 FAILED_FUNCS=""
 
+export PYTHON=$(which python3 2>/dev/null)
+
 # pass a test, printing out MSG
 #  usage: passed message
 function passed {
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 25f7268..1921943 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -59,43 +59,6 @@
     fi
 }
 
-# Ubuntu Repositories
-#--------------------
-# Enable universe for bionic since it is missing when installing from ISO.
-function fixup_ubuntu {
-    if [[ "$DISTRO" != "bionic" ]]; then
-        return
-    fi
-
-    # This pulls in apt-add-repository
-    install_package "software-properties-common"
-
-    # Enable universe
-    sudo add-apt-repository -y universe
-
-    if [[ -f /etc/ci/mirror_info.sh ]] ; then
-        # If we are on a nodepool provided host and it has told us about
-        # where we can find local mirrors then use that mirror.
-        source /etc/ci/mirror_info.sh
-        sudo apt-add-repository -y "deb $NODEPOOL_UCA_MIRROR bionic-updates/ussuri main"
-    else
-        # Enable UCA:ussuri for updated versions of QEMU and libvirt
-        sudo add-apt-repository -y cloud-archive:ussuri
-    fi
-    REPOS_UPDATED=False
-    apt_get_update
-
-    # Since pip10, pip will refuse to uninstall files from packages
-    # that were created with distutils (rather than more modern
-    # setuptools).  This is because it technically doesn't have a
-    # 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 -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
 # ---------------
 
@@ -194,7 +157,6 @@
 
 function fixup_all {
     fixup_keystone
-    fixup_ubuntu
     fixup_fedora
     fixup_suse
 }
diff --git a/tools/mlock_report.py b/tools/mlock_report.py
index b15a0bf..1b081bb 100644
--- a/tools/mlock_report.py
+++ b/tools/mlock_report.py
@@ -24,17 +24,19 @@
         # iterate over the /proc/%pid/status files manually
         try:
             s = open("%s/%d/status" % (psutil.PROCFS_PATH, proc.pid), 'r')
-        except EnvironmentError:
+            with s:
+                for line in s:
+                    result = LCK_SUMMARY_REGEX.search(line)
+                    if result:
+                        locked = int(result.group('locked'))
+                        if locked:
+                            mlock_users.append({'name': proc.name(),
+                                                'pid': proc.pid,
+                                                'locked': locked})
+        except OSError:
+            # pids can disappear, we're ok with that
             continue
-        with s:
-            for line in s:
-                result = LCK_SUMMARY_REGEX.search(line)
-                if result:
-                    locked = int(result.group('locked'))
-                    if locked:
-                        mlock_users.append({'name': proc.name(),
-                                            'pid': proc.pid,
-                                            'locked': locked})
+
 
     # produce a single line log message with per process mlock stats
     if mlock_users:
diff --git a/tox.ini b/tox.ini
index ed28636..ec764ab 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-minversion = 1.6
+minversion = 3.18.0
 skipsdist = True
 envlist = bashate
 
@@ -13,7 +13,7 @@
 # modified bashate tree
 deps =
    {env:BASHATE_INSTALL_PATH:bashate==2.0.0}
-whitelist_externals = bash
+allowlist_externals = bash
 commands = bash -c "find {toxinidir}             \
          -not \( -type d -name .?\* -prune \)    \
          -not \( -type d -name doc -prune \)     \
@@ -34,8 +34,10 @@
          -print0 | xargs -0 bashate -v -iE006 -eE005,E042"
 
 [testenv:docs]
-deps = -r{toxinidir}/doc/requirements.txt
-whitelist_externals = bash
+deps =
+  -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+  -r{toxinidir}/doc/requirements.txt
+allowlist_externals = bash
 setenv =
   TOP_DIR={toxinidir}
 commands =
@@ -43,7 +45,7 @@
 
 [testenv:pdf-docs]
 deps = {[testenv:docs]deps}
-whitelist_externals =
+allowlist_externals =
    make
 commands =
    sphinx-build -W -b latex doc/source doc/build/pdf