Merge "Fix $LOGDIR owner to be stack.stack"
diff --git a/.zuul.yaml b/.zuul.yaml
index 948a9af..356acec 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -79,6 +79,16 @@
           - controller
 
 - nodeset:
+    name: devstack-single-node-debian-bookworm
+    nodes:
+      - name: controller
+        label: debian-bookworm
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
     name: devstack-single-node-debian-bullseye
     nodes:
       - name: controller
@@ -420,6 +430,7 @@
         /var/log/mysql: logs
         /var/log/libvirt: logs
         /etc/libvirt: logs
+        /etc/lvm: logs
         /etc/sudoers: logs
         /etc/sudoers.d: logs
         '{{ stage_dir }}/iptables.txt': logs
@@ -700,6 +711,19 @@
     voting: false
 
 - job:
+    name: devstack-platform-debian-bookworm
+    parent: tempest-full-py3
+    description: Debian Bookworm platform test
+    nodeset: devstack-single-node-debian-bookworm
+    timeout: 9000
+    voting: false
+    vars:
+      configure_swap_size: 4096
+      devstack_localrc:
+        # TODO(frickler): drop this once wheel build is fixed
+        MYSQL_GATHER_PERFORMANCE: false
+
+- job:
     name: devstack-platform-debian-bullseye
     parent: tempest-full-py3
     description: Debian Bullseye platform test
@@ -707,6 +731,9 @@
     timeout: 9000
     vars:
       configure_swap_size: 4096
+      devstack_localrc:
+        # TODO(frickler): drop this once wheel build is fixed
+        MYSQL_GATHER_PERFORMANCE: false
 
 - job:
     name: devstack-platform-rocky-blue-onyx
@@ -718,13 +745,6 @@
       configure_swap_size: 4096
 
 - job:
-    name: devstack-platform-ubuntu-focal
-    parent: tempest-full-py3
-    description: Ubuntu 20.04 LTS (focal) platform test
-    nodeset: openstack-single-node-focal
-    timeout: 9000
-
-- job:
     name: devstack-platform-ubuntu-jammy-ovn-source
     parent: devstack-platform-ubuntu-jammy
     description: Ubuntu 22.04 LTS (jammy) platform test (OVN from source)
@@ -916,9 +936,9 @@
         - devstack-ipv6
         - devstack-enforce-scope
         - devstack-platform-centos-9-stream
+        - devstack-platform-debian-bookworm
         - devstack-platform-debian-bullseye
         - devstack-platform-rocky-blue-onyx
-        - devstack-platform-ubuntu-focal
         - devstack-platform-ubuntu-jammy-ovn-source
         - devstack-platform-ubuntu-jammy-ovs
         - devstack-platform-openEuler-22.03-ovn-source
@@ -967,7 +987,6 @@
         - devstack
         - devstack-ipv6
         - devstack-platform-debian-bullseye
-        - devstack-platform-ubuntu-focal
         - devstack-platform-rocky-blue-onyx
         - devstack-enforce-scope
         - devstack-multinode
diff --git a/files/apache-horizon.template b/files/apache-horizon.template
index efcfc03..da7a7d2 100644
--- a/files/apache-horizon.template
+++ b/files/apache-horizon.template
@@ -39,4 +39,5 @@
     CustomLog /var/log/%APACHE_NAME%/horizon_access.log combined
 </VirtualHost>
 
+%WSGIPYTHONHOME%
 WSGISocketPrefix /var/run/%APACHE_NAME%
diff --git a/functions-common b/functions-common
index 5e1aa34..f752271 100644
--- a/functions-common
+++ b/functions-common
@@ -1522,6 +1522,7 @@
     mkdir -p $SYSTEMD_DIR
 
     iniset -sudo $unitfile "Unit" "Description" "Devstack $service"
+    iniset -sudo $unitfile "Service" "Environment" "\"PATH=$PATH\""
     iniset -sudo $unitfile "Service" "User" "$user"
     iniset -sudo $unitfile "Service" "ExecStart" "$command"
     iniset -sudo $unitfile "Service" "KillMode" "process"
@@ -1549,6 +1550,7 @@
     mkdir -p $SYSTEMD_DIR
 
     iniset -sudo $unitfile "Unit" "Description" "Devstack $service"
+    iniset -sudo $unitfile "Service" "Environment" "\"PATH=$PATH\""
     iniset -sudo $unitfile "Service" "SyslogIdentifier" "$service"
     iniset -sudo $unitfile "Service" "User" "$user"
     iniset -sudo $unitfile "Service" "ExecStart" "$command"
@@ -1614,6 +1616,9 @@
     fi
     local env_vars="$5"
     if [[ "$command" =~ "uwsgi" ]] ; then
+        if [[ "$GLOBAL_VENV" == "True" ]] ; then
+            cmd="$cmd --venv $DEVSTACK_VENV"
+        fi
         write_uwsgi_user_unit_file $systemd_service "$cmd" "$group" "$user" "$env_vars"
     else
         write_user_unit_file $systemd_service "$cmd" "$group" "$user" "$env_vars"
diff --git a/inc/python b/inc/python
index a24f4e9..cc6e01f 100644
--- a/inc/python
+++ b/inc/python
@@ -32,6 +32,23 @@
 # Python Functions
 # ================
 
+# Setup the global devstack virtualenvs and the associated environment
+# updates.
+function setup_devstack_virtualenv {
+    # We run devstack out of a global virtualenv.
+    if [[ ! -d $DEVSTACK_VENV ]] ; then
+        # Using system site packages to enable nova to use libguestfs.
+        # This package is currently installed via the distro and not
+        # available on pypi.
+        python$PYTHON3_VERSION -m venv --system-site-packages $DEVSTACK_VENV
+        pip_install -U pip
+    fi
+    if [[ ":$PATH:" != *":$DEVSTACK_VENV/bin:"* ]] ; then
+        export PATH="$DEVSTACK_VENV/bin:$PATH"
+        export PYTHON="$DEVSTACK_VENV/bin/python3"
+    fi
+}
+
 # Get the path to the pip command.
 # get_pip_command
 function get_pip_command {
@@ -60,8 +77,11 @@
     fi
     $xtrace
 
-    local PYTHON_PATH=/usr/local/bin
-    echo $PYTHON_PATH
+    if [[ "$GLOBAL_VENV" == "True" ]] ; then
+        echo "$DEVSTACK_VENV/bin"
+    else
+        echo "/usr/local/bin"
+    fi
 }
 
 # Wrapper for ``pip install`` that only installs versions of libraries
@@ -166,6 +186,14 @@
     if [[ -n ${PIP_VIRTUAL_ENV:=} && -d ${PIP_VIRTUAL_ENV} ]]; then
         local cmd_pip=$PIP_VIRTUAL_ENV/bin/pip
         local sudo_pip="env"
+    elif [[ "${GLOBAL_VENV}" == "True" && -d ${DEVSTACK_VENV} ]] ; then
+        # We have to check that the DEVSTACK_VENV exists because early
+        # devstack boostrapping needs to operate in a system context
+        # too bootstrap pip. Once pip is bootstrapped we create the
+        # global venv and can start to use it.
+        local cmd_pip=$DEVSTACK_VENV/bin/pip
+        local sudo_pip="env"
+        echo "Using python $PYTHON3_VERSION to install $package_dir"
     else
         local cmd_pip="python$PYTHON3_VERSION -m pip"
         # See
@@ -439,7 +467,7 @@
 
     pip_install $flags "$project_dir$extras"
     # ensure that further actions can do things like setup.py sdist
-    if [[ "$flags" == "-e" ]]; then
+    if [[ "$flags" == "-e" && "$GLOBAL_VENV" == "False" ]]; then
         safe_chown -R $STACK_USER $1/*.egg-info
     fi
 }
diff --git a/inc/rootwrap b/inc/rootwrap
index 2a6e4b6..4c65440 100644
--- a/inc/rootwrap
+++ b/inc/rootwrap
@@ -60,6 +60,11 @@
     sudo install -o root -g root -m 644 $rootwrap_conf_src_dir/rootwrap.conf /etc/${project}/rootwrap.conf
     sudo sed -e "s:^filters_path=.*$:filters_path=/etc/${project}/rootwrap.d:" -i /etc/${project}/rootwrap.conf
 
+    # Rely on $PATH set by devstack to determine what is safe to execute
+    # by rootwrap rather than use explicit whitelist of paths in
+    # rootwrap.conf
+    sudo sed -e 's/^exec_dirs=.*/#&/' -i /etc/${project}/rootwrap.conf
+
     # Set up the rootwrap sudoers
     local tempfile
     tempfile=$(mktemp)
diff --git a/lib/apache b/lib/apache
index 76eae9c..cf7215b 100644
--- a/lib/apache
+++ b/lib/apache
@@ -290,7 +290,7 @@
         apache_conf=$(apache_site_config_for $name)
         iniset "$file" uwsgi socket "$socket"
         iniset "$file" uwsgi chmod-socket 666
-        echo "ProxyPass \"${url}\" \"unix:${socket}|uwsgi://uwsgi-uds-${name}\" retry=0 " | sudo tee -a $apache_conf
+        echo "ProxyPass \"${url}\" \"unix:${socket}|uwsgi://uwsgi-uds-${name}\" retry=0 acquire=1 " | sudo tee -a $apache_conf
         enable_apache_site $name
         restart_apache_server
     fi
@@ -351,7 +351,7 @@
     apache_conf=$(apache_site_config_for $name)
     echo "KeepAlive Off" | sudo tee $apache_conf
     echo "SetEnv proxy-sendchunked 1" | sudo tee -a $apache_conf
-    echo "ProxyPass \"${url}\" \"http://$APACHE_LOCAL_HOST:$port\" retry=0 " | sudo tee -a $apache_conf
+    echo "ProxyPass \"${url}\" \"http://$APACHE_LOCAL_HOST:$port\" retry=0 acquire=1 " | sudo tee -a $apache_conf
     enable_apache_site $name
     restart_apache_server
 }
@@ -370,7 +370,7 @@
 
     echo "KeepAlive Off" | sudo tee $apache_conf
     echo "SetEnv proxy-sendchunked 1" | sudo tee -a $apache_conf
-    echo "ProxyPass \"${loc}\" \"$url\" retry=0 " | sudo tee -a $apache_conf
+    echo "ProxyPass \"${loc}\" \"$url\" retry=0 acquire=1 " | sudo tee -a $apache_conf
     enable_apache_site $name
     restart_apache_server
 }
diff --git a/lib/cinder b/lib/cinder
index f8682d5..dad1798 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -414,7 +414,9 @@
     if [[ ! -z "$CINDER_COORDINATION_URL" ]]; then
         iniset $CINDER_CONF coordination backend_url "$CINDER_COORDINATION_URL"
     elif is_service_enabled etcd3; then
-        iniset $CINDER_CONF coordination backend_url "etcd3+http://${SERVICE_HOST}:$ETCD_PORT"
+        # NOTE(jan.gutter): api_version can revert to default once tooz is
+        # updated with the etcd v3.4 defaults
+        iniset $CINDER_CONF coordination backend_url "etcd3+http://${SERVICE_HOST}:$ETCD_PORT?api_version=v3"
     fi
 
     if [[ "$CINDER_ENFORCE_SCOPE" == True || "$ENFORCE_SCOPE" == True ]] ; then
diff --git a/lib/databases/mysql b/lib/databases/mysql
index 27d1ec6..e069e12 100644
--- a/lib/databases/mysql
+++ b/lib/databases/mysql
@@ -20,7 +20,7 @@
     MYSQL_SERVICE_NAME=mysql
     if is_fedora && ! is_oraclelinux; then
         MYSQL_SERVICE_NAME=mariadb
-    elif [[ "$DISTRO" == "bullseye" ]]; then
+    elif [[ "$DISTRO" =~ bookworm|bullseye ]]; then
         MYSQL_SERVICE_NAME=mariadb
     fi
 fi
@@ -122,7 +122,8 @@
     # In mariadb e.g. on Ubuntu socket plugin is used for authentication
     # as root so it works only as sudo. To restore old "mysql like" behaviour,
     # we need to change auth plugin for root user
-    if is_ubuntu && [[ "$DISTRO" != "bullseye" ]] && [ "$MYSQL_SERVICE_NAME" == "mariadb" ]; then
+    # TODO(frickler): simplify this logic
+    if is_ubuntu && [[ ! "$DISTRO" =~ bookworm|bullseye ]] && [ "$MYSQL_SERVICE_NAME" == "mariadb" ]; then
         if [[ "$DISTRO" == "jammy" ]]; then
             # For Ubuntu 22.04 (jammy) we follow the model outlined in
             # https://mariadb.org/authentication-in-mariadb-10-4/
diff --git a/lib/glance b/lib/glance
index 430d94d..3cf8230 100644
--- a/lib/glance
+++ b/lib/glance
@@ -47,6 +47,9 @@
 # from CINDER_ENABLED_BACKENDS
 GLANCE_CINDER_DEFAULT_BACKEND=${GLANCE_CINDER_DEFAULT_BACKEND:-lvmdriver-1}
 GLANCE_STORE_ROOTWRAP_BASE_DIR=/usr/local/etc/glance
+if [[ "$GLOBAL_VENV" == "True" ]] ; then
+    GLANCE_STORE_ROOTWRAP_BASE_DIR=${DEVSTACK_VENV}/etc/glance
+fi
 # When Cinder is used as a glance store, you can optionally configure cinder to
 # optimize bootable volume creation by allowing volumes to be cloned directly
 # in the backend instead of transferring data via Glance.  To use this feature,
@@ -581,9 +584,10 @@
     write_uwsgi_user_unit_file devstack@g-api-r.service "$(which uwsgi) \
                                --procname-prefix \
                                glance-api-remote \
-                               --ini $glance_remote_uwsgi" \
+                               --ini $glance_remote_uwsgi \
+                               --venv $DEVSTACK_VENV" \
                                "" "$STACK_USER"
-    iniset -sudo ${SYSTEMD_DIR}/devstack@g-api-r.service \
+    iniadd -sudo ${SYSTEMD_DIR}/devstack@g-api-r.service \
            "Service" "Environment" \
            "OS_GLANCE_CONFIG_DIR=$glance_remote_conf_dir"
 
diff --git a/lib/horizon b/lib/horizon
index f76f9e5..611329d 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -115,6 +115,11 @@
     local horizon_conf
     horizon_conf=$(apache_site_config_for horizon)
 
+    local wsgi_venv_config=""
+    if [[ "$GLOBAL_VENV" == "True" ]] ; then
+        wsgi_venv_config="WSGIPythonHome $DEVSTACK_VENV"
+    fi
+
     # Configure apache to run horizon
     # Set up the django horizon application to serve via apache/wsgi
     sudo sh -c "sed -e \"
@@ -124,6 +129,7 @@
         s,%APACHE_NAME%,$APACHE_NAME,g;
         s,%DEST%,$DEST,g;
         s,%WEBROOT%,$HORIZON_APACHE_ROOT,g;
+        s,%WSGIPYTHONHOME%,$wsgi_venv_config,g;
     \" $FILES/apache-horizon.template >$horizon_conf"
 
     if is_ubuntu; then
diff --git a/lib/lvm b/lib/lvm
index 57d2cd4..b7e84d9 100644
--- a/lib/lvm
+++ b/lib/lvm
@@ -137,15 +137,17 @@
     # Start with a clean volume group
     _create_lvm_volume_group $vg $size
 
-    # Remove iscsi targets
-    if [ "$CINDER_TARGET_HELPER" = "lioadm" ]; then
-        sudo cinder-rtstool get-targets | sudo xargs -rn 1 cinder-rtstool delete
-    elif [ "$CINDER_TARGET_HELPER" = "tgtadm" ]; then
-        sudo tgtadm --op show --mode target | awk '/Target/ {print $3}' | sudo xargs -r -n1 tgt-admin --delete
-    elif [ "$CINDER_TARGET_HELPER" = "nvmet" ]; then
-        # If we don't disconnect everything vgremove will block
-        sudo nvme disconnect-all
-        sudo nvmetcli clear
+    if is_service_enabled cinder; then
+        # Remove iscsi targets
+        if [ "$CINDER_TARGET_HELPER" = "lioadm" ]; then
+            sudo cinder-rtstool get-targets | sudo xargs -rn 1 cinder-rtstool delete
+        elif [ "$CINDER_TARGET_HELPER" = "tgtadm" ]; then
+            sudo tgtadm --op show --mode target | awk '/Target/ {print $3}' | sudo xargs -r -n1 tgt-admin --delete
+        elif [ "$CINDER_TARGET_HELPER" = "nvmet" ]; then
+            # If we don't disconnect everything vgremove will block
+            sudo nvme disconnect-all
+            sudo nvmetcli clear
+        fi
     fi
     _clean_lvm_volume_group $vg
 }
@@ -198,7 +200,7 @@
     filter_string=$filter_string$filter_suffix
 
     clean_lvm_filter
-    sudo sed -i "/# global_filter = \[*\]/a\    $global_filter$filter_string" /etc/lvm/lvm.conf
+    sudo sed -i "/# global_filter = \[.*\]/a\        $filter_string" /etc/lvm/lvm.conf
     echo_summary "set lvm.conf device global_filter to: $filter_string"
 }
 
diff --git a/lib/neutron b/lib/neutron
index a6de722..5407f8a 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -570,8 +570,15 @@
 # Start running OVN processes
 function start_ovn_services {
     if [[ $Q_AGENT == "ovn" ]]; then
-        init_ovn
-        start_ovn
+        if [ "$VIRT_DRIVER" != 'ironic' ]; then
+            # NOTE(TheJulia): Ironic's devstack plugin needs to perform
+            # additional networking configuration to setup a working test
+            # environment with test virtual machines to emulate baremetal,
+            # which requires OVN to be up and running earlier to complete
+            # that base configuration.
+            init_ovn
+            start_ovn
+        fi
         if [[ "$OVN_L3_CREATE_PUBLIC_NETWORK" == "True" ]]; then
             if [[ "$NEUTRON_CREATE_INITIAL_NETWORKS" != "True" ]]; then
                 echo "OVN_L3_CREATE_PUBLIC_NETWORK=True is being ignored "
@@ -1075,7 +1082,10 @@
         sudo install -o root -g root -m 644 $NEUTRON_DIR/etc/rootwrap.conf $Q_RR_CONF_FILE
     fi
     sudo sed -e "s:^filters_path=.*$:filters_path=$Q_CONF_ROOTWRAP_D:" -i $Q_RR_CONF_FILE
-    sudo sed -e 's:^exec_dirs=\(.*\)$:exec_dirs=\1,/usr/local/bin:' -i $Q_RR_CONF_FILE
+    # Rely on $PATH set by devstack to determine what is safe to execute
+    # by rootwrap rather than use explicit whitelist of paths in
+    # rootwrap.conf
+    sudo sed -e 's/^exec_dirs=.*/#&/' -i $Q_RR_CONF_FILE
 
     # Specify ``rootwrap.conf`` as first parameter to neutron-rootwrap
     ROOTWRAP_SUDOER_CMD="$NEUTRON_ROOTWRAP $Q_RR_CONF_FILE *"
diff --git a/lib/tls b/lib/tls
index a1e162d..0a598e1 100644
--- a/lib/tls
+++ b/lib/tls
@@ -364,8 +364,11 @@
 function fix_system_ca_bundle_path {
     if is_service_enabled tls-proxy; then
         local capath
-        capath=$(python3 -c $'try:\n from requests import certs\n print (certs.where())\nexcept ImportError: pass')
-
+        if [[ "$GLOBAL_VENV" == "True" ]] ; then
+            capath=$($DEVSTACK_VENV/bin/python3 -c $'try:\n from requests import certs\n print (certs.where())\nexcept ImportError: pass')
+        else
+            capath=$(python3 -c $'try:\n from requests import certs\n print (certs.where())\nexcept ImportError: pass')
+        fi
         if [[ ! $capath == "" && ! $capath =~ ^/etc/.* && ! -L $capath ]]; then
             if is_fedora; then
                 sudo rm -f $capath
@@ -541,9 +544,11 @@
 
     # Avoid races (at the cost of performance) to re-use a pooled connection
     # where the connection is closed (bug 1807518).
+    # Set acquire=1 to disable waiting for connection pool members so that
+    # we can determine when apache is overloaded (returns 503).
     SetEnv proxy-initial-not-pooled
     <Location />
-        ProxyPass http://$b_host:$b_port/ retry=0 nocanon
+        ProxyPass http://$b_host:$b_port/ retry=0 nocanon acquire=1
         ProxyPassReverse http://$b_host:$b_port/
     </Location>
     ErrorLog $APACHE_LOG_DIR/tls-proxy_error.log
diff --git a/stack.sh b/stack.sh
index e0caafa..8f003c5 100755
--- a/stack.sh
+++ b/stack.sh
@@ -1,5 +1,6 @@
 #!/usr/bin/env bash
 
+
 # ``stack.sh`` is an opinionated OpenStack developer installation.  It
 # installs and configures various combinations of **Cinder**, **Glance**,
 # **Horizon**, **Keystone**, **Nova**, **Neutron**, and **Swift**
@@ -229,7 +230,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="bullseye|focal|jammy|rhel8|rhel9|openEuler-22.03"
+SUPPORTED_DISTROS="bookworm|bullseye|jammy|rhel8|rhel9|openEuler-22.03"
 
 if [[ ! ${DISTRO} =~ $SUPPORTED_DISTROS ]]; then
     echo "WARNING: this script has not been tested on $DISTRO"
@@ -826,6 +827,19 @@
 source $TOP_DIR/tools/fixup_stuff.sh
 fixup_all
 
+if [[ "$GLOBAL_VENV" == "True" ]] ; then
+    # TODO(frickler): find a better solution for this
+    sudo ln -sf /opt/stack/data/venv/bin/cinder-rtstool /usr/local/bin
+    sudo ln -sf /opt/stack/data/venv/bin/glance /usr/local/bin
+    sudo ln -sf /opt/stack/data/venv/bin/nova-manage /usr/local/bin
+    sudo ln -sf /opt/stack/data/venv/bin/openstack /usr/local/bin
+    sudo ln -sf /opt/stack/data/venv/bin/privsep-helper /usr/local/bin
+    sudo ln -sf /opt/stack/data/venv/bin/rally /usr/local/bin
+    sudo ln -sf /opt/stack/data/venv/bin/tox /usr/local/bin
+
+    setup_devstack_virtualenv
+fi
+
 # Install subunit for the subunit output stream
 pip_install -U os-testr
 
diff --git a/stackrc b/stackrc
index dcc0ce4..ff30d37 100644
--- a/stackrc
+++ b/stackrc
@@ -183,6 +183,23 @@
 # each services ${SERVICE}_ENFORCE_SCOPE variables
 ENFORCE_SCOPE=$(trueorfalse False ENFORCE_SCOPE)
 
+# Devstack supports the use of a global virtualenv. These variables enable
+# and disable this functionality as well as set the path to the virtualenv.
+# Note that the DATA_DIR is selected because grenade testing uses a shared
+# DATA_DIR but different DEST dirs and we don't want two sets of venvs,
+# instead we want one global set.
+DEVSTACK_VENV=${DEVSTACK_VENV:-$DATA_DIR/venv}
+
+# NOTE(kopecmartin): remove this once this is fixed
+# https://bugs.launchpad.net/devstack/+bug/2031639
+# This couldn't go to fixup_stuff as that's called after projects
+# (e.g. certain paths) are set taking GLOBAL_VENV into account
+if [[ "$os_VENDOR" =~ (CentOSStream|Rocky) ]]; then
+    GLOBAL_VENV=$(trueorfalse False GLOBAL_VENV)
+else
+    GLOBAL_VENV=$(trueorfalse True GLOBAL_VENV)
+fi
+
 # Enable use of Python virtual environments.  Individual project use of
 # venvs are controlled by the PROJECT_VENV array; every project with
 # an entry in the array will be installed into the named venv.
@@ -720,11 +737,11 @@
 EXTRA_CACHE_URLS=""
 
 # etcd3 defaults
-ETCD_VERSION=${ETCD_VERSION:-v3.3.12}
-ETCD_SHA256_AMD64=${ETCD_SHA256_AMD64:-"dc5d82df095dae0a2970e4d870b6929590689dd707ae3d33e7b86da0f7f211b6"}
-ETCD_SHA256_ARM64=${ETCD_SHA256_ARM64:-"170b848ac1a071fe7d495d404a868a2c0090750b2944f8a260ef1c6125b2b4f4"}
-ETCD_SHA256_PPC64=${ETCD_SHA256_PPC64:-"77f807b1b51abbf51e020bb05bdb8ce088cb58260fcd22749ea32eee710463d3"}
-# etcd v3.2.x doesn't have anything for s390x
+ETCD_VERSION=${ETCD_VERSION:-v3.4.27}
+ETCD_SHA256_AMD64=${ETCD_SHA256_AMD64:-"a32d21e006252dbc3405b0645ba8468021ed41376974b573285927bf39b39eb9"}
+ETCD_SHA256_ARM64=${ETCD_SHA256_ARM64:-"ed7e257c225b9b9545fac22246b97f4074a4b5109676e92dbaebfb9315b69cc0"}
+ETCD_SHA256_PPC64=${ETCD_SHA256_PPC64:-"eb8825e0bc2cbaf9e55947f5ee373ebc9ca43b6a2ea5ced3b992c81855fff37e"}
+# etcd v3.2.x and later doesn't have anything for s390x
 ETCD_SHA256_S390X=${ETCD_SHA256_S390X:-""}
 # Make sure etcd3 downloads the correct architecture
 if is_arch "x86_64"; then
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 80a83bb..faea44f 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -94,11 +94,6 @@
     if [[ $os_VENDOR == "CentOSStream" && $os_RELEASE -eq 8 ]]; then
         sudo sysctl -w net.ipv4.ping_group_range='0 2147483647'
     fi
-    # TODO(ykarel): Workaround for systemd issue, remove once fix is
-    # included in systemd rpm https://bugs.launchpad.net/devstack/+bug/2029335
-    if [[ $os_VENDOR == "CentOSStream" && $os_RELEASE -eq 9 ]]; then
-        echo 'LIBVIRTD_ARGS=""' | sudo tee /etc/sysconfig/libvirtd
-    fi
 }
 
 function fixup_ovn_centos {
diff --git a/tools/install_prereqs.sh b/tools/install_prereqs.sh
index f2d57c8..bb470b2 100755
--- a/tools/install_prereqs.sh
+++ b/tools/install_prereqs.sh
@@ -79,6 +79,8 @@
     fi
 fi
 
+# TODO(clarkb) remove these once we are switched to global venv by default
+export PYTHON=$(which python${PYTHON3_VERSION} 2>/dev/null || which python3 2>/dev/null)
 
 # Mark end of run
 # ---------------
diff --git a/tools/memory_tracker.sh b/tools/memory_tracker.sh
index 6c36534..2f404c2 100755
--- a/tools/memory_tracker.sh
+++ b/tools/memory_tracker.sh
@@ -14,7 +14,12 @@
 
 set -o errexit
 
-PYTHON=${PYTHON:-python3}
+# TODO(frickler): make this use stackrc variables
+if [ -x /opt/stack/data/venv/bin/python ]; then
+    PYTHON=/opt/stack/data/venv/bin/python
+else
+    PYTHON=${PYTHON:-python3}
+fi
 
 # time to sleep between checks
 SLEEP_TIME=20